Windows.Detection.Malfind

This artifact checks the VAD for executable sections that are not maped to disk and has suspicious content which may indicate process injection.

User options allow targetting process, modifying suspicious content yara, or upload of suspicious section.

Default suspicious content includes headers: MZ, default cobalt strike stomped, or well known suspicious strings, meterpreter and Cobalt Strike.

Note: Add additional yara as desired.
Expect some false positives and triage accordingly.


name: Windows.Detection.Malfind
author: Matt Green - @mgreen27
description: |
    This artifact checks the VAD for executable sections that are not maped to disk 
    and has suspicious content which may indicate process injection.
    
    User options allow targetting process, modifying suspicious content yara, or 
    upload of suspicious section.
    
    Default suspicious content includes headers: MZ, default cobalt strike stomped, 
    or well known suspicious strings, meterpreter and Cobalt Strike.
    
    Note: Add additional yara as desired.  
    Expect some false positives and triage accordingly.  
    
parameters:
  - name: ProcessRegex
    description: A regex applied to process names.
    default: .
    type: regex
  - name: PidRegex
    default: .
    type: regex
  - name: ProtectionRegex
    description: |
        Protection of section. Default is Executable but can customise for other usecases.  
        Examples: x for executable, r for read, w for write.  
        (x|r|w) or xrw for multiple.
        x-w for strict.
    default: xrw
    type: regex
  - name: SectionDataGuiSize
    description: Size of SectionData to show in gui. For large files, use UploadSection
    default: 10000
    type: int
  - name: SuspiciousContent
    description: A yara rule of suspicious section content 
    type: yara
    default: |
        rule win_cobalt_strike_auto {
         meta:
           author = "Felix Bilstein - yara-signator at cocacoding dot com"
           date = "2019-11-26"
           version = "1"
           description = "autogenerated rule brought to you by yara-signator"
           tool = "yara-signator 0.2a"
           malpedia_reference = "https://malpedia.caad.fkie.fraunhofer.de/details/win.cobalt_strike"
           malpedia_license = "CC BY-SA 4.0"
           malpedia_sharing = "TLP:WHITE"

         strings:
           $sequence_0 = { 3bc7 750d ff15???????? 3d33270000 }
           $sequence_1 = { e9???????? eb0a b801000000 e9???????? }
           $sequence_2 = { 8bd0 e8???????? 85c0 7e0e }
           $sequence_3 = { ffb5f8f9ffff ff15???????? 8b4dfc 33cd e8???????? c9 c3 }
           $sequence_4 = { e8???????? e9???????? 833d?????????? 7505 e8???????? }
           $sequence_5 = { 250000ff00 33d0 8b4db0 c1e908 }
           $sequence_6 = { ff75f4 ff7610 ff761c ff75fc }
           $sequence_7 = { 8903 6a06 eb39 33ff 85c0 762b 03f1 }
           $sequence_8 = { 894dd4 8b458c d1f8 894580 8b45f8 c1e818 0fb6c8 }
           $sequence_9 = { 890a 8b4508 0fb64804 81e1ff000000 c1e118 8b5508 0fb64205 }
           $sequence_10 = { 33d2 e8???????? 48b873797374656d3332 4c8bc7 488903 49ffc0 }
           $sequence_11 = { 488bd1 498d4bd8 498943e0 498943e8 }
           $sequence_12 = { b904000000 486bc90e 488b542430 4c8b442430 418b0c08 8b0402 }
           $sequence_13 = { ba80000000 e8???????? 488d4c2438 e8???????? 488d4c2420 8bd0 e8???????? }
           $sequence_14 = { 488b4c2430 8b0401 89442428 b804000000 486bc004 }
           $sequence_15 = { 4883c708 4883c304 49ffc3 48ffcd 0f854fffffff 488d4c2420 }

        condition:
            7 of them
        }

        rule win_meterpreter_auto {
            meta:
                author = "Felix Bilstein - yara-signator at cocacoding dot com"
                date = "2022-08-05"
                version = "1"
                description = "Detects win.meterpreter."
                info = "autogenerated rule brought to you by yara-signator"
                tool = "yara-signator v0.6.0"
                signator_config = "callsandjumps;datarefs;binvalue"
                malpedia_reference = "https://malpedia.caad.fkie.fraunhofer.de/details/win.meterpreter"
                malpedia_rule_date = "20220805"
                malpedia_hash = "6ec06c64bcfdbeda64eff021c766b4ce34542b71"
                malpedia_version = "20220808"
                malpedia_license = "CC BY-SA 4.0"
                malpedia_sharing = "TLP:WHITE"

            strings:
                $sequence_0 = { e22b e5f6 4f 1c8b }
                $sequence_1 = { 90 90 90 55 e4ec 53 8b22 }
                $sequence_2 = { 50 686cd4408e ffd6 8b0d???????? 83c18a }
                $sequence_3 = { 008b35a8c19f 006860 2f 0000 52 ffd6 }
                $sequence_4 = { 8b87047945f4 6a01 50 52 c745fc00000000 ff08 98 }
                $sequence_5 = { 57 40 388bf083c4cf 86f6 }
                $sequence_6 = { 8932 8b700c 83c204 4e 3bce 74ef ff9a0c8b5c03 }
                $sequence_7 = { 043b 8801 41 0fc2049088 0135???????? 4f 75b5 }
                $sequence_8 = { 6c 50 048b 55 1491 48 }
                $sequence_9 = { 76e1 8bf0 85f6 750e }

            condition:
                7 of them
        }
        rule suspicious {
            meta:
                author = "Matt Green - @mgreen27"
                description = "Suspicious unbacked on disk executable section content"
                date = "2022-09-30"

            strings:
                $header1 = "MZ"
                $header2 = { 00 00 41 52 55 48 } // cobalt strike stomped dll

                $body1 = "This program cannot be run in DOS mode" 
                $body2 = { FC E8 8? 00 00 00 60 }     // shellcode prologe in metasploit

            condition:
                $header1 at 0 or $header2
                    or any of ($body*) 
        }
        rule shellcode_get_eip
        {
            meta:
                author = "William Ballenthin"
                email = "william.ballenthin@fireeye.com"
                license = "Apache 2.0"
                copyright = "FireEye, Inc"
                description = "Match x86 that appears to fetch $PC."

            strings:
               $x86 = { e8 00 00 00 00 (58 | 5b | 59 | 5a | 5e | 5f) }

            condition:
               $x86
        }

        rule shellcode_peb_parsing
        {
            meta:
                author = "William Ballenthin"
                email = "william.ballenthin@fireeye.com"
                license = "Apache 2.0"
                copyright = "FireEye, Inc"
                description = "Match x86 that appears to manually traverse the TEB/PEB/LDR data."

            strings:
               $peb_parsing = { (64 a1 30 00 00 00 | 64 8b (1d | 0d | 15 | 35 | 3d) 30 00 00 00 | 31 (c0 | db | c9 | d2 | f6 | ff) [0-8] 64 8b ?? 30 ) [0-8] 8b ?? 0c [0-8] 8b ?? (0c | 14 | 1C) [0-8] 8b ?? (28 | 30) }
               $peb_parsing64 = { (48 65 A1 60 00 00 00 00 00 00 00 | 65 (48 | 4C) 8B ?? 60 00 00 00 | 65 A1 60 00 00 00 00 00 00 00 | 65 8b ?? ?? 00 FF FF | (48 31 (c0 | db | c9 | d2 | f6 | ff) | 4D 31 (c0 | c9))  [0-16] 65 (48 | 4d | 49 | 4c) 8b ?? 60) [0-16] (48 | 49 | 4C) 8B ?? 18 [0-16] (48 | 49 | 4C) 8B ?? (10 | 20 | 30) [0-16] (48 | 49 | 4C) 8B ?? (50 | 60) }

            condition:
               $peb_parsing or $peb_parsing64
        }

        rule shellcode_stack_strings
        {
            meta:
                author = "William Ballenthin"
                email = "william.ballenthin@fireeye.com"
                license = "Apache 2.0"
                copyright = "FireEye, Inc"
                description = "Match x86 that appears to be stack string creation."

            strings:
                // stack string near the frame pointer.
                $ss_small_bp = /(\xC6\x45.[a-zA-Z0-9 -~]){4,}\xC6\x45.\x00/

                // dword stack string near the frame pointer.
                $ss_small_bp_dword = /(\xC7\x45.[a-zA-Z0-9 -~]\x00[a-zA-Z0-9 -~]\x00){2,}\xC7\x45..\x00\x00\x00/

                // stack strings further away from the frame pointer.
                $ss_big_bp = /(\xC6\x85.[\xF0-\xFF]\xFF\xFF[a-zA-Z0-9 -~]){4,}\xC6\x85.[\xF0-\xFF]\xFF\xFF\x00/

                // stack string near the stack pointer.
                $ss_small_sp = /(\xC6\x44\x24.[a-zA-Z0-9 -~]){4,}\xC6\x44\x24.\x00/

                // stack strings further away from the stack pointer.
                $ss_big_sp = /(\xC6\x84\x24.[\x00-\x0F]\x00\x00[a-zA-Z0-9 -~]){4,}\xC6\x84\x24.[\x00-\x0F]\x00\x00\x00/

            condition:
                $ss_small_bp or $ss_small_bp_dword or $ss_big_bp or $ss_small_sp or $ss_big_sp
        }

        rule shellcode_shikataganai_encoding
        {
            meta:
                author    = "Steven Miller"
                company   = "FireEye"
                reference = "https://www.fireeye.com/blog/threat-research/2019/10/shikata-ga-nai-encoder-still-going-strong.html"
            strings:
                $varInitializeAndXorCondition1_XorEAX = { B8 ?? ?? ?? ?? [0-30] D9 74 24 F4 [0-10] ( 59 | 5A | 5B | 5C | 5D | 5E | 5F ) [0-50] 31 ( 40 | 41 | 42 | 43 | 45 | 46 | 47 ) ?? }
                $varInitializeAndXorCondition1_XorEBP = { BD ?? ?? ?? ?? [0-30] D9 74 24 F4 [0-10] ( 58 | 59 | 5A | 5B | 5C | 5E | 5F ) [0-50] 31 ( 68 | 69 | 6A | 6B | 6D | 6E | 6F ) ?? }
                $varInitializeAndXorCondition1_XorEBX = { BB ?? ?? ?? ?? [0-30] D9 74 24 F4 [0-10] ( 58 | 59 | 5A | 5C | 5D | 5E | 5F ) [0-50] 31 ( 58 | 59 | 5A | 5B | 5D | 5E | 5F ) ?? }
                $varInitializeAndXorCondition1_XorECX = { B9 ?? ?? ?? ?? [0-30] D9 74 24 F4 [0-10] ( 58 | 5A | 5B | 5C | 5D | 5E | 5F ) [0-50] 31 ( 48 | 49 | 4A | 4B | 4D | 4E | 4F ) ?? }
                $varInitializeAndXorCondition1_XorEDI = { BF ?? ?? ?? ?? [0-30] D9 74 24 F4 [0-10] ( 58 | 59 | 5A | 5B | 5C | 5D | 5E ) [0-50] 31 ( 78 | 79 | 7A | 7B | 7D | 7E | 7F ) ?? }
                $varInitializeAndXorCondition1_XorEDX = { BA ?? ?? ?? ?? [0-30] D9 74 24 F4 [0-10] ( 58 | 59 | 5B | 5C | 5D | 5E | 5F ) [0-50] 31 ( 50 | 51 | 52 | 53 | 55 | 56 | 57 ) ?? }
                $varInitializeAndXorCondition2_XorEAX = { D9 74 24 F4 [0-30] B8 ?? ?? ?? ?? [0-10] ( 59 | 5A | 5B | 5C | 5D | 5E | 5F ) [0-50] 31 ( 40 | 41 | 42 | 43 | 45 | 46 | 47 ) ?? }
                $varInitializeAndXorCondition2_XorEBP = { D9 74 24 F4 [0-30] BD ?? ?? ?? ?? [0-10] ( 58 | 59 | 5A | 5B | 5C | 5E | 5F ) [0-50] 31 ( 68 | 69 | 6A | 6B | 6D | 6E | 6F ) ?? }
                $varInitializeAndXorCondition2_XorEBX = { D9 74 24 F4 [0-30] BB ?? ?? ?? ?? [0-10] ( 58 | 59 | 5A | 5C | 5D | 5E | 5F ) [0-50] 31 ( 58 | 59 | 5A | 5B | 5D | 5E | 5F ) ?? }
                $varInitializeAndXorCondition2_XorECX = { D9 74 24 F4 [0-30] B9 ?? ?? ?? ?? [0-10] ( 58 | 5A | 5B | 5C | 5D | 5E | 5F ) [0-50] 31 ( 48 | 49 | 4A | 4B | 4D | 4E | 4F ) ?? }
                $varInitializeAndXorCondition2_XorEDI = { D9 74 24 F4 [0-30] BF ?? ?? ?? ?? [0-10] ( 58 | 59 | 5A | 5B | 5C | 5D | 5E ) [0-50] 31 ( 78 | 79 | 7A | 7B | 7D | 7E | 7F ) ?? }
                $varInitializeAndXorCondition2_XorEDX = { D9 74 24 F4 [0-30] BA ?? ?? ?? ?? [0-10] ( 58 | 59 | 5B | 5C | 5D | 5E | 5F ) [0-50] 31 ( 50 | 51 | 52 | 53 | 55 | 56 | 57 ) ?? }
            condition:
                any of them
        }
        
  - name: NumberOfHits
    description: THis artifact will stop by default at one hit. This setting allows additional hits
    default: 1
    type: int
  - name: ContextBytes
    description: Include this amount of bytes around hit as context.
    default: 0
    type: int
  - name: UploadSection
    description: Upload suspicious section.
    type: bool


sources:
  - query: |
      LET processes = SELECT Pid, Name,Exe,CommandLine,CreateTime
        FROM pslist()
        WHERE Name =~ ProcessRegex
            AND format(format="%d", args=Pid) =~ PidRegex
            AND log(message="Scanning pid %v : %v", args=[Pid, Name])

      LET hits = SELECT * FROM foreach(
          row=processes,
          query={
            SELECT CreateTime,Pid, Name,
                format(format='%x-%x', args=[Address, Address+Size]) AS AddressRange,
                Protection, Address as _Address,
                Size as SectionSize,
                pathspec(
                    DelegateAccessor="process",
                    DelegatePath=Pid,
                    Path=Address) AS _PathSpec
            FROM vad(pid=Pid)
            WHERE NOT MappingName
                AND Protection =~ ProtectionRegex
          })
          
      LET results <= SELECT *,
            format(format='% x',args=read_file(
                     accessor='offset',
                     filename=_PathSpec,
                     length=2)) as HexHeader,
            magic(path=_PathSpec, accessor='offset') as DataMagic,
            base64encode(string=read_file(
                     accessor='offset',
                     filename=_PathSpec,
                     length= if(condition= SectionDataGuiSize > SectionSize,
                                then= SectionSize,
                                else= SectionDataGuiSize)
                        )) as SectionData,
            YaraHit, _PathSpec
        FROM foreach(row=hits,
            query={
                SELECT
                    CreateTime,Pid, Name,_Address, AddressRange,Protection,SectionSize,
                    enumerate(items=dict(
                        Rule=Rule,
                        Meta=Meta,
                    	Tags=Tags,
                    	String=String)) as YaraHit,
                    _PathSpec
                FROM yara(
                            accessor='offset',
                            files=_PathSpec, 
                            rules=SuspiciousContent,
                            end=SectionSize,  key='X', 
                            number=NumberOfHits,
                            context=ContextBytes
                        )
                GROUP BY CreateTime,Pid, Name, AddressRange
            })
        
        
      LET upload_section = SELECT *,
                upload(accessor='sparse', 
                  file=pathspec(
                    DelegateAccessor="process",
                    DelegatePath=Pid,
                    Path=[dict(Offset=_Address, Length=SectionSize),]), 
                    name=format(format='%v-%v_%v.bin',args= [ Name, Pid, AddressRange ])
                    ) as SectionDump
            FROM results
            GROUP BY CreateTime,Pid, Name,_Address, AddressRange
      
      SELECT *,
        process_tracker_callchain(id=Pid).Data as ProcessChain
      FROM if(condition= UploadSection,
                then= upload_section,
                else= results)
        
column_types:
  - name: SectionData
    type: base64hex