dissect.cobaltstrike 1.2.1.dev4__tar.gz → 1.2.1.dev6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/PKG-INFO +1 -1
  2. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/_version.py +2 -2
  3. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/beacon.py +98 -0
  4. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/c2profile.lark +31 -0
  5. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/c2profile.py +53 -0
  6. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/tests/test_c2profile.py +101 -0
  7. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/.github/workflows/dissect-ci.yml +0 -0
  8. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/.gitignore +0 -0
  9. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/.lfs-assets-id +0 -0
  10. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/.pre-commit-config.yaml +0 -0
  11. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/.readthedocs.yml +0 -0
  12. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/LICENSE +0 -0
  13. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/README.rst +0 -0
  14. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/codecov.yml +0 -0
  15. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/__init__.py +0 -0
  16. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/artifact.py +0 -0
  17. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/c2.py +0 -0
  18. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/c_c2.py +0 -0
  19. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/client.py +0 -0
  20. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/pcap.py +0 -0
  21. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/pe.py +0 -0
  22. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/utils.py +0 -0
  23. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/version.py +0 -0
  24. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/xordecode.py +0 -0
  25. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/Makefile +0 -0
  26. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/c2profile-grammar.rst +0 -0
  27. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/codespell-ignore-words.txt +0 -0
  28. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/conf.py +0 -0
  29. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/examples.rst +0 -0
  30. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/images/beacon-client.png +0 -0
  31. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/images/teamserver-echoclient.png +0 -0
  32. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/images/teamserver_ls.png +0 -0
  33. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/images/vt-cobaltstrike-43-zip.png +0 -0
  34. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/index.rst +0 -0
  35. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/installation.rst +0 -0
  36. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/license.rst +0 -0
  37. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/scripts/checksum8-accesslogs.rst +0 -0
  38. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/scripts/dump_beacon_keys.rst +0 -0
  39. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/scripts/example_client.rst +0 -0
  40. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/scripts/index.rst +0 -0
  41. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/structures/c2_def.rst +0 -0
  42. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/structures/cs_def.rst +0 -0
  43. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/structures/index.rst +0 -0
  44. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/structures/pe_def.rst +0 -0
  45. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/tools/beacon-artifact.rst +0 -0
  46. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/tools/beacon-client.rst +0 -0
  47. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/tools/beacon-dump.rst +0 -0
  48. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/tools/beacon-pcap.rst +0 -0
  49. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/tools/beacon-xordecode.rst +0 -0
  50. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/tools/c2profile-dump.rst +0 -0
  51. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/tutorials/decrypt_cobaltstrike_pcaps.rst +0 -0
  52. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/tutorials/index.rst +0 -0
  53. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/docs/tutorials/minimal_beacon_client.rst +0 -0
  54. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/pyproject.toml +0 -0
  55. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/scripts/checksum8-accesslogs.py +0 -0
  56. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/scripts/dump_beacon_keys.py +0 -0
  57. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/scripts/example_client.py +0 -0
  58. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/tests/conftest.py +0 -0
  59. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/tests/test_beacon.py +0 -0
  60. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/tests/test_c2.py +0 -0
  61. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/tests/test_client.py +0 -0
  62. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/tests/test_pe.py +0 -0
  63. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/tests/test_utils.py +0 -0
  64. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/tests/test_version.py +0 -0
  65. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/tests/test_xordecode.py +0 -0
  66. {dissect_cobaltstrike-1.2.1.dev4 → dissect_cobaltstrike-1.2.1.dev6}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dissect.cobaltstrike
3
- Version: 1.2.1.dev4
3
+ Version: 1.2.1.dev6
4
4
  Summary: a Python library for dissecting Cobalt Strike related data
5
5
  Project-URL: Homepage, https://github.com/fox-it/dissect.cobaltstrike
6
6
  Project-URL: Documentation, https://dissect-cobaltstrike.readthedocs.io/
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.2.1.dev4'
16
- __version_tuple__ = version_tuple = (1, 2, 1, 'dev4')
15
+ __version__ = version = '1.2.1.dev6'
16
+ __version_tuple__ = version_tuple = (1, 2, 1, 'dev6')
@@ -170,6 +170,12 @@ enum BeaconSetting: uint16 {
170
170
 
171
171
  // CobaltStrike version >= 4.7 (Aug 17, 2022)
172
172
  SETTING_MASKED_WATERMARK = 74,
173
+
174
+ // CobaltStrike version >= 4.9 (Sep 19, 2023)
175
+ SETTING_DATA_STORE_SIZE = 76,
176
+
177
+ // CobaltStrike version >= 4.10 (Jul 16, 2024)
178
+ SETTING_BEACON_GATE = 78,
173
179
  };
174
180
 
175
181
  enum DeprecatedBeaconSetting: uint16 {
@@ -247,6 +253,39 @@ enum InjectExecutor: uint8 {
247
253
  CreateRemoteThread_ = 7,
248
254
  NtQueueApcThread_s = 8
249
255
  };
256
+
257
+ enum BofAllocator: uint16 {
258
+ VirtualAlloc = 0,
259
+ MapViewOfFile = 1,
260
+ HeapAlloc = 2,
261
+ };
262
+
263
+ // https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics/beacon-gate.htm
264
+ struct BeaconGateOptions {
265
+ uint8 InternetOpenA; // commms
266
+ uint8 InternetConnectA;
267
+ uint8 VirtualAlloc; // core
268
+ uint8 VirtualAllocEx;
269
+ uint8 VirtualProtect;
270
+ uint8 VirtualProtectEx;
271
+ uint8 VirtualFree;
272
+ uint8 GetThreadContext;
273
+ uint8 SetThreadContext;
274
+ uint8 ResumeThread;
275
+ uint8 CreateThread;
276
+ uint8 CreateRemoteThread;
277
+ uint8 OpenProcess;
278
+ uint8 OpenThread;
279
+ uint8 CloseHandle;
280
+ uint8 CreateFileMappingA;
281
+ uint8 MapViewOfFile;
282
+ uint8 UnmapViewOfFile;
283
+ uint8 VirtualQuery;
284
+ uint8 DuplicateHandle;
285
+ uint8 ReadProcessMemory;
286
+ uint8 WriteProcessMemory;
287
+ uint8 ExitThread; // cleanup
288
+ };
250
289
  """
251
290
 
252
291
  cs_struct = cstruct.cstruct(endian=">")
@@ -262,6 +301,8 @@ CryptoScheme = cs_struct.CryptoScheme
262
301
  ProxyServer = cs_struct.ProxyServer
263
302
  InjectAllocator = cs_struct.InjectAllocator
264
303
  InjectExecutor = cs_struct.InjectExecutor
304
+ BofAllocator = cs_struct.BofAllocator
305
+ BeaconGateOptions = cs_struct.BeaconGateOptions
265
306
 
266
307
  DEFAULT_XOR_KEYS: List[bytes] = [b"\x69", b"\x2e", b"\x00"]
267
308
  """ Default XOR keys used by Cobalt Strike for obfuscating Beacon config bytes """
@@ -574,6 +615,61 @@ def parse_pivot_frame(data: bytes) -> bytes:
574
615
  return p.read(length - 4)
575
616
 
576
617
 
618
+ def parse_beacon_gate(data: bytes) -> BeaconGateOptions:
619
+ """Parse ``SETTING_BEACON_GATE`` (`.stage.beacon_gate`) data"""
620
+ return BeaconGateOptions(data)
621
+
622
+
623
+ def beacon_gate_options_string(bgo: BeaconGateOptions) -> list[str]:
624
+ """Return the enabled BeaconGate WinAPI's as a list of strings"""
625
+ options = {k for k, v in bgo._values.items() if v}
626
+
627
+ comms = {"InternetOpenA", "InternetConnectA"}
628
+ core = {
629
+ "VirtualAlloc",
630
+ "VirtualAllocEx",
631
+ "VirtualProtect",
632
+ "VirtualProtectEx",
633
+ "VirtualFree",
634
+ "GetThreadContext",
635
+ "SetThreadContext",
636
+ "ResumeThread",
637
+ "CreateThread",
638
+ "CreateRemoteThread",
639
+ "OpenProcess",
640
+ "OpenThread",
641
+ "CloseHandle",
642
+ "CreateFileMappingA",
643
+ "MapViewOfFile",
644
+ "UnmapViewOfFile",
645
+ "VirtualQuery",
646
+ "DuplicateHandle",
647
+ "ReadProcessMemory",
648
+ "WriteProcessMemory",
649
+ }
650
+ cleanup = {"ExitThread"}
651
+
652
+ ret = []
653
+ if options.issuperset(comms | core | cleanup):
654
+ ret.append("All")
655
+ options -= comms | core | cleanup
656
+
657
+ if options.issuperset(comms):
658
+ ret.append("Comms")
659
+ options -= comms
660
+
661
+ if options.issuperset(core):
662
+ ret.append("Core")
663
+ options -= core
664
+
665
+ if options.issuperset(cleanup):
666
+ ret.append("Cleanup")
667
+ options -= cleanup
668
+
669
+ ret.extend(options)
670
+ return ret
671
+
672
+
577
673
  def sha256sum_pubkey(der_data: bytes) -> str:
578
674
  """Return the SHA-256 digest of `der_data`"""
579
675
  return hashlib.sha256(der_data.rstrip(b"\x00")).hexdigest()
@@ -635,6 +731,8 @@ SETTING_TO_PRETTYFUNC: Dict[BeaconSetting, Callable] = {
635
731
  BeaconSetting.SETTING_DNS_IDLE: lambda x: str(ipaddress.IPv4Address(x)),
636
732
  BeaconSetting.SETTING_WATERMARKHASH: lambda x: null_terminated_bytes(x) if isinstance(x, bytes) else x,
637
733
  BeaconSetting.SETTING_MASKED_WATERMARK: lambda x: x.hex(),
734
+ BeaconSetting.SETTING_BOF_ALLOCATOR: lambda x: BofAllocator(x).name,
735
+ BeaconSetting.SETTING_BEACON_GATE: lambda x: beacon_gate_options_string(parse_beacon_gate(x)),
638
736
  # BeaconSetting.SETTING_PROTOCOL: lambda x: BeaconProtocol(x).name,
639
737
  # BeaconSetting.SETTING_CRYPTO_SCHEME: lambda x: CryptoScheme(x).name,
640
738
  # BeaconSetting.SETTING_PROXY_BEHAVIOR: lambda x: ProxyServer(x).name,
@@ -137,6 +137,8 @@ stage_options: "string" string ";" -> string
137
137
  | "set" "rich_header" string ";" -> rich_header
138
138
  | "set" "checksum" string ";" -> checksum
139
139
  | "set" "syscall_method" string ";" -> syscall_method // introduced in Cobalt Strike 4.8
140
+ | "set" "data_store_size" string ";" -> data_store_size // introduced in Cobalt Strike 4.9
141
+ | "beacon_gate" "{" beacon_gate_options* "}" -> beacon_gate // introduced in Cobalt Strike 4.10
140
142
 
141
143
  process_inject_options: "set" "allocator" string ";" -> allocator
142
144
  | "set" "min_alloc" string ";" -> min_alloc
@@ -158,6 +160,35 @@ execute_options: "CreateThread" string ";" -> createthread_special
158
160
  | "RtlCreateUserThread" ";" -> rtlcreateuserthread
159
161
  | "SetThreadContext" ";" -> setthreadcontext
160
162
 
163
+ beacon_gate_options: "None" ";" -> none
164
+ | "Comms" ";" -> comms
165
+ | "Core" ";" -> core
166
+ | "Cleanup" ";" -> cleanup
167
+ | "All" ";" -> all
168
+ | "InternetOpenA" ";" -> internetopena
169
+ | "InternetConnectA" ";" -> internetconnecta
170
+ | "VirtualAlloc" ";" -> virtualalloc
171
+ | "VirtualAllocEx" ";" -> virtualallocex
172
+ | "VirtualProtect" ";" -> virtualprotect
173
+ | "VirtualProtectEx" ";" -> virtualprotextex
174
+ | "VirtualFree" ";" -> virtualfree
175
+ | "GetThreadContext" ";" -> getthreadcontext
176
+ | "SetThreadContext" ";" -> setthreadcontext
177
+ | "ResumeThread" ";" -> resumethread
178
+ | "CreateThread" ";" -> createthread
179
+ | "CreateRemoteThread" ";" -> createremotethread
180
+ | "OpenProcess" ";" -> openprocess
181
+ | "OpenThread" ";" -> openthread
182
+ | "CloseHandle" ";" -> closehandle
183
+ | "CreateFileMappingA" ";" -> createfilemappinga
184
+ | "MapViewOfFile" ";" -> mapviewoffile
185
+ | "UnmapViewOfFile" ";" -> unmapviewoffile
186
+ | "VirtualQuery" ";" -> virtualquery
187
+ | "DuplicateHandle" ";" -> duplicatehandle
188
+ | "ReadProcessMemory" ";" -> readprocessmemory
189
+ | "WriteProcessMemory" ";" -> writeprocessmemory
190
+ | "ExitThread" ";" -> exitthread
191
+
161
192
  postex_options: "set" "spawnto_x86" string ";" -> spawnto_x86
162
193
  | "set" "spawnto_x64" string ";" -> spawnto_x64
163
194
  | "set" "obfuscate" string ";" -> obfuscate
@@ -350,6 +350,50 @@ class ExecuteOptionsBlock(ConfigBlock):
350
350
  return block
351
351
 
352
352
 
353
+ class BeaconGateBlock(ConfigBlock):
354
+ """`.stage.beacon_gate` block"""
355
+
356
+ __name__ = "BeaconGateBlock"
357
+
358
+ none = ConfigBlock._enable
359
+ comms = ConfigBlock._enable
360
+ core = ConfigBlock._enable
361
+ cleanup = ConfigBlock._enable
362
+ all = ConfigBlock._enable
363
+ internetopena = ConfigBlock._enable
364
+ internetconnecta = ConfigBlock._enable
365
+ virtualalloc = ConfigBlock._enable
366
+ virtualallocex = ConfigBlock._enable
367
+ virtualprotect = ConfigBlock._enable
368
+ virtualprotextex = ConfigBlock._enable
369
+ virtualfree = ConfigBlock._enable
370
+ getthreadcontext = ConfigBlock._enable
371
+ setthreadcontext = ConfigBlock._enable
372
+ resumethread = ConfigBlock._enable
373
+ createthread = ConfigBlock._enable
374
+ createremotethread = ConfigBlock._enable
375
+ openprocess = ConfigBlock._enable
376
+ openthread = ConfigBlock._enable
377
+ closehandle = ConfigBlock._enable
378
+ createfilemappinga = ConfigBlock._enable
379
+ mapviewoffile = ConfigBlock._enable
380
+ unmapviewoffile = ConfigBlock._enable
381
+ virtualquery = ConfigBlock._enable
382
+ duplicatehandle = ConfigBlock._enable
383
+ readprocessmemory = ConfigBlock._enable
384
+ writeprocessmemory = ConfigBlock._enable
385
+ exitthread = ConfigBlock._enable
386
+
387
+ @classmethod
388
+ def from_beacon_gate_option_strings(cls, options: list[str]):
389
+ block = cls()
390
+
391
+ for option in options:
392
+ block._enable(option.lower(), True)
393
+
394
+ return block
395
+
396
+
353
397
  class C2Profile(ConfigBlock):
354
398
  """A :class:`C2Profile` object represents a parsed Malleable C2 Profile
355
399
 
@@ -618,6 +662,15 @@ class C2Profile(ConfigBlock):
618
662
  dns_beacon.set_option("dns_sleep", value)
619
663
  elif setting == BeaconSetting.SETTING_MAXDNS:
620
664
  dns_beacon.set_option("maxdns", value)
665
+ elif setting == BeaconSetting.SETTING_PROCINJ_BOF_REUSE_MEM and value:
666
+ proc_inj.set_option("bof_reuse_memory", "true")
667
+ elif setting == BeaconSetting.SETTING_BOF_ALLOCATOR:
668
+ proc_inj.set_option("bof_allocator", value)
669
+ elif setting == BeaconSetting.SETTING_DATA_STORE_SIZE:
670
+ stage.set_option("data_store_size", value)
671
+ elif setting == BeaconSetting.SETTING_BEACON_GATE and value:
672
+ block = BeaconGateBlock.from_beacon_gate_option_strings(value)
673
+ stage.set_config_block("beacon_gate", block)
621
674
 
622
675
  if c2_recover:
623
676
  http_get.set_non_empty_config_block("server", HttpOptionsBlock(output=DataTransformBlock(steps=c2_recover)))
@@ -1,3 +1,5 @@
1
+ import pytest
2
+
1
3
  from dissect.cobaltstrike import beacon, c2profile
2
4
 
3
5
  C2PROFILE_SOURCE = """
@@ -294,3 +296,102 @@ def test_c2profile_v48():
294
296
  """
295
297
  profile = c2profile.C2Profile.from_text(c2profile_fragment)
296
298
  assert profile.properties["stage.syscall_method"] == ["direct"]
299
+
300
+
301
+ def test_c2profile_v49():
302
+ c2profile_fragment = """
303
+ stage {
304
+ set data_store_size "1024";
305
+ }
306
+ """
307
+ profile = c2profile.C2Profile.from_text(c2profile_fragment)
308
+ assert profile.properties["stage.data_store_size"] == ["1024"]
309
+
310
+
311
+ def test_c2profile_v410():
312
+ c2profile_fragment = """
313
+ stage {
314
+ beacon_gate {
315
+ All;
316
+ }
317
+ }
318
+ """
319
+ profile = c2profile.C2Profile.from_text(c2profile_fragment)
320
+ assert profile.properties["stage.beacon_gate"] == ["All"]
321
+
322
+ c2profile_fragment = """
323
+ stage {
324
+ beacon_gate {
325
+ VirtualAlloc;
326
+ WriteProcessMemory;
327
+ }
328
+ }
329
+ """
330
+ profile = c2profile.C2Profile.from_text(c2profile_fragment)
331
+ assert profile.properties["stage.beacon_gate"] == ["VirtualAlloc", "WriteProcessMemory"]
332
+
333
+
334
+ @pytest.mark.parametrize(
335
+ ("allocator_enum", "bof_allocator"),
336
+ [
337
+ (beacon.BofAllocator.HeapAlloc, "HeapAlloc"),
338
+ (beacon.BofAllocator.MapViewOfFile, "MapViewOfFile"),
339
+ (beacon.BofAllocator.HeapAlloc, "HeapAlloc"),
340
+ ],
341
+ )
342
+ def test_c2profile_bof_allocator(allocator_enum, bof_allocator):
343
+ data = beacon.Setting(
344
+ index=beacon.BeaconSetting.SETTING_BOF_ALLOCATOR,
345
+ type=beacon.SettingsType.TYPE_SHORT,
346
+ length=0x2,
347
+ value=allocator_enum.dumps(),
348
+ ).dumps()
349
+ bconfig = beacon.BeaconConfig(data)
350
+ profile = c2profile.C2Profile.from_beacon_config(bconfig)
351
+ assert profile.properties["process-inject.bof_allocator"] == [bof_allocator]
352
+
353
+
354
+ @pytest.mark.parametrize(
355
+ ("bof_reuse_memory",),
356
+ [
357
+ (True,),
358
+ (False,),
359
+ ],
360
+ )
361
+ def test_c2profile_bof_reuse_memory(bof_reuse_memory):
362
+ data = beacon.Setting(
363
+ index=beacon.BeaconSetting.SETTING_PROCINJ_BOF_REUSE_MEM,
364
+ type=beacon.SettingsType.TYPE_SHORT,
365
+ length=0x2,
366
+ value=beacon.cs_struct.uint16(bof_reuse_memory).dumps(),
367
+ ).dumps()
368
+ bconfig = beacon.BeaconConfig(data)
369
+ profile = c2profile.C2Profile.from_beacon_config(bconfig)
370
+ if bof_reuse_memory:
371
+ assert profile.properties["process-inject.bof_reuse_memory"] == ["true"]
372
+ else:
373
+ assert "project.inject.bof_reuse_memory" not in profile.properties
374
+
375
+
376
+ def test_c2profile_beacon_gate():
377
+ # beacon gate with all options
378
+ data = beacon.Setting(
379
+ index=beacon.BeaconSetting.SETTING_BEACON_GATE,
380
+ type=beacon.SettingsType.TYPE_PTR,
381
+ length=0x40,
382
+ value=b"\x01" * 0x40,
383
+ ).dumps()
384
+ bconfig = beacon.BeaconConfig(data)
385
+ profile = c2profile.C2Profile.from_beacon_config(bconfig)
386
+ assert profile.properties["stage.beacon_gate"] == ["All"]
387
+
388
+ # empty beacon gate should not result into any profile setting
389
+ data = beacon.Setting(
390
+ index=beacon.BeaconSetting.SETTING_BEACON_GATE,
391
+ type=beacon.SettingsType.TYPE_PTR,
392
+ length=0x40,
393
+ value=b"\x00" * 0x40,
394
+ ).dumps()
395
+ bconfig = beacon.BeaconConfig(data)
396
+ profile = c2profile.C2Profile.from_beacon_config(bconfig)
397
+ assert "stage.beacon_gate" not in profile.properties