Skip to content

Instantly share code, notes, and snippets.

@FernandoDoming
Created November 18, 2020 19:12
Show Gist options
  • Save FernandoDoming/753151d3e2fbf9260dcd58aa23dddec4 to your computer and use it in GitHub Desktop.
Save FernandoDoming/753151d3e2fbf9260dcd58aa23dddec4 to your computer and use it in GitHub Desktop.
# Copyright (C) 2010-2015 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# This signature was contributed by RedSocks - http://redsocks.nl
# See the file 'docs/LICENSE' for copying permission.
import struct
from builtins import bytes
from lib.cuckoo.common.abstracts import Signature
class QuantloaderDump(Signature):
name = "quantloader_dump"
description = "Quantloader detected - Configuration dumped"
severity = 5
categories = ["trojan", "loader"]
families = ["quantloader"]
authors = ["FDD"]
minimum = "2.0"
procmem = None
decrypt_config_offsets = {
"cnc1": (148, 152),
"url_param1": (176, 180),
"url_param2": (224, 228),
"url_param3": (281, 285),
"url_param4": (329, 333),
"url_param5": (424, 428),
}
# ============== Generic memory management functions ==============
def get_region(self, addr):
if not self.procmem:
return None
for region in self.procmem["regions"]:
if addr >= int(region["addr"], 16) and addr <= int(region["end"], 16):
return region
return None
def get_region_from_offset(self, offset):
if not self.procmem:
return None
for region in self.procmem["regions"]:
if offset >= region["offset"] and offset <= region["offset"] + region["size"]:
return region
return None
def offset_to_rva(self, offset):
data = None
if not self.procmem:
return data
region = self.get_region_from_offset(offset)
if not region:
return data
region_offset = offset - region["offset"]
rva = int(region["addr"], 16) + region_offset
return rva
def deref(self, addr, size):
data = None
if not self.procmem:
return data
region = self.get_region(addr)
if not region:
return data
rva = addr - int(region["addr"], 16)
f = open(self.procmem["file"])
f.seek(region["offset"] + rva)
data = f.read(size)
f.close()
return data
def deref_string(self, addr):
data = ""
p = addr
while True:
byte = self.deref(p, 1)
if byte == '\x00' or byte is None:
return data
p += 1
data += byte
return data
def read_offset(self, offset, len):
f = open(self.procmem["file"])
f.seek(offset)
data = f.read(len)
f.close()
return data
# ============== Malware specific functions ==============
def decode(self, data, key):
result = list()
m = bytes(data)
k = bytes(key)
for i in range(0, len(m)):
result.append(m[i] - k[i % len(k)])
return bytes(result)
# ======================== Main ===========================
def on_complete(self):
config = {}
for procmem in self.get_results("procmemory", []):
for yara_matches in procmem.get("yara", []):
if yara_matches["name"] != "QuantLoaderCfg":
continue
self.procmem = procmem
rawdata = self.read_offset(yara_matches["offsets"]["decryptstring"][0][0], 150)
keyaddr = struct.unpack("<I", rawdata[59:63])[0]
key = self.deref_string(keyaddr)
if not key:
continue
key = key[1:] + "\x00"
config["string_key"] = key
cfg_decrypt_addr = yara_matches["offsets"]["configdecrypt"][0][0]
asm = self.read_offset(cfg_decrypt_addr, 512)
for ioc, offset in self.decrypt_config_offsets.iteritems():
try:
addr = struct.unpack("<I", asm[offset[0]:offset[1]])[0]
_str = self.deref_string(addr)
_str = self.decode(_str, key)
if _str:
config[ioc] = _str
except Exception:
continue
config["family"] = "quantloader"
self.marks.append({
"type": "config",
"config": config
})
return self.has_marks()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment