diff --git a/tools/aegis4j/build.gradle b/tools/aegis4j/build.gradle index e4084b0261..e3242d73ca 100644 --- a/tools/aegis4j/build.gradle +++ b/tools/aegis4j/build.gradle @@ -37,6 +37,7 @@ dependencies { testImplementation "io.netty:netty:3.10.5.Final" testImplementation "com.h2database:h2:$h2_version" testImplementation "javax.servlet:javax.servlet-api:${servlet_version}" // Needed to load H2 Web Console + testImplementation "org.liquibase:liquibase-core:$liquibase_version" } sourceCompatibility = 8 diff --git a/tools/aegis4j/src/main/java/net/gredler/aegis4j/Patcher.java b/tools/aegis4j/src/main/java/net/gredler/aegis4j/Patcher.java index 64fd104443..a7fff21698 100644 --- a/tools/aegis4j/src/main/java/net/gredler/aegis4j/Patcher.java +++ b/tools/aegis4j/src/main/java/net/gredler/aegis4j/Patcher.java @@ -106,13 +106,18 @@ public final class Patcher implements ClassFileTransformer { System.err.println("ERROR: Method not found: " + className + "." + mod.methodName); } for (CtMethod method : methods) { - method.setBody(mod.newBody); + if (mod.newBody.startsWith("throw ")) { + method.setBody(mod.newBody); + } else { + method.insertBefore(mod.newBody); + } } } } } return clazz.toBytecode(); } catch (NotFoundException | CannotCompileException | IOException e) { + e.printStackTrace(); return null; } } diff --git a/tools/aegis4j/src/test/java/net/gredler/aegis4j/CVE_2022_0839.java b/tools/aegis4j/src/test/java/net/gredler/aegis4j/CVE_2022_0839.java new file mode 100644 index 0000000000..8e8919db19 --- /dev/null +++ b/tools/aegis4j/src/test/java/net/gredler/aegis4j/CVE_2022_0839.java @@ -0,0 +1,95 @@ +/* Copyright (c) 2022, Daniel Gredler. All rights reserved. */ + +package net.gredler.aegis4j; + +import liquibase.changelog.ChangeLogParameters; +import liquibase.exception.ChangeLogParseException; +import liquibase.parser.core.xml.XMLChangeLogSAXParser; +import liquibase.resource.ResourceAccessor; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.ConnectException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.fail; + +/** + * Tests Liquibase patching. + */ +public class CVE_2022_0839 { + @AfterAll + public static void uninstallAgent() throws Exception { + System.clearProperty("aegis4j.additional.args"); + } + + @Test + public void test() throws Exception { + TestUtils.installAgent("path=" + System.getProperty("aegis4j.projectDir") + "/src/test/resources/liquibase-mods.properties"); + try { + /* + String xmlpoc = "]>&xxe;"; + + SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); + saxParserFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + SAXParser saxParser = saxParserFactory.newSAXParser(); + saxParser.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "http,https"); + saxParser.parse(new ByteArrayInputStream(xmlpoc.getBytes()), new HandlerBase()); + */ + + String changeLog = "\n" + + "]>\n" + + "\n" + + "\n" + + " \n" + + " &xxe;\n" + + " \n" + + ""; + + + XMLChangeLogSAXParser parser = new XMLChangeLogSAXParser(); + parser.parse("", new ChangeLogParameters(), new StringResourceAccessor("", changeLog)); + + } catch (ChangeLogParseException e) { + if (e.getCause() instanceof ConnectException) { + fail("Exception not expected", e); + } + } + } + + static class StringResourceAccessor implements ResourceAccessor { + private String resource; + private String resourceContent; + + public StringResourceAccessor(String resource, String resourceContent) { + this.resource = resource; + this.resourceContent = resourceContent; + } + + @Override + public Set getResourcesAsStream(String path) throws IOException { + if (resource.equals(path)) { + return Collections.singleton(new ByteArrayInputStream(resourceContent.getBytes(StandardCharsets.UTF_8))); + } else { + return Collections.singleton(this.getClass().getResource(path).openStream()); + } + } + + @Override + public Set list(String relativeTo, String path, boolean includeFiles, boolean includeDirectories, boolean recursive) throws IOException { + return null; + } + + @Override + public ClassLoader toClassLoader() { + return null; + } + } +} diff --git a/tools/aegis4j/src/test/resources/liquibase-mods.properties b/tools/aegis4j/src/test/resources/liquibase-mods.properties new file mode 100644 index 0000000000..69d39476a7 --- /dev/null +++ b/tools/aegis4j/src/test/resources/liquibase-mods.properties @@ -0,0 +1,4 @@ +# format: ..= +# CVE-2022-0839 +LIQUIBASE.liquibase.parser.core.xml.XMLChangeLogSAXParser.parseToNode=saxParserFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true); +LIQUIBASE.liquibase.parser.core.xml.XMLChangeLogSAXParser.trySetSchemaLanguageProperty=parser.setProperty(javax.xml.XMLConstants.ACCESS_EXTERNAL_SCHEMA, "http,https"); diff --git a/tools/cliutils/src/main/resources/mods.properties b/tools/cliutils/src/main/resources/mods.properties index c9f1e19983..b880c228f3 100644 --- a/tools/cliutils/src/main/resources/mods.properties +++ b/tools/cliutils/src/main/resources/mods.properties @@ -69,4 +69,5 @@ H2.org.h2.server.web.WebServlet.WebServlet=throw new java.lang.RuntimeException( # CVE-2021-23463 H2.org.h2.jdbc.JdbcSQLXML.getSource=throw new java.lang.RuntimeException("H2 SQL XML blocked by aegis4j"); # CVE-2022-0839 -#LIQUIBASE.liquibase.parser.core.xml.XMLChangeLogSAXParser.parseToNode=saxParserFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); \ No newline at end of file +LIQUIBASE.liquibase.parser.core.xml.XMLChangeLogSAXParser.parseToNode=saxParserFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true); +LIQUIBASE.liquibase.parser.core.xml.XMLChangeLogSAXParser.trySetSchemaLanguageProperty=parser.setProperty(javax.xml.XMLConstants.ACCESS_EXTERNAL_SCHEMA, "http,https");