rsp-∞

[DreamHack] Mitigation : Stack Canary - canary.c 실습 동적 분석 본문

Write-ups/system

[DreamHack] Mitigation : Stack Canary - canary.c 실습 동적 분석

portrait.kim 2024. 5. 18. 10:36
#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 옵션을 집어넣으면 스택 버퍼 오버플로우 여부를 검증하는 canary 기능이 없도록 컴파일된다. 실행하면 별다른 출력 없이 사용자로부터 입력을 받도록 되어 있는데, 위 canary.c의 코드를 보면 알 수 있듯 스택 버퍼 오버플로우가 발생하는 구조의 코드이기 때문에 no_canary를 실행하고 무작위의 긴 입력값을 집어넣으면 아래와 같은 결과가 나온다.

$ ./no_canary
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)

 

core dumped로 스택 버퍼 오버플로우가 발생하였음을 알 수 있다. 

이번에는 gcc -o canary canary.c 명령어로 canary 보호 기법을 적용시켜서 컴파일해 보았다.

$ ./canary
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
*** stack smashing detected ***: terminated
Aborted (core dumped)

 

stakc smashing detected, aborted 문구를 통해 스택 버퍼 오버플로우가 감지되어 프로세스가 강제적으로 종료되었음을 알 수 있다. no_canary와 canary 간 main 함수를 disass하여 어떤 코드 상의 차이점이 있는지 살펴보자.

 

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

no_canary의 main을 디스어셈블한 결과이다.

 

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의 main을 디스어셈블한 결과이다. canary가 사용자의 입력값을 받아 canary 영역의 값과 비교하여 스택 버퍼 오버플로우가 발생하는지의 여부를 검증하므로 no_canary에 비하여 main+8부터 시작하는 훨씬 더 긴 코드를 가지고 있다.

 

이 자리에 breakpoint를 걸고 run을 시켜 보자.

 

► 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]

 

disass 영역에서 확인할 수 있는 코드로, 조금 생소하지만 fs:[0x28]을 눈여겨볼 만하다. ni 명령어를 통해 해당 코드가 실행되도록 하고, fs:[0x28]의 값이 들어간 rax를 출력하여 그 값을 확인해 볼 수 있다.

 

pwndbg> print /a $rax
$1 = 0x478feddde830900

 

리틀 엔디언 방식이므로 해당 값을 뒤에서부터 읽으면 00, 즉 null값으로 시작하는 8바이트 데이터가 저장되어 있음을 알 수 있다. 그러면 이 rax는 어디로 갈까? main+21에서 rax를 rbp-0x8에 저장하게 하는 코드가 보인다.

 

이하 main+54에서 rax가 저장된 rbp-0x8의 값을 rdx로 이동시키고 있다. main+58에서 rdx와 fs:[0x28]의 값을 빼는 연산을 하면서 그 다음 줄에서 je 분기를 실행하고 있다. 값이 같으면 뺄셈 연산의 값이 0이 되면서 분기 조건을 만족하게 되고, main 함수를 정상적으로 반환시킨다. 그렇지 않으면 <__stack_chk_fail@plt> 함수를 실행시키면서 프로세스를 강제로 종료시킨다.

'Write-ups > system' 카테고리의 다른 글

[DreamHack] ssp_001  (0) 2024.05.18
[DreamHack] Mitigation : Stack Canary - 카나리 생성 과정  (0) 2024.05.18
[Bomb Lab] Phase_4  (0) 2024.05.12
[DreamHack] shell_basic  (0) 2024.05.12
[DreamHack] basic_exploitation_000  (0) 2024.03.30