blob: 5572634fa6c7c8d296f5557271fcacac8380ca30 [file]
/*
*
* 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);
}
}
}
}