学 pwn 到现在快三个月了,在 BUU 上做了前五页共160题,不能把刷过的题的技巧都给忘了,再做一遍还不是得心应手的题,同时堆题很灵活,要多总结才能举一反三。 现在写下刚开始学的时候栈和格式化字符串值得注意的点,堆的要单独一篇发 1.为什么 32 位的ROP 是 p32(system) + p32(0) + p32(binsh) 首先要了解 C 语言函数调用栈的过程 如 main 函数中进入 backdoor 函数执行 system('/bin/sh'); main 函数 backdoor 函数 call 指令会把当前 eip 的值压入栈中,也就是 ret ,然后进行 backdoor 参数,再把 ebp 压栈,就成了我们平时在 IDA 中看到的栈布局 同样的,执行到 call _system 指令时,需要将当前 eip 的值压入栈中,也就是 ret ,payload 中的 p32(system) + p32(0) + p32(binsh) 的 p32(0) 就是为了充当 ret ,也就是执行完 system 的返回地址 当然,也可以直接利用 call _system 构造 payload ,如 p32(call_system) + p32(binsh) ,这样就不需要我们去伪造 ret 了,因为 call _system 指令会自动将当前 eip 的值压入栈作为 ret 2.为什么 p64(pop_rdi_ret) + p64(binsh) + p64(system) 需要用到 pop_rdi_ret 指令 这里直接用gdb调试下,执行 pop rdi 前 执行 pop rdi 后,可以看到 /bin/sh 被放入了 rdi 寄存器 执行 ret 前,可以看到这里的 esp 执行了 system@plt 执行 ret 后 最后可以看到成功进入了 system 函数 由上可见,pop rdi 是用来控制 system 的参数为 /bin/sh 的, ret 则是帮助我们继续执行 system 函数 3.格式化字符串中 addr%n$n 和 addr%n$s 为什么可以实现任意地址写和任意地址读。 printf 函数执行的时候调用的参数实际上是存在着该参数数据的地址,而存在着该参数数据的地址也在栈上,并且是与 printf 的参数有一定偏移,因此我们就可以确定该参数数据的地址是 pintf 函数的第几个参数,这也是我们要调试出偏移的目的。并且因为参数数据是可控的,我们可以写入参数数据为某一内存地址,利用 %n$n/s 来将参数数据作为地址调用,那么就可以实现任意地址写和任意地址读。 4.为什么要栈对齐和为什么 ret 指令能对齐栈 ubuntu18及以上在调用system函数的时候会先进行一个检测,如果此时的栈没有16字节对齐的话,就会强行把程序crash掉,所以需要栈对齐。(https://www.cnblogs.com/Rookle/p/12871878.html) 以64位程序为例,ret 指令是将 rsp 执行的八个字节送入 rip ,然后 rsp + 8 所以如果 payload 是 b'a'*offset + p64(ret) + p64(pop_rdi_ret) + p64(binsh) + p64(system) 这里的 ret 相当于 ret pop_rdi_ret (看第二个问题),也就是继续执行 pop_rdi_ret ,和正常执行
b'a'*offset + p64(pop_rdi_ret) + p64(binsh) + p64(system)没什么区别,但是 esp 被抬高了 0x8 ,这样执行到 system 时 esp 就是 0x10 对齐的了 5.栈迁移为什么要用到 leave 和 ret 指令(以 32 位程序为例) leave => mov esp,ebp ; pop ebp ret => pop eip 而我们能够通过栈溢出控制 ebp 的值间接控制 esp 的值,再通过 ret 指令控制 eip 劫持程序执行 这里看下 leave 指令的执行 leave 指令执行前 执行 leave 后 相当于执行了 mov esp,ebp ; pop ebp mov esp,ebp 将 esp 劫持到了我们输入数据的开头, pop ebp 则是将我们数据开头的前四个字节作为 ebp 的值,也就是 aaaa 接下来是 ret 指令,将 eip 劫持为 system@plt 可以看到执行后程序进行了 system 函数,成功攻击 6.关于在 cmcc_simplerop 这一题中,当 payload 为
payload = b'a'*0x20 + p32(eax) + p32(0xb) + p32(edx_ecx_ebx) + p32(0)*2 + p32(next(elf.search(b'sh\x00'))) + p32(int_0x80)为什么会攻击失败,因为 execve 调用参数时会将参数代表的文件作为二进制文件读 所以如果为 execve('sh', 0, 0) 会报错 然后是思维导图
格式化字符串
堆
标签:BUUCTF,p64,五页,system,ret,p32,pop,PWN,rdi From: https://www.cnblogs.com/xshhc/p/16939678.html