간단한 C코드를 작성한 뒤 카나리 기법이 적용된 방식으로 컴파일했다.
컴파일된 실행파일을 동적 분석하면서 카나리가 어떤 방식으로 Stack Buffer Overflow 공격을 탐지하는지 확인했다.
활용한 C코드(canary.c)는 아래와 같다.
#include <unistd.h>
int main() {
char buf[8];
read(0, buf, 32);
return 0;
}
크기가 8 바이트인 buf 변수에 32바이트를 읽어오므로 Stack Buffer Overflow 취약점이 있는 코드이다.
이 코드를 아래와 같이 컴파일하면 카나리가 적용된 실행파일로 컴파일된다.
$ gcc -o canary canary.c -fstack-protector
Ubuntu 22.04에서는 -fstack-protector 옵션이 없어도 기본으로 카나리가 적용되는데 나는 칼리에서 컴파일했기 때문에 위와 같이 옵션을 추가해야 했다.
컴파일한 실행파일을 gdb로 열었다.
$ gdb canary
main 함수를 disassemble 했다.
pwndbg> disass main
네모 박스 친 두 부분이 카나리와 관련된 부분이다.
첫 네모를 함수의 프롤로그, 뒷 네모를 함수의 에필로그라고 하자.
main+8 부분에서 rax에 어떤 값이 저장되는지 보기 위해 해당 지점에 중단점을 설정한 뒤 실행시킨다.
pwndbg> break *main+8
pwndbg> run
한 줄 실행 명령어를 입력해서 main+8 의 명령어를 실행한다.
pwndbg> ni
이 지점에서 rax값을 출력해보면 아래와 같이 첫 바이트가 널 바이트인 8바이트 데이터가 저장되어있는 것을 확인할 수 있다.
이것이 카나리 값이다.
이 값은 main+17에서 rbp-0x8에 저장된다.
이제 이 카나리 값으로 어떻게 return address 변조를 탐지하는지 확인해보자.
먼저 에필로그인 main+50에 중단점을 설정한뒤 바이너리를 계속 실행시킨다.
pwndbg> break *main+50
pwndbg> continue
입력값으로는 임의의 문자('L') 16개를 입력해서 return address까지 overwrite 되도록 했다.
main+50에서는 rbp-0x8에 저장되었던 카나리 값을 rcx로 옮긴다.
바로 다음 줄인 main+54에서는 그 rcx를 fs:0x28에 저장된 원본 카나리 값과 xor한다.
두 값이 동일하면 연산 결과가 0이 되어 이후 je 명령어를 통해 main+70으로 건너뛰지만
두 값이 다르면 main+65로 명령 흐름이 진행되어 __stack_chk_fail이 호출되면서 프로그램이 강제 종료된다.
main+50에서 멈춘 시점에서 ni를 한번 입력해서 rdx에 rbp-0x8의 값이 이동하도록 한 뒤 해당 값을 출력해본다.
'L'문자로 덮어쓰기 되어 0x4c4c4c4c4c4c4c4c 가 들어있는 것을 확인할 수 있다.
이후 ni를 두번 더 입력하면 rip가 main+65로 이동하는 것을 볼 수 있다.
ni로 해당 call 명령어를 실행하면 아래와 같이 메시지가 출력되며 프로세스가 강제 종료된다.
여기까지 카나리의 작동원리를 동적 분석을 통해 알아보았다.
'드림핵' 카테고리의 다른 글
[드림핵 워게임]shell_basic (0) | 2024.05.12 |
---|---|
[드림핵 System Hacking Fundamental] Stage 5 (0) | 2024.04.07 |
[드림핵 System Hacking Fundamental] Stage3 (0) | 2024.04.07 |
[드림핵 System Hacking Fundamental] Stage 1,2 (1) | 2024.04.07 |
[드림핵 워게임] basic_exploitation_001 (0) | 2024.03.31 |