2021祥云杯-secrets_of_admin
知识点概要:审计、XSS+SSRF组合拳
首页
有源码,源码结构:

在database.ts文件里有admin账户密码,可以直接登录。另外可以看出有users和files两个表,还有一个superuser用户

登录之后进入/admin目录,在文本框随便输入,会提示,大致意思是保存成了pdf文件,但是需要知道怎么下载
源码分析
审计index.ts,几个关键路由
根目录,做登录验证,登录成功设置cookie,并重定向到/admin目录

/admin目录
1.POST提交content参数值,会有几个类似XSS的黑名单过滤;
2.将content直接拼接到一段HTML代码标签里;
3.将HTML代码写入pdf文件,pdf以uuid()的值命名,存在./files/路径下;
4.用文件名计算checksum值,将superuser、文件名、checksum值一块存入数据库。

/api/files目录
1.正则匹配请求地址的socket(IP:PORT),要求冒号前(即IP地址)只能是127.0.0.1;
2.GET参数:username、filename、checksum只能是字符串类型,会被存储到数据库

/api/files/:id目录
1.token的username不能是superuser;
2.token的username和请求参数id会被用来查找数据库文件名;
3.全场唯一读文件函数readFile读取../files/路径下的对应文件。

数据库getFile()操作代码如下,可以看到传入的参数id对应checksum,从files表中查找对应的文件名

捋一下思路
1.读文件需要token的username是admin,并且知道文件对应的checksum值;
2.要保证token的username是admin,只需正常登录admin账户就可以;
3.文件对应的checksum值则有两条路线,一条是/admin路由的uuid()生成的文件名计算所得,这不可控,另一条是通过/api/files路由的GET参数传入。并且从最初的database.ts可以看到,flag文件属于superuser用户,所以这里也需要通过/api/files路由传入一条username=admin&filename=flag的数据,那么checksum就可以随意设置;
4.又因为/api/files路由只能通过127.0.0.1访问,所以必须通过/admin路由的XSS来构造SSRF请求;
5./admin路由的XSS过滤只是对content参数过滤,所以只需以content数组的形式传入即可绕过。
综上,目标:以admin登录,/admin路由POST传入payload,/api/files/:id访问
Payload如下
content[]=<img+src%3D"http%3A//127.0.0.1:8888/api/files?username%3Dadmin%26filename%3D./flag%26checksum%3D999">
或
content[]=%3Cscript%3E%0Avar%20xhr%20%3D%20new%20XMLHttpRequest()%3Bxhr.open(%22GET%22%2C%20%22http%3A%2F%2F127.0.0.1%3A8888%2Fapi%2Ffiles%3Fusername%3Dadmin%26filename%3D.%2Fflag%26checksum%3D999%22%2C%20true)%3Bxhr.send()%3B%0A%3C%2Fscript%3E

再访问/api/files/999,就会把文件下载下来,打开即可看到flag
注:这里端口用8888是因为app.ts文件中设置路由监听端口是8888
