다양한 기록

Dirty COW #2 mmap(), madvise() 본문

운영체제보안

Dirty COW #2 mmap(), madvise()

라구넹 2024. 12. 6. 20:58

Kernel Local Privilege Escalation "Dirty COW", CVE-2016-5195

레이스 컨디션의 한 케이스

 

int main()
{
    struct stat st;
    char content[20];
    char *new_content = "New Content";
    void *map;
    
    int f = open("./zzz", O_RDWR)
    fstat(f, &st);
    
    map = mmap(NULL, st.size, PROT_READ | PROT_WRITE, MAP_SHARED, f, 0);
    
    // read 10 bytes
    memcpy( (void*)content, map, 10 );
    printf("read: %s\n", content);
    
    // write
    memcpy(map+5, new_content, strlen(new_content));
    
    munmap(map, st.st_size);
    
    close(f);
    
    return 0;
}

mmap -> 파일을 메모리에 매핑해서 메모리처럼 읽고 쓸 수 있음

mmap(1, 2, 3, 4, 5, 6)

1: 매핑 시작 주소, NULL이면 OS가 알아서 함

2: 매핑할 사이즈

3: 읽기 쓰기 .. open해서 열었던 거랑 맞춰야 함 (O_RDWR로 열었으니 PROT_READ | PROT_WRITE로 맞춤)

4: 다른 프로세스가 쓰려고 할 때 공유할지(MAP_SHARED) 혼자 쓸지(MAP_PRIVATE)

5: 매핑하려는 파일의 fd

6. 매핑 시 파일의 시작 오프셋

 

memcpy(void dest[restrict .n], const void src[restrict .n], size_t n);

n만큼 read, write가 가능

첫번째 인자의 map+5대로면 앞에 5개는 냅두고 write하겠다는 의미가 됨

 

munmap((void *)map, size_t n)

매핑 해제


MAP_SHARED & MAP_PRIVATE

MAP_SHARED

- 매핑된 메모리는 두 프로세스 간의 공유 메모리처럼 동작

- 여러 프로세스가 동일한 파일을 메모리에 매핑할 때 파일을 서로 다른 가상 메모리 주소에 매핑할 수 있지만 파일 콘텐츠가 저장되는 물리적 주소는 동일함

 

MAP_PRIVATE

- 파일이 호출 프로세스 전용 메모리에 매핑

- 오리지널 메모리의 내용이 프라이빗 메모리에 카피될 필요 있음

- 쓰기 시도 시 피지컬 메모리에 새 블록을 할당하고 컨텐츠 카피해둠

- 이후 매핑된 버추얼 메모리는 새 피지컬 메모리를 가리킴


madvise(void *addr, size_t length, int advice)

- addr ~ addr + length까지 메모리에 대해 커널에 어드바이스 하는 용도

advice가 MADV_DONTNEED => 안쓴다고 생각하고 버림

 

카피본에 이걸 쓰면 날려버리고 기존 페이지 테이블은 원본을 가리킴


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>

int main()
{
    char *content = "**content**";
    char buffer[30];
    struct stat st;
    void* map;

    int f = open("./zzz", O_RDONLY);
    fstat(f, &st);
    
    map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, f, 0);

    int fm = open("/proc/self/mem", O_RDWR);

    lseek(fm, (off_t)map + 5, SEEK_SET);

    write(fm, content, strlen(content));

    memcpy(buffer, map, 29);
    printf("Content after write: %s\n", buffer);

    madvise(map, st.st_size, MADV_DONTNEED);
    memcpy(buffer, map, 29);
    printf("Content after write: %s\n", buffer);
}

결과

1. ./zzz가 리드 온리로 열리고 리드온리 및 private로 메모리 매핑됨

2. /proc/self/mem 파일 열어서 메모리 접근 .. 리드/라이트로 열음(자기 거니까 됨)

3. 위치를 lseek로 옮겨서 매핑된 곳 + 5 자리로 옮김

4. 쓰기

5. 출력 => 덮어써진게 보임

6. madvise

7. 출력 => 원본이 보임

 

'운영체제보안' 카테고리의 다른 글

Dirty COW #4 완화법  (0) 2024.12.06
Dirty COW #3 Exploit  (0) 2024.12.06
Dirty COW #1 Copy-on-Write  (0) 2024.12.06
ToCTToU (Time of Check-to-Time of Use) Vulnerability  (0) 2024.12.06
Race Condition Problem  (0) 2024.12.06