Merge pull request #10 from nbareil/master

Enables parse_beacon_config.py to process encrypted BEACONS that are not PE and added unittests
This commit is contained in:
Kristal-g
2021-05-09 12:47:43 +03:00
committed by GitHub
7 changed files with 78 additions and 3 deletions
+3 -1
View File
@@ -55,7 +55,10 @@ def get_beacon_data(url, arch):
eicar_offset = buf.find(b'EICAR-STANDARD-ANTIVIRUS-TEST-FILE')
if eicar_offset != -1:
return buf
return decrypt_beacon(buf)
def decrypt_beacon(buf):
offset = buf.find(b'\xff\xff\xff')
if offset == -1:
_cli_print('[-] Unexpected buffer received')
@@ -74,5 +77,4 @@ def get_beacon_data(url, arch):
b = struct.unpack_from('<I', buf, i*4+4)[0]
с = a ^ b
decoded_data += struct.pack('<I', с)
return decoded_data
+10 -2
View File
@@ -472,6 +472,10 @@ class cobaltstrikeConfig:
return None
def parse_encrypted_config_non_pe(self, version=None, quiet=False, as_json=False):
self.data = decrypt_beacon(self.data)
return self.parse_config(version=version, quiet=quiet, as_json=as_json)
def parse_encrypted_config(self, version=None, quiet=False, as_json=False):
'''
Parses beacon's configuration from stager dll or memory dump
@@ -481,7 +485,11 @@ class cobaltstrikeConfig:
:bool as_json: Whether to dump as json
'''
pe = pefile.PE(data=self.data)
try:
pe = pefile.PE(data=self.data)
except pefile.PEFormatError:
return self.parse_encrypted_config_non_pe(version=version, quiet=quiet, as_json=as_json)
data_sections = [s for s in pe.sections if s.Name.find(b'.data') != -1]
if not data_sections:
_cli_print("Failed to find .data section")
@@ -541,4 +549,4 @@ if __name__ == '__main__':
exit(0)
print("[-] Failed to find any beacon configuration")
exit(1)
exit(1)
+65
View File
@@ -0,0 +1,65 @@
#! /usr/bin/python3
import io
import os
import unittest
from parse_beacon_config import cobaltstrikeConfig
from zipfile import ZipFile
def decrypt_sample(zip_path):
with ZipFile(zip_path) as z:
for fn in z.namelist():
return io.BytesIO(z.read(fn, pwd=bytes("infected", "ascii")))
class TestBeaconParsing(unittest.TestCase):
def test_non_pe_x86(self):
path = os.path.join(
os.path.dirname(__file__),
"samples",
"13e954be0b0c022c392c956e9a800201a75dab7e288230b835bcdd4a9d68253d.zip",
)
f = decrypt_sample(path)
parser = cobaltstrikeConfig(f)
conf = parser.parse_encrypted_config()
self.assertEqual(conf.get("HttpPostUri"), "/submit.php")
def test_encrypted_x86_64(self):
path = os.path.join(
os.path.dirname(__file__),
"samples",
"10fd211ba97ddf12aecb1e7931d92c3ba37421c362cb1490e0203c1bd88ec141.zip",
)
f = decrypt_sample(path)
parser = cobaltstrikeConfig(f)
conf = parser.parse_encrypted_config()
self.assertEqual(conf.get("PublicKey_MD5"), "d2c8ec15d925e2514714d619022f7cdf")
def test_encrypted_x86(self):
path = os.path.join(
os.path.dirname(__file__),
"samples",
"7773169ca4ea81203a550dfebe53f091a8c57a3a5b12386e51c5a05194fef3ff.zip",
)
f = decrypt_sample(path)
parser = cobaltstrikeConfig(f)
conf = parser.parse_encrypted_config()
self.assertEqual(conf.get("PublicKey_MD5"), "8ac540617dddcdf575f6dc207abb7344")
def test_trial_beacon_x86(self):
path = os.path.join(
os.path.dirname(__file__),
"samples",
"4d1d732125e4d1a3ba0571e0cd892cf8e0dce854387ee405f75df4dcfb0f616b.zip",
)
f = decrypt_sample(path)
parser = cobaltstrikeConfig(f)
conf = parser.parse_config()
self.assertIn('header "CGGGGG"', conf.get("HttpGet_Metadata").get("Metadata"))
if __name__ == "__main__":
unittest.main()