※ 질문/내용오류/공유할 내용이 있다면 jinkilee73@gmail.com으로 메일 주세요 :-)
Format String Vulnerability는 printf 함수의 취약점을 이용한 기법이다.
http://operatingsystems.tistory.com/entry/Kernel-printf-%ED%95%A8%EC%88%98
위의 포스팅에서 printf 함수는 매우 자세하게 설명해놨다. 위의 내용을 이해하지 못 하면 이번 Format String Vulnerability는 이해하기 힘들 것이다. 따라서 위의 포스팅을 자세하게 이해한 후에 이번 포스팅을 공부하도록 하자.
자 다들 공부하셨나요?(뭐지?)
이제부터 본격적으로 Format String Vulnerability에 대해서 공부해보도록 하겠다. Format String Vulnerability는 프로그램에서 스트링 문자열 값을 조작하여 해당 프로그램의 스택 구조 혹은 원하는 메모리 값을 얻을 수 있는 취약점이다. 문자열을 출력하는 대표적인 함수인 C언어의 printf도 이와 같은 취약점이 있다.
아까도 말했지만 printf 함수가 스택에서 어떻게 작동하는지 자세하게 아는 사람만 Format String Vulnerability를 완벽하게 이해할 수 있다. 모두 이해하고 있다는 가정 하에 만일 사용자가 출력할 str 문자열을 아래와 같이 printf(str); 출력을 시도하면 어떻게 될까? 예를 들어 str이 ab라면 정상적으로 ab가 출력된다. 하지만 만일 str이 ab%x이면 어떻게 될까? 아래와 같은 스택 구조를 보이게 된다.
printf(str);와 같이 printf를 호출하면 파라미터가 하나(str) 밖에 쌓이지 않는다. 그런데 막상 str의 값을 보면 ab%x이다. a와 b는 정상적으로 출력하면 된다.. 그런데 그 다음이 %x이다. 즉, 옵션을 수행해야 하므로 printf.c에서의 x에 대한 case문이 수행된다.
case x에서는 243번째 줄의 break문을 통해 switch문을 벗어나서 va_arg(arg, unsigned int) 함수를 수행하게 되고 str address 위의 스택 값인 ‘임의의 스택 값’을 가리키게 된다. 그런데 그 다음에 number 함수를 수행해서 옵션(%x)의 결과값인 ‘임의의 스택 값’이 가지고 있는 값을 출력하게 된다. 따라서 printf(str); 과 같이 printf 함수를 사용할 경우 입력 값이 ab%x이면 ab2F3C와 같이 임의의 스택 값(0x2F3C)을 함께 출력해버린다.
만일 ab%x대신 ab%s를 사용하면 어떻게 될까? ‘임의의 스택 값’이 아닌 ‘임의의 스택 값’이 가리키는 값을 출력하게 될 것이다.
정리하면 이와 같다. printf 함수에서 파라미터는 차례대로 쌓이게 되어있는데 이 쌓여진 스택의 주소는 va_arg함수를 통해서 한 칸씩 올라갈 수 있다. 이 va_arg 함수는 검사할 문자열에 %x와 같이 어떤 옵션이 주어졌을 경우에만 수행이 되는데, 해당 옵션에 대한 파라미터 체크를 하지 않는다는 것이 가장 큰 문제이다.
따라서 printf(str);에서 (str = “ab%x”) %x일 경우에는 스택에 어떤 값이 있든 간에 스택에 있는 값을 출력하게 되고 %s일 경우에는 마찬가지로 스택 값이 가리키는 곳의 값이 무엇이든 간에 그 값을 출력하게 된다.
아래의 프로그램을 보자.
간단하게 설명하면 9번째 줄에 있는 strcpy 함수가 사용자의 입력 값을 text에 복사할 것이고 11번째 줄에 있는 printf 함수가 그것을 출력하고 마지막으로 main 함수 내에 선언되어 있는 test_val이라는 static int 변수의 값과 주소를 출력하고 끝낼 것이다.
위의 프로그램을 수행할 때 아래와 같은 파라미터를 출력하려고 하면 어떤 일이 벌어질까?
$(printf “\x18\xa0\x04\x08”)..%x..%x..%x..%x..%x..%x..%x..%x..%x..%x%s
이럴 경우 아래와 같이 스택 값이 쌓이게 된다.
※ $(printf “\x18\xa0\x04\x08”) → 0x0804a018
위의 그림에서 return address의 값이 있는 0xbffff11E0 위부터 printf 함수에 전달한 문자열$(printf…의 주소 값이 쌓이고 그 문자열에 옵션이 11개가 있으므로(%x 10개, %s 1개) printf 함수는 fmt의 문자열을 계속 따라가면서 출력을 하다가 %x나 %s가 나타날 때마다 fmt (0xbffff11E4)에서 위로(+4) 한 칸씩 이동한다. 이동하면서 %x일 경우에는 해당 스택의 값을 16진수로 출력을 하고 %s일 경우에는 해당 스택이 가리키는 곳의 값을 출력한다.
이번에 테스트한 값은 %x%x%x%x%x%x%x%x%x%x%s이므로 10칸의 스택(임의의 스택값 1~10)을 출력한 다음 그 다음 칸에 있는 0x0804a018이 가리키는 곳의 값(보고싶은 값)을 출력한다.
위의 프로그램에 대하여 왜 하필 0x0804a018 이라는 주소 값을 넣어줬을까? test_val이라는 변수가(-255) 저장되어있는 주소 값이 0x0804a018이고 우리는 이 값을 참고로 하여 해당 값을 읽어볼 수 있다.
이제 실제로 Format String Vulnerability를 시현해보도록 하자. 이 공격을 하기 위해서 위의 프로그램을 디버깅 해보도록 하겠다.
38번째 줄을 보면 call printf를 수행하여 printf를 실행하는데 그 전에 37번째 줄에서 movl명령을 통해 파라미터($(printf “\x18\xa0\x04\x08”)..%x..%x..%x..%x..%x..%x..%x..%x..%x..%x%s)의 주소값을 %esp가 가리키는 곳 (%esp)에 넣는다. 38번째 라인을 수행하기 전에 스택 값을 조사해보면 아래와 같이 보인다. 결국 위에서 설명한 스택 그림과 같은 구조로 보이게 된다.
위의 결과를 놓고 보았을 때, esp의 값(0xbffff270)과 0xbffff29c의 차이를 계산해보면 10진수로 44가 나온다. 4 byte의 스택 한 칸을 기준으로 놓고 보면 11칸 차이가 난다는 것을 알 수 있다. 아래의 그림을 다시 보자.
지금 이 그림을 다시 보면 이해가 더 쉬울 것 같다.(그림 재탕) 비록 메모리 주소 값이 실제 테스트한 메모리 값과는 조금 다르지만 개념을 이해하기에는 충분히 정확하게 되어있다고 생각한다.
해당 취약점을 이용하여 0x804a2c에 저장되어있는 변수 값을 %n을 이용하여 바꿔보았다.
위와 같이 프로그램에 선언되어있는 값이 변경된다. 그런데 이런 생각 안 드나?
취약하긴 한데... 이걸로 뭐 할껀데?
사실 Format String Vulnerability는 요즘 자주 쓰이는 공격 방법은 아니다. 그럼에 도 불구하고 이렇게 자세하게 그리고 오랫동안 공부한 이유는 기초를 위해서이다. 버퍼 오버플로우 취약점을 통해서 NOP을 일정 수준으로 쌓은 다음에 return 값을 자기가 원하는 값으로 바꿔서 특정 주소에 있는 (공격자의) shellcode를 수행하는 공격은 공부하기에 재미있지만 무언가 초보자에게는 모래 위에 집을 쌓는 과정일 수도 있다고 생각이 되었다.
Format String Vulnerability를 통해서 가장 확실하게 쌓을 수 있는 기초는 Procedure Call에 관한 이해도와 머리 속에 스택의 참조 개념을 확실하게 잡을 수 있다는 점이다. 또한, printf 함수의 커널 소스를 분석함으로서(printf 함수는 커널 전체의 빙산의 일각의 일각에 불구하지만) 조그마한 소스이지만 내가 분석해냈다는 자신감(-_-;)도 얻을 수 있었다고 생각한다.
조금은 억지스럽지만, 또 다른 유익함을 찾고자 한다면, 해당 함수가 스택을 볼 수 있는 기능을 가졌으므로 여러가지 형태의 BOF를 exploit 하는데 도움이 되는 stack examine tool로 사용할 수도 있지 않을까 싶다.
'Vulnerability' 카테고리의 다른 글
[Vul] CVE-2012-1823 Vulnerability (1) | 2014.01.21 |
---|---|
[Vul] Shellcode Execution (4) | 2013.12.30 |
[Vul] Slowloris DoS Tool (0) | 2013.10.28 |
[Vul] DEDECMS SQL Injection (1) | 2013.08.12 |
시작하며... (0) | 2013.08.12 |