Stack Canaries
StackGuard 및 StackShield
StackGuard는 EIP (Extended Instruction Pointer) 앞에 특별한 값인 가나리를 삽입합니다. 이 값은 버퍼 오버플로우로부터 보호하기 위해 0x000aff0d
로 설정됩니다 (null, newline, EOF, carriage return을 나타냄). 그러나 recv()
, memcpy()
, read()
, bcopy()
와 같은 함수는 취약한 상태를 유지하며 **EBP (Base Pointer)**를 보호하지 않습니다.
StackShield는 Global Return Stack을 유지함으로써 StackGuard보다 더 정교한 방식을 취합니다. 이 스택은 모든 반환 주소 (EIPs)를 저장하며 오버플로우가 발생해도 피해를 입히지 않도록 합니다. 이는 저장된 반환 주소와 실제 반환 주소를 비교하여 오버플로우 발생을 감지할 수 있도록 하는 것입니다. 또한 StackShield는 반환 주소를 기대 데이터 공간 외부를 가리키는지 여부를 감지하기 위해 반환 주소를 경계 값과 비교할 수 있습니다. 그러나 Return-to-libc, ROP (Return-Oriented Programming), 또는 ret2ret과 같은 기법을 통해 이 보호 기능을 우회할 수 있으며, 이는 StackShield가 로컬 변수를 보호하지 못한다는 것을 의미합니다.
스택 스매시 프로텍터 (ProPolice) -fstack-protector
:
-fstack-protector
:이 메커니즘은 EBP 앞에 가나리를 배치하고 로컬 변수를 재배치하여 버퍼가 다른 변수를 덮어쓰지 못하도록 높은 메모리 주소에 버퍼를 위치시킵니다. 또한 스택 위에 전달된 인수를 안전하게 복사하고 이 복사본을 인수로 사용합니다. 그러나 8개 미만의 요소를 가진 배열이나 사용자 구조체 내의 버퍼를 보호하지 않습니다.
가나리는 /dev/urandom
에서 파생된 무작위 숫자이거나 기본값인 0xff0a0000
입니다. 이는 **TLS (Thread Local Storage)**에 저장되어 스레드 간에 공유 메모리 공간을 가지게 합니다. 이러한 변수들은 초기에 부모 프로세스에서 복사되며, 자식 프로세스는 데이터를 변경할 수 있지만 부모나 형제에 영향을 주지 않습니다. 그러나 fork()
를 사용하여 새로운 가나리를 생성하지 않고 사용하는 경우, 모든 프로세스(부모 및 자식)가 동일한 가나리를 공유하게 되어 취약해집니다. i386 아키텍처에서 가나리는 gs:0x14
에, x86_64에서는 fs:0x28
에 저장됩니다.
이 로컬 보호는 공격에 취약한 버퍼를 가진 함수를 식별하고 이러한 함수의 시작 부분에 가나리를 배치하고 무결성을 확인하기 위해 함수 끝에 코드를 삽입합니다.
웹 서버가 fork()
를 사용할 때, 가나리를 한 바이트씩 추측하는 브루트 포스 공격을 활성화할 수 있습니다. 그러나 fork()
후 execve()
를 사용하면 메모리 공간이 덮어쓰여 공격이 무효화됩니다. vfork()
는 쓰기를 시도할 때까지 중복을 생성하지 않고 자식 프로세스가 실행되도록 허용하며, 프로세스 생성 및 메모리 처리에 대한 다른 접근 방식을 제공합니다.
길이
x64
바이너리에서 가나리 쿠키는 0x8
바이트 큐워드입니다. 첫 일곱 바이트는 무작위이며 마지막 바이트는 널 바이트입니다.
x86
바이너리에서 가나리 쿠키는 0x4
바이트 dword입니다. 첫 세 바이트는 무작위이며 마지막 바이트는 널 바이트입니다.
두 가나리의 가장 낮은 유효 바이트는 널 바이트입니다. 왜냐하면 낮은 주소에서부터 스택으로 오는 첫 번째 바이트이기 때문에 문자열을 읽는 함수는 해당 바이트를 읽기 전에 중지합니다.
우회
가나리를 유출하고 그 값을 덮어쓰기(예: 버퍼 오버플로우)합니다.
가나리가 자식 프로세스에서 생성된 경우 한 번에 한 바이트씩 브루트 포스할 수 있습니다:
이진 파일에서 가나리를 유출하거나 임의의 읽기 취약점이 있는 경우 유출할 수 있습니다:
스택에 저장된 포인터 덮어쓰기
스택이 스택 오버플로우에 취약하면 문자열이나 함수 주소를 덮어쓸 수 있는 주소가 포함될 수 있으므로 스택 가나리에 도달하지 않아도 취약점을 악용할 수 있습니다. 확인:
Pointer Redirecting마스터 및 스레드 가나리 수정
가나리로 보호된 스레드 함수에서의 버퍼 오버플로우는 스레드의 마스터 가나리를 수정하는 데 사용될 수 있습니다. 결과적으로 검사는 두 가나리가 동일하게 사용되는 경우(수정된 경우)에도 무용지물이 됩니다.
또한 가나리로 보호된 스레드 함수에서의 버퍼 오버플로우는 TLS에 저장된 스레드의 마스터 가나리를 수정하는 데 사용될 수 있습니다. 이는 스레드의 스택에서 bof를 통해 TLS가 저장된 메모리 위치에 도달할 수 있기 때문입니다. 결과적으로 검사는 두 가나리가 동일하게 사용되는 경우(수정된 경우)에도 무용지물이 됩니다. 이 공격은 다음의 writeup에서 수행됩니다: http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads
https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015의 발표도 확인하세요. 이 발표에서는 보통 TLS가 **mmap
**에 의해 저장되며 스레드의 스택이 생성될 때도 mmap
에 의해 생성된다고 언급되어 있습니다. 이는 이전 writeup에서 보여진 것처럼 오버플로우를 허용할 수 있습니다.
__stack_chk_fail
의 GOT 항목 수정
이진 파일이 Partial RELRO를 가지고 있는 경우 임의 쓰기를 사용하여 __stack_chk_fail
의 GOT 항목을 수정하여 가나리가 수정되어도 프로그램이 차단되지 않는 더미 함수로 설정할 수 있습니다.
이 공격은 다음의 writeup에서 수행됩니다: https://7rocky.github.io/en/ctf/other/securinets-ctf/scrambler/
참고 자료
Last updated