KOROMOON

착한 사마리아인이 되고 싶습니다.

2/15/2022

보이지 않는 샌드박스 회피 (Invisible Sandbox Evasion 번역)


공부 차원에서 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/


댓글 없음:

댓글 쓰기