Full Hancitor malware analysis
As-salamu Alaykum
Introduction
Hancitor is a famous malware loader that has been in use for years since first being observed in 2015. A malware loader drops the actual malicious content on the system then executes the first stage of the attack. Hancitor has been the attacker’s loader of choice to deliver malwares like: FickerStealer, Sendsafe, and Cobalt Strike if the victim characteristics are met. In recent months, more threat intelligence has been gathered to confirm the selection of Hancitor by Cuba Ransomware gangs as well. The popularity of Hancitor among threat actors is considered to last for a while. ref
Unpacking process
We tried in a later article to unpack Hancitor malware. see it from Here.
But we will try another way to unpack it in this sample. Open the sample in the x32dbg
and set two BPs on VirtualAlloc
and VirtualProtect
then run F9
. We hit VirtualAlloc
and then Execute till return
then Follow in Dump
of EAX. Keep doing that to hit the VirtualProtect
BP we will see M8Z
an indicator of Aplib compression.
Then if we scroll down to the offset xx4380 we will get the MZ
string which is the start of our unpacked file used in the analysis. Then save it to a file to start the analysis.
Abnormal Entry point
In Hancitor, there are 3 exports BNJAFSRSQIX
, SDTECHWMHHONG
, DllEntryPoint
. And the first two functions have the same address. For the first thought the entry point will be DllEntryPoint
, but if we check this function it only returns 1. From the malicious document which we extract the maliciuos Hancitor malware. Launched the rundll32.exe command to execute the BNJAFSRSQIX
function, so it must be the real entry point for this DLL.
Gathering victim information
We enter BNJAFSRSQIX
we see a call to a function which i renamed it to Hancitor_Main
. Then enter sub_10001AA0
renamed info_gathering
.
First the malware gathers information about the host contains: OS version, Computer name, Domains names, Victim IP address, whether the machine is x64 or x86 OS.
Then we enter getVictimID
then enter sub_10001C70
which is the function that generates a unique ID for the victim. First, calling GetAdaptersAddresses
to get the addresses associated with the network adapters on the victim machine then XOR-ing each (MAC) adapter with its address together. And calling GetVolumeInformationA
to retrieve the volume serial number of the machine then XORs it with the result
to create the victim’s unique ID.
How to get the IP of the victim? By sending a GET request to hxxp://api[.]ipify[.]org
. If the malware is unable to contact the website, it uses 0.0.0.0
as the victim’s IP address.
Then the final gathering step is to concatinate the gathered victim’s information in two ways to be sent to C2 server:
GUID=<Victim's GUID>&BUILD=<Build ID>&INFO=<Machine Information>&EXT=<Network domain names>&IP=<Victim's IP address>&TYPE=1&WIN=<Windows major version>.<Windows minor version>(x64)
GUID=<Victim's GUID>&BUILD=<Build ID>&INFO=<Machine Information>&EXT=<Network domain names>&IP=<Victim's IP address>&TYPE=1&WIN=<Windows major version>.<Windows minor version>(x32)
Configuration Extraction
Manually Extraction
Malware authors use tricks to obfuscate thier C2 IoCs. One of them is to encrypt it into a big chunk of data as in Hancitor Malware. So we need to search for big chunk of data which later will be decrypted during the runtime. We will find our encrypted configuration in the begining of .data
section and if we select the hex values then press D
we see it has refernces which means that it’s used somewhere.
There are two chunks of data Pbdata
and byte_10005018
. If we press x
over one of them it takes us to a function which will be called mw_Decrypt_Data
which is the function decrypts the configuration of the malware. And these two chuncks of data are parameters in this function.
Now enter this mw_Decrypt_Data
function. We see there are 5 API calls:
CryptCreateHash
: initiates the hashing of a stream of data.CryptCreateHash
the 2nd parameter is Algid
which identifies the hash algorithm to use. Its value is 0x8004u
which used SHA1 see the decumentation of Algid
CryptDeriveKey
: generates cryptographic session keys derived from a base data value CryptDeriveKey
the 2nd parameter is Algid
Its value is 0x6801u
which used RC4
The 4th parameter is dwflags
which determinte the size of the key which the key is a set with the upper 16 bits of 0x280011u
which is 0x28
divided by 8. Then the key size is 5 bytes.
We get the SHA1 key from pbdata
. And we get the RC4 data from byte_10005018
and decrypte the RC4-encrypted data using the first 5 bytes of the SHA1 key.
*How we select hex values only? Do as in the figure 9. then choose hex string (unspaced)
. then copy what is in the preview
.
Then open cyberchef using from hex
and SHA1
we get the SHA1 key ad818c687f8dc7f281135753e567eababa03d0ba
. Then select the whole chunck of data of byte_10005018
and copy hex as we did in SHA1 and use RC4
and using the first 5 bytes of SHA1.
Here are 3 C2 servers which the malware communicate with for further commands based on the collected victim host information. Build ID 2909_xplw
.
hxxp://forkineler(.)com/8/forum.php
hxxp://yemodene(.)ru/8/forum.php
hxxp://fordecits(.)ru/8/forum.php
Automated Extraction
import pefile #Parse data into a PE format
import re #Use a regular expression to locate our config data
import struct # Convert binary data into numeric values.
import hashlib #Generate a SHA1 hash
file_path = r'file path' #file path
data = open(file_path,'rb').read() # read it in binary
pe = pefile.PE(data=data) # to parse data as PE file to acess sections and offsets
# Use Regular Expression to Locate The Decryption Code
key = rb'\x6a(.)\x68(....)\x68\x00\x20\x00\x00' #opcode as in the figure(6).
m = re.search(key, data) # extract the data that was matched by the wildcard.
if m != None:
print("key length: %r" % m.group(1))
print("key address: %r" % m.group(2))
# Convert The Extracted Key Information
struct.unpack('b', m.group(1))[0] #converte into bytes
hex(struct.unpack('<I', m.group(2))[0]) # converting an unsigned integer (DWORD) stored in little-endian format in hex
# Use The Key Information To Extract The Key Data
key_len = struct.unpack('b', m.group(1))[0]
key_address = struct.unpack('<I', m.group(2))[0]
key_rva = key_address - pe.OPTIONAL_HEADER.ImageBase
key_offset = pe.get_offset_from_rva(key_rva)
key_data = data[key_offset:key_offset+key_len]
config_data = data[key_offset+key_len:key_offset+key_len+0x2000]
# Hash The Key Data To Create The Key
m = hashlib.sha1()
m.update(key_data)
key = m.digest()[:5]
# RC4 Decryption
def rc4crypt(data, key):
#If the input is a string convert to byte arrays
if type(data) == str:
data = data.encode('utf-8')
if type(key) == str:
key = key.encode('utf-8')
x = 0
box = list(range(256))
for i in range(256):
x = (x + box[i] + key[i % len(key)]) % 256
box[i], box[x] = box[x], box[i]
x = 0
y = 0
out = []
for c in data:
x = (x + 1) % 256
y = (y + box[x]) % 256
box[x], box[y] = box[y], box[x]
out.append(c ^ box[(box[x] + box[y]) % 256])
return bytes(out)
# Parsing The Config
config = rc4crypt(config_data, key)
build_id = config.split(b'\x00')[0]
c2_string = b''
for s in config.split(b'\x00')[1:]:
if s != b'':
c2_string = s
break
c2_list = c2_string.split(b'|')
print("BUILD: %s" % build_id)
for c2 in c2_list:
if c2 != b'':
print("C2: %s" % c2)
For better understanding visit OALabs github
C2 server Communication
After collecting all victim information and put into one fourm as we mentioned above. the malware tries to get the C2 URL from the configuration and sends the data to C2 the servers. If we enter the function getNext_URL
. It tries to get the next URL address from the list using the location of |
between the C2 servers.
Then we enter send_Data_To_C2
function, we see 3 API calls which are an indication of sending data to C2 server:
HttpOpenRequestA: Creates an HTTP POST request handle.
HttpSendRequestA: Sends the specified request or data to the HTTP server.
InternetReadFile: Reads data or commands from a handle opened by the HttpOpenRequest function.
From PCAP of Malware-Traffic-Analysis.net. We ping the 1st C2 server ( hxxp://forkineler(.)com/8/forum.php) to get the IP 194.147.115.132
which will help us to get the C2 response. Open the PCAP file and search with ip.addr == 194.147.115.132
then follow
then TCP stream
. As we see POST and GET request and base64 encoded response.
base64 encoded C2 response: VZAEARZAEg4OCkBVVU4XGw8IChUUDlQID1VOSwlUGBMUBwEWQBIODgpAVVVOFxsPCAoVFA5UCA9VTktUGBMUBw==
. Then enter check_C2_response
function:
first it checks the 1st 4 chars IsUpperCase? if Not in upper case the check fails and return 0.
If the check is valid then it decodes the base64 C2 response and XORs the result with the letter z
using CyberChef
.
As we see in the last figure the response command contains:
A specific action from `{'b','e','l','n','r'}`
A colon (:) char
A URL is used to download malicious content
Download content and inject
Explaining the set of actions the malware will do from {'b','e','l','n','r'}
.
b
action
The downloaded content will be injected into a new svchost.exe
process using the APIs: VirtualAllocEx
and WriteProcessMemory
. First the malware downloads content, as in the download_content
function, from the malicious URL in the C2 response then inject it into a new svchost
process.
Entering download_content
then entering decrypt_decompress_downloadedContent
. We will see that it’s trying to decrypt the downlaoded content by XOR-ing with its first 8 bytes then using RtlDecompressBuffer
function to decompress. See doc then continue
using VirtualAllocEx
to allocate a buffer in the memory, then w_HeapAlloc
to allocate a heap buffer, and then WriteProcessMemory
to write the payload from the heap to svchost allocated memory.
b
action in brief:
- Download the malicious content from the URL.
- Decrypting and decompress the content.
- create
svchost
in a suspended state. - Allocate a buffer in the memory for the malicous content.
- Load the malicious content into the allocated buffer.
- Make the entryPoint of the injected malicous content is the entryPoint of
svchost.exe
process - Then resume the process
e
action
The downloaded content will be injected into the current running process. Download then inject.
e
action in brief:
- Download the malicious content from the URL.
- Decrypting and decompress the content.
- Allocate memory for the content in the current running process.
- Load import table
- Load the malicious content into the allocated buffer.
- launch the thread using two methods:
- using
CreateThread
which resolves the entryPoint Or - using the returned entryPoint after writing the content in memory.
- using
i
action
Downloads shellcode and inject it into the current process or into svchost.exe
.
i
action in brief:
- Download the shellcode from the URL.
- Decrypting and decompress the content which is shellcode.
- inject the shellcode by one methode:
- Creating
svchost.exe
process then inject the process then resumes the process. Or - Inject into the current running process.
- Creating
n
action
Does nothing, or it’s used to ping the victim.
r
action
Drop an EXE or DLL in the Temp
folder then inject into svchost.exe
.
r
action in brief:
- Download the the content from the URL.
- Decrypting and decompress the content.
- Gets the path of TEMP folder and create a file and its random name begins with
BN
in the path of TEMP folder. - Execute the downloaded content which depends on if it’s an EXE or DLL:
- An EXE it will be executed normally.
- a DLL executed by using
rundll32.exe
.
Summary
Abnormal Entry point: DllEntryPoint
is not the real entryPoint, but BNJAFSRSQIX
is the EnryPoint.
Gathering victim information: Gatheing info about the victim which gives choice for the malware to generate unique ID and downlaod which content.
Configuration Extraction: The key is encrypted in SHA1 and the embedded configuration encrypted in RC4. The malware uses CryptoAPI
to do the decryption using the first 5 bytes of the SHA1 key.
C2 server Communication: The victim information will be sended to the C2 server. After decoding the base64 encoded with additional layer of single-byte XOR C2 response. The decoded C2 response has Build ID
and URLs
from which the malicous content is downloaded.
Download content and inject: From the decoded C2 response, the malware will decide which content will be downlaoded then injected then executed. If it’s a malicious EXE, DLL, or shellcodes.
IoCs
No. | Description | Hash and URLs |
---|---|---|
1 | The packed DLL (MD5 ) | 32799A01C72148AB003AF600F8EB40DC |
2 | The unpacked DLL (MD5) | B7679D55FC9B5F3447FF743EEAAB7493 |
3 | C2 response server | hxxp://4maurpont.ru/41s.bin (194.147.115.132) |
4 | C2 Server 1 | hxxp://forkineler(.)com/8/forum.php |
5 | C2 Server 2 | hxxp://yemodene(.)ru/8/forum.php |
6 | C2 Server 3 | hxxp://fordecits(.)ru/8/forum.php |
Article quote
سبحانك! ما أضيق الطريق عليّ ما لم تكن دليله! وما أوحشه عليّ مَن لم تكن أنيسه
REF
-
https://cyber-anubis.github.io/malware%20analysis/hancitor/#the-b-command
-
https://www.0ffset.net/reverse-engineering/malware-analysis/hancitor-analysing-the-main-loader/
-
https://www.binarydefense.com/analysis-of-hancitor-when-boring-begets-beacon/
-
https://elis531989.medium.com/dissecting-and-automating-hancitors-config-extraction-1a6ed85d99b8