포맷 스트링 공격
1) 개요
- 포맷 스트링은 C언어의 printf() 등의 함수에서 사용되는 문자열의 입/출력 형태를 정의하는 문자열로 서식 문자열이라 표현한다.
- 포맷 스트링을 인자로 하는 함수의 취약점을 이용한 공격으로 외부로부터 입력된 값을 검증하지 않고 입출력 함수의 포맷 스트링을 그대로 사용하는 경우 발생할 수 있는 취약점이다.
- 공격자는 포맷 스트링을 이용하여 취약한 프로세스를 공격하거나 메모리 내용을 읽거나 쓸 수 있다. 그 결과, 공격자는 취약한 프로세스의 권한을 획득하여 임의의 코드를 실행할 수 있다.
- 출력문에서 올바르지 못한 방법을 악용하여 크래커들이 실제 메모리 번지를 공격하여 원하는 값으로 변경하거나 시스템의 루트 권한을 획득하는 공격
2) 공격 원리 실습
%n : 이전까지 출력한 총 바이트 수를 지정한 변수에 저장 (악성코드의 주소값으로 설정)
- printf 함수를 살펴보면, 포맷 스트링으로 %s 식별자를 통해 name 변수에 저장된 문자열을 출력하고, %d 식별자를 통해 age 변수에 저장된 정수값을 출력하고, %n을 통해 지금까지 출력한 문자열의 바이트 수를 nbytes 변수에 저장한다.
(&nbytes는 변수의 주소값을 의미)
- 포맷 스트링의 식별자는 대부분 출력을 위한 용도이지만 %n의 경우에는 값을 저장하는 용도로 사용한다.
- 소스코드의 printf 함수를 보면 포맷 스트링을 외부 입력으로부터 받도록 되어 있다. %x 식별자를 통해 현재 수행되는 스택의 메모리 정보를 읽어올 수 있다.
- var1 변수에 저장된 10, var2 변수에 저장된 11등이 출력되는 것을 볼 수 있다.
3) 포맷 스트링의 취약점
- 포맷 스트링을 인자로 하는 함수 사용 시 포맷 스트링을 지정하지 않고 사용자 입력을 통해서 포맷 스트링이 결정된다면 공격자는 이를 조작하여 메모리 내용을 참조하고 특정 영역의 값을 변경할 수 있다.
- 가령, 공격자는 스택 프레임 구조를 고려하면서 %x를 통해 메모리 내용 참조 및 원하는 위치(RET)로 이동한 후 %n을 통해 Return address를 악성코드가 위치한 주소로 변조하여 악성코드를 실행할 수 있다.
4) 대응방안
- 포맷스트링을 함수의 입력 파라미터로 직접 사용하지 않는다.
printf(argv[1]) -> printf("%s", argv[1])
안전한 코드의 예처럼 함수 사용 시 포맷 스트링을 지정하여 간접적으로 참조가 되도록 한다.