2024-09-04 12:40:55 -04:00
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
|
From: Sebastian Pipping <sebastian@pipping.org>
|
|
|
|
Date: Mon, 4 Mar 2024 23:49:06 +0100
|
2024-09-04 19:10:33 -04:00
|
|
|
Subject: [PATCH] lib/xmlparse.c: Detect billion laughs attack with isolated
|
|
|
|
external parser
|
2024-09-04 12:40:55 -04:00
|
|
|
|
|
|
|
When parsing DTD content with code like ..
|
|
|
|
|
|
|
|
XML_Parser parser = XML_ParserCreate(NULL);
|
|
|
|
XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL);
|
|
|
|
enum XML_Status status = XML_Parse(ext_parser, doc, (int)strlen(doc), XML_TRUE);
|
|
|
|
|
|
|
|
.. there are 0 bytes accounted as direct input and all input from `doc` accounted
|
|
|
|
as indirect input. Now function accountingGetCurrentAmplification cannot calculate
|
|
|
|
the current amplification ratio as "(direct + indirect) / direct", and it did refuse
|
|
|
|
to divide by 0 as one would expect, but it returned 1.0 for this case to indicate
|
|
|
|
no amplification over direct input. As a result, billion laughs attacks from
|
|
|
|
DTD-only input were not detected with this isolated way of using an external parser.
|
|
|
|
|
|
|
|
The new approach is to assume direct input of length not 0 but 22 -- derived from
|
|
|
|
ghost input "<!ENTITY a SYSTEM 'b'>", the shortest possible way to include an external
|
|
|
|
DTD --, and do the usual "(direct + indirect) / direct" math with "direct := 22".
|
|
|
|
|
|
|
|
GitHub issue #839 has more details on this issue and its origin in ClusterFuzz
|
|
|
|
finding 66812.
|
|
|
|
---
|
|
|
|
lib/xmlparse.c | 6 +++++-
|
|
|
|
1 file changed, 5 insertions(+), 1 deletion(-)
|
|
|
|
|
|
|
|
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
|
|
|
index 18d9201c..ab45db09 100644
|
|
|
|
--- a/lib/xmlparse.c
|
|
|
|
+++ b/lib/xmlparse.c
|
|
|
|
@@ -7539,6 +7539,8 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
|
|
|
|
|
|
|
|
static float
|
|
|
|
accountingGetCurrentAmplification(XML_Parser rootParser) {
|
|
|
|
+ // 1.........1.........12 => 22
|
|
|
|
+ const size_t lenOfShortestInclude = sizeof("<!ENTITY a SYSTEM 'b'>") - 1;
|
|
|
|
const XmlBigCount countBytesOutput
|
|
|
|
= rootParser->m_accounting.countBytesDirect
|
|
|
|
+ rootParser->m_accounting.countBytesIndirect;
|
|
|
|
@@ -7546,7 +7548,9 @@ accountingGetCurrentAmplification(XML_Parser rootParser) {
|
|
|
|
= rootParser->m_accounting.countBytesDirect
|
|
|
|
? (countBytesOutput
|
|
|
|
/ (float)(rootParser->m_accounting.countBytesDirect))
|
|
|
|
- : 1.0f;
|
|
|
|
+ : ((lenOfShortestInclude
|
|
|
|
+ + rootParser->m_accounting.countBytesIndirect)
|
|
|
|
+ / (float)lenOfShortestInclude);
|
|
|
|
assert(! rootParser->m_parentParser);
|
|
|
|
return amplificationFactor;
|
|
|
|
}
|