disass main으로 main 함수를 disassemble하고, print하기 전에 Stack 구조와, regi에 어떤 값들이 들어가 있는지 확인하자.
현재 printf를 실행할 때는 인자를 4개 받는다. 각각 String, A, B, B의 주소 값이 들어있다.
이제 Format String에 대한 이해를 돕기 위한 코드를 분석하자.
#include <stdio.h>
int main(){
int buf[30];
gets(buf);
printf(buf);
printf("\n%s\n",buf);
return 0;
}
간단하게 말하면, buffer(4 * 30)을 만들어 버퍼 값을 보는 코드이다. 그런데 해당 코드에선 print할 때 필요한 인자가 부족하다. 그렇게 되면 출력할 형태를 지정해주지 않았기 때문에 공격자가 원하는 값을 넣을 수 있어 취약한 코드가 된다.
esp 값을 봤을 때, 2번째 들어있는 값이 0x00000001이다. 이건 Stack의 값이다. 아까 전 %x를 수행헀을 때 나왔던 1과 같다. 즉, 이런 식으로 메모리 주소를 들여다볼 수 있다는 말이다.
같은 식으로, %x 를 10개 넣고 수행한다면, 메모리를 10개 들여다볼 수 있다.
이제는 %n이 어떤 역할을 하는지 알아보자.
#include <stdio.h>
void main () {
int num = 0;
char buf[100];
gets(buf);
printf("buf=%s%n", buf, &num);
printf("\nnum=%d\n", num);
}
다음 코드는 사용값을 저장해서 출력하는 코드이다. 여기서 %n은 지정한 변수에 데이터 쓰기가 가능한 format string이다.
따라서 해당 프로그램의 %n을 이용해서 원하는 임의의 값을 num에 넣을 수 있다.
마지막 실습으로 FSB를 이용해서 num 값을 7777로 overwrite하고 Success를 출력하게 하는 코드를 수행해보자.
#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);
}
여기서 little endian으로 읽기 때문에, buffer의 시작인 30303030부터, 25 78 20... 이렇게 들어가는데, ASCII 코드표를 보면 30 : 0, 25 : %, 78 : x, 20 : (space) 이다. "즉, %s "가 계속 들어가고 있다는 뜻이다. 그리고, Stack에서 $esp부터 6개 만큼 떨어져 있기 때문에 24byte 만큼 떨어져 있는 곳이 Buffer의 주소인 것을 알 수 있다.
elf는 linux의 실행파일이다. windows에서 실행파일(바이너리)파일이라고 생각하면 된다. readelf를 사용하면 compile된 binary를 분석해서 정보를 알려준다.