# 6.1.21 pwn HITCONCTF2016 Secret_Holder - [题目复现](#题目复现) - [题目解析](#题目解析) - [漏洞利用](#漏洞利用) - [参考资料](#参考资料) [下载文件](../src/writeup/6.1.21_pwn_hitconctf2016_secret_holder) ## 题目复现 ``` $ file SecretHolder SecretHolder: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=1d9395599b8df48778b25667e94e367debccf293, stripped $ checksec -f SecretHolder RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH Yes 0 2 SecretHolder $ strings libc.so.6 | grep "GNU C" GNU C Library (Ubuntu GLIBC 2.23-0ubuntu3) stable release version 2.23, by Roland McGrath et al. Compiled by GNU CC version 5.3.1 20160413. ``` 64 位程序,开启了 Canary 和 NX,默认开启 ASLR。 在 Ubuntu-16.04 上玩一下: ``` $ ./SecretHolder Hey! Do you have any secret? I can help you to hold your secrets, and no one will be able to see it :) 1. Keep secret 2. Wipe secret 3. Renew secret 1 Which level of secret do you want to keep? 1. Small secret 2. Big secret 3. Huge secret 1 Tell me your secret: AAAA 1. Keep secret 2. Wipe secret 3. Renew secret 3 Which Secret do you want to renew? 1. Small secret 2. Big secret 3. Huge secret 1 Tell me your secret: BBBB 1. Keep secret 2. Wipe secret 3. Renew secret 2 Which Secret do you want to wipe? 1. Small secret 2. Big secret 3. Huge secret 1 ``` 该程序运行我们输入 small、big、huge 三种 secret,且每种 secret 只能输入一个。通过 Renew 可以修改 secret 的内容。Wipe 用于删除 secret。 猜测三种 secret 应该是有不同的 chunk 大小,但程序没有我们常见的打印信息这种选项来做信息泄漏。 ## 题目解析 下面我们逐个来逆向这些功能。 #### Keep secret ``` [0x00400780]> pdf @ sub.Which_level_of_secret_do_you_want_to_keep_86d / (fcn) sub.Which_level_of_secret_do_you_want_to_keep_86d 442 | sub.Which_level_of_secret_do_you_want_to_keep_86d (); | ; var int local_14h @ rbp-0x14 | ; var int local_10h @ rbp-0x10 | ; var int local_8h @ rbp-0x8 | ; CALL XREF from 0x00400d6e (main) | 0x0040086d push rbp | 0x0040086e mov rbp, rsp | 0x00400871 sub rsp, 0x20 | 0x00400875 mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40 | 0x0040087e mov qword [local_8h], rax | 0x00400882 xor eax, eax | 0x00400884 mov edi, str.Which_level_of_secret_do_you_want_to_keep ; 0x400e28 ; "Which level of secret do you want to keep?" | 0x00400889 call sym.imp.puts ; int puts(const char *s) | 0x0040088e mov edi, str.1._Small_secret ; 0x400e53 ; "1. Small secret" | 0x00400893 call sym.imp.puts ; int puts(const char *s) | 0x00400898 mov edi, str.2._Big_secret ; 0x400e63 ; "2. Big secret" | 0x0040089d call sym.imp.puts ; int puts(const char *s) | 0x004008a2 mov edi, str.3._Huge_secret ; 0x400e71 ; "3. Huge secret" | 0x004008a7 call sym.imp.puts ; int puts(const char *s) | 0x004008ac lea rax, [local_10h] | 0x004008b0 mov edx, 4 | 0x004008b5 mov esi, 0 | 0x004008ba mov rdi, rax | 0x004008bd call sym.imp.memset ; void *memset(void *s, int c, size_t n) | 0x004008c2 lea rax, [local_10h] | 0x004008c6 mov edx, 4 | 0x004008cb mov rsi, rax | 0x004008ce mov edi, 0 | 0x004008d3 mov eax, 0 | 0x004008d8 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | 0x004008dd lea rax, [local_10h] | 0x004008e1 mov rdi, rax | 0x004008e4 call sym.imp.atoi ; int atoi(const char *str) | 0x004008e9 mov dword [local_14h], eax | 0x004008ec mov eax, dword [local_14h] | 0x004008ef cmp eax, 2 ; 2 | ,=< 0x004008f2 je 0x400963 | | 0x004008f4 cmp eax, 3 ; 3 | ,==< 0x004008f7 je 0x4009bc | || 0x004008fd cmp eax, 1 ; 1 | ,===< 0x00400900 je 0x400907 | ,====< 0x00400902 jmp 0x400a11 | |||| ; JMP XREF from 0x00400900 (sub.Which_level_of_secret_do_you_want_to_keep_86d) | |`---> 0x00400907 mov eax, dword [0x006020c0] ; [0x6020c0:4]=0 | | || 0x0040090d test eax, eax | |,===< 0x0040090f je 0x400916 | ,=====< 0x00400911 jmp 0x400a11 | ||||| ; JMP XREF from 0x0040090f (sub.Which_level_of_secret_do_you_want_to_keep_86d) | ||`---> 0x00400916 mov esi, 0x28 ; '(' ; 40 | || || 0x0040091b mov edi, 1 | || || 0x00400920 call sym.imp.calloc ; void *calloc(size_t nmeb, size_t size) | || || 0x00400925 mov qword [0x006020b0], rax ; [0x6020b0:8]=0 | || || 0x0040092c mov dword [0x006020c0], 1 ; [0x6020c0:4]=0 | || || 0x00400936 mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: " | || || 0x0040093b call sym.imp.puts ; int puts(const char *s) | || || 0x00400940 mov rax, qword [0x006020b0] ; [0x6020b0:8]=0 | || || 0x00400947 mov edx, 0x28 ; '(' ; 40 | || || 0x0040094c mov rsi, rax | || || 0x0040094f mov edi, 0 | || || 0x00400954 mov eax, 0 | || || 0x00400959 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | ||,===< 0x0040095e jmp 0x400a11 | ||||| ; JMP XREF from 0x004008f2 (sub.Which_level_of_secret_do_you_want_to_keep_86d) | ||||`-> 0x00400963 mov eax, dword [0x006020b8] ; [0x6020b8:4]=0 | |||| 0x00400969 test eax, eax | ||||,=< 0x0040096b je 0x400972 | ,======< 0x0040096d jmp 0x400a11 | |||||| ; JMP XREF from 0x0040096b (sub.Which_level_of_secret_do_you_want_to_keep_86d) | |||||`-> 0x00400972 mov esi, 0xfa0 ; 4000 | ||||| 0x00400977 mov edi, 1 | ||||| 0x0040097c call sym.imp.calloc ; void *calloc(size_t nmeb, size_t size) | ||||| 0x00400981 mov qword [0x006020a0], rax ; [0x6020a0:8]=0 | ||||| 0x00400988 mov dword [0x006020b8], 1 ; [0x6020b8:4]=0 | ||||| 0x00400992 mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: " | ||||| 0x00400997 call sym.imp.puts ; int puts(const char *s) | ||||| 0x0040099c mov rax, qword [0x006020a0] ; [0x6020a0:8]=0 | ||||| 0x004009a3 mov edx, 0xfa0 ; 4000 | ||||| 0x004009a8 mov rsi, rax | ||||| 0x004009ab mov edi, 0 | ||||| 0x004009b0 mov eax, 0 | ||||| 0x004009b5 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | |||||,=< 0x004009ba jmp 0x400a11 | |||||| ; JMP XREF from 0x004008f7 (sub.Which_level_of_secret_do_you_want_to_keep_86d) | ||||`--> 0x004009bc mov eax, dword [0x006020bc] ; [0x6020bc:4]=0 | |||| | 0x004009c2 test eax, eax | ||||,==< 0x004009c4 je 0x4009c8 | ,=======< 0x004009c6 jmp 0x400a11 | ||||||| ; JMP XREF from 0x004009c4 (sub.Which_level_of_secret_do_you_want_to_keep_86d) | |||||`--> 0x004009c8 mov esi, 0x61a80 | ||||| | 0x004009cd mov edi, 1 | ||||| | 0x004009d2 call sym.imp.calloc ; void *calloc(size_t nmeb, size_t size) | ||||| | 0x004009d7 mov qword [0x006020a8], rax ; [0x6020a8:8]=0 | ||||| | 0x004009de mov dword [0x006020bc], 1 ; [0x6020bc:4]=0 | ||||| | 0x004009e8 mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: " | ||||| | 0x004009ed call sym.imp.puts ; int puts(const char *s) | ||||| | 0x004009f2 mov rax, qword [0x006020a8] ; [0x6020a8:8]=0 | ||||| | 0x004009f9 mov edx, 0x61a80 | ||||| | 0x004009fe mov rsi, rax | ||||| | 0x00400a01 mov edi, 0 | ||||| | 0x00400a06 mov eax, 0 | ||||| | 0x00400a0b call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | ||||| | 0x00400a10 nop | ||||| | ; XREFS: JMP 0x00400902 JMP 0x00400911 JMP 0x0040095e JMP 0x0040096d JMP 0x004009ba JMP 0x004009c6 | `````-`-> 0x00400a11 mov rax, qword [local_8h] | 0x00400a15 xor rax, qword fs:[0x28] | ,=< 0x00400a1e je 0x400a25 | | 0x00400a20 call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void) | | ; JMP XREF from 0x00400a1e (sub.Which_level_of_secret_do_you_want_to_keep_86d) | `-> 0x00400a25 leave \ 0x00400a26 ret ``` #### Wipe secret ``` [0x00400780]> pdf @ sub.Which_Secret_do_you_want_to_wipe_a27 / (fcn) sub.Which_Secret_do_you_want_to_wipe_a27 247 | sub.Which_Secret_do_you_want_to_wipe_a27 (); | ; var int local_14h @ rbp-0x14 | ; var int local_10h @ rbp-0x10 | ; var int local_8h @ rbp-0x8 | ; CALL XREF from 0x00400d7a (main) | 0x00400a27 push rbp | 0x00400a28 mov rbp, rsp | 0x00400a2b sub rsp, 0x20 | 0x00400a2f mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40 | 0x00400a38 mov qword [local_8h], rax | 0x00400a3c xor eax, eax | 0x00400a3e mov edi, str.Which_Secret_do_you_want_to_wipe ; 0x400e98 ; "Which Secret do you want to wipe?" | 0x00400a43 call sym.imp.puts ; int puts(const char *s) | 0x00400a48 mov edi, str.1._Small_secret ; 0x400e53 ; "1. Small secret" | 0x00400a4d call sym.imp.puts ; int puts(const char *s) | 0x00400a52 mov edi, str.2._Big_secret ; 0x400e63 ; "2. Big secret" | 0x00400a57 call sym.imp.puts ; int puts(const char *s) | 0x00400a5c mov edi, str.3._Huge_secret ; 0x400e71 ; "3. Huge secret" | 0x00400a61 call sym.imp.puts ; int puts(const char *s) | 0x00400a66 lea rax, [local_10h] | 0x00400a6a mov edx, 4 | 0x00400a6f mov esi, 0 | 0x00400a74 mov rdi, rax | 0x00400a77 call sym.imp.memset ; void *memset(void *s, int c, size_t n) | 0x00400a7c lea rax, [local_10h] | 0x00400a80 mov edx, 4 | 0x00400a85 mov rsi, rax | 0x00400a88 mov edi, 0 | 0x00400a8d mov eax, 0 | 0x00400a92 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | 0x00400a97 lea rax, [local_10h] | 0x00400a9b mov rdi, rax | 0x00400a9e call sym.imp.atoi ; int atoi(const char *str) | 0x00400aa3 mov dword [local_14h], eax | 0x00400aa6 mov eax, dword [local_14h] | 0x00400aa9 cmp eax, 2 ; 2 | ,=< 0x00400aac je 0x400ad3 | | 0x00400aae cmp eax, 3 ; 3 | ,==< 0x00400ab1 je 0x400aee | || 0x00400ab3 cmp eax, 1 ; 1 | ,===< 0x00400ab6 jne 0x400b08 | ||| 0x00400ab8 mov rax, qword [0x006020b0] ; [0x6020b0:8]=0 | ||| 0x00400abf mov rdi, rax | ||| 0x00400ac2 call sym.imp.free ; void free(void *ptr) | ||| 0x00400ac7 mov dword [0x006020c0], 0 ; [0x6020c0:4]=0 | ,====< 0x00400ad1 jmp 0x400b08 | |||| ; JMP XREF from 0x00400aac (sub.Which_Secret_do_you_want_to_wipe_a27) | |||`-> 0x00400ad3 mov rax, qword [0x006020a0] ; [0x6020a0:8]=0 | ||| 0x00400ada mov rdi, rax | ||| 0x00400add call sym.imp.free ; void free(void *ptr) | ||| 0x00400ae2 mov dword [0x006020b8], 0 ; [0x6020b8:4]=0 | |||,=< 0x00400aec jmp 0x400b08 | |||| ; JMP XREF from 0x00400ab1 (sub.Which_Secret_do_you_want_to_wipe_a27) | ||`--> 0x00400aee mov rax, qword [0x006020a8] ; [0x6020a8:8]=0 | || | 0x00400af5 mov rdi, rax | || | 0x00400af8 call sym.imp.free ; void free(void *ptr) | || | 0x00400afd mov dword [0x006020bc], 0 ; [0x6020bc:4]=0 | || | 0x00400b07 nop | || | ; 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 0x00400aec (sub.Which_Secret_do_you_want_to_wipe_a27) | ``-`-> 0x00400b08 mov rax, qword [local_8h] | 0x00400b0c xor rax, qword fs:[0x28] | ,=< 0x00400b15 je 0x400b1c | | 0x00400b17 call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void) | | ; JMP XREF from 0x00400b15 (sub.Which_Secret_do_you_want_to_wipe_a27) | `-> 0x00400b1c leave \ 0x00400b1d ret ``` #### Renew secret ``` [0x00400780]> pdf @ sub.Which_Secret_do_you_want_to_renew_b1e / (fcn) sub.Which_Secret_do_you_want_to_renew_b1e 330 | sub.Which_Secret_do_you_want_to_renew_b1e (); | ; var int local_14h @ rbp-0x14 | ; var int local_10h @ rbp-0x10 | ; var int local_8h @ rbp-0x8 | ; CALL XREF from 0x00400d86 (main) | 0x00400b1e push rbp | 0x00400b1f mov rbp, rsp | 0x00400b22 sub rsp, 0x20 | 0x00400b26 mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40 | 0x00400b2f mov qword [local_8h], rax | 0x00400b33 xor eax, eax | 0x00400b35 mov edi, str.Which_Secret_do_you_want_to_renew ; 0x400ec0 ; "Which Secret do you want to renew?" | 0x00400b3a call sym.imp.puts ; int puts(const char *s) | 0x00400b3f mov edi, str.1._Small_secret ; 0x400e53 ; "1. Small secret" | 0x00400b44 call sym.imp.puts ; int puts(const char *s) | 0x00400b49 mov edi, str.2._Big_secret ; 0x400e63 ; "2. Big secret" | 0x00400b4e call sym.imp.puts ; int puts(const char *s) | 0x00400b53 mov edi, str.3._Huge_secret ; 0x400e71 ; "3. Huge secret" | 0x00400b58 call sym.imp.puts ; int puts(const char *s) | 0x00400b5d lea rax, [local_10h] | 0x00400b61 mov edx, 4 | 0x00400b66 mov esi, 0 | 0x00400b6b mov rdi, rax | 0x00400b6e call sym.imp.memset ; void *memset(void *s, int c, size_t n) | 0x00400b73 lea rax, [local_10h] | 0x00400b77 mov edx, 4 | 0x00400b7c mov rsi, rax | 0x00400b7f mov edi, 0 | 0x00400b84 mov eax, 0 | 0x00400b89 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | 0x00400b8e lea rax, [local_10h] | 0x00400b92 mov rdi, rax | 0x00400b95 call sym.imp.atoi ; int atoi(const char *str) | 0x00400b9a mov dword [local_14h], eax | 0x00400b9d mov eax, dword [local_14h] | 0x00400ba0 cmp eax, 2 ; 2 | ,=< 0x00400ba3 je 0x400be9 | | 0x00400ba5 cmp eax, 3 ; 3 | ,==< 0x00400ba8 je 0x400c1f | || 0x00400baa cmp eax, 1 ; 1 | ,===< 0x00400bad jne 0x400c52 | ||| 0x00400bb3 mov eax, dword [0x006020c0] ; [0x6020c0:4]=0 | ||| 0x00400bb9 test eax, eax | ,====< 0x00400bbb je 0x400be7 | |||| 0x00400bbd mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: " | |||| 0x00400bc2 call sym.imp.puts ; int puts(const char *s) | |||| 0x00400bc7 mov rax, qword [0x006020b0] ; [0x6020b0:8]=0 | |||| 0x00400bce mov edx, 0x28 ; '(' ; 40 | |||| 0x00400bd3 mov rsi, rax | |||| 0x00400bd6 mov edi, 0 | |||| 0x00400bdb mov eax, 0 | |||| 0x00400be0 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | ,=====< 0x00400be5 jmp 0x400c52 | ||||| ; JMP XREF from 0x00400bbb (sub.Which_Secret_do_you_want_to_renew_b1e) | ,=`----> 0x00400be7 jmp 0x400c52 | || ||| ; JMP XREF from 0x00400ba3 (sub.Which_Secret_do_you_want_to_renew_b1e) | || ||`-> 0x00400be9 mov eax, dword [0x006020b8] ; [0x6020b8:4]=0 | || || 0x00400bef test eax, eax | || ||,=< 0x00400bf1 je 0x400c1d | || ||| 0x00400bf3 mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: " | || ||| 0x00400bf8 call sym.imp.puts ; int puts(const char *s) | || ||| 0x00400bfd mov rax, qword [0x006020a0] ; [0x6020a0:8]=0 | || ||| 0x00400c04 mov edx, 0xfa0 ; 4000 | || ||| 0x00400c09 mov rsi, rax | || ||| 0x00400c0c mov edi, 0 | || ||| 0x00400c11 mov eax, 0 | || ||| 0x00400c16 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | ||,====< 0x00400c1b jmp 0x400c52 | |||||| ; JMP XREF from 0x00400bf1 (sub.Which_Secret_do_you_want_to_renew_b1e) | ,=====`-> 0x00400c1d jmp 0x400c52 | |||||| ; JMP XREF from 0x00400ba8 (sub.Which_Secret_do_you_want_to_renew_b1e) | |||||`--> 0x00400c1f mov eax, dword [0x006020bc] ; [0x6020bc:4]=0 | ||||| 0x00400c25 test eax, eax | ||||| ,=< 0x00400c27 je 0x400c51 | ||||| | 0x00400c29 mov edi, str.Tell_me_your_secret: ; 0x400e80 ; "Tell me your secret: " | ||||| | 0x00400c2e call sym.imp.puts ; int puts(const char *s) | ||||| | 0x00400c33 mov rax, qword [0x006020a8] ; [0x6020a8:8]=0 | ||||| | 0x00400c3a mov edx, 0x61a80 | ||||| | 0x00400c3f mov rsi, rax | ||||| | 0x00400c42 mov edi, 0 | ||||| | 0x00400c47 mov eax, 0 | ||||| | 0x00400c4c call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | ||||| | ; JMP XREF from 0x00400c27 (sub.Which_Secret_do_you_want_to_renew_b1e) | ||||| `-> 0x00400c51 nop | ||||| ; JMP XREF from 0x00400bad (sub.Which_Secret_do_you_want_to_renew_b1e) | ||||| ; JMP XREF from 0x00400be5 (sub.Which_Secret_do_you_want_to_renew_b1e) | ||||| ; JMP XREF from 0x00400be7 (sub.Which_Secret_do_you_want_to_renew_b1e) | ||||| ; JMP XREF from 0x00400c1b (sub.Which_Secret_do_you_want_to_renew_b1e) | ||||| ; JMP XREF from 0x00400c1d (sub.Which_Secret_do_you_want_to_renew_b1e) | `````---> 0x00400c52 mov rax, qword [local_8h] | 0x00400c56 xor rax, qword fs:[0x28] | ,=< 0x00400c5f je 0x400c66 | | 0x00400c61 call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void) | | ; JMP XREF from 0x00400c5f (sub.Which_Secret_do_you_want_to_renew_b1e) | `-> 0x00400c66 leave \ 0x00400c67 ret ``` ## 漏洞利用 ## 参考资料 - https://ctftime.org/task/2954