Debug source code glibc và MeePwn CTF Quals 2018 — house_of_card
Tiện thể viết writeup cho house_of_card thì mình cũng share cách mình debug source code glibc luôn .
Debug source code glibc
Đầu tiên là mình phải check versions glibc.
root@3232d3975981:~# ldd --version
ldd (Ubuntu GLIBC 2.23-0ubuntu10) 2.23
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
Download source glibc và giải nén.
root@3232d3975981:~# wget https://ftp.gnu.org/gnu/glibc/glibc-2.23.tar.bz2
root@3232d3975981:~# tar xvf glibc-2.23.tar.bz2
Sau khi giải nén source code thì bạn tạo 2 folder là build
và 2.23
. Bởi vì glibc
không thể compile
trong folder chứa source
nên phải build nó đến folder khác. Folder 2.23
là nơi glibc được install.
root@3232d3975981:~# ls
2.23 build glibc-2.23 glibc-2.23.tar.bz2
Đây là bước Configuring
.
root@3232d3975981:~/build# ../glibc-2.23/configure --prefix=/root/2.23 CFLAGS="-g -g3 -ggdb -gdwarf-4 -Og -Wno-uninitialized -Wno-error" CXXFLAGS="-g -g3 -ggdb -gdwarf-4 -Og -Wno-uninitialized -Wno-error"
Cuối cùng là compile
và install
.
root@3232d3975981:~/build# make -j8 && make install
Sau khi compile
và install
xong thì có thể sử dụng glibc
bằng cách là export LD_LIBRARY_PATH=/root/2.23/lib
.
root@3232d3975981:/home/ctf/solutions_by_me/meepwn_2018/house_of_card# export LD_LIBRARY_PATH=/root/2.23/lib
root@3232d3975981:/home/ctf/solutions_by_me/meepwn_2018/house_of_card# ldd house_of_card
linux-vdso.so.1 => (0x00007ffff7ffa000)
libseccomp.so.2 => /lib/x86_64-linux-gnu/libseccomp.so.2 (0x00007ffff798e000)
libc.so.6 => /root/2.23/lib/libc.so.6 (0x00007ffff75ff000)
/lib64/ld-linux-x86-64.so.2 (0x00007ffff7dd7000)
Còn cách khác là export
trong file solved.py
.
from pwn import *local = Falseif local:
r = process(['./house_of_card'],env {"LD_LIBRARY_PATH":"/root/2.23/lib"})
else:
r = remote('178.128.87.12', 31336 )
Bây giờ debug trong malloc()
thử 😏
RDI 0x80
RSI 0x7ffff7b8e780 (_IO_stdfile_1_lock) ◂— 0x0
R8 0x7ffff7fed700 ◂— 0x7ffff7fed700
R9 0x0
R10 0x5555555559c8 ◂— jae 0x555555555a2f /* 'Description:\n' */
R11 0x246
R12 0x7ffff7b8d710 (stdin) —▸ 0x7ffff7b8c8e0 (_IO_2_1_stdin_) ◂— 0xfbad208b
R13 0x80
R14 0x80
R15 0x7fffffffecd0 ◂— 0x555500383231 /* '128' */
*RBP 0x80
*RSP 0x7fffffffec70 —▸ 0x7fffffffecd0 ◂— 0x555500383231 /* '128' */
*RIP 0x7ffff7876067 (malloc+9) ◂— mov rax, qword ptr [rip + 0x315e8a]
─────────────────────────────[ DISASM]──────────────────────────────
0x555555554b90 jmp qword ptr [rip + 0x202412] <0x7ffff787605e>
↓
0x7ffff787605e <malloc> push rbp
0x7ffff787605f <malloc+1> push rbx
0x7ffff7876060 <malloc+2> sub rsp, 8
0x7ffff7876064 <malloc+6> mov rbp, rdi
► 0x7ffff7876067 <malloc+9> mov rax, qword ptr [rip + 0x315e8a]
0x7ffff787606e <malloc+16> mov rax, qword ptr [rax]
0x7ffff7876071 <malloc+19> test rax, rax
0x7ffff7876074 <malloc+22> je malloc+36 <0x7ffff7876082>
0x7ffff7876076 <malloc+24> mov rsi, qword ptr [rsp + 0x18]
0x7ffff787607b <malloc+29> call rax
─────────────────────────[ SOURCE (CODE) ]──────────────────────────
2903 {
2904 mstate ar_ptr;
2905 void *victim;
2906
2907 void *(*hook) (size_t, const void *)
► 2908 = atomic_forced_read (__malloc_hook);
2909 if (__builtin_expect (hook != NULL, 0))
2910 return (*hook)(bytes, RETURN_ADDRESS (0));
2911
2912 arena_get (ar_ptr, bytes);
2913
─────────────────────────[ STACK ]────────────────────────────-─────
00:0000│ rsp 0x7fffffffec70 —▸ 0x7fffffffecd0 ◂— 0x555500383231 /* '128' */
01:0008│ 0x7fffffffec78 ◂— 0x0
02:0010│ 0x7fffffffec80 —▸ 0x5555555558f0 ◂— push r15
03:0018│ 0x7fffffffec88 —▸ 0x555555555232 ◂— mov r15, rax
04:0020│ 0x7fffffffec90 ◂— 'new note'
05:0028│ 0x7fffffffec98 ◂— 0x0
... ↓
──────────────────────────────────[ BACKTRACE ]─────────────────────
► f 0 7ffff7876067 malloc+9
f 1 555555555232
f 2 555555555867
f 3 7ffff7822c09 __libc_start_main+385
pwndbg>
Mọi người có thể print value của các biến cho dễ debug.
pwndbg> p __malloc_hook
$1 = (void *(*)(size_t, const void *)) 0x7ffff7877255 <malloc_hook_ini>
Vậy ok rồi 👌
house_of_card
Đầu tiên vô lúc nào cũng phải check mitigation 👊
root@3232d3975981:/home/ctf/solutions_by_me/meepwn_2018/house_of_card# checksec house_of_card
[*] '/home/ctf/solutions_by_me/meepwn_2018/house_of_card/house_of_card'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
Chương trình có 3 chức năng là new note
, edit note
và del note
.
root@3232d3975981:/home/ctf/solutions_by_me/meepwn_2018/house_of_card# ./house_of_card
============ 📚 Notes 📚 ============
1. New Note
2. Edit Note
3. Del Note
4. Quit
⛩
1. New 📓
Đầu tiên chương trình sẽ cho nhập name
với tối đa là 63 kí tự
. Nếu gặp kí tự ‘\n’
sẽ thay bằng kí tự ‘\x00’
và ko nhập nữa.
+--------------------------+
NAME[64] | A | A | A | ... | A |\x00|
+--------------------------+
0 1 2 62 63
Tiếp theo là nhập chiều dài của chuỗi description
. Chiều dài của description
phải 0x1000 ≥ len ≥ 0x80
. Sau đó chuỗi description
được cấp phát với malloc(len)
.
+--------------------------+
HEAP | DESCRIPTION | TOP_CHUNK |
+--------------------------+
Sau khi nhập description
thì sẽ cấp phát 1 vùng nhớ có chiều dài len + 68
mình sẽ gọi vùng nhớ này là content
.content
lưu trữ name[64] ➕ len ➕ description[len]
.
+-------------------------------------+
HEAP | DESCRIPTION | CONTENT |TOP_CHUNK |
+-------------------------------------+ +----------------------------------------+
CONTENT | NAME[64] | LEN | DESCRIPTION[LEN] |
+----------------------------------------+
0 -> 63 64 -> 67 68 -> LEN + 67
Sau khi copy qua content
thì vùng nhớ description
bị 🆓.
+-----------------------------+
HEAP | FREE | CONTENT |TOP_CHUNK |
+-----------------------------+
Cuối cùng là cấp phát 1 vùng nhớ có chiều dài 0️⃣ ❌ 1️⃣ 8️⃣ mình sẽ gọi vùng nhớ này là note
. Vì chương trình lưu trữ các note
dưới dạng danh sách liên kết đôi (Doubly Linked List)
nên sẽ có note trước là fd
và note sau là bk
.
+-------------------------------------+
NOTE1 | CONTENT | FD = NOTE1 | BK = NOTE2 |
+-------------------------------------+
+-------------------------------------+
NOTE2 | CONTENT | FD = NOTE1 | BK = NULL |
+-------------------------------------+
Dưới đây là heap sau khi tạo 1 note
mới.
+---------------------------------------+
HEAP | NOTE1 | FREE | CONTENT1 | TOP_CHUNK |
+---------------------------------------+
2. Edit 📓
Ở edit note
có thể thay đổi name, len và description
. Trong trường hợp nếu len mới > len cũ
sẽ xảy ra 1 lỗi heap overflow
bởi vì chiều dài của chuỗi description
là len
mà mình có thể nhập là len + 68
.
+------------------------------------------------------+
CONTENT | NAME[64] | LEN | DESCRIPTION[LEN] | OVERFLOW |
+------------------------------------------------------+
0 -> LEN - 1 LEN -> LEN + 67
3. Del 📓
Gọi note
được chọn remove là ❌ và danh sách liên kết lưu các note
là notes
. Đầu tiên ❌sẽ được remove khỏi notes
, tiếp theo lần lượt là content
và ❌sẽ được 🆓.
+-------------------------------------+
NOTE1 | CONTENT | FD = NOTE1 | BK = NOTE3 |
+-------------------------------------+
+-------------------------------------+
FREE | FREE | FD = NOTE1 | BK = NOTE3 | REMOVE NOTE2
+-------------------------------------+ +-------------------------------------+
NOTE3 | CONTENT | FD = NOTE1 | BK = NULL |
+-------------------------------------+
4. Exploit
Đã tìm được 🐞 rồi bây giờ lên kế hoạch để exploit thôi 😏
Ý tưởng là sẽ
overflow
quacontent
củanote
nằm saudescription
mà mình đangedit
. Sau đó là mình có quyềnwrite
tùy ý rồi 👍
+--------------------------------------------------------+
HEAP | ... |NAME[64]|LEN|DESCRIPTION[LEN]| OVERFLOW | FD | BK |
+--------------------------------------------------------+
0 -> LEN - 1 LEN -> LEN + 7
Dưới đây là payload.
Cuối cùng là gửi lời chân thành cảm ơn đến các anh trong team Meepwn đã làm ra những challenge thật là thú vị và bổ ích .Cảm ơn mọi người đã bỏ thời gian đọc bài viết của mình 😃