· 포맷 스트링
- 일반적으로 사용자로부터 입력을 받아들이거나 결과를 출력하기 위하여 사용하는 형식
- 프로그래밍 언어에서 사용하는 서식 문자
· 포맷 스트링 취약점
- C언어의 printf()와 같은 함수에서 사용자의 입력에 대한 출력을 수행할 때 발생할 수 있는 취약점이다.
** 실행되는 코드들은 칼리 리눅스에서 실행되었음.
* 설명을 위한 예제 코드
#include <stdio.h>
#include <string.h>
int main(int argc,char* argv[]) {
char a[100];
strcpy(a,argv[1]);
printf("%s\n",a);
printf(a);
return 0;
}
* 출력
문자열만 입력되었을 때는 두 printf 에서 모두 같은 출력을 수행했지만, 입력 문자열에 포맷 스트링이 입력되었을 경우,
printf(a) 에서는 문자열 이외의 값이 출력된다.
printf(a) 에서 저런 값이 출력되는 이유는 aaaaa 가 입력되었을 때는 printf("aaaaa"); 과 같이 명령이 수행되어
aaaaa가 출력되지만 aaaa%x 가 입력된다면 printf("aaaa%x"); 와 같이 명령이 수행되기 때문이다.
보통 printf에서 서식문자를 사용한다면 printf("%s",a); 와 같이 변수명을 적어줌으로써 출력할 변수를 알려주지만
위의 printf 함수에서는 참조할 함수 인자가 존재하지 않기 때문에 스택에서 그 함수 인자가 있어야 할 곳에 있는
메모리를 참조하여 임의의 값을 출력하게 된다.
* 왜 문제가 되는가?
단순하게 출력을 위해서라면 printf 함수에 포맷스트링을 입력해주지 않아도 정상적으로 출력되지만 포맷스트링 중에는
메모리의 값을 출력하는 것 뿐 아니라 값을 쓸 수 있는 것도 존재하기 때문이다.
· %n : 이전까지 입력된 문자의 갯수를 더하여 참조하는 메모리 주소에 쓴다.
일단 다음 이미지를 보자.
aaaa를 입력하고 %x 를 여러번 입력했을 때 출력되는 값 중에 여덟번째 포맷 스트링에서 a에 해당하는 0x61이 4번 출력
되는 것을 볼 수 있다. %x 가 입력되어 0x61616161 이 출력되었지만 만약 %s 가 입력되었다면 0x61616161 주소에서
문자열을 출력하려 할 것이다.
0x61616161 은 적절한 주소가 아니기 때문에 세그멘테이션 오류가 나게 된다.
그렇다면 aaaa 부분에 임의의 주솟값을 넣어두고 여덟번째 포맷스트링에 %s 나 %n 을 사용한다면 어떻게 될 것인가?
%s 라면 임의의 주솟값에 있는 문자열을 출력하려 할 것이고, %n 이라면 %n 이전에 출력된 문자열의 갯수를 세어
임의의 주솟값에 쓸 것이다.
* 방법
%c 나 %d를 사용할 때 앞에 숫자를 쓰면 쓰여진 숫자만큼의 공백의 문자 또는 정수를 출력할 수 있다.
이것을 이용하면 %n으로 쓰여질 값을 마음대로 설정할 수 있다.
예) %1024c%n --> 임의의 주솟값에 0x400 을 기록. 0x400 은 %n 앞의 1024개의 문자 갯수를 세어서 그 값을 16진수로
변환하여 저장한다.
따라서 이 방법을 통해 임의의 주소에 원하는 값(주솟값)을 저장함으로써 프로그램의 흐름을 조작할 수 있게 된다.