Stack Canary

2026. 5. 18. 00:27·공부한 것

Stack Canary

스택 카나리(Stack Canary): 스택 버퍼 오버플로우를 탐지하기 위해 함수의 스택 프레임에 삽입되는 보호 값.

Stack Canary 보호 기법

Stack Canary는 함수가 실행될 때 스택에 임의의 값을 넣어두고, 함수가 종료되기 전에 해당 값이 변조되었는지 확인하는 보호 기법이다.

버퍼 오버플로우가 발생하면 일반적으로 지역 변수 영역을 넘어 SFP, Return Address 까지 덮어쓰게 된다.

이때 Return Address 앞에 Canary 값을 배치해두면, Return Address가 덮이기 전에 Canary 값이 먼저 변조된다.

char buf[0x30];

read(0, buf, 0x100);

위의 C언어 코드는 buf의 크기보다 더 많은 데이터를 입력받기 때문에 Stack Buffer Overflow가 발생할 수 있다.

Stack Canary가 적용되지 않은 경우 스택 구조는 다음과 같다.

위치 내용

높은 주소 Return Address
  SFP
  buf
낮은 주소  

Stack Canary가 적용된 경우 스택 구조는 다음과 같다.

위치 내용

높은 주소 Return Address
  SFP
  Canary
  buf
낮은 주소  

즉, buf를 넘어서 Return Address를 덮으려면 Canary 값을 먼저 덮게 된다.

  1. Stack Canary가 없는 경우
void vuln() {
	char buf[0x30];

	read(0, buf, 0x100);
}
push rbp
mov rbp, rsp
sub rsp, 0x30

lea rax, [rbp-0x30]
mov edx, 0x100
mov rsi, rax
mov edi, 0x0
call read

leave
ret

위 코드는 buf에 0x100 만큼 입력받지만 buf의 크기는 0x30 이다.

따라서 buf를 넘어 SFP, Return Address 까지 덮을 수 있다.

  1. Stack Canary가 있는 경우
void vuln() {
	char buf[0x30];

	read(0, buf, 0x100);
}
push rbp
mov rbp, rsp
sub rsp, 0x40

mov rax, QWORD PTR fs:0x28
mov QWORD PTR [rbp-0x8], rax
xor eax, eax

lea rax, [rbp-0x40]
mov edx, 0x100
mov rsi, rax
mov edi, 0x0
call read

mov rax, QWORD PTR [rbp-0x8]
sub rax, QWORD PTR fs:0x28
je .L1
call __stack_chk_fail

.L1:
leave
ret

Stack Canary가 적용되면 함수 시작 부분에서 Canary 값을 스택에 저장한다.

mov rax, QWORD PTR fs:0x28
mov QWORD PTR [rbp-0x8], rax

그리고 함수가 종료되기 전에 스택에 저장된 Canary 값과 원래 Canary 값을 비교한다.

mov rax, QWORD PTR [rbp-0x8]
sub rax, QWORD PTR fs:0x28
je .L1
call __stack_chk_fail

만약 Canary 값이 변조되었다면 __stack_chk_fail 함수가 호출되고 프로그램이 종료된다.

  1. Stack Canary 확인
checksec ./vuln
Canary                        : ✓

위와 같이 Canary가 활성화되어 있으면 Stack Canary 보호 기법이 적용된 것이다.

  1. Stack Canary 컴파일 옵션

Stack Canary는 gcc의 stack protector 옵션으로 적용할 수 있다.

gcc -fstack-protector -o vuln vuln.c
gcc -fstack-protector-all -o vuln vuln.c
gcc -fno-stack-protector -o vuln vuln.c

옵션 설명

-fstack-protector 일부 위험한 함수에 Stack Canary 적용
-fstack-protector-all 모든 함수에 Stack Canary 적용
-fno-stack-protector Stack Canary 비활성화
  1. Stack Canary 예제 코드
#include <unistd.h>

void vuln() {
	char buf[0x30];

	read(0, buf, 0x100);
}

int main() {
	vuln();
}

위 코드를 Stack Canary가 적용된 상태로 컴파일해보자.

gcc -fstack-protector-all -o canary canary.c

그리고 입력을 길게 넣으면 Canary 값이 변조되어 프로그램이 종료된다.

python3 -c 'print("A" * 100)' | ./canary
*** stack smashing detected ***: terminated
Aborted

이는 buf를 넘어서 입력한 값이 Canary를 덮었고, 함수가 종료될 때 Canary 값 검증에 실패했기 때문이다.

Stack Canary의 동작 과정

Stack Canary는 다음과 같은 순서로 동작한다.

  1. 함수가 호출된다.
push rbp
mov rbp, rsp
sub rsp, 0x40
  1. 원래 Canary 값을 가져온다.
mov rax, QWORD PTR fs:0x28
  1. 가져온 Canary 값을 스택에 저장한다.
mov QWORD PTR [rbp-0x8], rax
  1. 함수 내부 코드가 실행된다.
lea rax, [rbp-0x40]
mov edx, 0x100
mov rsi, rax
mov edi, 0x0
call read
  1. 함수가 종료되기 전에 Canary 값을 비교한다.
mov rax, QWORD PTR [rbp-0x8]
sub rax, QWORD PTR fs:0x28
je .L1
  1. Canary 값이 다르면 프로그램을 종료한다.
call __stack_chk_fail
  1. Canary 값이 같으면 정상적으로 함수가 종료된다.
leave
ret

Stack Canary 우회

Stack Canary가 적용되어 있으면 단순히 Return Address를 덮는 방식의 BOF 공격은 실패한다.

Return Address를 덮기 전에 Canary 값이 같이 덮이기 때문이다.

따라서 Stack Canary를 우회하려면 Canary 값을 알아내고, 기존 Canary 값을 그대로 유지한 채 Return Address를 덮어야한다.

payload = padding + canary + sfp + ret

예를 들어 buf의 크기가 0x30 이고 Canary가 존재하는 경우 payload 구조는 다음과 같다.

영역 값

buf padding
Canary 원래 Canary 값
SFP dummy
Return Address 원하는 주소

즉, Canary 값을 모른 상태에서 Return Address를 덮으면 프로그램은 종료된다.

하지만 Canary 값을 알고 있다면 다음과 같이 payload를 구성할 수 있다.

payload = b"A" * 0x30
payload += canary
payload += b"B" * 0x8
payload += ret

위 payload는 Canary 값을 변조하지 않고 그대로 넣어주기 때문에 Canary 검사를 통과할 수 있다.

Stack Canary Leak

Stack Canary를 우회하기 위해서는 일반적으로 Canary Leak이 필요하다.

Canary Leak은 프로그램의 출력 기능이나 포맷 스트링 취약점 등을 통해 Canary 값을 알아내는 것이다.

예를 들어 다음과 같은 코드가 있다고 하자.

#include <stdio.h>
#include <unistd.h>

void vuln() {
	char buf[0x30];

	read(0, buf, 0x100);
	printf("%s", buf);
}

int main() {
	vuln();
}

buf에 널 바이트가 없도록 입력하면, printf가 buf 이후의 스택 값까지 출력할 수 있다.

이때 Canary 값이 출력되면 해당 값을 이용해 BOF를 수행할 수 있다.

Canary는 일반적으로 마지막 1바이트가 NULL이다.

00 ?? ?? ?? ?? ?? ?? ??

따라서 문자열 출력 함수로 Canary를 Leak 할 때는 NULL 바이트 때문에 바로 출력되지 않을 수 있다.

이 경우 Canary 앞까지 채운 뒤, NULL 바이트를 덮어서 뒤의 Canary 일부를 출력하게 만드는 방식이 사용될 수 있다.

Stack Canary의 한계

Stack Canary는 Return Address 변조를 탐지하는 보호 기법이다.

하지만 모든 공격을 막는 것은 아니다.

한계 설명

Canary Leak Canary 값을 알아내면 우회 가능
Non-control-data Attack Return Address를 건드리지 않는 공격은 탐지 어려움
Heap Overflow 스택이 아닌 힙 영역 오버플로우는 직접적으로 방어하지 못함
Format String Bug Canary 값을 유출할 수 있음
Stack Pivot 조건에 따라 다른 공격 기법과 조합 가능

즉, Stack Canary는 BOF 공격을 어렵게 만들지만 완전히 막지는 못한다.

Stack Canary의 컴파일, 실행

Stack Canary를 확인하기 위해 취약한 C언어 코드를 작성한다.

아래는 Stack Canary가 적용된 코드와, Stack Canary가 적용되지 않은 코드이다.

#include <unistd.h>

void vuln() {
	char buf[0x30];

	read(0, buf, 0x100);
}

int main() {
	vuln();
}

Stack Canary를 적용해서 컴파일한다.

gcc -fstack-protector-all -o canary_on canary.c

Stack Canary를 비활성화해서 컴파일한다.

gcc -fno-stack-protector -o canary_off canary.c

각각 checksec으로 확인한다.

checksec ./canary_on
Canary                        : ✓
checksec ./canary_off
Canary                        : ✘

Stack Canary가 적용된 바이너리에 긴 입력을 넣어보자.

python3 -c 'print("A" * 100)' | ./canary_on
*** stack smashing detected ***: terminated
Aborted

Stack Canary가 적용되지 않은 바이너리에 긴 입력을 넣어보자.

python3 -c 'print("A" * 100)' | ./canary_off
Segmentation fault

Stack Canary가 적용된 경우 Canary 검증에 실패하여 __stack_chk_fail 이 호출된다.

Stack Canary가 적용되지 않은 경우 Return Address가 변조되어 Segmentation fault가 발생할 수 있다.

정리

Stack Canary는 스택 버퍼 오버플로우 공격을 탐지하기 위해 Return Address 앞에 삽입되는 보호 값이다.

함수가 시작될 때 Canary 값을 스택에 저장하고, 함수가 종료될 때 원래 값과 비교한다.

Canary 값이 변조되었다면 __stack_chk_fail 이 호출되고 프로그램은 종료된다.

하지만 Canary 값을 Leak 할 수 있다면 원래 Canary 값을 payload에 포함하여 보호 기법을 우회할 수 있다.

'공부한 것' 카테고리의 다른 글

Calling Convention  (0) 2026.05.19
Shellcode - orw Shellcode  (0) 2026.05.17
CVE-2026-28466  (0) 2026.05.14
CVE-2026-26954  (0) 2026.05.13
CVE-2026-3672  (0) 2026.05.12
'공부한 것' 카테고리의 다른 글
  • Calling Convention
  • Shellcode - orw Shellcode
  • CVE-2026-28466
  • CVE-2026-26954
hsnyus
hsnyus
rev, pwn
  • hsnyus
    hsnyus
    hsnyus
  • 전체
    오늘
    어제
    • 분류 전체보기 (106)
      • About (1)
      • 대외활동 (16)
      • 보안관제 (2)
      • 학교 (3)
      • 개발일지 (5)
      • 공부한 것 (34)
      • 사이버가디언즈 (9)
      • 일반 (8)
      • 스터디 (10)
      • Wargame (18)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    문제풀이
    드림핵
    워게임
    ctf
    사이버가디언즈
    프로그래밍
    c언어
    스터디
    개발
    DreamHack
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
hsnyus
Stack Canary
상단으로

티스토리툴바