Mitigation : Stack Canary

곽무경·2022년 7월 1일
0

System Hacking

목록 보기
11/27

Stack Canary

스택 버퍼 오버플로우로부터 반환 주소를 보호하는 방법

  • 스택 버퍼와 반환 주소 사이에 임의의 값을 삽입
  • 함수의 에필로그에서 해당 값의 변조를 확인하는 기법
  • 카나리가 적용되면, 스택 버퍼 오버플로우가 탐지되면 프로세스가 강제 종료됨

예제 코드

#include <unistd.h>
int main() {
  char buf[8];
  read(0, buf, 32); // 32자리를 받아오므로 오버플로우 가능성
  return 0;
}

카나리가 적용되지 않았을 때 :

긴 입력이 들어오면 Segmentation fault가 발생한다.

카나리가 적용되었을 때 :

긴 입력이 들어오면 stack smashing detected , Aborted 가 발생한다.
프로세스가 강제로 종료되었다.


fs : 세그먼트 레지스터의 일종, 리눅스는 프로세스가 시작될 때 fs:0x28에 랜덤 값을 저장한다.
fs:0x28에서 데이터를 읽어서 rax에 저장한다. (랜덤 값)

rax 에 널 바이트로 시작하는 8바이트의 데이터가 저장되어 있고, 이는 rbp-0x8에 저장된다.


rbp-0x8에 저장해뒀던 카나리를 rcx로 옮기고,
xor 연산으로 기존의 카나리(fs:0x28) 와 비교합니다.
xor 연산의 결과가 0이라면, je의 조건을 만족하여 정상적으로 반환된다.
두 값이 동일하지 않으면 __stack_chk_fail이 호출되면서 프로그램이 강제로 종료된다.

카나리 값의 생성 과정

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

주소 파악

fs는 TLS를 가리키므로 fs의 값을 알면 TLS의 주소를 알 수 있다.
fs값을 설정할 때 호출되는 arch_prctl 시스템 콜을 통해 알아낸다.

카나리 우회

무차별 대입 (Brute Force)

맨 앞의 널 바이트를 제외하면
x64 아키텍처에서는 7바이트의 랜덤한 값을,
x86-64 아키텍처에서는 3바이트의 랜덤한 값을 가진다.
이를 무차별 대입하여 카나리 값을 알아낸다.

TLS 접근

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

스택 카나리 릭

함수의 프롤로그에서 스택에 카나리 값을 저장하므로, 이를 읽어낼 수 있으면
카나리를 우회할 수 있다. 가장 현실적인 방법

0개의 댓글