Joe Orton wrote:
> 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.
Looks good, please commit for 1.3 and 0.9 since I'll roll both overnight.
Bill
> 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 <>=\">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
>
>
>