System Hacking - Stage 6 : Stack Canary

진주찬·2023년 5월 14일
0

System Hacking

목록 보기
6/6

스택 카나리

: 함수의 프롤로그에서 스택 버퍼와 반환 주소 사이에 임의의 값을 삽입하고, 함수의 에필로그에서 해당 값의 변조를 확인하는 보호 기법. 카나리 값의 변조가 확인되면 프로세스는 강제 종료

스택 버퍼 오버플로우로 반환 주소를 덮으려면 반드시 카나리를 먼저 덮어야 하므로 카나리 값을 모르는 공격자는 반환 주소를 덮을 때 카나리 값을 변조하게 되는데, 에필로그에서 변조가 확인되어 공격자는 실행 흐름을 획득하지 못한다.

2. 카나리의 작동 원리

카나리 정적 분석

카나리 비활성화

gcc는 기본적으로 스택 카나리를 적용하여 바이너리를 컴파일하는데 -fno-stack-protector옵션을 추가하면 카나리 없이 컴파일 함

카나리 활성화

카나리 동적 분석

카나리 저장

main+8은 fs:0x28의 데이터를 읽어서 rax에 저장한다.
fs는 세그먼트 레지스터의 일종으로, 리눅스는 프로세스가 시작될 때 fs:0x28에 랜덤 값을 저장한다. 따라서 main+8의 결과로 rax에는 리눅스가 생성한 랜덤 값이 저장된다.

카나리 검사

1) main+50은 rbp-8에 저장한 카나리를 rcx로 옮긴다.
2) main+54에서 rcx를 fs:0x28에 저장된 카나리와 xor한다.
3) 두 값이 동일하면 연산 결과가 0이되면서 je의 조건을 만족하게 되고, main함수는 정상적으로 반환되지만, 두 값이 동일하지 않으면 __stack_chk_fail이 호출되면서 프로그램이 강제로 종료됩니다.

3. 카나리 생성 과정

카나리 값은 프로세스가 시작될 때, TLS에 전역 변수로 저장되고, 각 함수마다 프롤로그와 에필로그에서 이 값을 참조한다.

TLS의 주소 파악

fs는 TLS를 가리키므로 fs의 값을 알면 TLS의 주소를 알 수 있다. 그러나 리눅스에서 fs의 값은 특정 시스템 콜을 사용해야만 조회하거나 설정할 수 있다.

fs의 값을 설정할 때 호출되는 arch_prctl(int code, unsigned long addr) 시스템 콜에 중단점을 설정하여 fs가 어떤 값으로 설정되는지 조사한다. 이 시스템 콜을 arch_prctl(ARCH_SET_FS, addr)의 형태로 호출하면 fs의 값은 addr로 설정됩니다.

이 명령어로 arch_prctl에 catchpoint를 설정하고 실습에 사용했던 canary를 실행하면

$ gdb -q ./canary
pwndbg> catch syscall arch_prctl
Catchpoint 1 (syscall 'arch_prctl' [158])
pwndbg> run

catchpoint에 도달했을 때, rdi의 값이 0x1002인데 이 값은 ARCH_SET_FS의 상숫값입니다. rsi의 값이 0x7ffff7fdb4c0이므로, 이 프로세스는 TLS를 0x7ffff7fdb4c0에 저장할 것이며, fs는 이를 가리키게 될 것입니다.

카나리가 저장될 fs+0x28(0x7ffff7fdb4c0+0x28)의 값을 보면, 아직 어떠한 값도 설정되어 있지 않은 것을 확인 가능하다.

Catchpoint 1 (call to syscall arch_prctl), 0x00007ffff7dd6024 in init_tls () at rtld.c:740
740	rtld.c: No such file or directory.
 ► 0x7ffff7dd4024 <init_tls+276>    test   eax, eax
   0x7ffff7dd4026 <init_tls+278>    je     init_tls+321 <init_tls+321>
   0x7ffff7dd4028 <init_tls+280>    lea    rbx, qword ptr [rip + 0x22721]
pwndbg> info register $rdi
rdi            0x1002   4098          // ARCH_SET_FS = 0x1002
pwndbg> info register $rsi
rsi            0x7ffff7fdb4c0   140737354032320 
pwndbg> x/gx 0x7ffff7fdb4c0+0x28
0x7ffff7fdb4e8:	0x0000000000000000

카나리 값 설정

watch : 특정 주소에 저장된 값이 변경되면 프로세스를 중단시키는 명령어

watchpoint를 설정하고 프로세스를 계속 진행시키면 security_init함수에서 프로세스가 멈춘다.

4. 카나리 우회

1) 무차별 대입

x64 아키텍처에서는 8바이트의 카나리 생성
x86 아키텍처에서는 4바이트의 카나리 생성
각각의 카나리에는 NULL바이트가 포함되어 있으므로 실제로는 7바이트, 3바이트의 랜덤 값이 포함된다.

무차별 대입으로 x64 아키텍처의 카나리 값을 알아내려면 최대 256^7번 이므로 연산량이 너무 많아 현실적으로 어려워, 실제 서버를 대상으로 저정도 횟수의 무차별 대입을 시도하는 것은 불가능하다.

2) TLS 접근

카나리는 TLS에 전역변수로 저장되고, 매 함수마다 이를 참조해서 사용한다. TLS의 주소는 매 실행마다 바뀌지만 만약 실행중에 TLS의 주소를 알 수 있고, 임의 주소에 대한 읽기 또는 쓰기가 가능하다면 TLS에 설정된 카나리 값을 읽거나, 이를 임의의 값으로 조작 가능하다.

그 뒤, 스택 버퍼 오버플로우를 수행할 때 알아낸 카나리 값 또는 조작한 카나리 값으로 스택 카나리를 덮으면 함수의 에필로그에 있는 카나리 검사를 우회 가능하다.

3) 스택 카나리 릭

스택 카나리를 읽을 수 있는 취약점이 있다면, 이를 이용하여 카나리 검사를 우회할 수 있다.

0개의 댓글