dissect.cobaltstrike 1.2.1.dev5__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.dev5 → dissect_cobaltstrike-1.2.1.dev6}/PKG-INFO +1 -1
  2. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/_version.py +2 -2
  3. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/beacon.py +90 -0
  4. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/c2profile.lark +31 -0
  5. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/c2profile.py +49 -0
  6. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/tests/test_c2profile.py +57 -0
  7. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/.github/workflows/dissect-ci.yml +0 -0
  8. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/.gitignore +0 -0
  9. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/.lfs-assets-id +0 -0
  10. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/.pre-commit-config.yaml +0 -0
  11. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/.readthedocs.yml +0 -0
  12. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/LICENSE +0 -0
  13. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/README.rst +0 -0
  14. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/codecov.yml +0 -0
  15. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/__init__.py +0 -0
  16. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/artifact.py +0 -0
  17. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/c2.py +0 -0
  18. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/c_c2.py +0 -0
  19. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/client.py +0 -0
  20. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/pcap.py +0 -0
  21. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/pe.py +0 -0
  22. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/utils.py +0 -0
  23. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/version.py +0 -0
  24. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/dissect/cobaltstrike/xordecode.py +0 -0
  25. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/Makefile +0 -0
  26. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/c2profile-grammar.rst +0 -0
  27. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/codespell-ignore-words.txt +0 -0
  28. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/conf.py +0 -0
  29. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/examples.rst +0 -0
  30. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/images/beacon-client.png +0 -0
  31. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/images/teamserver-echoclient.png +0 -0
  32. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/images/teamserver_ls.png +0 -0
  33. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/images/vt-cobaltstrike-43-zip.png +0 -0
  34. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/index.rst +0 -0
  35. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/installation.rst +0 -0
  36. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/license.rst +0 -0
  37. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/scripts/checksum8-accesslogs.rst +0 -0
  38. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/scripts/dump_beacon_keys.rst +0 -0
  39. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/scripts/example_client.rst +0 -0
  40. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/scripts/index.rst +0 -0
  41. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/structures/c2_def.rst +0 -0
  42. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/structures/cs_def.rst +0 -0
  43. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/structures/index.rst +0 -0
  44. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/structures/pe_def.rst +0 -0
  45. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/tools/beacon-artifact.rst +0 -0
  46. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/tools/beacon-client.rst +0 -0
  47. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/tools/beacon-dump.rst +0 -0
  48. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/tools/beacon-pcap.rst +0 -0
  49. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/tools/beacon-xordecode.rst +0 -0
  50. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/tools/c2profile-dump.rst +0 -0
  51. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/tutorials/decrypt_cobaltstrike_pcaps.rst +0 -0
  52. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/tutorials/index.rst +0 -0
  53. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/docs/tutorials/minimal_beacon_client.rst +0 -0
  54. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/pyproject.toml +0 -0
  55. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/scripts/checksum8-accesslogs.py +0 -0
  56. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/scripts/dump_beacon_keys.py +0 -0
  57. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/scripts/example_client.py +0 -0
  58. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/tests/conftest.py +0 -0
  59. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/tests/test_beacon.py +0 -0
  60. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/tests/test_c2.py +0 -0
  61. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/tests/test_client.py +0 -0
  62. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/tests/test_pe.py +0 -0
  63. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/tests/test_utils.py +0 -0
  64. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/tests/test_version.py +0 -0
  65. {dissect_cobaltstrike-1.2.1.dev5 → dissect_cobaltstrike-1.2.1.dev6}/tests/test_xordecode.py +0 -0
  66. {dissect_cobaltstrike-1.2.1.dev5 → 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.dev5
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.dev5'
16
- __version_tuple__ = version_tuple = (1, 2, 1, 'dev5')
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 {
@@ -253,6 +259,33 @@ enum BofAllocator: uint16 {
253
259
  MapViewOfFile = 1,
254
260
  HeapAlloc = 2,
255
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
+ };
256
289
  """
257
290
 
258
291
  cs_struct = cstruct.cstruct(endian=">")
@@ -269,6 +302,7 @@ ProxyServer = cs_struct.ProxyServer
269
302
  InjectAllocator = cs_struct.InjectAllocator
270
303
  InjectExecutor = cs_struct.InjectExecutor
271
304
  BofAllocator = cs_struct.BofAllocator
305
+ BeaconGateOptions = cs_struct.BeaconGateOptions
272
306
 
273
307
  DEFAULT_XOR_KEYS: List[bytes] = [b"\x69", b"\x2e", b"\x00"]
274
308
  """ Default XOR keys used by Cobalt Strike for obfuscating Beacon config bytes """
@@ -581,6 +615,61 @@ def parse_pivot_frame(data: bytes) -> bytes:
581
615
  return p.read(length - 4)
582
616
 
583
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
+
584
673
  def sha256sum_pubkey(der_data: bytes) -> str:
585
674
  """Return the SHA-256 digest of `der_data`"""
586
675
  return hashlib.sha256(der_data.rstrip(b"\x00")).hexdigest()
@@ -643,6 +732,7 @@ SETTING_TO_PRETTYFUNC: Dict[BeaconSetting, Callable] = {
643
732
  BeaconSetting.SETTING_WATERMARKHASH: lambda x: null_terminated_bytes(x) if isinstance(x, bytes) else x,
644
733
  BeaconSetting.SETTING_MASKED_WATERMARK: lambda x: x.hex(),
645
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)),
646
736
  # BeaconSetting.SETTING_PROTOCOL: lambda x: BeaconProtocol(x).name,
647
737
  # BeaconSetting.SETTING_CRYPTO_SCHEME: lambda x: CryptoScheme(x).name,
648
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
 
@@ -622,6 +666,11 @@ class C2Profile(ConfigBlock):
622
666
  proc_inj.set_option("bof_reuse_memory", "true")
623
667
  elif setting == BeaconSetting.SETTING_BOF_ALLOCATOR:
624
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)
625
674
 
626
675
  if c2_recover:
627
676
  http_get.set_non_empty_config_block("server", HttpOptionsBlock(output=DataTransformBlock(steps=c2_recover)))
@@ -298,6 +298,39 @@ def test_c2profile_v48():
298
298
  assert profile.properties["stage.syscall_method"] == ["direct"]
299
299
 
300
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
+
301
334
  @pytest.mark.parametrize(
302
335
  ("allocator_enum", "bof_allocator"),
303
336
  [
@@ -338,3 +371,27 @@ def test_c2profile_bof_reuse_memory(bof_reuse_memory):
338
371
  assert profile.properties["process-inject.bof_reuse_memory"] == ["true"]
339
372
  else:
340
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