BUUCTF-reverse2 记录一次来自BUUCTF平台reverse系列进入平台点击该题目首先下载题目中给的压缩包并打开然后解压拖到IDA中寻找main函数,发现有类似的flag值但结果不是此flag值于是按f5查看先做个分析一、变量定义部分int stat_loc; // [rsp4h] [rbp-3Ch] BYREF int i; // [rsp8h] [rbp-38h] __pid_t pid; // [rspCh] [rbp-34h] char s2[24]; // [rsp10h] [rbp-30h] BYREF unsigned __int64 v8; // [rsp28h] [rbp-18h]stat_loc用于存储子进程的退出状态BYREF表示该变量会按引用传递给waitpid函数。i循环计数器用于遍历flag字符串的每个字符。7天pid存储fork()函数返回的进程 IDPID__pid_t是进程 ID 的专用类型。s2[24]存储用户输入的 flag数组长度 24 是为了预留足够空间后续scanf限制最多读 20 个字符。v8栈保护相关变量栈金丝雀 / Stack Canary__readfsqword(0x28u)是读取操作系统fs段的栈保护值防止栈溢出攻击属于编译器自动插入的安全代码。二.栈保护初始化v8 __readfsqword(0x28u);读取fs段偏移0x28处的栈保护值并赋值给v8函数结束时会校验该值是否被篡改以此检测栈溢出。三. 创建子进程pid fork();fork()是 Linux/Unix 系统调用作用是创建一个新的子进程。核心特性调用 1 次返回 2 次1.父进程中返回子进程的 PID非 0 值2.子进程中返回0。四. 父进程分支逻辑if ( pid ) { waitpid(pid, stat_loc, 0); }因为父进程中pid是非 0 值所以进入该分支。waitpid(pid, stat_loc, 0)阻塞父进程等待pid对应的子进程执行完毕stat_loc会接收子进程的退出状态比如正常退出 / 异常终止。五. 子进程分支逻辑else { for ( i 0; i strlen(flag); i ) { if ( flag[i] 105 || flag[i] 114 ) flag[i] 49; } }子进程中pid0进入该分支核心是修改flag字符串1.strlen(flag)计算原始flag字符串的长度不包含结束符\02.循环范围i0到istrlen(flag)遍历flag的每个字符包含结束符\03.ASCII 码对应关系105 i114 r49 14.逻辑遍历flag的每个字符只要是字符i或r就替换成1。六. 核心漏洞点子进程是父进程的内存副本写时复制机制子进程对flag的修改仅作用于自身的内存空间不会影响父进程的flag变量。也就是说子进程的修改完全是 “无效操作”。七. 读取用户输入并验证 flagprintf(input the flag:); __isoc99_scanf(%20s, s2); if ( !strcmp(flag, s2) ) return puts(this is the right flag!); else return puts(wrong flag!);printf(input the flag:)提示用户输入 flag__isoc99_scanf(%20s, s2)读取用户输入的字符串%20s限制最多读 20 个字符防止数组越界strcmp(flag, s2)比较flag和用户输入的s21.相等时返回 0!0为真输出 “this is the right flag!”2.不相等时返回非 0!非0为假输出 “wrong flag!”由于子进程的修改不影响父进程这里对比的是原始的flag而非修改后的版本。答案为flag{hack1ng_fo1_fun}