| /* |
| * |
| * Derby - Class org.apache.derbyTesting.functionTests.tests.lang.XMLXXETest.java |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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. |
| */ |
| package org.apache.derbyTesting.functionTests.tests.lang; |
| |
| import java.io.File; |
| import java.io.PrintWriter; |
| import java.net.URL; |
| |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.sql.Statement; |
| import java.util.Locale; |
| |
| import junit.framework.Test; |
| |
| import org.apache.derbyTesting.junit.BaseJDBCTestCase; |
| import org.apache.derbyTesting.junit.BaseTestSuite; |
| import org.apache.derbyTesting.junit.JDBC; |
| import org.apache.derbyTesting.junit.LocaleTestSetup; |
| import org.apache.derbyTesting.junit.SecurityManagerSetup; |
| import org.apache.derbyTesting.junit.SupportFilesSetup; |
| import org.apache.derbyTesting.junit.TestConfiguration; |
| import org.apache.derbyTesting.junit.XML; |
| |
| /** |
| * XMLXXETest this test suite runs with NO SECURITY MANAGER. It is designed |
| * to explore the so-called XXE family of vulnerabilities. For more |
| * information, try: |
| * |
| * http://h3xstream.github.io/find-sec-bugs/bugs.htm#XXE_DOCUMENT |
| * https://www.owasp.org/index.php/XML_External_Entity_%28XXE%29_Processing |
| * http://www.ws-attacks.org/index.php/XML_Entity_Expansion |
| * http://www.ws-attacks.org/index.php/XML_External_Entity_DOS |
| * http://www.ws-attacks.org/index.php/XML_Entity_Reference_Attack |
| */ |
| public final class XMLXXETest extends BaseJDBCTestCase { |
| |
| public XMLXXETest(String name) |
| { |
| super(name); |
| } |
| |
| /** |
| * Only return a suite IF the testing classpath has the |
| * required XML classes. Otherwise just return an empty suite. |
| */ |
| public static Test suite() |
| { |
| BaseTestSuite suite = |
| new BaseTestSuite("XML XXE Vulnerability tests\n"); |
| |
| if (!XML.classpathMeetsXMLReqs()) |
| return suite; |
| |
| String[] testFiles = new String[] { |
| "functionTests/tests/lang/xmlOptimizerXXE1Payload.trace", |
| "functionTests/tests/lang/xmlOptimizerXXE1.trace", |
| "functionTests/tests/lang/xmlOptimizerXXE2.trace" |
| }; |
| |
| suite.addTest( new SupportFilesSetup( |
| TestConfiguration.defaultSuite(XMLXXETest.class), |
| testFiles ) ); |
| |
| // Need to run in US locale because the test checks error messages |
| // which may be different in different locales (DERBY-6869). |
| return new LocaleTestSetup( |
| SecurityManagerSetup.noSecurityManager(suite), |
| Locale.US); |
| } |
| |
| /** |
| * Test for Derby-6807. We create a file with some (presumably sensitive) |
| * data in it, and check whether an XML query can be tricked into reading |
| * the data from that file. If it can, a security leak has occurred. |
| */ |
| |
| public void testDerby6807FileAccess () |
| throws Exception |
| { |
| File password = null; |
| String path; |
| |
| password = new File("test6807.txt"); |
| PrintWriter writer = new PrintWriter("test6807.txt", "UTF-8"); |
| writer.print("HelloWorld"); |
| writer.close(); |
| path = password.getAbsolutePath(); |
| |
| Statement s = createStatement(); |
| |
| s.execute("CREATE TABLE xml_data(xml_col XML)"); |
| |
| String stmt = "INSERT INTO xml_data(xml_col) VALUES(XMLPARSE(DOCUMENT" |
| + "'<!DOCTYPE foo [<!ENTITY xxe SYSTEM \"file:" |
| + path +"\" >]><yolo>&xxe;</yolo>'" |
| + "PRESERVE WHITESPACE))"; |
| |
| // System.out.println( stmt ); |
| |
| s.execute( stmt ); |
| |
| JDBC.assertSingleValueResultSet( |
| s.executeQuery( |
| "SELECT XMLSERIALIZE(xml_col AS CLOB) FROM xml_data"), |
| "<yolo/>"); |
| |
| password.delete(); |
| } |
| |
| public void testDerby6807BillionLaughs() throws SQLException |
| { |
| Statement st = createStatement(); |
| st.executeUpdate("create table xml_billion_laughs( xml_col xml )"); |
| |
| String xmlBillionLaughs = "insert into xml_billion_laughs( xml_col ) values(" + |
| " xmlparse(document '" + |
| "<!DOCTYPE lolz [" + |
| " <!ENTITY lol \"lol\">" + |
| " <!ELEMENT lolz (#PCDATA)>" + |
| " <!ENTITY lol1 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">" + |
| " <!ENTITY lol2 \"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;\">" + |
| " <!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">" + |
| " <!ENTITY lol4 \"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;\">" + |
| " <!ENTITY lol5 \"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;\">" + |
| " <!ENTITY lol6 \"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;\">" + |
| " <!ENTITY lol7 \"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;\">" + |
| " <!ENTITY lol8 \"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;\">" + |
| " <!ENTITY lol9 \"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;\">" + |
| "]>" + |
| "<lolz>&lol9;</lolz>' PRESERVE WHITESPACE))"; |
| |
| assertStatementError( "2200M", st, xmlBillionLaughs ); |
| |
| // Since we can't even parse the document, we never get to the point |
| // where we might try to serialize it back out. |
| // "select xmlserialize(xml_col as clob) from xml_billion_laughs"); |
| } |
| |
| public void testDerby6807FileAccessVTI() |
| throws Exception |
| { |
| String VULNERABLE_XML = "xmlOptimizerXXE1.trace"; |
| URL traceURL = SupportFilesSetup.getReadOnlyURL( VULNERABLE_XML ); |
| //URL payloadURL = SupportFilesSetup.getReadOnlyURL( |
| // "xmlOptimizerXXE1Payload.trace" ); |
| |
| Statement s = createStatement(); |
| s.execute( |
| "create function decorationURLChildOnly\n" + |
| "(\n" + |
| " urlString varchar( 32672 ),\n" + |
| " rowTag varchar( 32672 ),\n" + |
| " childTags varchar( 32672 )...\n" + |
| ")\n" + |
| "returns table\n" + |
| "(\n" + |
| " conglomerateName varchar( 36 ),\n" + |
| " joinStrategy varchar( 200 ),\n" + |
| " estimatedCost double,\n" + |
| " estimatedRowCount varchar( 200 )\n" + |
| ")\n" + |
| "language java parameter style derby_jdbc_result_set no sql\n" + |
| "external name 'org.apache.derby.vti.XmlVTI.xmlVTIFromURL'\n" |
| ); |
| s.execute( |
| "create view decorationURLChildOnly as\n" + |
| "select * from table\n" + |
| "(\n" + |
| " decorationURLChildOnly\n" + |
| " (\n" + |
| " '" + traceURL.toString() + "',\n" + |
| " 'decoration',\n" + |
| " 'decConglomerateName', 'decJoinStrategy',\n" + |
| " 'ceEstimatedCost', 'ceEstimatedRowCount'\n" + |
| " )\n" + |
| ") v\n" |
| ); |
| ResultSet rs = s.executeQuery( |
| "select distinct conglomerateName, joinStrategy," + |
| " estimatedCost, estimatedRowCount\n" + |
| "from decorationURLChildOnly\n" + |
| "where conglomerateName like '%_A' and " + |
| " estimatedCost is not null\n" + |
| "order by conglomerateName, joinStrategy, " + |
| " estimatedCost, estimatedRowCount\n" |
| ); |
| assertTrue( rs.next() ); |
| |
| assertEquals( "null", rs.getString( 4 ).trim() ); |
| |
| assertFalse( rs.next() ); |
| } |
| |
| public void testDerby6807BillionLaughsVTI() |
| throws Exception |
| { |
| String VULNERABLE_XML = "xmlOptimizerXXE2.trace"; |
| URL traceURL = SupportFilesSetup.getReadOnlyURL( VULNERABLE_XML ); |
| |
| Statement s = createStatement(); |
| s.execute( |
| "create function lolzURL\n" + |
| "(\n" + |
| " urlString varchar( 32672 ),\n" + |
| " rowTag varchar( 32672 ),\n" + |
| " childTags varchar( 32672 )...\n" + |
| ")\n" + |
| "returns table\n" + |
| "(\n" + |
| " lolz varchar( 32000 )\n" + |
| ")\n" + |
| "language java parameter style derby_jdbc_result_set no sql\n" + |
| "external name 'org.apache.derby.vti.XmlVTI.xmlVTIFromURL'\n" |
| ); |
| s.execute( |
| "create view lolzURL as\n" + |
| "select * from table\n" + |
| "(\n" + |
| " lolzURL\n" + |
| " (\n" + |
| " '" + traceURL.toString() + "',\n" + |
| " 'lolz'\n" + |
| " )\n" + |
| ") v\n" |
| ); |
| try { |
| ResultSet rs = s.executeQuery( "select lolz from lolzURL" ); |
| assertTrue( rs.next() ); |
| |
| // This next line will need to change once DERBY-6807 is fixed: |
| fail( "Expected SAXParseException" ); |
| } catch ( Throwable e ) { |
| if (!e.getMessage().contains("entity expansions")) { |
| fail("Expected SAXParseException", e); |
| } |
| } |
| } |
| } |