CERT-Polska / malduck

:duck: Malduck is your ducky companion in malware analysis journeys
GNU General Public License v3.0
318 stars 30 forks source link

Add support for embedded Yara rules #115

Open 0xThiebaut opened 11 months ago

0xThiebaut commented 11 months ago

This PR introduces the capability to embed Yara rules within Extractors. This capability opens the door to shipping the Extractors as standalone scripts not relying on Karton. The Citadel example can be modified as follow, while remaining compatible with Karton's config extractor.

import argparse
import json

from malduck import Extractor
from malduck.extractor import ExtractManager, ExtractorModules

@Extractor.yara(r"""
rule citadel
{
    meta:
        author = "mak"
        module = "citadel"
    strings:
        $briankerbs = "Coded by BRIAN KREBS for personal use only. I love my job & wife."
        $cit_aes_xor = {81 30 [4] 0F B6 50 03 0F B6 78 02 81 70 04 [4] 81 70 08 [4] 81 70 0C [4] C1 E2 08 0B D7 }
        $cit_salt = { 8A D1 80 E2 07 C0 E9 03 47 83 FF 04 }
        $cit_login = { 30 [1-2] 8A 8? [4] 32  }
        $cit_getpes = { 68 [2] 00 00 8D ( 84 24 | 85) [4] 50 8D ( 85 ?? ?? ?? ?? | 44 24 ?? ) 50 E8 [4] B8 [2] 00 00 50 68 }
        $cit_base_off = { 5? 8D 85 [4] E8 [4] 6A 20 68 [4] 8D [2] 50 E8 [4] 8D 85 [4] 50 }
    condition:
        3 of them
}
""")
class Citadel(Extractor):
    family = "citadel"

    @Extractor.string("briankerbs")
    def citadel_found(self, p, addr, match):
        log.info('[+] `Coded by Brian Krebs` str @ %X' % addr)
        return True

    @Extractor.string
    def cit_login(self, p, addr, match):
        log.info('[+] Found login_key xor @ %X' % addr)
        hit = p.uint32v(addr + 4)
        print(hex(hit))
        if p.is_addr(hit):
            return {'login_key': p.asciiz(hit)}

        hit = p.uint32v(addr + 5)
        print(hex(hit))
        if p.is_addr(hit):
            return {'login_key': p.asciiz(hit)}

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('input', type=str)
    args = parser.parse_args()

    manager = ExtractManager(ExtractorModules())
    manager.push_file(args.input)
    for config in manager.config:
        print(json.dumps(config))
0xThiebaut commented 11 months ago

Fixes #36