Skip to main content

Mwdb config parser

Project description


Python 3 library useful for getting structured IOC data from mwdb configs.


Warning: this project is only relevant to mwdb and malduck users.

Malduck configs (like the ones in mwdb) are usually unstructured (they're just JSONs with some additional metadata). On the other hand, automated processing often requires structured data.

For example, URL processing and extraction is a common use case for analysts. Sadly, every module stores them a bit differently (and due to backward compatibility we're not ready to change that).

For example, compare how we store URLs from ISFB:

"domains": [
        "cnc": ""
        "cnc": ""


"urls": [
        "url": ""
        "url": ""
        "url": ""

And mirai:

"cncs": [
        "host": "",
        "port": 1024

With mwdb-iocextract you can handle all these (and dozens more) config types in the same way:

from mwdb_iocextract import parse

config_family = "mirai"
config_data = {
    "cncs": [
            "host": "",
            "port": 1024
    "table_key": "0xdedefbaf",
    "variant": "OWARI",
    "type": "mirai"

iocs = parse(config_family, config_data)
> python

See below for more usage examples.


$ pip install mwdb-iocextract

You can find the newest version here:

How does it work

Most configs can be parsed without any change to this library. Standard keys, like "urls", are automatically recognised and parsed correctly.

Plurality of the key name does not matter. When value is a list, all elements are added separately. For example both of these are equivalent:

    "urls": ""
    "url": [""]

Right now this library supports:

Network Locations

"Network Locations" can be IP, domain, URL, etc. There are many available formats:

    "hosts": [
        "",  # format 1 - IP
        "",  # format 2 - URL
        { "cnc": "" },  # format 3 - URL in a dict.
        # Allowed keys: "cnc", "url", "ip", "domain", "host" (all handled in the same way)
        { "cnc": "", "port": 1337 },  # format 4 - domain/port pair

        # NOT allowed: url + port
        # { "cnc": "", "port": 1337 },

Config keys: urls, c2, ips, domains, url, cnc, cncs, hosts, host, cncurl.


Passwords hardcoded in malware. Plain text.

Config keys: password.


Config keys: mtx, mutex. Plain text.


Emails used by malware and hardcoded in the source. Plain text.

Config keys: email, emails.

Ransom messages

HTML or txt ransom messages hardcoded in the source. Plain text.

Config keys: ransommessage.

RSA keys

RSA public or private keys hardcoded in the binary.

There are two supported formats:

  • plaintext ("-----BEGIN PUBLIC KEY-----...")
  • parsed - a dict with "n", "e", and (optionall) "d" keys.

Parsed format is more deterministic and recommended for your modules, but you can use both.

Config keys: publickey, rsapub, rsakey, pubkey, privkey.

AES keys

AES keys hardcoded in the binary. Plaintext.

Config keys: aeskey.

XOR keys

XOR keys hardcoded in the binary. Plaintext.

Config keys: xorkey.

Serpent keys

XOR keys hardcoded in the binary. Plaintext.

Config keys: serpentkey.

Encryption keys

Other key types found in the malware. Extracted as a key of type "unknown". Plaintext.

Config keys: encryptionkey, key, keys.

Dropped files

Paths or filenames of files dropped by the malware. Plaintext.

Config keys: drop_name.


If you want to extend this library or add support for more modules, feel free to contribute to this repository. We're only interested in modules at least partially publicly accessible. So module support is OK to merge, but your in-house internal TLP:RED modules - probably no.


Scan mwdb

How to download config from mwdb and parse it:

from mwdb_iocextract import parse
from mwdblib import Malwarecage

def main():
    # See also
    mwdb = Malwarecage()
    mwdb.login("msm", "my_secret_password")

    for cfg in mwdb.recent_configs():
        if cfg.type != "static":
            # This library only works with configs of type "static" 
            # (default mwdb config type).
        iocs = parse(, cfg.cfg)
        print(iocs.prettyprint())  # convert all IoCs to string

if __name__ == "__main__":

Working with IOCs

The result of a parse() is a IocCollection object. You can use it like a normal Python object, for example:

iocs = parse(, cfg.cfg)

for rsa_key in iocs.rsa_keys:
    e = rsa_key.e  # get e
    n = rsa_key.n  # get n

for netloc in iocs.network_locations:
    ip = netloc.ip  # get IP if known
    domain = netloc.domain  # get domain if known
    host =  # get domain if known, otherwise IP
    port = netloc.port  # get port if known
    loctype = netloc.location_type  # get type (usually CNC)
    url = netloc.url  # get host + port + path

for key_type, key_data in iocs.keys:
    # key_type - for example "rc4"
    # key_data - hexencoded bytes, for example "6123541243"

iocs.passwords  # passwords or similar data found in the config
iocs.mutexes  # mutex names used by malware
iocs.dropped_filenames  # filenames dropped on the disk
iocs.emails  # emails used by malware

MISP integration

You can convert IocCollection to a MISP object:

def upload_to_misp(family, config):
    iocs = parse(family, config)

    if not iocs:
        # Nothing actionable found - skip the config

    # Be careful not to upload duplicated events.
    # We use uuid5s generated from mwdb dhash as unique deterministic UUIDs.
    event = MISPEvent()
    event.add_tag(f"mwdb:family:{family}") = f"Malware configuration ({family})"

    for o in iocs.to_misp():


Project details

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mwdb-iocextract-1.2.1.tar.gz (11.1 kB view hashes)

Uploaded source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page