apr-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Joe Orton <jor...@redhat.com>
Subject [PATCH] prevent "billion laughs" attack against expat
Date Tue, 02 Jun 2009 16:29:34 GMT
http://milw0rm.com/exploits/8842

This should disable all entity expansions with expat and seems to work 
correctly, though I haven't done extensive testing.

Index: xml/apr_xml.c
===================================================================
--- xml/apr_xml.c	(revision 781032)
+++ xml/apr_xml.c	(working copy)
@@ -347,6 +347,25 @@
     return APR_SUCCESS;
 }
 
+#if XML_MAJOR_VERSION > 0
+/* XML_StopParser is present in expat 2.x */
+#define HAVE_XML_STOPPARSER
+#endif
+
+#ifdef HAVE_XML_STOPPARSER
+/* Stop the parser if an entity declaration is hit. */
+static void entity_declaration(void *userData, const XML_Char *entityName,
+                               int is_parameter_entity, const XML_Char *value,
+                               int value_length, const XML_Char *base,
+                               const XML_Char *systemId, const XML_Char *publicId,
+                               const XML_Char *notationName)
+{
+    apr_xml_parser *parser = userData;
+
+    XML_StopParser(parser->xp, XML_FALSE);
+}
+#endif
+
 APU_DECLARE(apr_xml_parser *) apr_xml_parser_create(apr_pool_t *pool)
 {
     apr_xml_parser *parser = apr_pcalloc(pool, sizeof(*parser));
@@ -372,6 +391,17 @@
     XML_SetElementHandler(parser->xp, start_handler, end_handler);
     XML_SetCharacterDataHandler(parser->xp, cdata_handler);
 
+    /* Prevent the "billion laughs" attack against expat by disabling
+     * internal entity expansion.  With 2.x, forcibly stop the parser
+     * if an entity is declared - this is safer and a more obvious
+     * failure mode.  With older versions, simply prevent expenansion
+     * of such entities. */
+#ifdef HAVE_XML_STOPPARSER
+    XML_SetEntityDeclHandler(parser->xp, entity_declaration);
+#else
+    XML_SetDefaultHandler(parser->xp, NULL);
+#endif
+
     return parser;
 }
 
Index: test/testxml.c
===================================================================
--- test/testxml.c	(revision 781032)
+++ test/testxml.c	(working copy)
@@ -75,7 +74,7 @@
 
     for (i = 0; i < 5000; i++) {
         rv = apr_file_puts("<hmm roast=\"lamb\" "
-                           "for=\"dinner\">yummy</hmm>\n", *fd);
+                           "for=\"dinner &lt;&gt;&#x3D;\">yummy</hmm>\n",
*fd);
         ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
     }
 
@@ -103,7 +102,7 @@
         a = e->attr;
         ABTS_PTR_NOTNULL(tc, a);
         ABTS_STR_EQUAL(tc, "for", a->name);
-        ABTS_STR_EQUAL(tc, "dinner", a->value);
+        ABTS_STR_EQUAL(tc, "dinner <>=", a->value);
         a = a->next;
         ABTS_PTR_NOTNULL(tc, a);
         ABTS_STR_EQUAL(tc, "roast", a->name);
@@ -149,11 +148,29 @@
     ABTS_TRUE(tc, rv != APR_SUCCESS);
 }
 
+static void test_billion_laughs(abts_case *tc, void *data)
+{
+    apr_file_t *fd;
+    apr_xml_parser *parser;
+    apr_xml_doc *doc;
+    apr_status_t rv;
+
+    rv = apr_file_open(&fd, "billion-laughs.xml", 
+                       APR_FOPEN_READ, 0, p);
+    apr_assert_success(tc, "open billion-laughs.xml", rv);
+
+    rv = apr_xml_parse_file(p, &parser, &doc, fd, 2000);
+    ABTS_TRUE(tc, rv != APR_SUCCESS);
+
+    apr_file_close(fd);
+}
+
 abts_suite *testxml(abts_suite *suite)
 {
     suite = ADD_SUITE(suite);
 
     abts_run_test(suite, test_xml_parser, NULL);
+    abts_run_test(suite, test_billion_laughs, NULL);
 
     return suite;
 }
Index: test/billion-laughs.xml
===================================================================
--- test/billion-laughs.xml	(revision 0)
+++ test/billion-laughs.xml	(revision 0)
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<!DOCTYPE billion [
+<!ELEMENT billion (#PCDATA)>
+<!ENTITY laugh0 "ha">
+<!ENTITY laugh1 "&laugh0;&laugh0;">
+<!ENTITY laugh2 "&laugh1;&laugh1;">
+<!ENTITY laugh3 "&laugh2;&laugh2;">
+<!ENTITY laugh4 "&laugh3;&laugh3;">
+<!ENTITY laugh5 "&laugh4;&laugh4;">
+<!ENTITY laugh6 "&laugh5;&laugh5;">
+<!ENTITY laugh7 "&laugh6;&laugh6;">
+<!ENTITY laugh8 "&laugh7;&laugh7;">
+<!ENTITY laugh9 "&laugh8;&laugh8;">
+<!ENTITY laugh10 "&laugh9;&laugh9;">
+<!ENTITY laugh11 "&laugh10;&laugh10;">
+<!ENTITY laugh12 "&laugh11;&laugh11;">
+<!ENTITY laugh13 "&laugh12;&laugh12;">
+<!ENTITY laugh14 "&laugh13;&laugh13;">
+<!ENTITY laugh15 "&laugh14;&laugh14;">
+<!ENTITY laugh16 "&laugh15;&laugh15;">
+<!ENTITY laugh17 "&laugh16;&laugh16;">
+<!ENTITY laugh18 "&laugh17;&laugh17;">
+<!ENTITY laugh19 "&laugh18;&laugh18;">
+<!ENTITY laugh20 "&laugh19;&laugh19;">
+<!ENTITY laugh21 "&laugh20;&laugh20;">
+<!ENTITY laugh22 "&laugh21;&laugh21;">
+<!ENTITY laugh23 "&laugh22;&laugh22;">
+<!ENTITY laugh24 "&laugh23;&laugh23;">
+<!ENTITY laugh25 "&laugh24;&laugh24;">
+<!ENTITY laugh26 "&laugh25;&laugh25;">
+<!ENTITY laugh27 "&laugh26;&laugh26;">
+<!ENTITY laugh28 "&laugh27;&laugh27;">
+<!ENTITY laugh29 "&laugh28;&laugh28;">
+<!ENTITY laugh30 "&laugh29;&laugh29;">
+]>
+<billion>&laugh30;</billion>

Property changes on: test/billion-laughs.xml
___________________________________________________________________
Added: svn:eol-style
   + native


Mime
View raw message