💡Format String Bug (FSB)란?
위와 같이,
format functions에서,
format string 내의 format specifier의 개수 != format string을 제외한 argument의 개수
인 경우,
짝이 없는 format specifier는 메모리 내의 임의의 데이터를 출력해낸다.
즉 memory disclosure 취약점이 발생하는 것이다.
※ format function의 종류
※ format specifier란?
※ format string란?
format specifier가 포함된 string
💡배경 지식: Variable length arguments를 이용하는 함수는 어떻게 구현돼있을까?
✅Variable length arguments를 이용하는 함수의 아주 기초적인 구조는 아래와 같다.
#include <stdarg.h>
int myfunc(int x, ...) {
va_list list_a;
va_start(list_a, x);
}
※ <stdarg.h> 안에 va_start() 등이 정의되어 있는 것이다.
✅그럼 이런 구조를 가진 예시 함수 myprint()를 보자.
- va_list 포인터는 optional argument에 접근한다.
- va_start()는
Narg의 시작 주소를 얻고,
data의 type을 바탕으로 크기를 파악한 뒤,
va_list의 시작 위치를 세팅한다.
🚩
- va_arg()는 format specifier가 발견될 때마다 호출되어 ap(va_list)가 가리키는 argument를 반환하며, ap를 해당 argument의 type의 크기만큼 위(higher address)로 이동시킨다.
- 모든 optional arguent가 접근 완료된 후에는, va_end()가 호출된다.
- myprint(2, 2, 3.5, 3, 4.5);에서의 스택 프레임은 아래와 같다.
✅printf() 역시 variable length arguments를 가진다.
: format string은 필수, 그 이후는 선택
printf()의 동작 과정을 보자. 예시 코드는 아래와 같다.
- printf()가 호출되면 argument들은 stack에 push된다.
- format string은 처음부터 쭉 scan되면서,
format specifier가 발견될 때마다 va_arg()가 호출되고,
이 함수는 va_list가 가리키는 optional argument를 반환하며, 다음 argument로 이동한다.
💡이럴 때 FSB가 발생한다
바로 위 예시와 쪼끔만 달라진 아래 코드를 보자.
뭐가 달라졌는가? 바로 optional argument가 하나 줄어들어서, 이제 갯수가 매치되지 않는단 것이다.
그럼 아래와 같이,
🚩
'마지막 format specifier에 대응되는 optional argument가 있었어야 할 위치', 즉 '주소가 현재의 va_list값인 곳'에서 냅다 fetch해와버린다.
즉, memory disclosure가 발생하는 것이다.
물론 va_list는 optional argument가 하나 빠진 줄 모르고 여전히 멍청히 이동을 계속한다.
printf(user_input); 처럼,
사용자의 임의의 입력이 printf()의 유일한 argument로 들어가며 format string 형태인 경우 FSB 취약점이 생긴다.
💡FSB에 대한 countermeasure
✅안전한 함수 사용
개발자는 format function이 아니라 strcpy() 등을 사용해야 한다.
✅ASLR
조작하려는 메모리의 주소나 셸코드의 주소를 추론하기 어렵게 한다.
✅NX bit?
하지만 이건 ROP나 return to libc에 뚫린다.
✅Stack canary?
는 소용 없다. FSB는 조작하려는 메모리만 수정할 뿐, 다른 메모리는 오염시키지 않기 때문이다.