CTF题解记录-web篇
记录打比赛时期做的一些web练习题
后几周的因为架构变复杂了,就懒得记录里,思路其实都差不多。
WEEK 1
加了报错的注入
- 查看页面源码,注释说明有sql注入
- 然后测试一下username和password,发现有过滤
- bp简单爆破一下过滤字符,得出结果:
username过滤了()=#等字符以及order by等关键字, password过滤了#=;等字符以及updatexml()和ext()两个报错函数。
- 在伟大的搜索引擎的帮助下,我们查到了这两个报错函数的信息,顺便搜到了注入格式。
- 爆数据库测试一下可用
- 然后爆表名,列名,flag,注意到
=被过滤,换LIKE,结果LIKE也被过滤,于是换上了正则regexp,同样show也被过滤。
1 | payload: |
看了dalao的wp后发现password并没有过滤exp()函数可以直接利用password构造payload,不用分割注入。
flag{err0r_b4sed_sqli_+_hpf}
上传绕过
- 这道题看上去是一个文件上传漏洞,查看源码,注意到了
hidden表单,感觉有用。 - 按要求传文件,报错要jpg,传jpg又报错要php,估计是有两次验证。
- 直接字符截断没有用,想起那个表单,估计是要构造路径截断。
- 构造路径,进hex改包再发即可得flag!
flag{SimCTF_huachuan}
后台登录
- 源码有代码,估计是要构造md5注入。
- 怎么构造?
- 怎么构造?
- 怎么构造?
- 算了看wp吧
- ……
- 直接用文件名
ffifdyop构造md5有了'or'xxxx'的语句,注入成功,告辞。
flag{ffifdyop_has_trash}
WEEK 2
1. 没时间解释了
moctf: http://119.23.73.3:5006/web2/index2.php
抓包阻止重定向->路径构造->时间竞争
常规性的F12查看源码,并没有找到什么有用的信息,测试一波.svn和.git的源码泄露,仍然并没有什么有用的信息,遂放弃。
自闭一段时间后,观察到urlindex2.php觉得可能有问题,然后尝试访问index.php,重定向回了index2.php,于是改用BP截包,得到index.php返回如下:
访问uploadsomething.php,令人熟悉的表单映入眼帘,随意输入后显示:
估计是一个路径构造,写了php一句话webshell并没有连上,试着访问了一下返回Too Slow,根据标题和该返回猜测是一个时间竞争,同时观察到不同提交返回的base64的路径是相同的,那么可以写脚本去构造路径并读取。
然而……
脚本都是Too Slow!这不合理啊!后来用BP Intruder持续抓包,获得flag,至于脚本比BP慢这实在没有想到。
2. welcome to bugkuctf
bugkuctf: http://123.206.87.240:8006/test1/
php协议->文件包含->php反序列化
这道题小硬核。
常规F12得到源码:
尝试直接访问hint.php无果,注意到源码中文件包含函数file_get_contents(),
于是可以利用php协议操作数据流的方法,将字符串构造文件利用php://input上传$user,再利用php://filter获取返回的数据里,相关参考PHP官方文档:http://php.net/manual/zh/wrappers.php.php 。php://filter相关信息:
利用这个方法我们可以这样构造payload:
从而获得index.php和hint.php的源码base64:
继续代码审计。
观察得到这是一个php反序列化漏洞,利用在hint.php中得到的类名为Flag,使用的是__toString魔法函数,由此我们可以构造反序列化payload为O:4:"Flag":1:{s:4:"file";s:8:"flag.php";},
并得到flag:
3. never give up
bugkuctf: http://123.206.87.240:8006/test/hello.php
php任意文件读取->解码*3->字符截断
这道题比较逗逼。
常规F12得到两行代码,带入注释里的1p.html发现重定向到了BugkuCTF的首页,于是Postman抓包得到如下结果:
URL解码一把梭!
base64解码一把梭!
又是URL?一把梭!
经过三次大起大落的编码转化,最后得到源码:
注意到代码中有两处矛盾:
$id即是0又不是0$b的首位字符必须是4又不能是4
对于前者,采用php松散比较,利用'aaa' == 0的特性绕过,对于后者,采用字符截断(对于php版本的判断可以根据$data实现,这里已确定可截断)
那么!那么!那么!
就阔以构造粗配楼德payload:
id=aaa&a=php://input&b=%00lutyhhh
得到flag:
似乎安排得明明白白…直到我看到其他大佬的wp…
我的表情:
看来似出题人忘记屏蔽隔离了,不然这题也是小硬核。
WEEK 3
Shrine
Tokyo Western CTF 2018
Link: https://github.com/CTFTraining/westerns_2018_shrine
Python模板注入 Python沙箱绕过 flask审计
因为了解Python方面的审计去网上找到了这道CTF题目,这道题是用docker本地复现的,因为涉及到不会的知识点,所以到一半就照着WP继续了。
1. 代码审计
这道题目非常直接,进入就是代码,在调整格式后我们可以得到源码如下:
从源码可知:
- 设定了两个路由,一个路由读取文件源码,另一个路由读进一个字符串对象。
- 第二个路由中读进的字符串,经过了函数
safe_jinja的过滤,过滤函数删除了(和)并强行将config和self字符设为空。
这时候思路已经比较明确了,要么读config['FLAG']要么读os.envrion.pop('FLAG'),但因( )被过滤第二个显然不行,config和self都被强制置空,那么就只能想别的方法,然而我没有想到。
SSTI
继续分析元源码,程序利用模板向后端传入了变量,因此存在模板注入(SSTI),我们可以简单构造一个Poc验证:
在这里现补充模板的相关知识,模板用于将服务端返还内容渲染成前端页面,为了分离前端代码和后端变量,模板中设定 {{ }}包括的内容为后端变量,{% %} 包括的内容为逻辑语句。
相关模板类型的Poc可以参考下图:
沙箱逃逸
之后参考了一个大佬的WP(tokyo-westerns-ctf-2018-web-wp)
,学习到了一些骚炫的姿势,这里复现一下他的思路
- 既然
config和self都被置空了,那么只能用其他方法读取到这个全局变量,就需要参考Flask框架的文档,这里应用到了python沙箱逃逸的方法,就我理解这方法就是利用python对象之间的引用关系来调用被禁用的函数对象,文中提供了一个flask上下文本地变量current_app,使用方法__globals__['current_app'].config['FLAG']。,通过在尝试这个payload后的到以下结果:
显然不能直接访问__globals__全局变量,目测有WAF需要再次逃逸。
2. 文中又提供了两种方法:get_flashed_messages和url_for这两个函数添加了current_app的引用,于是构造:url_for.__globals__['current_app'].config['FLAG']
最后payload:http://127.0.0.1:8081/shrine/{{url_for.__globals__['current_app'].config['FLAG']}}
获得flag:
在沙箱逃逸这一块的还有很多问题,比如怎么查到各对象之间的引用关系,哪些对象在哪些作用域有效,这里只积累了一个思路,以后需要在挖掘。
PS:刚刚写完SSTI的内容,hexo deploy的时候报错,一查结果是前面模板的花括号导致了解析,妙啊妙啊。




































