Keyhole Analysis

By: Joshua Platt, Jonathan McCay and Jason Reaves

Keyhole is a multi-functional VNC/Backconnect component used extensively by IcedID/Anubis. While the malware contains functionality that has been previously reported on as typical VNC and HDESK capabilities, a general lack of technical information appears to exist around some of the expanded functionality currently present. In fact, the functionality we mapped out for the main Keyhole component rivals that of IcedID itself:

  • Collect system information
  • VNC
  • HDESK
  • Socks/Backconnect
  • Console command detonation via cmd.exe or powershell
  • Multiple methods of injecting explorer.exe
  • LDAP queries
  • File retrieval from infected systems
  • Hijacking browser profiles
  • Deleting browser profiles
  • Lower security of running browsers via command line manipulation
  • Checking for webcam existence
  • Taking pictures with webcam
  • Turning on microphone in registry for apps
  • Enumerating servers and shares in network

Portions of this functionality are spread out over multiple open-source reports but significant analysis appears to be missing on several noteworthy improvements.

Technical Overview

Initial loader piece:

The initial component involves a loader that can handle both PE files or shellcode versions of the main component; they also can utilize a custom PE loader with mangled headers. Ultimately the loader is responsible for decoding and loading the core module and executing it properly.

Decoding Core Module:

The core module is encoded using a 256 byte array of swap values, (key). After locating the initial blob containing the key values, a parsing algorithm is implemented to create the array.

Unparsed Key Blob:

An integer is given to decide how many bytes to pull from the unparsed blob. This integer will double in size until the value exceeds 256. Adding 1 to the amount of times the integer could double before exceeding 256 will be the number of bytes pulled.

Finding an integer to decide how many bytes to pull that is not 0 will result in different behavior. The first time this happens, the appropriate amount of bytes will be added followed by the addition of a null byte. All bytes until the next 0 will be grabbed, and the process repeats until the key len is 256.

Python: Parse Key

Parsed Key / Encoded Core Module

To decode the core module, each byte in the encoded module will be used as an index value. The byte in the key at that index will replace the encoded byte.

Python: Decode Core Module

Subset of Strings in Decoded Core Module

user32.dll
bad pathname
path not found
Default
TOP WND
RtlExitUserProcess
MS Shell Dlg
already exists
explorer.exe
move
test
auth
-err-
runasinvoker
mkdir
disk
busy
__compat_layer
invalid name
hcddir
action not found
Allow
CREATE
AD not found
Value
HIDE
Settings
WINMM.DLL
2500
disk not found
combase.dll
MOVE
divice not readed
mdiclient
f%0.8X
User32.dll
Lucida Console
ntdll.dll
memory alloc
shell32.dll
access denied
open
error
{%0.8X-%0.4X-%0.4X-%0.4X-%0.4X%0.8X}
abcedfikmnopsutw
SelectObject
GetCurrentObject
GetObjectA
CreateCompatibleDC
DeleteDC
CreateDIBSection
CreateCompatibleBitmap
GetViewportOrgEx
BitBlt
NetShareEnum
NetApiBufferFree
NetGetDCName
NETAPI32.dll
RtlLargeIntegerDivide
RtlGetVersion
NtReadVirtualMemory
NtWriteVirtualMemory
NtFlushInstructionCache
NtProtectVirtualMemory
NtAllocateVirtualMemory
LoadCursorA
GetAncestor
CreateDesktopA
GetKeyboardLayoutList
GetKeyboardLayout
VkKeyScanA
VkKeyScanExA
VkKeyScanExW
MapVirtualKeyA
MapVirtualKeyExA
OpenClipboard
CloseClipboard
SetClipboardData
EmptyClipboard
EnumWindows
SetWinEventHook
GetThreadDesktop
RegisterClassExA
GetClipboardData
AddClipboardFormatListener
OpenInputDesktop
VkKeyScanW
SendInput
WinExec
GetPrivateProfileIntW
ACTIVEDS.dll
WS2_32.dll
TRUE
name
Exec
High Definition Audio

Main component:

The main component of Keyhole can contain encoded strings, the really interesting part is that the algorithm used is the same as a sample I analyzed in June of 2017:

1cd6f992fbeee0a66e1c329e15db71fe891ae0e845867d6d30df867babe5bed6

Old IcedID string decoding routine:

Keyhole string decoding routine:

Decoding the strings in Keyhole took only a small edit in my old IcedID IDA script:

def gen_key(k):
return(((k << 0x1d) | (k >> 3)) & 0xffffffff)

for addr in XrefsTo(0x322630, flags=0):
addr = addr.frm
addr = idc.PrevHead(addr)
while GetMnem(addr) != "push":
addr = idc.PrevHead(addr)
print(hex(addr))
data_addr = GetOperandValue(addr,0)

xork_init = Dword(data_addr)
data_addr += 4
length_delta = Word(data_addr)
data_addr += 2
length = (xork_init ^ length_delta) & 0xffff
out = ""
xork = xork_init
for i in range(length):
xork = gen_key(xork)
xork += i
out += chr((Byte(data_addr) ^ (xork & 0xFF)) & 0xFF)
data_addr += 1
if out[-2:] == '\x00\x00':
print(out.decode('utf16'))
MakeRptCmt(addr, out.decode('utf16').encode('ascii'))
else:
print(out)
MakeRptCmt(addr, out)

The main component of Keyhole also expects a parameter to be passed, this address will be the shellcode blob from the previous loader component.

First a DWORD value is pulled out, which will eventually be used as the initial seed value for the XOR loop:

Next every byte after the DWORD is pulled out and run through a XOR loop where the initial seed value is manipulated every iteration, we can also see below that the blob being decoded is 0x14e bytes in length:

The decoded blob turns out to be the config for the main Keyhole component, so the loader component passes the config in itself off to the main component as a parameter. After decoding we have a few values that will be used for C2 communications:

['', '', '', '', '\xbb\xa2\xbd\x9e\x1f\x8b\x08\x08', 
'', '', '', '', '', '', '', '', '', '', '', '', '', '',
<..snip..>
'', '', '', '', '', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', 'hdesk', '', '', '',
'', '', '', '', '', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '', '', '', '',
'91.238.]50.101', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '', '', '', '', '\x90\x1f’]

A few values we can see, especially the C2 address but also the C2 traffic marker of ‘\x1f\x8b\x08\x08’ and the port 0x1f90 which is 8080.

Functionality

As mentioned the malware will retrieve some basic information about the infected system:

It can also enumerate servers and shares in the network and leverages LDAP queries in the process:

File retrievals are limited in size to roughly 33MB:

Browsers

For Chrome the module can copy the User Data to a new folder in temp:

Along with the parameter of ‘C\\’ we can see below that the folder structure will be built under temp:

In this case with a flag value of 0 we end up having the profile copied to:

%TEMP%\D\C\

Browser are manipulated in a similar way that old banking trojans used to do it, the module contains code for hooking NtCreateUserProcess which will then look for browsers to be spawned:

This lets the core module manipulate the command lines of the browsers, however it does more depending on which browser is found to be executing.

Chrome

- Copies profile data to
- %TEMP%\D\C\
- Modifies command line
- --user-data-dir=”
- " --ash-force-desktop --disable-3d-apis --disable-accelerated-layers --disable-accelerated-plugins --disable-audio --disable-gpu --disable-d3d11 --disable-d3d12 --disable-accelerated-2d-canvas --disable-deadline-scheduling --disable-ui-deadline-scheduling --aura-no-shadows --no-zygote --do-not-de-elevate --mute-audio --disable-renderer-accessibility --disable-direct-compositing --disable-gpu-compositing --disable-renderer-backgrounding --in-process-gpu

FireFox

- Reads default profile path from profiles.ini file
- Copies default profile to
- %TEMP%\D\F\
- Edits prefs.js
- user_pref("browser.preferences.defaultPerformanceSettings.enabled", false);
- user_pref("layers.acceleration.disabled", true);
- Modifies command line
- --no-remote -no-deelevate -profile “

Edge

- Sets registry keys
- SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers
- C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe=~ WIN8RTM
- Copies profile data to
- %TEMP%\D\E\
- Modifies command line
- --user-data-dir=”
- " --ash-force-desktop --disable-3d-apis --disable-accelerated-layers --disable-accelerated-plugins --disable-audio --disable-gpu --disable-d3d11 --disable-d3d12 --disable-accelerated-2d-canvas --disable-deadline-scheduling --disable-ui-deadline-scheduling --aura-no-shadows --no-zygote --do-not-de-elevate --mute-audio --disable-renderer-accessibility --disable-direct-compositing --disable-gpu-compositing --disable-renderer-backgrounding --in-process-gpu

Internet Explorer

- Modifies registry
- Software\Microsoft\Internet Explorer\Main
- NoProtectedModeBanner=1
- TabProcGrowth=0
- Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\3
- 2500=3
- Modifies command line
- -nomerge -noframemerging -nohome

Keyhole can also perform injection into newly started explorer processes in a variety of ways:

For BackConnect commands:

For console command line detonation a string is expected for either powershell or cmd detonation:

Also above it can be seen for commands related to the microphone, these are primarily related to manipulating registry values:

After the modifications the explorer will be restarted.

  • SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone\NonPackaged
  • SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone
  • SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StuckRects3
  • cmd.exe /c taskkill /f /im explorer.exe && start explorer.exe

YARA

rule keyhole_32
{
strings:
$config_decode = {694d0cfd43030081c1c39e2600894d0c}
condition:
all of them
}


rule keyhole_loader
{
strings:
$64exe = {5390 9cbb be0c c453 9d5b 5290}
$64dll = {7809 7407 7305 eb03 3941}
$32exe = {770c 569c c1e6 0ac1 ee09 f7d6}
$32dll = {5781 f7fc 46d8 5083 c728 bf0b}
condition:
any of them
}

IOCS

Sample hash

74aa61cc1157529fb98b757fb879616ffc2b54e4d4ff08c9b9d5b6dcec868c2a

Command Line

powershell.exe  -c "[Console]::OutputEncoding = [Console]::InputEncoding = [System.Text.Encoding]::GetEncoding('utf-8'); cd c:\; powershell"

cmd.exe /K chcp 65001 && c: && cd c:\

cmd.exe /c taskkill /f /im explorer.exe && start explorer.exe

cmd.exe /c start /wait explorer.exe /factory, {75dff2b7-6936-4c06-a8bb-676a7b00b24b}

explorer.exe shell:mycomputerfolder

explorer.exe shell:mycomputerfolder

Browsers with params:
--user-data-dir="

" --ash-force-desktop --disable-3d-apis --disable-accelerated-layers --disable-accelerated-plugins --disable-audio --disable-gpu --disable-d3d11 --disable-d3d12 --disable-accelerated-2d-canvas --disable-deadline-scheduling --disable-ui-deadline-scheduling --aura-no-shadows --no-zygote --do-not-de-elevate --mute-audio --disable-renderer-accessibility --disable-direct-compositing --disable-gpu-compositing --disable-renderer-backgrounding --in-process-gpu

--no-remote -no-deelevate -profile

File activity:

%TEMP%\D\C\
%TEMP%\D\F\
%TEMP%\D\E\

References

  1. https://info.phishlabs.com/blog/the-unrelenting-evolution-of-vawtrak
  2. https://malpedia.caad.fkie.fraunhofer.de/details/win.vawtrak
  3. https://www.justice.gov/usao-sdny/pr/russian-hacker-who-used-neverquest-malware-steal-money-victims-bank-accounts-sentenced
  4. https://securityintelligence.com/new-banking-trojan-icedid-discovered-by-ibm-x-force-research/
  5. https://blog.fox-it.com/2018/08/09/bokbot-the-rebirth-of-a-banker/
  6. https://blog.nviso.eu/2023/03/20/icedids-vnc-backdoors-dark-cat-anubis-keyhole/
  7. https://www.netresec.com/?page=Blog&month=2023-10&post=Forensic-Timeline-of-an-IcedID-Infection
  8. https://svch0st.medium.com/can-you-track-processes-accessing-the-camera-and-microphone-7e6885b37072

Keyhole Analysis was originally published in Walmart Global Tech Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.

Article Link: Keyhole Analysis. By: Joshua Platt, Jonathan McCay and… | by Jason Reaves | Walmart Global Tech Blog | Jan, 2024 | Medium