char buf[0x50] = {};
scanf("%s", buf);
printf(buf);
printf와 같은 함수의 첫 인자로 변수를 사용한다면, FSB를 유발할 수 있다.
x86-64에서 scanf 입력으로 %p를 입력한다면 rsi의 값이 출력됨을 확인할 수 있다.
함수 호출 규약에 따르면 rdi, rsi, rdx, rcx, r8, r9, rsp, rsp+0x8, ... 이므로,
위 코드에 %p %p %p %p %p %p 를 입력으로 주면 rsi ~ rsp까지의 값을 출력한다.
int a = 0;
printf("Test\n");
printf("Hello World!\n%n", &a);
printf("%d\n", a); // 13
형식 인자 %n을 사용하여 현재까지 사용된 문자열의 길이를 저장할 수 있다.
위 코드를 수행하면 Hello World!\n 으로 a는 13이 된다.
파라미터를 사용하면 인덱스를 지정할 수 있다.
int a=1, b=2, c=3;
printf("%3$d %2$d %1$d", a, b, c); // 3 2 1
파라미터와 %n을 조합하면 임의의 인덱스에 임의의 값을 넣을 수 있다.
// i386-32-little, NX enabled, No PIE, Partial RELRO
void doSomething() {
char buf[0x50] = {};
scanf("%s", buf);
printf(buf);
exit(0);
printf("Hello World!");
return 0;
}
위 코드에서 exit@got를 overwrite를 하면 Hello World!가 출력된다.
printf 가 수행될 때, esp는 printf의 리턴주소, esp+4는 buf 주소라고 하자.
p32(exit_got) + f"%{overwrite-4}c%1$n".encode()
이때, 위와 같은 입력으로 exploit 할 수 있다.
i386-32-little의 함수 호출 규약에 따라 %1$n은 esp+4를 의미한다.
printf가 수행될 때 esp+4는 buf의 주소이고 esp+4의 4바이트는 exit_got이다.
%{overwrite-4}c 는 공백을 overwrite-4만큼 출력한다.
p32(exit_got)는 4바이트이므로 overwrite에서 4를 빼준다.
즉, overwrite 값을 esp+4(exit_got)에 저장하는 것과 같다.