これは、Exim RCE (CVE-2018-6789) を調査するための環境を構築するための、一連のファイル、スクリプト、メモなどのセットです。Exim のデバッグ、エクスプロイトの作成、Exim 関数呼び出しのトレース、Exim のカスタムメモリ管理 (storeblocks) についての学習、実際のエクスプロイトがどのように機能するかを調べるために使用できます。
これは学術的な目的のみに使用するべきです!
vagrant-libvirt プラグイン)次のコマンドを実行して Exim のソースコードをダウンロードします
$ git submodule update --init
ルートディレクトリに Vagrantfile があります。これは仮想化プロバイダーとして libvirt を使用します。Vagrant Cloud のダウンロードが現在壊れているため (HCP 移行)、ボックスは box_url を介して Fedora のミラーから直接取得されます。Fedora はそこに libvirt と VirtualBox のボックスのみを公開しており (VMware はなし)、直接の box_url はプロバイダー固有であるため、このセットアップは libvirt のみを対象としています。
# -*- mode: ruby -*-
# vi: set ft=ruby :
memory = 8192 # in MiB
cpus = 4
Vagrant.configure("2") do |config|
# Vagrant Cloud downloads are broken; pull the box from Fedora's mirror
config.vm.box = "fedora-44-cloud-base"
config.vm.box_url = "https://download.fedoraproject.org/pub/fedora/linux/releases/44/Cloud/x86_64/images/Fedora-Cloud-Base-Vagrant-libvirt-44-1.7.x86_64.vagrant.libvirt.box"
config.vm.provider "libvirt" do |lv|
lv.memory = memory
lv.cpus = cpus
end
config.vm.provision "shell", inline: <<-SHELL
/vagrant/scripts/setup_vm.sh
SHELL
end
設定はお好みで変更できますが、例えば setup_vm.sh スクリプトは dnf を使用してパッケージをインストールすることに注意してください。Ubuntu を使用したい場合は、dnf install の行を apt-get install に置き換え、パッケージ名を適宜調整する必要があります。ただし、セットアップが正しく動作する保証はありません。
設定に満足したら、次のコマンドを実行するだけです:
$ vagrant up
マシンをセットアップしたら、次を実行します:
$ vagrant ssh
接続します。Vagrant の使い方がわからない場合は、こちらを参照してください: https://www.vagrantup.com/intro/getting-started/
Vagrant はカレントディレクトリ (つまり、クローンしたばかりのリポジトリ) を共有ディレクトリとして /vagrant にマップします。Exim 用の Docker イメージを作成して実行するには、VM 内 (vagrant ssh) で次のコマンドを入力します。
[vagrant@localhost ~]$ cd /vagrant
[vagrant@localhost vagrant]$ ./scripts/reset_docker.sh
初回は Exim がソースからビルドされるため、はるかに時間がかかります。デバッグスクリプトや、Docker コンテナにコピーされる他のファイルを変更した場合は、いつでも ./scripts/reset_docker.sh を使用して Docker イメージを再ビルドできます。もちろん、スクリプトから必要な行だけを抜き出して、単一のコマンドとして実行することもできます。
すべてが完了すると、root コンソールが表示されるはずです:
Successfully tagged exim:latest
787f310ef922a1e519cf8bb47f1c4fed5f510da705e7ceefd48f160c980e969c
root@787f310ef922:/opt#
奇妙な文字列はお使いのマシンでは異なって見えるかもしれませんが、ホストマシン上の Fedora VM 内で実行されている Debian Docker コンテナに入ったことになります。
まず、2つのターミナルウィンドウで vagrant ssh を使用して2つの SSH セッションを作成できます。1つはエクスプロイトを実行し、SMTP を介して Exim と対話するために使用できます。もう1つは、Docker コンテナ内で Exim を起動、実行、デバッグなどに使用します。VM では ASLR が無効になっているため、デバッグセッション中に変わらない信頼性の高いブレークポイントを設定できます。
セッション例:
最初のターミナル:
$ vagrant ssh
[vagrant@localhost vagrant]$
2番目のターミナル:
$ vagrant ssh
[vagrant@localhost vagrant]$ cd /vagrant
[vagrant@localhost vagrant]$ ./scripts/reset_docker.sh
...
# now you are inside the Debian Docker container
root@99296cf63016:/opt# ./run_exim.sh
root@99296cf63016:/opt# ./attach_exim.sh
run_exim.sh スクリプトは終了し、Exim はバックグラウンドで実行されます。./attach_exim.sh スクリプトは、実行中の Exim デーモンプロセスに gdb をアタッチし、次のような出力を表示するはずです:
...
pwndbg: loaded 170 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Attaching to process 14
Reading symbols from /usr/exim/bin/exim-4.89_1-1-fc6d6586-XX-1...done.
...
0x00007ffff6b7f5e3 in __select_nocancel () at ../sysdeps/unix/syscall-template.S:84
84 ../sysdeps/unix/syscall-template.S: No such file or directory.
Breakpoint 1 at 0x5555555c03d2: file smtp_in.c, line 1762.
Breakpoint 2 at 0x5555555c051d: file smtp_in.c, line 1884.
Breakpoint 3 at 0x55555556a2c8: file base64.c, line 154.
Breakpoint 4 at 0x5555555c6aca: file smtp_in.c, line 3690.
Exim は実行され、リクエストを待機しています。設定されたブレークポイントは debugging/breakpoints ファイルに由来します。Ctrl+C を使用してプロセスを中断し、gdb に制御を渡すことができます。提供されているエクスプロイトスクリプトのいずれかを実行して、すべてが期待どおりに動作するかテストすることもできます:
最初のターミナル:
[vagrant@localhost ~]$ cd /vagrant/sploits/
[vagrant@localhost sploits]$ ./sploit_0.py
[+] Opening connection to localhost on port 25: Done
2番目のターミナル:
Thread 2.1 "exim" hit Breakpoint 2, smtp_reset (reset_point=reset_point@entry=0x555555843078) at smtp_in.c:1884
1884 {
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────
RAX 0x555555843078 ◂— 0x0
RBX 0x0
RCX 0x555555824b40 (store_last_get) —▸ 0x555555843078 ◂— 0x0
RDX 0x555555820b30 (yield_length) ◂— 0x15800001c38
RDI 0x555555843078 ◂— 0x0
RSI 0x0
R8 0x3
R9 0x52
R10 0x73
R11 0x246
R12 0x5555555ec7fa ◂— 'daemon.c'
R13 0x555555843078 ◂— 0x0
R14 0x0
R15 0x0
RBP 0x5555555ee3db ◂— and byte ptr [rax], ah /* ' %s\n' */
RSP 0x7ffffffbe528 —▸ 0x5555555c31d1 (smtp_setup_msg+67) ◂— mov dword ptr [rip + 0x260b6d], 0
RIP 0x5555555c051d (smtp_reset) ◂— push rbp
────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────
► 0x5555555c051d <smtp_reset> push rbp
0x5555555c051e <smtp_reset+1> push rbx
0x5555555c051f <smtp_reset+2> sub rsp, 8
0x5555555c0523 <smtp_reset+6> mov rbp, rdi
0x5555555c0526 <smtp_reset+9> mov qword ptr [rip + 0x263657], 0 <0x555555823b88>
0x5555555c0531 <smtp_reset+20> mov dword ptr [rip + 0x263645], 0 <0x555555823b80>
0x5555555c053b <smtp_reset+30> mov dword ptr [rip + 0x26364f], 0 <0x555555823b94>
0x5555555c0545 <smtp_reset+40> mov dword ptr [rip + 0x263699], 0 <0x555555823be8>
0x5555555c054f <smtp_reset+50> mov dword ptr [rip + 0x263687], 0 <0x555555823be0>
0x5555555c0559 <smtp_reset+60> mov dword ptr [rip + 0x263679], 0 <0x555555823bdc>
0x5555555c0563 <smtp_reset+70> mov dword ptr [rip + 0x263677], 0 <0x555555823be4>
────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────
In file: /opt/exim/src/src/smtp_in.c
1879 Returns: nothing
1880 */
1881
1882 static void
1883 smtp_reset(void *reset_point)
► 1884 {
1885 recipients_list = NULL;
1886 rcpt_count = rcpt_defer_count = rcpt_fail_count =
1887 raw_recipients_count = recipients_count = recipients_list_max = 0;
1888 cancel_cutthrough_connection("smtp reset");
1889 message_linecount = 0;
────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────
00:0000│ rsp 0x7ffffffbe528 —▸ 0x5555555c31d1 (smtp_setup_msg+67) ◂— mov dword ptr [rip + 0x260b6d], 0
01:0008│ 0x7ffffffbe530 —▸ 0x7ffffffbe600 ◂— 0x0
02:0010│ 0x7ffffffbe538 —▸ 0x7ffffffbe540 —▸ 0x555555605f1a ◂— add byte ptr [rip + 0x25203a73], ah
03:0018│ 0x7ffffffbe540 —▸ 0x555555605f1a ◂— add byte ptr [rip + 0x25203a73], ah
04:0020│ 0x7ffffffbe548 —▸ 0x555555843078 ◂— 0x0
05:0028│ 0x7ffffffbe550 ◂— 0x0
06:0030│ 0x7ffffffbe558 —▸ 0x7ffff6b7f5e3 (__select_nocancel+10) ◂— cmp rax, -0xfff
07:0038│ 0x7ffffffbe560 ◂— 0x7ffffffbe560
──────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────
► f 0 5555555c051d smtp_reset
f 1 5555555c31d1 smtp_setup_msg+67
f 2 55555556de43 daemon_go+10909
f 3 55555556de43 daemon_go+10909
f 4 555555583ca5 main+21601
f 5 7ffff6abe2e1 __libc_start_main+241
──────────────────────────────────────────────────────────────────────────────────────────────────────────
Breakpoint smtp_reset
pwndbg>
d ですべてのブレークポイントを削除し、c で継続して sploit_0.py スクリプトを終了まで実行させることができます:
2番目のターミナル:
Breakpoint smtp_reset
pwndbg> d
pwndbg> c
Continuing.
[Inferior 2 (process 42) exited with code 01]
最初のターミナル:
...
220 787f310ef922 ESMTP Exim 4.89_1-1-fc6d6586-XX Mon, 02 Mar 2020 14:47:24 +0000
250-787f310ef922 Hello test.example.org [172.17.0.1]
250-SIZE 52428800
250-8BITMIME
250-PIPELINING
250-AUTH PLAIN
250-CHUNKING
250-PRDR
250 HELP