공부 차원에서 2022년 02월 07일에 기재된 Check Point 사 Alexey Bukhteyev 연구원님의 글을 번역함.
링크 :
https://research.checkpoint.com/2022/invisible-cuckoo-cape-sandbox-evasion/
악성코드는 샌드박스 회피 기술을 사용하여 샌드박스 내부의 악성 행위가 노출 및 탐지되는 것을 방지함.
< 그림1 - 샌드박스 회피 기술 >
일반적인 회피 기술에는 특정 어셈블리 명령을 사용하여 특정 레지스트리 키 또는 파일 이름을 찾는 것이 포함됨.
이러한 회피 기술은 숙련된 분석가가 쉽게 발견할 수 있으며 서명을 사용하는 샌드박스에서도 탐지할 수 있음.
< 그림2 - Cuckoo Sandbox 서명은 회피 기술을 감지함 >
완전히 일반 코드처럼 보이지만 쉽게 감지할 수 없는 회피 기술이 있습니까?
Cuckoo 모니터에서 발견한 문제로 인해 실제로 가능함.
CAPE 샌드박스도 영향을 받음.
Cuckoo Sandbox 와 CAPE 는 연구 및 보호 영역에서 사용되는 일반적인 오픈 소스 샌드박스 환경이라는 점을 강조해야 함.
따라서 발견된 회피 기법은 많은 연구원과 기업에 영향을 미칠 수 있음.
우리는 Cuckoo 와 CAPE 에서만 테스트했지만 설명된 기술은 다른 샌드박스에도 적용될 수 있음.
( 1 ) 하나의 Windows API 함수 호출에서 Cuckoo Sandbox 회피 기법
ntdll.dll 에는 일반적으로 샌드박스에 연결되는 400개 이상의 기본 API 함수(또는 Nt 함수)가 있음.
이러한 큰 목록에는 다양한 유형의 실수(mistakes)를 위한 충분한 공간이 있음.
후크된 Nt-funtions 을 확인하고 몇 가지 문제를 발견했음.
그 중 하나는 후크된 함수와 원래 NTloadKeyEx 함수의 인수 갯수가 일치하지 않는다는 것임.
함수가 잘못 연결되면 커널 모드에서 운영 체제가 충돌할 수 있음.
잘못된 사용자 모드 후크는 그다지 중요하지 않음.
그러나 분석된 응용 프로그램이 충돌하거나 쉽게 감지될 수 있음.
NtLoadKeyEx 함수를 살펴봄.
이 함수는 Windows Server 2003 에서 처음 도입되었으며 인수가 4개 뿐임.
; Exported
entry 235. NtLoadKeyEx ; Exported
entry 1072. ZwLoadKeyEx ; __stdcall
NtLoadKeyEx(x, x, x, x) public
_NtLoadKeyEx@16 |
나주에 이 기능이 크게 변경됨.
Windows Vista 부터 최신 버저느이 Windows 10 까지 8개 인수가 있음.
; Exported
entry 318. NtLoadKeyEx ; Exported
entry 1450. ZwLoadKeyEx ; __stdcall
NtLoadKeyEx(x, x, x, x, x, x, x, x) public
_NtLoadKeyEx@32 |
그러나 Cuckoo 모니터에서 NtLoadKeyEx 선언에는 여전히 4개의 인수만 있음.
* POBJECT_ATTRIBUTES TargetKey * POBJECT_ATTRIBUTES SourceFile ** ULONG Flags
flags ** HANDLE
TrustClassKey trust_class_key |
우리는 이 레거시 프로토타입이 다른 소스에서도 사용되는 것을 발견함.
예를 들어 CAPE 모니터에도 동일한 문제가 있음.
extern
HOOKDEF(NTSTATUS, WINAPI, NtLoadKeyEx, __in
POBJECT_ATTRIBUTES TargetKey, __in
POBJECT_ATTRIBUTES SourceFile, __in
ULONG Flags, __in_opt
HANDLE TrustClassKey ); |
따라서 샌드박스가 최신 Windows OS 를 사용하는 경우 이 기능이 잘못 연결됨.
잘못 연결된 함수를 호출한 후 스택 포인터 값이 유효하지 않게 됨.
따라서 NtLoadKeyEx 를 호출하는 RegLoadAppKeyW 함수에 대한 합법적인 호출은 예외로 이어짐.
이 사실은 이 함수를 한 번만 호출하여 Cuckoo 및 CAPE 샌드박스를 회피하는 데 사용할 수 있음.
회피 기술은 매우 간단함.
샌드박스에서 실행할 때 일부 코드를 숨기려면 이 코드 전에 유효한 인수를 사용하여 RegLoadAppKeyW 를 호출해야 함.
샌드박스에서는 예외로 인해 이 코드에 도달하지 않음.
RegLoadAppKeyW(L"storage.dat",
&hKey, KEY_ALL_ACCESS, 0, 0); // 어플리케이션이 샌드박스에서 실행 중인 경우 예외가 발생함 // 아래 코드는 실행되지 않음
// 주의를 산만하게 하기 위해 hKey 와 함께 작동하는
일부 합법적인 코드가 여기에 있음 // ... RegCloseKey(hKey); // 악성 코드가 여기에 있음 // ... printf("Some
malicious code"); |
< 그림3 - Cuckoo Sandbox 행동 분석 보고서 >
RegLoadAppKeyW 를 사용하는 대신 NtLoadKeyEx 함수를 직접 호출하고 호출 후 ESP 값을 확인할 수 있음.
후크된 NtLoadKeyEx 함수 내부에 예외가 발생한 경우 응용 프로그램이 충돌하는 것을 방지하려면 예외 처리를 추가할 수도 있음.
__try { _asm mov old_esp, esp NtLoadKeyEx(&TargetKey,
&SourceFile, 0, 0, 0, KEY_ALL_ACCESS, &hKey, &ioStatus); _asm mov new_esp, esp _asm mov esp, old_esp if (old_esp != new_esp) printf("Sandbox detected!"); } __except
(EXCEPTION_EXECUTE_HANDLER) { printf("Sandbox detected!"); } |
이제 분석가 입장에서 무엇을 찾아야 하는지 알게 됨.
( 2 ) 후크된 함수의 인수에 대한 필수검사 부족 기법
Cuckoo Sandbox 와 CAPE 에서 발견된 또 다른 문제는 후크된 함수의 모든 인수에 대해 필요한 검사가 부족하다는 것임.
예를 들어 Sleep 함수를 호출할 때마다 호출되는 매우 자주 사용되는 함수 NtDelayExecution 을 살펴봄.
NTSTATUS NTAPI NtDelayExecution( IN BOOLEAN Alertable, IN PLARGE_INTEGER DelayInterval); |
NtDelayExecution 함수의 두 번째 인수는 지연 간격 값에 대한 포인터임.
커널 모드에서 NtDelayExecution 함수는 이 포인터의 유효성을 검사하고 다음 값을 반환할 수도 있음.
> STATUS_ACCESS_VIOLATION - 포인터 값이 유효한 사용자 모드 주소가 아닌 경우
> STATUS_DATATYPE_MISALIGNMENT - 주소가 정렬되지 않은 경우 (DelayInterval & 3 != 0)
샌드박스에서 NtDelayExection 및 유사한 함수에 대한 입력 인수가 올바르게 처리되지 않을 수 있음.
DelayInterval 에 대해 정렬되지 않은 포인터로 NtDelayExecution 을 호출하면 일반적으로 STATUS_DATATYPE_MISALIGNMENT 를 반환함.
그러나 샌드박스에서 DelayInterval 값은 적절한 검사 없이 새 변수에 복사될 수 있음.
이 경우지연이 수행되고 반환된 값은 STATUS_SUCCESS 가 됨.
이것은 샌드박스를 감지하는 데 사용할 수 있음.
__declspec(align(4))
BYTE aligned_bytes[sizeof(LARGE_INTEGER) * 2]; DWORD Timeout
= 10000; //10 seconds PLARGE_INTEGER
DelayInterval = (PLARGE_INTEGER)(aligned_bytes + 1); //unaligned
DelayInterval->QuadPart
= Timeout * (-10000LL); if
(NtDelayExecution(TRUE, DelayInterval) != STATUS_DATATYPE_MISALIGNMENT) printf("Sandbox detected"); |
반면에 DelayInterval 에 액세스할 수 없는 주소가 설정된 경우 반환 코드는 STATUS_ACCESS_VIOLATION 이어야 함.
이것은 샌드박스를 감지하는 데에도 사용할 수 있음.
if
(NtDelayExecution(FALSE, (PLARGE_INTEGER)0) != STATUS_ACCESS_VIOLATION) printf("Sandbox detected"); |
DelayInterval 인수가 액세스되기 전에 확인되지 않으면 잘못된 포인터를 사용하는 경우 예외가 발생할 수 있음.
예를 들어 다음 코드는 Cuckoo 모니터를 충돌시킴.
NtDelayExecution(FALSE,
(PLARGE_INTEGER)0xFFDF0000); |
앞서 언급했듯이 일반적으로 이 호출은 예외를 일으키지 않고 STATUS_ACCESS_VIOLATION 을 반환해야 함.
이러한 기술과 기타 많은 기술은 아래 사이트에 설명되어 있음.
링크 : https://evasions.checkpoint.com/
댓글 없음:
댓글 쓰기