ClipBanker.2 Writing a static .NET config extractor in Python
Tags: #malware
2022-12-19
After reverse engineering a ClipBanker I decided to write a static config extractor for it and, as it turns out, coding a config extractor for .NET malware isn’t straight-forward.
The best two ways I’ve found are the following:
- .NET Reflection-based extractor (requires writing .NET :c)
- dnlib (Python) based extractor (pure Python :D)
The first method loads the binary as .NET assembly (without executing the entry point) and gets the different class members through Reflection. The second method reads and parses the assembly without loading it.
Thus, this blog post will showcase a basic but adaptable Python extractor script using dnlib and pythonnet. The full extractor script is available at the bottom of the page.
Extractor logic
The different addresses, mutex value, C2 URL and configuration data are stored unencrypted in the
Addresses
class.
As all the class members are static they are initialised by a call to .cctor
(the static/class/type constructor) when the Addresses
class is first instantiated.
All that’s needed to extract the malware’s config is to get a reference to Addresses::.cctor
and then to parse its CIL.
>>> mod = dnlib.DotNet.ModuleDefMD.Load('./clipper/crack.exe.malware')
>>> cctor = mod.Types[6].Methods[0]
>>> for inst in list(cctor.Body.Instructions):
... print(inst)
...
IL_0000: ldstr "0x9e60ca775c5c6c65e900795782be58e0de549615"
IL_0005: stsfld System.String Crypto.Crypto.Addresses::ethereum
IL_000A: ldstr "8AFcmXsQttSXuBeYCL9fpa2rn5JrDwwoihMerrwF48V7Ar1EKNTZyGa6G2tMFMhEZNEReroTLe2gPSMQw6VZLSD65AyBqzD"
IL_000F: stsfld System.String Crypto.Crypto.Addresses::xmr
IL_0014: ldstr "pqdXXeEmLRGXHCg1"
IL_0019: stsfld System.String Crypto.Crypto.Addresses::Mutexx
IL_001E: ldstr "yes"
IL_0023: stsfld System.String Crypto.Crypto.Addresses::startup
IL_0028: ldstr "1H8M6uYCSAquJuZjTjy33ruXs23hZy72E9"
IL_002D: stsfld System.String Crypto.Crypto.Addresses::btc
IL_0032: ldstr "http://www.example.com/log.php"
IL_0037: stsfld System.String Crypto.Crypto.Addresses::url
IL_003C: ldstr "yes"
IL_0041: stsfld System.String Crypto.Crypto.Addresses::ethereumE
IL_0046: ldstr "yes"
IL_004B: stsfld System.String Crypto.Crypto.Addresses::xmrE
IL_0050: ldstr "yes"
IL_0055: stsfld System.String Crypto.Crypto.Addresses::btcE
IL_005A: nop
IL_005B: ret
The method alternates between Ldstr
and Stsfld
OpCodes. First Ldstr
pushes a reference to the string and then Stsfld
affects this value to a class member. All that’s left to do is to loop over each pair of lines to generate the config.
Full extractor script
#!/usr/bin/env python
# Installation
# 1. install pythonnet
# pip3 install pythonnet
# 2. install the dotnet SDK
# sudo apt install -y dotnet-sdk-6.0
# 3. build dnlib and copy it to local dir
# git clone https://github.com/0xd4d/dnlib && cd dnlib
# dotnet build
# cp ./Examples/bin/Debug/net6.0/dnlib.dll ..
from pythonnet import load
load("coreclr")
import clr
clr.AddReference("dnlib")
import dnlib
from dnlib.DotNet import *
import sys, json, os
def extract(file: str) -> dict:
mod = dnlib.DotNet.ModuleDefMD.Load(file)
config = {}
# v------------ extraction logic goes here ------------v
insts = list(mod.Types[6].Methods[0].Body.Instructions)
for val, name in zip(insts[::2], insts[1::2]):
try:
config[name.Operand.name.ToString()] = val.Operand
except:
pass
# ^------------ extraction logic goes here ------------^
return config
if __name__ == '__main__':
# Take files from both ARGV and STDIN
files = sys.argv[1:]
if not sys.stdin.isatty():
files += [f.strip() for f in sys.stdin]
if not files:
print('Usage:\n Files : ARGV + STDIN\n Config: STDOUT')
exit(1)
for file in files:
# Resolve the actual path of the file
file = os.path.abspath(os.path.expanduser(os.path.expandvars(file)))
try:
print('Processing,', file, file=sys.stderr)
# Write the config to STDOUT
print(json.dumps(extract(file)))
except Exception as e:
print('Failed to extract from', file, 'because', e, file=sys.stderr)
The next blog post in this series will be about writing a yara rule for the ClipBanker. Stay tuned !