BMZCTF:file-vault

Posted 末 初

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BMZCTF:file-vault相关的知识,希望对你有一定的参考价值。

http://www.bmzclub.cn/challenges#file-vault

在这里插入图片描述
这是一道很好反序列化字符串溢出的题目,首先打开容器看到这是一个上传点
在这里插入图片描述
先进行目录扫描,发现存在vim的备份文件index.php~
在这里插入图片描述
查看index.php~得到源码如下

<?php

error_reporting(0);
include('secret.php');

$sandbox_dir = 'sandbox/'.sha1($_SERVER['REMOTE_ADDR']);
global $sandbox_dir;

function myserialize($a, $secret) {
    $b = str_replace("../","./", serialize($a)); 
    return $b.hash_hmac('sha256', $b, $secret); 
}

function myunserialize($a, $secret) { 
    if(substr($a, -64) === hash_hmac('sha256', substr($a, 0, -64), $secret)){
        return unserialize(substr($a, 0, -64));
    }
}
   
class UploadFile {

    function upload($fakename, $content) {
        global $sandbox_dir;
        $info = pathinfo($fakename);
        $ext = isset($info['extension']) ? ".".$info['extension'] : '.txt';
        file_put_contents($sandbox_dir.'/'.sha1($content).$ext, $content);
        $this->fakename = $fakename;        
        $this->realname = sha1($content).$ext;
    }
    function open($fakename, $realname) {
        global $sandbox_dir;
        $analysis = "$fakename is in folder $sandbox_dir/$realname.";
        return $analysis;
    }
}

if(!is_dir($sandbox_dir)) {
    mkdir($sandbox_dir);
}

if(!is_file($sandbox_dir.'/.htaccess')) {
    file_put_contents($sandbox_dir.'/.htaccess', "php_flag engine off");
}

if(!isset($_GET['action'])) {
    $_GET['action'] = 'home';
}


if(!isset($_COOKIE['files'])) {
    setcookie('files', myserialize([], $secret));
    $_COOKIE['files'] = myserialize([], $secret);
}


switch($_GET['action']){
    case 'home':
    default:
        $content = "<form method='post' action='index.php?action=upload' enctype='multipart/form-data'><input type='file' name='file'><input type='submit'/></form>";

        $files = myunserialize($_COOKIE['files'], $secret);
        if($files) {
            $content .= "<ul>";
            $i = 0;
            foreach($files as $file) {
                $content .= "<li><form method='POST' action='index.php?action=changename&i=".$i."'><input type='text' name='newname' value='".htmlspecialchars($file->fakename)."'><input type='submit' value='Click to edit name'></form><a href='index.php?action=open&i=".$i."' target='_blank'>Click to show locations</a></li>";
                $i++;
            }
            $content .= "</ul>";
        }
        echo $content;
        break;
    case 'upload':
        if($_SERVER['REQUEST_METHOD'] === "POST") {
            if(isset($_FILES['file'])) {
                $uploadfile = new UploadFile;
                $uploadfile->upload($_FILES['file']['name'], file_get_contents($_FILES['file']['tmp_name']));
                $files = myunserialize($_COOKIE['files'], $secret);
                $files[] = $uploadfile;
                setcookie('files', myserialize($files, $secret));
                header("Location: index.php?action=home");
                exit;
            }
        }
        break;
    case 'changename':
        if($_SERVER['REQUEST_METHOD'] === "POST") {        
            $files = myunserialize($_COOKIE['files'], $secret);
            if(isset($files[$_GET['i']]) && isset($_POST['newname'])){
                $files[$_GET['i']]->fakename = $_POST['newname'];
            }
            setcookie('files', myserialize($files, $secret));            
        }
        header("Location: index.php?action=home");
        exit;
    case 'open':
        $files = myunserialize($_COOKIE['files'], $secret);
        if(isset($files[$_GET['i']])){
            echo $files[$_GET['i']]->open($files[$_GET['i']]->fakename, $files[$_GET['i']]->realname);
        }
        exit;
    case 'reset':
        setcookie('files', myserialize([], $secret));
        $_COOKIE['files'] = myserialize([], $secret);
        array_map('unlink', glob("$sandbox_dir/*"));
        header("Location: index.php?action=home");
        exit;
}

代码稍微比较多一点,我们一段一段来分析一下,先看第一段

$sandbox_dir = 'sandbox/'.sha1($_SERVER['REMOTE_ADDR']);
global $sandbox_dir;

function myserialize($a, $secret) {
    $b = str_replace("../","./", serialize($a)); 
    return $b.hash_hmac('sha256', $b, $secret); 
}

function myunserialize($a, $secret) { 
    if(substr($a, -64) === hash_hmac('sha256', substr($a, 0, -64), $secret)){
        return unserialize(substr($a, 0, -64));
    }
}

$sanbox_dir即将访问者的IP经过SHA1加密拼接在sanbox后构成单独的路径,例如:sanbox/4b84b15bff6ee5796152495a230e45e3d7e947d9

myserialize(),将传入的$a序列化,然后进行一个字符串的替换(这里是形成反序列化字符串溢出的关键点)得到$b,最后返回SHA256有未知密钥($secret)加密后的$b作为签名,拼接上$b的结果。

myunserialize(),首先截取$a的后64位部分与SHA256加密后的截掉末尾64位$a,这里就是做一个签名验证,验证序列化字符串加密后是否还是myserialize()返回的正确签名,防止攻击者私自修改序列化字符串。最终返回反序列化后得对象。


接着看这段代码

if(!is_dir($sandbox_dir)) {
    mkdir($sandbox_dir);
}

if(!is_file($sandbox_dir.'/.htaccess')) {
    file_put_contents($sandbox_dir.'/.htaccess', "php_flag engine off");
}

if(!isset($_GET['action'])) {
    $_GET['action'] = 'home';
}


if(!isset($_COOKIE['files'])) {
    setcookie('files', myserialize([], $secret));
    $_COOKIE['files'] = myserialize([], $secret);
}

$sanbox_dir路径不存在时,创建$sanbox_dir。检测在$sanbox_dir下是否存在.htaccess文件,不存在的话在$sandbox_dir下创建.htaccess,并写入php_flag engine off。该配置作用是禁用当前目录下的PHP解析功能。
在这里插入图片描述
action默认操作为home,检查是否设置Cookie['files'],未设置的话设置Cookie: files,值为myserialize($a, $secret)的返回值,$a的类型为数组。$secert一直都是未知的。


接着分析

class UploadFile {

    function upload($fakename, $content) {
        global $sandbox_dir;
        $info = pathinfo($fakename);
        $ext = isset($info['extension']) ? ".".$info['extension'] : '.txt';
        file_put_contents($sandbox_dir.'/'.sha1($content).$ext, $content);
        $this->fakename = $fakename;        
        $this->realname = sha1($content).$ext;
    }
    function open($fakename, $realname) {
        global $sandbox_dir;
        $analysis = "$fakename is in folder $sandbox_dir/$realname.";
        return $analysis;
    }
}

UploadFile类中存在upload()open()两个方法,先看UploadFile::upload(),将上传的文件写入$sandbox_dir下,存储名称为文件内容的SHA1加密后的字符,如无后缀即默认.txt后缀。没有文件类型限制。$this->fakename即上传文件的名称,$this->realname是文件在服务器上存储的名称。

UploadFile::open()即返回指定的fakename以及realname的存储路径。


接着分析action传入不同值的操作

switch($_GET['action']){
    case 'home':
    default:
        $content = "<form method='post' action='index.php?action=upload' enctype='multipart/form-data'><input type='file' name='file'><input type='submit'/></form>";

        $files = myunserialize($_COOKIE['files'], $secret);
        if($files) {
            $content .= "<ul>";
            $i = 0;
            foreach($files as $file) {
                $content .= "<li><form method='POST' action='index.php?action=changename&i=".$i."'><input type='text' name='newname' value='".htmlspecialchars($file->fakename)."'><input type='submit' value='Click to edit name'></form><a href='index.php?action=open&i=".$i."' target='_blank'>Click to show locations</a></li>";
                $i++;
            }
            $content .= "</ul>";
        }
        echo $content;
        break;
    case 'upload':
        if($_SERVER['REQUEST_METHOD'] === "POST") {
            if(isset($_FILES['file'])) {
                $uploadfile = new UploadFile;
                $uploadfile->upload($_FILES['file']['name'], file_get_contents($_FILES['file']['tmp_name']));
                $files = myunserialize($_COOKIE['files'], $secret);
                $files[] = $uploadfile;
                setcookie('files', myserialize($files, $secret));
                header("Location: index.php?action=home");
                exit;
            }
        }
        break;
    case 'changename':
        if($_SERVER['REQUEST_METHOD'] === "POST") {        
            $files = myunserialize($_COOKIE['files'], $secret);
            if(isset($files[$_GET['i']]) && isset($_POST['newname'])){
                $files[$_GET['i']]->fakename = $_POST['newname'];
            }
            setcookie('files', myserialize($files, $secret));            
        }
        header("Location: index.php?action=home");
        exit;
    case 'open':
        $files = myunserialize($_COOKIE['files'], $secret);
        if(isset($files[$_GET['i']])){
            echo $files[$_GET['i']]->open($files[$_GET['i']]->fakename, $files[$_GET['i']]->realname);
        }
        exit;
    

以上是关于BMZCTF:file-vault的主要内容,如果未能解决你的问题,请参考以下文章

BMZCTF WEB_ezeval

BMZCTF-WEB-simple_pop

BMZCTF-WEB-easy_exec

bmzctf刷题ssrfme

BMZCTF:个人所得税

bmzctf 刷题 hitcon_2017_ssrfme