[Night St0rm CTF] - WRITE UP PWN
Ban đầu cũng không có ý định viết write up bởi cũng rất lười viết :v . Nhưng nghe phong phanh được tin là có thưởng cho write up hay và thú vị :) thế là lại phải viết rồi :)
Cũng mong BTC đọc được dòng này rồi trao giải cho mình :)) vì để viết được chỗ write up này mình phải ngồi download lại file cũng như ngồi debug lại để chụp ảnh rồi balabala :)) ( Nói chung là kể khổ :v )
* PWN 1 - 50 pts
nc motacoin.nightst0rm.net 1337
Bước đầu tiên khi mình làm pwn là luôn kéo file binary vào IDA để hex-rays đọc code c :))
Ở bài này thì mình có được đoạn code sau :))
Ở bài này thì mình có được đoạn code sau :))
Thoạt nhìn qua thì mình đã copy nguyên xâu "KhongGiongTrenServer" để send lên server vì thấy là hàm memcmp chứ cũng chẳng quan tâm xâu đấy ý nghĩa là gì :v
Nhưng sau khi gửi lên thì thấy không thành công mới đọc kĩ lại @@
"KhongGiongTrenServer" bây giờ thì mới hiểu ý nghĩa của nó :v mình cũng nghĩ ngay ra cách khác để nhập vào bài này, tuy nhiên để cho mọi người hiểu rõ hơn mình sẽ phân tích cụ thể chức năng của các hàm được sử dụng trong bài này như sau.
"KhongGiongTrenServer" bây giờ thì mới hiểu ý nghĩa của nó :v mình cũng nghĩ ngay ra cách khác để nhập vào bài này, tuy nhiên để cho mọi người hiểu rõ hơn mình sẽ phân tích cụ thể chức năng của các hàm được sử dụng trong bài này như sau.
read(0, &s, 0x100);
Hàm trên đọc tối đa 0x100 byte từ con trỏ stdin(value = 0) và sẽ được lưu tại vùng nhớ có địa chỉ là địa chỉ của biến s. Với read chúng ta không cần phải kết thúc bởi '\n' ( '\x0a' ) nên có thể đưa vào bất kì ký tự gì mà mình mong muốn.
_________________________________
Phần trên là phần phân tích các hàm quan trọng của trường trình, dưới đây sẽ là ý tưởng khai thác của mình.
Trước tiên để vào được system('/bin/sh'); thí kết của của hàm memcmp phải được trả về = 0
Để làm được điều này thì số ký tự mà chúng ta nhập vào phải đúng bằng số ký tự đầu tiên của xâu được mang ra so sánh trên server, điều này có vẻ khó???
Tuy nhiên vẫn còn một cách đơn giản hơn là làm sao cho v3 = 0;
Nếu v3 = 0 thì memcmp luôn luôn = 0 mà ko cần quan tâm đến xâu được đem ra so sánh trên server là gì.
=> Để v3 = 0 thì length của xâu nhập vào cũng phải = 0
=> xâu nhập vào phải bắt đầu bằng byte null.
=> Lời giải của bài này là nhập vào 1 byte null.
strlen(&s);Hàm trên sử dụng để tính length của một xâu, length của một xâu được tính bằng số các byte khác null liên tiếp nhau kể tử vị trí đầu của byte.
memcmp(&s, "KhongGiongTrenServer", v3);Hàm trên sử dụng để so sánh v3 byte đầu của xâu được trỏ bởi &s và xâu "KhongGiongTrenServer". Kết quả trả về là 0 nếu v3 byte đầu của hai xâu đó bằng nhau hoặc trả về != 0 nếu không có một byte trong v3 byte đầu khác nhau.
_________________________________
Phần trên là phần phân tích các hàm quan trọng của trường trình, dưới đây sẽ là ý tưởng khai thác của mình.
Trước tiên để vào được system('/bin/sh'); thí kết của của hàm memcmp phải được trả về = 0
Để làm được điều này thì số ký tự mà chúng ta nhập vào phải đúng bằng số ký tự đầu tiên của xâu được mang ra so sánh trên server, điều này có vẻ khó???
Tuy nhiên vẫn còn một cách đơn giản hơn là làm sao cho v3 = 0;
Nếu v3 = 0 thì memcmp luôn luôn = 0 mà ko cần quan tâm đến xâu được đem ra so sánh trên server là gì.
=> Để v3 = 0 thì length của xâu nhập vào cũng phải = 0
=> xâu nhập vào phải bắt đầu bằng byte null.
=> Lời giải của bài này là nhập vào 1 byte null.
File: solve.py
* PWN 2 - 100 pts
nc banana.nightst0rm.net 1337
Ở bài này thì hôm thi chính thức tại KMA có chút vấn đề về việc up file.
Bạn đầu mình chạy server tại địa chỉ "nc banana.nightst0rm.net 1337" thì thấy có in ra dòng chữ
"intput username/passsword" nhưng mở file bin ra thì không thấy có code puts hay printf xâu như thế mình đã thắc mắc, nhưng lại nghĩ liệu có gì đó troll ở đây :))
Tiếp tục ngồi làm với file binary ban đầu thì mình càng thấy khó và thấy bế tắc..............
Sau 4 tiếng ngồi nhìn code trong vô vọng từ 8h30 sáng đến 12h30 chiều thì cuối cùng mình cũng gặp được anh ra đề, sau một lúc nói chuyện thì anh phát hiện ra là up nhầm file..... và lúc đó mình cũng vừa chuyển sang làm bài pwn 3-150 nên không làm bài này nữa mà về nhà mới làm lại vì theo như anh nói thì mình cũng có hướng làm bài này rồi nên ko đọc lại nữa (vì có làm cũng ko được giải mà :v )
Tiếp tục ngồi làm với file binary ban đầu thì mình càng thấy khó và thấy bế tắc..............
Sau 4 tiếng ngồi nhìn code trong vô vọng từ 8h30 sáng đến 12h30 chiều thì cuối cùng mình cũng gặp được anh ra đề, sau một lúc nói chuyện thì anh phát hiện ra là up nhầm file..... và lúc đó mình cũng vừa chuyển sang làm bài pwn 3-150 nên không làm bài này nữa mà về nhà mới làm lại vì theo như anh nói thì mình cũng có hướng làm bài này rồi nên ko đọc lại nữa (vì có làm cũng ko được giải mà :v )
Với file binary mới thì các công đoạn làm như sau.
Cũng như bài trước việc đầu tiên là vất vào IDA:
Nhìn thì thấy chương trình có sẵn một hàm goisystem(int a1) sẽ giúp thực hiện lệnh system('sh').
Tuy nhiên có điều kiện check tham số truyền vào == 0x133700
Quay lại với hàm main() thì ở đây chỉ thực hiện một lệnh quan trọng là call tới hàm check()
==> Đọc hàm check thôi :v
Ở hàm check() cho phép nhập vào chương trình 2 lần, mỗi lần nhập sẽ kết thúc nếu gặp byte null hoặc '\x0a' ('\n') và gán giá trị cho byte cuối được nhập vào là '\x00'
==> Không giới hạn số lượng byte nhập vào
==> Có thể overflow.
==> Bài này mình suy nghĩ đến 2 hướng để làm:
+ Hướng 1:(Có vẻ phức tạp hơn nhưng lại hữu dụng hơn trong các trường hợp overflow)
- Overflow return về puts để lấy địa chỉ GOT, leak libc rồi tính system với địa chỉ của '/bin/sh' sau đó quay lại check() để overflow lần tiếp theo.
+ Hướng 2:(Có vẻ đơn giản hơn nhưng chỉ dùng được với code này)
- Bài này có sẵn hàm để call system('sh')
--> return về goisystem() để call và truyền vào tham số: 0x133700
--> tuy nhiên vì 0x133700 có byte null nên sẽ phải nhập vào thật khóe léo.
--> nhập vào 0x1337xx với xx != null --> nhập vào xâu tiếp theo vừa đủ tới vị trí của xx để biến xx = 00
Với những gì phân tích ở trên thì mình có các lời giải như sau: solve.py có đầy đủ cả 2 cách.
+ Cách 1:
+ Cách 2:
* PWN 3 - 150
nc dancecoin.nightst0rm.net 1337
Chưa cần biết thế nào cứ vất vào IDA đã nào :v
Nhìn qua thì mình nhận thấy đây là một bài về bộ nhớ heap.
Với heap thì có rất nhiều các kĩ thuật tấn công phụ thuộc vào từng lỗ hỏng của chương trình tuy nhiên để thực hiện được các cuộc tấn công heap thì trước tiên cần phải hiểu được thế nào là heap.
Với những bạn mới tìm hiểu về heap thì mình có 2 link sau đây rất hữu hiệu để mọi người có thể học hỏi. ( mình cũng chưa hiểu hết được những kĩ thuật cũng như những kiến thức này ).
Link 1 : Understanding glibc malloc
Link 2 : Shellphish
Quay trở lại với nội dung chính là giải quyết bài toán này.
Ban đầu mình dùng gdb dể debug tuy nhiên mình gặp đôi chút khó khăn về "Time up" Vì người ra đề để time rất nhỏ. Chính vì thế mình đã patch lại chương trình bỏ đi phần alarm để có thể debug được lâu hơn.
Ở bài này mình sẽ giới thiệu với các bạn heap newbie như mình một kĩ thuật khá đơn giản nhưng mình cũng phải chờ được thông não thì mới hiểu được :))
Trong cấp phát bộ nhớ heap thì có những thứ sau mà mọi người cần quan tâm.
pwndbg> bin fastbins 0x10: 0x0 0x18: 0x0 0x20: 0x0 0x28: 0x0 0x30: 0x0 0x38: 0x0 0x40: 0x0 unsortedbin all: 0x804c418 ◂— 0xf7f907b0 smallbins empty largebins empty pwndbg>
Đó là các loại kích thước khi cấp phát sẽ được ưu tiên như thế nào.
Ở bài này, chúng ta thấy mỗi một lần create thì chương trình sẽ malloc một vùng nhớ có size là 8 và malloc một vùng nhớ với kích thước tùy ý do chúng ta chọn.
Như vậy nếu như tôi tạo 2 note với hai lần chọn kích thước là 100 ( Không thuộc fast bin) thì các địa chỉ được cấp phát như sau:
pwndbg> x/10x 0x804b080 0x804b080 <ga>: 0x00000000 0x0804c410 0x0804c488 0x00000000 0x804b090 <ga+16>: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b0a0 <ga+32>: 0x00000000 0x00000000 pwndbg> x/4x 0x0804c410 0x804c410: 0x0804c420 0x080487bb 0x00000000 0x00000069 pwndbg> x/4x 0x0804c488 0x804c488: 0x0804c498 0x080487bb 0x00000000 0x00000069 pwndbg>
Sau đó tôi free 2 note đó thì sẽ có được kết quả như sau:
pwndbg> bins fastbins 0x10: 0x804c408 —▸ 0x804c480 ◂— 0x0 0x18: 0x0 0x20: 0x0 0x28: 0x0 0x30: 0x0 0x38: 0x0 0x40: 0x0 unsortedbin all: 0x804c418 ◂— 0xf7f907b0 smallbins empty largebins empty pwndbg>
Nhận thấy ở fastbins 0x10 có 2 địa chỉ sẵn sàng được cấp phát, như vậy nếu ta cấp phát 2 vùng nhớ có kích thước là 8 liên tiếp nhau thì sẽ được cấp phát bởi 2 địa chỉ này.
Tôi create một note với kích thước là 8 và nhận được kết quả như sau:
pwndbg> bins fastbins 0x10: 0x0 0x18: 0x0 0x20: 0x0 0x28: 0x0 0x30: 0x0 0x38: 0x0 0x40: 0x0 unsortedbin all: 0x804c418 ◂— 0xf7f907b0 smallbins empty largebins empty pwndbg> x/10x 0x804b080 0x804b080 <ga>: 0x00000000 0x00000000 0x00000000 0x0804c410 0x804b090 <ga+16>: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b0a0 <ga+32>: 0x00000000 0x00000000 pwndbg> x/4x 0x0804c410 0x804c410: 0x0804c488 0x080487bb 0x00000000 0x00000069 pwndbg>
Như vậy là đã có thể ghi đè lên vùng nhớ của một note đã bị xóa trước đó.
Tuy nhiện khi note đã xóa thì lại không có cách nào để có thể sử dụng chức năng view() với note đó.
==> Quay lại hàm view() tôi thấy người ra đề không cho view trực tiếp trên flow chính mà lại được tạo ra với một thread khác, hàm doview() được viết như sau:
Sau khi hàm được gọi địa chỉ của note đã lập tức được lấy sau đó mới sleep chứ không sleep xong mới lấy nên chúng ta có thể tận dụng lỗi này của chương trình. Trong thời gian sleep có thể ghi đè lên vùng nhớ của note đó qua đó thực thi câu lệnh mà mình muốn.
Để thực hiện exploi thì tôi thực hiện các bước như sau:
Bước 1: Tạo 4 note với size = 100
Bước 2: View note 2
Bươc 3: Delete note 2 rồi đến note 1
Bước 4 create note với size = 8 và nội dung là puts got và địa chỉ hàm abcprint.
Bước 5: Chờ nhận về địa chỉ của puts rồi dùng libc tính địa chỉ của system.
Bước 6: Làm lại bước 2 và bước 3.
Bước 7: create note với size = 8 và nội dung là 'sh\x00\x00' + địa chỉ hàm system tính ở bước 5.
Bước 8: Chờ và gửi cat /home/pwn3/flag lên server để nhận về flag.
chung96vn@ubuntu:~/github/nightst0rm/pwn/pwn3-150$ python solve.py [*] '/home/chung96vn/github/nightst0rm/pwn/pwn3-150/libc.so' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Opening connection to dancecoin.nightst0rm.net on port 1337: Done [*] Add 4 note! [*] View note 2! [*] Delete 2 note [*] Attack note [*] Waitting... [*] 0xf7644ca0 [*] 0xf75e5000 [*] 0xf761fda0 [*] View note 2! [*] Delete 2 note [*] Waitting... [*] NightSt0rm{:'(_500k_vnd} [*] Switching to interactive mode NightSt0rm{:'(_500k_vnd}Times Up! [*] Got EOF while reading in interactive $
File lời giải: solve.py
Comments
Post a Comment