#include <unistd.h>
int main() {
char buf[8];
read(0, buf, 32);
return 0;
}
canary.c 파일 코드
gcc -o no_canary canary.c -fno-stack-protector 명령어로 컴파일하면,
no_canary 실행 파일이 생성되며 이 과정에서 스택 보호 기능이 비활성화됨
이후 프로그램을 실행하고 긴 입력값을 넣으면,
코드 특성상 스택 버퍼 오버플로우가 발생하게 됨
$ ./no_canary
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
$ ./canary
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
*** stack smashing detected ***: terminated
Aborted (core dumped)
core dumped로 스택 버퍼 오버플로우가 발생했다.
이번에는 gcc -o canary canary.c 명령어로 canary 보호 기법을 적용해 컴파일해본다.
stakc smashing detected, aborted 문구를 통해
스택 버퍼 오버플로우가 감지되어 프로세스가 강제적으로 종료된다.
pwndbg> disass main
Dump of assembler code for function main:
0x0000000000001149 <+0>: endbr64
0x000000000000114d <+4>: push rbp
0x000000000000114e <+5>: mov rbp,rsp
0x0000000000001151 <+8>: sub rsp,0x10
0x0000000000001155 <+12>: lea rax,[rbp-0x8]
0x0000000000001159 <+16>: mov edx,0x20
0x000000000000115e <+21>: mov rsi,rax
0x0000000000001161 <+24>: mov edi,0x0
0x0000000000001166 <+29>: call 0x1050 <read@plt>
0x000000000000116b <+34>: mov eax,0x0
0x0000000000001170 <+39>: leave
0x0000000000001171 <+40>: ret
위 코드는 디스어셈블한 코드이다.
pwndbg> disass main
Dump of assembler code for function main:
0x0000000000001169 <+0>: endbr64
0x000000000000116d <+4>: push rbp
0x000000000000116e <+5>: mov rbp,rsp
0x0000000000001171 <+8>: sub rsp,0x10
0x0000000000001175 <+12>: mov rax,QWORD PTR fs:0x28
0x000000000000117e <+21>: mov QWORD PTR [rbp-0x8],rax
0x0000000000001182 <+25>: xor eax,eax
0x0000000000001184 <+27>: lea rax,[rbp-0x10]
0x0000000000001188 <+31>: mov edx,0x20
0x000000000000118d <+36>: mov rsi,rax
0x0000000000001190 <+39>: mov edi,0x0
0x0000000000001195 <+44>: call 0x1070 <read@plt>
0x000000000000119a <+49>: mov eax,0x0
0x000000000000119f <+54>: mov rdx,QWORD PTR [rbp-0x8]
0x00000000000011a3 <+58>: sub rdx,QWORD PTR fs:0x28
0x00000000000011ac <+67>: je 0x11b3 <main+74>
0x00000000000011ae <+69>: call 0x1060 <__stack_chk_fail@plt>
0x00000000000011b3 <+74>: leave
0x00000000000011b4 <+75>: ret
canary는 사용자 입력을 받아 이를 canary 영역의 값과 비교하여 스택 버퍼 오버플로우 여부를 확인한다.
► 0x555555555171 <main+8> sub rsp, 0x10
0x555555555175 <main+12> mov rax, qword ptr fs:[0x28]
0x55555555517e <main+21> mov qword ptr [rbp - 8], rax
0x555555555182 <main+25> xor eax, eax
0x555555555184 <main+27> lea rax, [rbp - 0x10]
0x555555555188 <main+31> mov edx, 0x20
0x55555555518d <main+36> mov rsi, rax
0x555555555190 <main+39> mov edi, 0
0x555555555195 <main+44> call read@plt <read@plt>
0x55555555519a <main+49> mov eax, 0
0x55555555519f <main+54> mov rdx, qword ptr [rbp - 8]
fs:[0x28]의 값이 들어간 rax를 출력하여 값을 확인해본다.
pwndbg> print /a $rax
$1 = 0x478feddde830900
값이 같은 경우 뺄셈 연산의 값이 0이 되면서 분기 조건을 만족하게 된다.
그렇게 되면서 main 함수를 정상적으로 반환시킨다.
그렇지 않을 경우는 <__stack_chk_fail@plt> 함수를
실행시키면서 프로세스를 강제로 종료시킨다.