Format String Bug
Format String
Format String이란 데이터의 형식을 정해주는 지정해주는 문자열이다.
Format String | Type | Meaning |
---|---|---|
%d | int | 정수 |
%f | float | 실수 |
%c | char | 문자 |
%s | string | 문자열 |
%u | decimal | 10진수 |
%x | hexadecimal | 16진수 |
%n | int | 출력된 문자의 개수 |
아래와 같이 printf 함수에서 자주 볼 수 있다.
1
2
3
4
5
// 1번
printf("%d", number);
// 2번
printf(number);
위 두 코드는 number 변수를 출력하는 같은 기능을 수행하는 코드이다.
코드만 보았을 때 두 코드에 차이점은 Format String 사용 여부이다.
그렇다면 좀 더 코드 길이가 짧은 2번 코드가 좋은 코드라고 생각할 수 있다.
하지만 2번 코드는 FSB 취약점을 가지고 있다.
printf 함수
printf 함수의 원형을 보자
1
2
3
#include <stdio.h>
int printf(const char * restrict format, ...);
인자로 Format이 들어가는 것을 알 수 있다.
만약 Format을 정하지 않으면 어떻게 될까?
간단한 코드를 통해 확인해보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// printf_test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv){
char buf[100];
if(argc != 2)
return 0;
strcpy(buf, argv[1]);
printf(buf);
printf("\n");
return 0;
}
입력받은 두 번째 인자를 buf에 복사하고 출력하는 코드이다.
abc를 입력해보자.
printf 함수에 Format을 정하지 않았지만, 출력은 문제없이 작동한다.
이번에는 Format String을 입력해보자.
오류는 일어나지 않았지만 %x를 사용해 변수와 상관없이 Stack에서 4byte만큼 pop해서 출력된 것을 볼 수 있다.
AAAA를 입력 후 %x를 8번 출력했을 때 0x41414141 즉, AAAA가 저장되어있는 buf 배열인 것을 알 수 있다.
또한 printf 함수의 인자와 buf 배열에 거리가 %x * 7 = 28byte인 것을 알 수 있다.
Format String Bug ( FSB )
Format String을 통해 현재 메모리 주소와 원하는 메모리 주소 등 메모리 구조를 추측할 수 있다.
이를 통해 공격자들은 Format String Bug을 통해 메모리를 보거나 변조한다.
메모리 변조를 위해서는 변조할 주소와 변조를 통해 이동할 주소를 알아야 한다.
변조할 주소는 Debugging이나 Format String을 이용해 알아낼 수 있다.
변조를 통해 이동할 주소 즉, RET 주소는 Debugging이나 환경변수를 통해 알아낼 수 있다.
워게임으로 Format String Bug를 실습해보자.
실습 환경
가상머신 : VMware workstation
iso : exploit-exercises-protostar-2
ID : user
PW : user
워게임 : Protostar
문제 : format2
로그인 후 셸을 실행시켜준다.
문제를 풀기 위해 /opt/protostar/bin 디렉터리로 이동하자.
format2 문제를 풀어보자.
우선 파일을 Debugging 하기 위해 tmp 디렉터리로 복사 후 이동하자.
gdb를 통해 format2 파일을 Debugging 해보자.
main 함수를 disassemble 해봤지만, vuln 함수 호출 외에 다른 코드가 없는 것을 보니 vuln 함수를 보도록 하자.
vuln 함수는 입력받은 값을 512byte 변수에 저장하고 변수를 출력 후 0x80496e4 값이 64가 아니면 0x80496e4 값을 포함해 출력하고, 64가 맞다면 문자열만 출력한다.
코드를 확인해보면 0x80496e4가 변조해야 할 target 변수에 주소인 것을 알 수 있다.
다음으로는 %x를 이용해 printf와 buffer 사이 거리를 구해보자.
%x를 4번 출력했을 때 buffer 배열이 나온다.
printf와 buffer 사이 거리는 24byte인 것을 알 수 있다.
이제 거리와 주소를 알아냈으니 FSB를 통해 target 변수를 변조해보자.
메모리를 볼 때는 %x를 이용했다.
메모리를 변조할 때는 %n을 이용할 것이다.
%n은 출력된 바이트 수를 지정된 변수에 저장하는 Format String이다.
%n을 사용해 target 변수에 주소가 들어있는 배열에 64를 저장하면 문제를 해결할 수 있다.
target 변수가 변조되어 출력되는 것을 확인할 수 있다.
하지만 64가 아니라 문제가 해결되지는 않았다.
1byte를 출력하는 Format String인 %c를 이용해보자.
target 변수가 64로 변조되어 문제가 해결된 것을 볼 수 있다.
마무리하며
Format String Bug는 메모리를 볼 수 있고, 메모리를 변조할 수 있어 다양한 공격에 활용될 수 있다.
대응 방안으로는 FSB는 Format을 정해주지 않아서 발생하는 취약점이므로 코드를 작성할 때 정확한 Format String 지정이 필요하다.
참고
포맷스트링 공격(Format String Attack) - eli_ez3r Hacking Blog
작은 실수에서 오는 취약점, 포맷 스트링 - Knowllipop 놀리팝
Format Two :: Andrew Griffiths’ Exploit Education