Skip to content

Commit

Permalink
Merge pull request #472 from kevoreilly/master
Browse files Browse the repository at this point in the history
Updates
  • Loading branch information
kevoreilly authored Feb 11, 2020
2 parents c787a3a + 3bf67f4 commit 0d830d3
Show file tree
Hide file tree
Showing 13 changed files with 277 additions and 36 deletions.
Binary file modified analyzer/windows/dll/capemon.dll
Binary file not shown.
Binary file modified analyzer/windows/dll/capemon_x64.dll
Binary file not shown.
43 changes: 43 additions & 0 deletions analyzer/windows/modules/packages/Combo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Copyright (C) 2010-2015 Cuckoo Foundation, Optiv, Inc. ([email protected])
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.

import os
import shutil
from subprocess import call
from lib.common.abstracts import Package

class Combo(Package):
"""CAPE Combo analysis package."""

def __init__(self, options={}, config=None):
"""@param options: options dict."""
self.config = config
self.options = options
self.pids = []
self.options["combo"] = "1"

def start(self, path):
args = self.options.get("arguments")
appdata = self.options.get("appdata")
runasx86 = self.options.get("runasx86")

# If the file doesn't have an extension, add .exe
# See CWinApp::SetCurrentHandles(), it will throw
# an exception that will crash the app if it does
# not find an extension on the main exe's filename
if "." not in os.path.basename(path):
new_path = path + ".exe"
os.rename(path, new_path)
path = new_path

if appdata:
# run the executable from the APPDATA directory, required for some malware
basepath = os.getenv('APPDATA')
newpath = os.path.join(basepath, os.path.basename(path))
shutil.copy(path, newpath)
path = newpath
if runasx86:
# ignore the return value, user must have CorFlags.exe installed in the guest VM
call(["CorFlags.exe", path, "/32bit+"])
return self.execute(path, args, path)
47 changes: 47 additions & 0 deletions analyzer/windows/modules/packages/Combo_dll.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright (C) 2010-2015 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.

import os
import shutil

from lib.common.abstracts import Package

class Combo_dll(Package):
"""CAPE Combo DLL analysis package."""
PATHS = [
("SystemRoot", "system32", "rundll32.exe"),
]

def __init__(self, options={}, config=None):
"""@param options: options dict."""
self.config = config
self.options = options
self.options["combo"] = "1"

def start(self, path):
rundll32 = self.get_path("rundll32.exe")
function = self.options.get("function", "#1")
arguments = self.options.get("arguments")
dllloader = self.options.get("dllloader")

# Check file extension.
ext = os.path.splitext(path)[-1].lower()
# If the file doesn't have the proper .dll extension force it
# and rename it. This is needed for rundll32 to execute correctly.
# See ticket #354 for details.
if ext != ".dll":
new_path = path + ".dll"
os.rename(path, new_path)
path = new_path

args = "{0},{1}".format(path, function)
if arguments:
args += " {0}".format(arguments)

if dllloader:
newname = os.path.join(os.path.dirname(rundll32), dllloader)
shutil.copy(rundll32, newname)
rundll32 = newname

return self.execute(rundll32, args, path)
3 changes: 2 additions & 1 deletion analyzer/windows/modules/packages/Emotet.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def __init__(self, options={}, config=None):
self.pids = []
self.options["extraction"] = "1"
self.options["procdump"] = "0"
self.options["exclude-apis"] = "RegOpenKeyExA"
self.options["single-process"] = "1"
self.options["exclude-apis"] = "RegOpenKeyExA:SendMessageA"

def start(self, path):
args = self.options.get("arguments")
Expand Down
42 changes: 42 additions & 0 deletions analyzer/windows/modules/packages/Hancitor_dll.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright (C) 2010-2015 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.

import os
import shutil

from lib.common.abstracts import Package

class Hancitor_Dll(Package):
"""CAPE Hancitor DLL analysis package."""
PATHS = [
("SystemRoot", "system32", "rundll32.exe"),
]

def start(self, path):
rundll32 = self.get_path("rundll32.exe")
function = self.options.get("function", "#1")
arguments = self.options.get("arguments")
dllloader = self.options.get("dllloader")
self.options["hancitor"] = "1"

# Check file extension.
ext = os.path.splitext(path)[-1].lower()
# If the file doesn't have the proper .dll extension force it
# and rename it. This is needed for rundll32 to execute correctly.
# See ticket #354 for details.
if ext != ".dll":
new_path = path + ".dll"
os.rename(path, new_path)
path = new_path

args = "\"{0}\",{1}".format(path, function)
if arguments:
args += " {0}".format(arguments)

if dllloader:
newname = os.path.join(os.path.dirname(rundll32), dllloader)
shutil.copy(rundll32, newname)
rundll32 = newname

return self.execute(rundll32, args, path)
3 changes: 2 additions & 1 deletion data/yara/CAPE/Emotet.yar
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ rule Emotet
$snippet4 = {33 C0 C7 05 ?? ?? ?? ?? ?? ?? ?? ?? C7 05 ?? ?? ?? ?? ?? ?? ?? ?? A3 ?? ?? ?? ?? A3 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 00 40 A3 ?? ?? ?? ?? 83 3C C5 ?? ?? ?? ?? 00 75 F0 51 E8 ?? ?? ?? ?? 83 C4 04 C3}
$snippet5 = {8B E5 5D C3 B8 ?? ?? ?? ?? A3 ?? ?? ?? ?? A3 ?? ?? ?? ?? 33 C0 21 05 ?? ?? ?? ?? A3 ?? ?? ?? ?? 39 05 ?? ?? ?? ?? 74 18 40 A3 ?? ?? ?? ?? 83 3C C5 ?? ?? ?? ?? 00 75 F0 51 E8 ?? ?? ?? ?? 59 C3}
$snippet6 = {33 C0 21 05 ?? ?? ?? ?? A3 ?? ?? ?? ?? 39 05 ?? ?? ?? ?? 74 18 40 A3 ?? ?? ?? ?? 83 3C C5 ?? ?? ?? ?? 00 75 F0 51 E8 ?? ?? ?? ?? 59 C3}
$snippet7 = {8B 48 ?? C7 [5-6] C7 40 ?? ?? ?? ?? ?? C7 ?? ?? 00 00 00 [0-1] 83 3C CD ?? ?? ?? ?? 00 74 0E 41 89 48 ?? 83 3C CD ?? ?? ?? ?? 00 75 F2}
condition:
//check for MZ Signature at offset 0
uint16(0) == 0x5A4D and (($snippet1) and ($snippet2)) or ($snippet3) or ($snippet4) or ($snippet5) or ($snippet6)
uint16(0) == 0x5A4D and (($snippet1) and ($snippet2)) or ($snippet3) or ($snippet4) or ($snippet5) or ($snippet6) or ($snippet7)
}
17 changes: 10 additions & 7 deletions data/yara/CAPE/REvil.yar
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ rule REvil
description = "REvil Payload"
cape_type = "REvil Payload"
strings:
$RE1 = "expand 32-byte kexpand 16-byte k" ascii fullword
$RE2 = "sysshadow" ascii fullword
$RE3 = "SCROLLBAR" ascii fullword
$RE4 = "msctfime ui" ascii fullword
$RE5 = "\\BaseNamedObjects\\%S" wide fullword
$decode = {33 D2 8A 9C 3D FC FE FF FF 8B C7 0F B6 CB F7 75 0C 8B 45 08 0F B6 04 02 03 C6 03 C8 0F B6 F1 8A 84 35 FC FE FF FF 88 84 3D FC FE FF FF 47 88 9C 35 FC FE FF FF 81 FF 00 01 00 00 72 C3}
$OtherRE1 = "expand 32-byte kexpand 16-byte k" ascii fullword
$OtherRE2 = "sysshadow" ascii fullword
$OtherRE3 = "SCROLLBAR" ascii fullword
$OtherRE4 = "msctfime ui" ascii fullword
$OtherRE5 = "\\BaseNamedObjects\\%S" wide fullword
$RE_dec = "rwdec_x86_debug.pdb" ascii
$GCREvil_string_decoder_opcodes = {33 D2 8A 9C 3D FC FE FF FF 8B C7 0F B6 CB F7 75 0C 8B 45 08 0F B6 04 02 03 C6 03 C8 0F B6 F1 8A 84 35 FC FE FF FF 88 84 3D FC FE FF FF 47 88 9C 35 FC FE FF FF 81 FF 00 01 00 00 72 C3 }
$REvil_string_decoder_opcodes1 = {8B C1 8A 1C 39 33 D2 0F B6 CB F7 75 ?? 8B 45 ?? 0F B6 04 02 03 C6 03 C8 0F B6 F1 8B 4D ?? 8A 04 3E 88 04 39 41 88 1C 3E 89 4D ?? 81 F9 00 01 00 00 }
condition:
uint16(0) == 0x5A4D and $decode and any of ($RE*)
uint16(0) == 0x5a4d
and (($GCREvil_string_decoder_opcodes and any of ($OtherRE*)) or any of ($REvil_string_decoder_opcodes*)) and not $RE_dec
}
21 changes: 14 additions & 7 deletions modules/processing/parsers/malwareconfig/TrickBot.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,16 @@ def get_rsrc(pe):
ret.append((name,data,resource_lang.data.struct.Size,resource_type))
return ret

def va_to_fileoffset(pe, va):
rva = va - pe.OPTIONAL_HEADER.ImageBase
for section in pe.sections:
if rva >= section.VirtualAddress and rva < section.VirtualAddress + section.Misc_VirtualSize:
return rva - section.VirtualAddress + section.PointerToRawData

def decode_onboard_config(data):
try:
pe = pefile.PE(data=data)
rsrcs = get_rsrc(pe)
for section in pe.sections:
if b".text" in section.Name:
delta = pe.OPTIONAL_HEADER.ImageBase + section.VirtualAddress - section.PointerToRawData
except:
return

Expand All @@ -130,9 +133,10 @@ def decode_onboard_config(data):
return
offset = int(snippet['$snippet1'])
key_len = struct.unpack("<L", data[offset+10:offset+14])[0]
key_offset = struct.unpack("<L", data[offset+15:offset+19])[0] - delta
data_offset = struct.unpack("<L", data[offset+20:offset+24])[0] - delta
size_offset = struct.unpack("<L", data[offset+53:offset+57])[0] - delta
key_offset = struct.unpack("<L", data[offset+15:offset+19])[0]
key_offset = va_to_fileoffset(pe, int(struct.unpack("<L", data[offset+15:offset+19])[0]))
data_offset = va_to_fileoffset(pe, int(struct.unpack("<L", data[offset+20:offset+24])[0]))
size_offset = va_to_fileoffset(pe, int(struct.unpack("<L", data[offset+53:offset+57])[0]))
size = size_offset - data_offset
key = data[key_offset:key_offset+key_len]
key = [key[i:i+4] for i in range(0, len(key), 4)]
Expand All @@ -147,7 +151,10 @@ def decode_onboard_config(data):

def config(data):
xml = decode_onboard_config(data)
root = ET.fromstring(xml)
try:
root = ET.fromstring(xml)
except:
return
raw_config = {}
for child in root:

Expand Down
85 changes: 72 additions & 13 deletions modules/processing/parsers/mwcp/parsers/Emotet.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@
$snippet4 = {33 C0 C7 05 ?? ?? ?? ?? ?? ?? ?? ?? C7 05 ?? ?? ?? ?? ?? ?? ?? ?? A3 ?? ?? ?? ?? A3 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 00 40 A3 ?? ?? ?? ?? 83 3C C5 ?? ?? ?? ?? 00 75 F0 51 E8 ?? ?? ?? ?? 83 C4 04 C3}
$snippet5 = {8B E5 5D C3 B8 ?? ?? ?? ?? A3 ?? ?? ?? ?? A3 ?? ?? ?? ?? 33 C0 21 05 ?? ?? ?? ?? A3 ?? ?? ?? ?? 39 05 ?? ?? ?? ?? 74 18 40 A3 ?? ?? ?? ?? 83 3C C5 ?? ?? ?? ?? 00 75 F0 51 E8 ?? ?? ?? ?? 59 C3}
$snippet6 = {33 C0 21 05 ?? ?? ?? ?? A3 ?? ?? ?? ?? 39 05 ?? ?? ?? ?? 74 18 40 A3 ?? ?? ?? ?? 83 3C C5 ?? ?? ?? ?? 00 75 F0 51 E8 ?? ?? ?? ?? 59 C3}
$snippet7 = {8B 48 ?? C7 [5-6] C7 40 ?? ?? ?? ?? ?? C7 ?? ?? 00 00 00 [0-1] 83 3C CD ?? ?? ?? ?? 00 74 0E 41 89 48 ?? 83 3C CD ?? ?? ?? ?? 00 75 F2}
$ref_rsa = {6A 00 6A 01 FF 76 ?? 8B 46 ?? FF D0 85 C0 74 ?? 8D 4D ?? E8 [4] 8D 45 ?? B9 [4] 8D 55 ?? 89 45 ?? E8}
condition:
//check for MZ Signature at offset 0
uint16(0) == 0x5A4D and (($snippet1) and ($snippet2)) or ($snippet3) or ($snippet4) or ($snippet5) or ($snippet6)
uint16(0) == 0x5A4D and (($snippet1) and ($snippet2)) or ($snippet3) or ($snippet4) or ($snippet5) or ($snippet6) or ($snippet7) or ($ref_rsa)
}
'''
Expand All @@ -55,6 +57,13 @@ def yara_scan(raw_data, rule_name):
addresses[item[1]] = item[0]
return addresses

def xor_data(data, key):
l = len(key)
decoded = ""
for i in range(0, len(data)):
decoded += chr(ord(data[i]) ^ ord(key[i % l]))
return decoded

# This function is originally by Jason Reaves (@sysopfb),
# suggested as an addition by @pollo290987.
# A big thank you to both.
Expand Down Expand Up @@ -113,16 +122,16 @@ def run(self):
try:
ip = struct.unpack('<I', filebuf[c2_list_offset:c2_list_offset+4])[0]
except:
return
break
if ip == 0:
return
break
c2_address = socket.inet_ntoa(struct.pack('!L', ip))
port = str(struct.unpack('H', filebuf[c2_list_offset+4:c2_list_offset+6])[0])

if c2_address and port:
self.reporter.add_metadata('address', c2_address+':' + port)
else:
return
break
c2_list_offset += 8
else:
refc2list = yara_scan(filebuf, '$snippet4')
Expand All @@ -142,16 +151,16 @@ def run(self):
try:
ip = struct.unpack('<I', filebuf[c2_list_offset:c2_list_offset+4])[0]
except:
return
break
if ip == 0:
return
break
c2_address = socket.inet_ntoa(struct.pack('!L', ip))
port = str(struct.unpack('H', filebuf[c2_list_offset+4:c2_list_offset+6])[0])

if c2_address and port:
self.reporter.add_metadata('address', c2_address+':' + port)
else:
return
break
c2_list_offset += 8
else:
refc2list = yara_scan(filebuf, '$snippet5')
Expand All @@ -171,16 +180,16 @@ def run(self):
try:
ip = struct.unpack('<I', filebuf[c2_list_offset:c2_list_offset+4])[0]
except:
return
break
if ip == 0:
return
break
c2_address = socket.inet_ntoa(struct.pack('!L', ip))
port = str(struct.unpack('H', filebuf[c2_list_offset+4:c2_list_offset+6])[0])

if c2_address and port:
self.reporter.add_metadata('address', c2_address+':' + port)
else:
return
break
c2_list_offset += 8
else:
refc2list = yara_scan(filebuf, '$snippet6')
Expand All @@ -200,14 +209,64 @@ def run(self):
try:
ip = struct.unpack('<I', filebuf[c2_list_offset:c2_list_offset+4])[0]
except:
return
break
if ip == 0:
return
break
c2_address = socket.inet_ntoa(struct.pack('!L', ip))
port = str(struct.unpack('H', filebuf[c2_list_offset+4:c2_list_offset+6])[0])

if c2_address and port:
self.reporter.add_metadata('address', c2_address+':' + port)
else:
return
break
c2_list_offset += 8
else:
refc2list = yara_scan(filebuf, '$snippet7')
if refc2list:
c2list_va_offset = int(refc2list['$snippet7'])
delta = 26
hb = struct.unpack('b', filebuf[c2list_va_offset+29:c2list_va_offset+30])[0]
if hb:
delta += 1
c2_list_va = struct.unpack('i', filebuf[c2list_va_offset+delta:c2list_va_offset+delta+4])[0]
if c2_list_va - image_base > 0x20000:
c2_list_rva = c2_list_va & 0xffff
else:
c2_list_rva = c2_list_va - image_base
try:
c2_list_offset = pe.get_offset_from_rva(c2_list_rva)
except PEFormatError as err:
pass

while 1:
try:
ip = struct.unpack('<I', filebuf[c2_list_offset:c2_list_offset+4])[0]
except:
break
if ip == 0:
break
c2_address = socket.inet_ntoa(struct.pack('!L', ip))
port = str(struct.unpack('H', filebuf[c2_list_offset+4:c2_list_offset+6])[0])

if c2_address and port:
self.reporter.add_metadata('address', c2_address+':' + port)
else:
break
c2_list_offset += 8

if not pem_key:
ref_rsa = yara_scan(filebuf, '$ref_rsa')
if ref_rsa:
ref_rsa_offset = int(ref_rsa['$ref_rsa'])
ref_rsa_va = struct.unpack('i', filebuf[ref_rsa_offset+28:ref_rsa_offset+32])[0]
ref_rsa_rva = ref_rsa_va - image_base
try:
ref_rsa_offset = pe.get_offset_from_rva(ref_rsa_rva)
except:
return
key = struct.unpack('<I', filebuf[ref_rsa_offset:ref_rsa_offset+4])[0]
xorsize = key ^ struct.unpack('<I', filebuf[ref_rsa_offset+4:ref_rsa_offset+8])[0]
rsa_key = xor_data(filebuf[ref_rsa_offset+8:ref_rsa_offset+8+xorsize], struct.pack('<I',key))
seq = asn1.DerSequence()
seq.decode(rsa_key)
self.reporter.add_metadata('other', {'RSA public key': RSA.construct((seq[0], seq[1])).exportKey()})
Loading

0 comments on commit 0d830d3

Please sign in to comment.