|  | 
|  | 1 | +package com.baeldung.rce; | 
|  | 2 | + | 
|  | 3 | +import com.sun.net.httpserver.HttpServer; | 
|  | 4 | +import com.thoughtworks.xstream.XStream; | 
|  | 5 | +import com.thoughtworks.xstream.security.NoTypePermission; | 
|  | 6 | +import com.thoughtworks.xstream.security.NullPermission; | 
|  | 7 | +import com.thoughtworks.xstream.security.PrimitiveTypePermission; | 
|  | 8 | + | 
|  | 9 | +import java.io.IOException; | 
|  | 10 | +import java.net.InetSocketAddress; | 
|  | 11 | +import java.util.HashSet; | 
|  | 12 | +import java.util.Set; | 
|  | 13 | + | 
|  | 14 | +/** | 
|  | 15 | + * Web application which is intentionally vulnerable to an XStream remote code | 
|  | 16 | + * exploitation (RCE). | 
|  | 17 | + * | 
|  | 18 | + * <p> | 
|  | 19 | + * This test application is meant to maintain a set of {@link Person} models. It | 
|  | 20 | + * exposes a "/persons" endpoint which supports the following operations: | 
|  | 21 | + * | 
|  | 22 | + * <ol> | 
|  | 23 | + * <li>{@code POST} XML for adding a new {@link Person} to the set | 
|  | 24 | + * <li>{@code GET} for retrieving the set of {@link Person} models as XML | 
|  | 25 | + * </ol> | 
|  | 26 | + * | 
|  | 27 | + * The {@code POST} handler is vulnerable to an RCE exploit. | 
|  | 28 | + */ | 
|  | 29 | +public final class App { | 
|  | 30 | + | 
|  | 31 | + public static App createHardened(int port) { | 
|  | 32 | + final XStream xstream = new XStream(); | 
|  | 33 | + xstream.addPermission(NoTypePermission.NONE); | 
|  | 34 | + xstream.addPermission(NullPermission.NULL); | 
|  | 35 | + xstream.addPermission(PrimitiveTypePermission.PRIMITIVES); | 
|  | 36 | + xstream.allowTypes(new Class<?>[] { Person.class }); | 
|  | 37 | + return new App(port, xstream); | 
|  | 38 | + } | 
|  | 39 | + | 
|  | 40 | + public static App createVulnerable(int port) { | 
|  | 41 | + return new App(port, new XStream()); | 
|  | 42 | + } | 
|  | 43 | + | 
|  | 44 | + private final int port; | 
|  | 45 | + private final Set<Person> persons; | 
|  | 46 | + private final XStream xstream; | 
|  | 47 | + private HttpServer server; | 
|  | 48 | + | 
|  | 49 | + private App(int port, XStream xstream) { | 
|  | 50 | + this.port = port; | 
|  | 51 | + persons = new HashSet<>(); | 
|  | 52 | + // this app is vulnerable because XStream security is not configured | 
|  | 53 | + this.xstream = xstream; | 
|  | 54 | + this.xstream.alias("person", Person.class); | 
|  | 55 | + } | 
|  | 56 | + | 
|  | 57 | + void start() throws IOException { | 
|  | 58 | + server = HttpServer.create(new InetSocketAddress("localhost", port), 0); | 
|  | 59 | + server.createContext("/persons", exchange -> { | 
|  | 60 | + switch (exchange.getRequestMethod()) { | 
|  | 61 | + case "POST": | 
|  | 62 | + final Person person = (Person) xstream.fromXML(exchange.getRequestBody()); | 
|  | 63 | + persons.add(person); | 
|  | 64 | + exchange.sendResponseHeaders(201, 0); | 
|  | 65 | + exchange.close(); | 
|  | 66 | + break; | 
|  | 67 | + case "GET": | 
|  | 68 | + exchange.sendResponseHeaders(200, 0); | 
|  | 69 | + xstream.toXML(persons, exchange.getResponseBody()); | 
|  | 70 | + exchange.close(); | 
|  | 71 | + break; | 
|  | 72 | + default: | 
|  | 73 | + exchange.sendResponseHeaders(405, 0); | 
|  | 74 | + exchange.close(); | 
|  | 75 | + } | 
|  | 76 | + }); | 
|  | 77 | + server.start(); | 
|  | 78 | + } | 
|  | 79 | + | 
|  | 80 | + void stop() { | 
|  | 81 | + if (server != null) { | 
|  | 82 | + server.stop(0); | 
|  | 83 | + } | 
|  | 84 | + } | 
|  | 85 | + | 
|  | 86 | + int port() { | 
|  | 87 | + if (server == null) | 
|  | 88 | + throw new IllegalStateException("Server not started"); | 
|  | 89 | + return server.getAddress() | 
|  | 90 | + .getPort(); | 
|  | 91 | + } | 
|  | 92 | +} | 
0 commit comments