[Sekai CTF 2022] Writeup Web
Last updated
Last updated
Đây là một giải khá thú vị và mình học được thêm những kiến thức mới, đây là write up hai bài mình biết làm và có 2 bài mình biết lỗ hổng nhưng cách khai thác vẫn chưa đủ kiến thức nên mình cũng viết ra ở đây cho mọi người tham khảo...Toàn bộ link source challenges sẽ ở đây, các bạn có thể down về và build docker để làm.~~~~~LINK~~~~~
Vào đầu bài chúng ta sẽ có một đoạn code PHP như này:
Nhìn qua đoạn code mình đoán nó là lỗ hổng PHP Object Injection, lỗi là không kiểm tra kỹ đầu vào unserialize()
Oke chúng ta sẽ phân tích code trên một xíu:
Trước tiên code sẽ thực hiện chèn file flag.php
vào file chính này, sau đó dòng 4 có một biến public $start
được đặt là true, tiếp theo có một function __destruct()
sẽ xử lý nếu start === true
thì sẽ in ra flag.
Oke nhưng chú ý tới phía dưới chúng ta có một public function __wakeup()
xử lý biến start trên là false.
__wakeup
in turn will be executed byunserialize
if it is present in class.
Khi unserialize()
thực hiện nó sẽ gọi tới hàm __wakeup()
sẽ được gọi ngay lập tức, vậy chúng ta sẽ luôn false.
Ý tưởng bây giờ chúng ta sẽ ghi đè đối tượng trong __wakeup
khi unserialize()
;
Cái này mình thử nhưng không được, mình nhận ra có điều gì đó, nên mình đã xem lại biến sekai_game.run
có bị filter cái gì không...
Sau một lúc research thì nhận ra, mình sẽ tổng hợp 3 ý chính ở đây:
Như payload ở trên thì chạy nhưng flag không hiện ra cái thứ nhất tên biến của PHP không được dùng dấu .
, vậy thì chúng ta dùng sekai_game.run
khi đó nó sẽ không nhận, các bạn có thể thấy đoạn code php ở trên khi có .
hay [
trong biến thì nó sẽ replace thành _
Nhưng mà khi có dấu chấm nó replace thành _
khi đó sẽ không đúng với tên biến, chúng ta sẽ dùng [
để thay vào biến, khi đó tham số(parameter) ở trên check tới dấu [
thì nó nghĩ là Array nên nó dừng lại thành ra skip được dấu .
, sau khi điền giá trị vào tham số trên thì nó replace _
=> bypass thành công !
Cái cuối thì khi unserialize()
thì wakeup()
sẽ chạy ngay lập tức => payload trên nó chạy vào wakeup()
, nhưng sẽ không in ra gì nên mình tìm được cái một ký tự "C
" trong serialize dành cho class nhưng bây giờ không được hỗ trợ nó biến đổi thành 'O
', khi đó có thể skip __wakeup()
: Link tham khảo
Từ đây payload cuối cùng là: sekai[game.run=C:10:"Sekai_Game":0:{}
Flag: SEKAI{W3lcome_T0_Our_universe}
Khi vào trang web chúng ta có sẽ có 3 bài thơ như trên, khi kích vào mỗi linh khi đó URL sẽ như này:
Khi bạn nhấn vào mỗi bài thời sẽ có một link khác nhau, chú ý trên URL lúc này ?id= được gắn cho một bài thơ, mình nghĩ nó là Directory traversal, vậy mình đã có gắng bypass để truy cập thư mục etc/passwd, tới khi truy cập được như này:
Oke vậy lúc này chúng ta xác định rõ nó là lỗ hổng Directory traversal, vì bài này cấm brute force nên mình check bằng tay. Có nhiều file mình có thể đọc được nhưng không có tác dụng cho tới khi mình đọc được thông tin từ file này /proc/self/cmdline:
Lúc này chúng ta thấy rằng lệnh ban đầu quá trình chạy khi đó chúng ta biết thêm một path là app/app.py. Sau khi thử thay thế etc/passwd chúng ta nhận được source code:
Chúng ta phân tích qua một xíu code nhé:
Chú ý rằng đoạn code có from config.secret import sekai
lúc này ta biết được sekai được import từ config.secret.
Nhìn xuống dưới thì chỉ là nối path để show poem ra thôi, chỉ có chỗ nếu parameter có ^../app
th in ra NO!! khi đó chúng ta có thể bypass bằng cách thêm ../../app/app.py
.
Oke bây giờ chúng ta cần chú ý xuống phần regex của nó: try except
, thì ban đầu có một session
được gán cho một cookie
được get về có key: name
và secret=sekai
, sau đó kiểm tra xem có session
hay không, nếu không nó sẽ đặt session
key:name
value:guest
sau đó set lại cookie
với value: name
, value đã được hash từ secret_key
(gọi là signature key
: khóa chữ ký, các bạn có thể đọc trong doc bottle
để hiểu rõ hơn) và session
. Sau đó retune ra template của guest
. Còn nếu session
có value là admin
thì nó sẽ return ra template của admin
Vì bây giờ chúng ta không thể biết được secret_key là gì nên chúng ta, phải đi tìm đường dẫn có chưa file secret. Oke sau một lúc search thì tôi nghĩ tới folder proc/self/cwd - đây là folder chứa thư mục hiện tại đang chạy và mình tìm được config/secret.py
Chúng ta đã có secret-key bây giờ chúng ta sẽ RCE. Như đã nói ở trên thứ chúng ta có thể kiểm soát được là cookie, thì mình tìm kiếm với key word: RCE via cookie python thì mình nhận ra đó là Remote Code Execution - Insecure Deserialization
Mình đã vào doc bottle để đọc và kiếm được bài này: LINK
Oke sau một lúc đọc doc
và nghiên cứu thì mình note các ý chính như sau:
Sau khi Cookie được sign thì hàm cookie_decode được gọi
Chúng ta có thể chèn code ở cookie_encode
Mình nhận ra khi setcookie hay get_cookie cũng có pickle.dumps và pickle.load nhưng mình test thử thì RCE được.
Oke vậy payload sẽ được viết như sau:
Đây là payload dựa trên set_cookie (Nhân tiện cảm ơn Quốc Anh đã fix mình ở payload này!)
Cảm ơn các bạn đã đọc WU mặc dù mình viết chưa được tốt, trong tương lai mình sẽ cố gắng trình bày tốt hơn...