因为最近做了一些栈迁移相关的题,就想着做一个整理,大佬轻喷/(ㄒoㄒ)/~~这里借用ctfwiki的介绍:6efK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0N6r3k6Q4x3X3c8%4K9h3E0A6i4K6u0W2L8%4u0Y4i4K6u0r3M7s2N6F1i4K6u0r3L8r3W2F1N6i4S2Q4x3V1k6#2M7$3g2J5i4K6u0V1L8h3!0V1k6g2)9J5c8Y4y4@1j5h3y4C8L8%4k6W2M7X3k6D9L8%4N6Q4x3V1k6^5z5o6k6Q4x3V1k6X3j5h3&6U0P5g2)9J5k6s2u0G2M7q4)9J5c8R3`.`.栈迁移(stack pivoting),正如它所描述的,该技巧就是劫持栈指针指向攻击者所能控制的内存处,然后再在相应的位置进行 ROP。一般来说,我们可能在以下情况需要使用 stack pivoting此外,利用 stack pivoting 有以下几个要求当然,还会有一些其它的姿势。比如说 libc_csu_init 中的 gadgets,我们通过偏移就可以得到控制 rsp 指针。上面的是正常的,下面的是偏移的。此外,还有更加高级的 fake frame。存在可以控制内容的内存,一般有如下一般我们可以把bp指针的内容覆盖为我们能控制的内存区域,返回地址设置为leave;ret的gadget就可以实现sp指针的修改。比如说以下这个例子以下是一些题目,程序都放在附件里了,可以在本地复现。先看保护,32位程序,可以看到存在RWX段,猜测栈上可以执行shellcode也可以通过vmmap验证一下pwn函数通过fgets写入0x32个字符,根据ida写的s到ebp的偏移是0x20,因此只能溢出0x32-0x20-0x4也就是14个字节,长度受限。程序还给了一个hint函数,这个函数里给了一个jmp esp的gadget,我们可以利用这个gadget把eip指到栈上。这道题我们可以通过把shellcode布置到s数组中,再填充字符直到返回地址,返回地址我们可以覆盖为jmp esp这个gadget的地址,这样就可以把eip指向我们的栈上了,接着我们还需要把eip指到我们的shellcode,我们可以在返回地址后布置"sub esp,0x28;jmp"这样一个汇编代码,使得eip指针指到shellcode,最终拿到shell这里shellcode|padding|fake ebp|ret jmp_esp_addr刚好占用了0x28个字节,所以才是sub esp,0x28本地调试可以看到,执行jmp esp后,eip和esp都指向了我们写的sub esp,0x28;jmp esp汇编接着就执行sub esp,0x28;jmp esp就可以让eip指到我们写的shellcode了成功得到shell先看保护,是32位程序。vul里有两个read操作,但是读入字节为0x30个字节,根据ida给出的偏移,我们只能刚好溢出到返回地址,溢出长度受限,需要用到栈迁移程序还有一个hack函数,执行了system函数,我们就可以利用system@plt来调用system函数了这道题的思路是:通过vul的第一个read和printf函数我们可以泄露ebp的地址,在第二个read我们就可以通过栈溢出把ebp覆盖为变量s的地址,返回地址覆盖为leave_ret完成栈迁移,我们只需要在s数组里布置好ROP链即可本地调试,可以看到第一次read之后ebp相对ROP链的偏移是0xa8-0x70=0x38第二次read之后,写入ROP链,ebp也覆盖为我们的ROP链位置,返回地址的leave;retn完成栈迁移最后就是执行ROP链,执行system("/bin/sh")拿到shell了先看程序保护,32位程序,发现有RWX段,说明栈上可执行shellcode可以看到vulnerable_function会打印buf数组的起始地址,我们可以获取这个地址,把返回地址填充为buf的地址,那我们不就可以执行存放在buf的shellcode了先看保护,32位程序。再看vul_function逻辑,可以看到第一个read函数会向s变量写入0x200个字节(s是一个全局变量,存在bss段),第二个read会向buf写入0x20个字节,根据ida给出的偏移,只能溢出到返回地址,长度受限,但是我们可以将栈迁移到s的位置(即把ebp覆盖为s的地址,返回地址覆盖为leave_ret的gadget),因为s的内容是可控的,可以布置rop链解题思路是:将栈迁移到bss段,执行ROP链泄露libc基址,之后就是常规的ret2libc了看程序保护,没开pie和canary看main函数的实现发现开了沙箱,读入0x200个字节,有栈溢出发现不让执行execve,想拿到flag的话,我们还可以用open,read,write函数即ORW来输出flag只有pop rdx的gadget能用,只能控制第二个参数能找到syscall我们注意到main函数结尾调用了read函数(.text:00000000004012B8),帮我们设置了rdi,rsi,rdx的值,而我们刚好能改rdx的值,rdx是用来设置read的读入长度的,而read函数会把成功读入的长度赋值给rax,这里可以想到使用SROP(Sigreturn Oriented Programming) ,原理详见:874K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0N6r3k6Q4x3X3c8%4K9h3E0A6i4K6u0W2L8%4u0Y4i4K6u0r3M7s2N6F1i4K6u0r3L8r3W2F1N6i4S2Q4x3V1k6#2M7$3g2J5i4K6u0V1L8h3!0V1k6g2)9J5c8Y4y4@1j5h3y4C8L8%4k6W2M7X3k6D9L8%4N6Q4x3V1k6^5z5o6k6Q4x3V1k6S2k6s2k6S2L8X3y4W2k6q4)9J5k6s2u0G2M7q4)9J5c8Y4y4J5L8%4m8Q4x3V1k6Q4x3U0y4K6K9h3N6F1j5h3H3`.,我们可以通过调用read@plt,只需要赋值rdx为15,就可以使用SROP了。在SigreturnFrame中,我们可以布置rdi为puts@got,rip为put_plt泄露libc地址,但是泄露完呢?我们该怎么写入ORW的ROP链呢?open函数还需要传入flag文件的路径:/flag,这又该如何写入,这里我们可以先把rbp指针转移到bss段,这样我们可以利用0x4012B8-0x4012C9的代码把返回地址和/flag字符串写入bss段泄露libc地址后可以在libc查找gdgetROPgadget --binary libc.so.6 --only "pop|ret"|grep "pop rdi"ROPgadget --binary libc.so.6 --only "pop|ret"|grep "pop rsi ; ret"ROPgadget --b
...(已截断)
---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-289300.htm
[原创]栈迁移的一次小小总结
294 浏览
0 回复
暂无回复,快来抢沙发吧!