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:

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:] == 'x00x00':
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:

['', '', '', '', 'xbbxa2xbdx9ex1fx8bx08x08', 
'', '', '', '', '', '', '', '', '', '', '', '', '', '',
<..snip..>
'', '', '', '', '', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', 'hdesk', '', '', '',
'', '', '', '', '', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '', '', '', '',
'91.238.]50.101', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '', '', '', '', 'x90x1f’]

A few values we can see, especially the C2 address but also the C2 traffic marker of ‘x1fx8bx08x08’ 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%DC

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%DC
- 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%DF
- 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
- SOFTWAREMicrosoftWindows NTCurrentVersionAppCompatFlagsLayers
- C:Program Files (x86)MicrosoftEdgeApplicationmsedge.exe=~ WIN8RTM
- Copies profile data to
- %TEMP%DE
- 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
- SoftwareMicrosoftInternet ExplorerMain
- NoProtectedModeBanner=1
- TabProcGrowth=0
- SoftwareMicrosoftWindowsCurrentVersionInternet SettingsZones3
- 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.

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%DC
%TEMP%DF
%TEMP%DE

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

IcedID gets LoadedJason Reaves

Jason Reaves

in

Walmart Global Tech Blog
IcedID gets LoadedBy: Joshua Platt and Jason Reaves

4 min readOct 20, 2023

Transforming Text Classification with Semantic Search Techniques — FaissHarika Samala

Harika Samala

in

Walmart Global Tech Blog
Transforming Text Classification with Semantic Search Techniques — FaissClassification models serve as supervised tools for organising documents into specific categories. Semantic search emerges as a practical…

8 min readMar 13, 2024

1

How to Determine Causal Effects when A/B Tests are Infeasible through Adopter AnalysisAvanti Chande

Avanti Chande

in

Walmart Global Tech Blog
How to Determine Causal Effects when A/B Tests are Infeasible through Adopter AnalysisTags: #DataScience #CausalInference #DifferenceinDifference #Matching #MatchedDiD #PropensityScoreMatching

6 min readApr 19, 2024

3

NewBot LoaderJason Reaves

Jason Reaves

in

Walmart Global Tech Blog
NewBot LoaderBy: Jason Reaves and Joshua Platt

5 min readMar 12, 2024

UAC-0133 (Sandworm) plans for cyber sabotage at almost 20 critical infrastructure facilities in…Simone Kraus

Simone Kraus

in

Detect FYI
UAC-0133 (Sandworm) plans for cyber sabotage at almost 20 critical infrastructure facilities in…Translation of the latest UA-CERT alert published today & Technical Analysis of QUEUESEED which is the KAPEKA Backdoor used in June 2022 in…

9 min readApr 19, 2024

Black and white pencil sketch of a Python snake wrapped around a computer displaying code, with subtle dark web elements and the Tor logo.Ervin Zubic

Ervin Zubic
Python for Dark Web OSINT: Automate Threat MonitoringLearn how to use Python to automate monitoring of dark web forums, leak sites, and marketplaces for actionable threat intelligence.

6 min readApr 26, 2024

2

Unfolding Agent Tesla: The Art of Credentials Harvesting. Dropper AnalysisOsama Ellahi

Osama Ellahi
Unfolding Agent Tesla: The Art of Credentials Harvesting. Dropper AnalysisAnalysis of Agent Tesla, A Close Look at Password Theft Technique

6 min readFeb 6, 2024

Malware Analysis — Ramcos RAT0xMrMagnezi

0xMrMagnezi
Malware Analysis — Ramcos RATAnalysis of Ramcos RAT reveals cybercriminals’ sophisticated techniques to evade detection and gain remote access, showcasing modern…

4 min readFeb 19, 2024

How i Find Database Credentials via Mass Recon & Recon Scoping on GcashPh.Hitachi

Ph.Hitachi
How i Find Database Credentials via Mass Recon & Recon Scoping on GcashHi guys,

6 min readApr 22, 2024

2

Memory Forensics with Volatility | PDF Malware Analysis with Any.Run | Cyber Incident ResponseMotasem Hamdan

Motasem Hamdan
Memory Forensics with Volatility | PDF Malware Analysis with Any.Run | Cyber Incident ResponseWe covered a cyber incident response case study that involved a malicious PDF malware delivered through a phishing email. The PDF malware…

2 min read3 days ago

Source: https://medium.com/walmartglobaltech/keyhole-analysis-60302922aa03

Tags: BANK, PASSWORD, BROWSER, PHISHING, DARK WEB, BACKDOOR, LEAK, EMAIL