diff --git a/src/main/java/com/aspectsecurity/unittestsweb/xxetestcases/ExternalDTDDisabledParameterTestCase.java b/src/main/java/com/aspectsecurity/unittestsweb/xxetestcases/ExternalDTDDisabledParameterTestCase.java new file mode 100644 index 0000000..3305f64 --- /dev/null +++ b/src/main/java/com/aspectsecurity/unittestsweb/xxetestcases/ExternalDTDDisabledParameterTestCase.java @@ -0,0 +1,40 @@ +package com.aspectsecurity.unittestsweb.xxetestcases; + +import com.aspectsecurity.unittestsweb.XXETestCase; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParserFactory; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + + +@WebServlet({"/externaldtddisabledxmlparameter", "/externaldtddisabled"}) +public class ExternalDTDDisabledParameterTestCase extends XXETestCase { + + @Override + protected void doTest(HttpServletRequest request, HttpServletResponse response) throws IOException { + try { + final SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + final XMLReader reader = factory.newSAXParser().getXMLReader(); + TestHandler testHandler = new TestHandler(); + reader.setContentHandler(testHandler); + InputStream is = new ByteArrayInputStream(request.getParameter("payload").getBytes()); + reader.parse(new InputSource(is)); // if contrast protect is blocking XXE, exception is thrown here. + // If testData were returned to the client, they would see secret/sensitive data. + printResults(false, testHandler.getTestData(), response); + } catch (SAXException|SecurityException ex) { + printResults(true, ex, response); // safe: exception thrown when parsing XML + } catch (ParserConfigurationException e) { + throw new IOException(e); + } + } + +} diff --git a/src/main/java/com/aspectsecurity/unittestsweb/xxetestcases/JAXBUnsafeSaxSourceTestCase.java b/src/main/java/com/aspectsecurity/unittestsweb/xxetestcases/JAXBUnsafeSaxSourceTestCase.java new file mode 100644 index 0000000..f4a314f --- /dev/null +++ b/src/main/java/com/aspectsecurity/unittestsweb/xxetestcases/JAXBUnsafeSaxSourceTestCase.java @@ -0,0 +1,50 @@ +package com.aspectsecurity.unittestsweb.xxetestcases; + +import com.aspectsecurity.unittests.jaxb.BookType; +import com.aspectsecurity.unittests.jaxb.Collection; +import com.aspectsecurity.unittestsweb.XXETestCase; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.sax.SAXSource; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.List; + +@WebServlet("/jaxbunsafesaxsource") +public class JAXBUnsafeSaxSourceTestCase extends XXETestCase { + + + @Override + protected void doTest(HttpServletRequest request, HttpServletResponse response) throws IOException { + try { + Unmarshaller unmarshaller = JAXBContext.newInstance(Collection.class).createUnmarshaller(); + SAXSource saxSource = new SAXSource( + SAXParserFactory.newInstance().newSAXParser().getXMLReader(), + new InputSource(new ByteArrayInputStream(request.getParameter("payload").getBytes()))); + Collection collection = unmarshaller.unmarshal(saxSource, Collection.class).getValue(); + Collection.Books booksType = collection.getBooks(); + List bookList = booksType.getBook(); + String discount = ""; + for (BookType book : bookList) { + discount = book.getPromotion().getDiscount().trim(); + } + printResults(false, discount, response); + } + catch (SecurityException ex) { + printResults(true, ex, response); // safe: exception thrown when parsing XML + } catch (ParserConfigurationException|SAXException|JAXBException e) { + throw new IOException(e); + } + } + + +} diff --git a/src/main/java/com/aspectsecurity/unittestsweb/xxetestcases/SaxParserExternalDTDDisabledParameterTestCase.java b/src/main/java/com/aspectsecurity/unittestsweb/xxetestcases/SaxParserExternalDTDDisabledParameterTestCase.java new file mode 100644 index 0000000..38d9933 --- /dev/null +++ b/src/main/java/com/aspectsecurity/unittestsweb/xxetestcases/SaxParserExternalDTDDisabledParameterTestCase.java @@ -0,0 +1,43 @@ +package com.aspectsecurity.unittestsweb.xxetestcases; + +import com.aspectsecurity.unittestsweb.XXETestCase; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + + +@WebServlet({"/saxparserexternaldtddisabledxmlparameter","/saxparserexternaldtddisabled"}) +public class SaxParserExternalDTDDisabledParameterTestCase extends XXETestCase { + @Override + protected void doTest(HttpServletRequest request, HttpServletResponse response) throws IOException { + try { + final SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + final SAXParser parser = factory.newSAXParser(); + TestHandler testHandler = new TestHandler(); + InputStream is = new ByteArrayInputStream(request.getParameter("payload").getBytes()); + parser.parse(new InputSource(is), testHandler); // if contrast protect is blocking XXE, exception is thrown here. + // If testData were returned to the client, they would see secret/sensitive data. + printResults(false, testHandler.getTestData(), response); + } catch (SAXException|SecurityException ex) { + printResults(true, ex, response); // safe: exception thrown when parsing XML + } catch (ParserConfigurationException e) { + throw new IOException(e); + } + } + + +} diff --git a/src/main/java/com/aspectsecurity/unittestsweb/xxetestcases/TestHandler.java b/src/main/java/com/aspectsecurity/unittestsweb/xxetestcases/TestHandler.java new file mode 100644 index 0000000..b85b7aa --- /dev/null +++ b/src/main/java/com/aspectsecurity/unittestsweb/xxetestcases/TestHandler.java @@ -0,0 +1,46 @@ +package com.aspectsecurity.unittestsweb.xxetestcases; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** Simple Handler that extracts the contents of any element named {@code }. */ +public class TestHandler extends DefaultHandler { + private final StringBuilder testData; + boolean inTest; + + public TestHandler() { + this.testData = new StringBuilder(); + inTest = false; + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { + super.startElement(uri, localName, qName, attributes); + if ("test".equals(qName) || "test".equals(localName)) { + inTest = true; + } + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + if ("test".equals(qName) || "test".equals(localName)) { + inTest = false; + } + super.endElement(uri, localName, qName); + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + if (inTest) { + testData.append(ch, start, length); + } + super.characters(ch, start, length); + } + + public String getTestData() { + return testData.toString(); + } +} + diff --git a/src/main/resources/maliciousdtd.dtd b/src/main/resources/maliciousdtd.dtd new file mode 100644 index 0000000..2c5c9a4 --- /dev/null +++ b/src/main/resources/maliciousdtd.dtd @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/xxetest2web.xml b/src/main/resources/xxetest2web.xml new file mode 100644 index 0000000..100cad1 --- /dev/null +++ b/src/main/resources/xxetest2web.xml @@ -0,0 +1,7 @@ + + + + %dtd; + ]> +&xxe; \ No newline at end of file diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp index fa94ef2..f327d7a 100644 --- a/src/main/webapp/index.jsp +++ b/src/main/webapp/index.jsp @@ -51,6 +51,8 @@