dissect.cobaltstrike 1.2.2.dev2__tar.gz → 1.2.2.dev3__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.
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/.pre-commit-config.yaml +8 -9
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/PKG-INFO +7 -5
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/README.rst +1 -1
- dissect_cobaltstrike-1.2.2.dev3/dissect/cobaltstrike/_version.py +24 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/beacon.py +1 -1
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/pe.py +44 -27
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/xordecode.py +1 -1
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/installation.rst +12 -2
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/pyproject.toml +40 -3
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/tests/test_beacon.py +2 -4
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/tests/test_c2profile.py +1 -1
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/tox.ini +11 -1
- dissect_cobaltstrike-1.2.2.dev2/dissect/cobaltstrike/_version.py +0 -21
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/.github/workflows/dissect-ci.yml +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/.gitignore +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/.lfs-assets-id +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/.readthedocs.yml +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/LICENSE +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/codecov.yml +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/__init__.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/artifact.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/c2.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/c2profile.lark +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/c2profile.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/c_c2.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/client.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/guardrails.py +2 -2
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/pcap.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/utils.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/version.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/Makefile +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/_ext/generate_beacon_version_csv.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/beacon-version-identification.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/c2profile-grammar.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/cobaltstrike-beacon-versions.csv +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/codespell-ignore-words.txt +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/conf.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/examples.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/images/beacon-client.png +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/images/teamserver-echoclient.png +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/images/teamserver_ls.png +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/images/vt-cobaltstrike-43-zip.png +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/index.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/license.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/scripts/checksum8-accesslogs.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/scripts/dump_beacon_keys.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/scripts/example_client.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/scripts/index.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/structures/c2_def.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/structures/cs_def.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/structures/guardrails_def.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/structures/index.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/structures/pe_def.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/tools/beacon-artifact.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/tools/beacon-client.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/tools/beacon-dump.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/tools/beacon-pcap.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/tools/beacon-xordecode.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/tools/c2profile-dump.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/tutorials/decrypt_cobaltstrike_pcaps.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/tutorials/index.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/tutorials/minimal_beacon_client.rst +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/scripts/checksum8-accesslogs.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/scripts/dump_beacon_keys.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/scripts/example_client.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/tests/conftest.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/tests/test_c2.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/tests/test_client.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/tests/test_guardrails.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/tests/test_pe.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/tests/test_utils.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/tests/test_version.py +0 -0
- {dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/tests/test_xordecode.py +0 -0
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
repos:
|
|
2
2
|
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
|
3
|
-
rev: v0.6
|
|
3
|
+
rev: v0.15.6
|
|
4
4
|
hooks:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
language_version: python3
|
|
5
|
+
- id: ruff # used by `fix`
|
|
6
|
+
alias: ruff-fix
|
|
7
|
+
args: [--fix]
|
|
8
|
+
- id: ruff # used by `lint`
|
|
9
|
+
alias: ruff-lint
|
|
10
|
+
- id: ruff-format # replaces black
|
|
12
11
|
- repo: https://github.com/codespell-project/codespell
|
|
13
|
-
rev: v2.
|
|
12
|
+
rev: v2.4.2
|
|
14
13
|
hooks:
|
|
15
14
|
- id: codespell
|
|
16
15
|
args: [--ignore-words=docs/codespell-ignore-words.txt]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dissect.cobaltstrike
|
|
3
|
-
Version: 1.2.2.
|
|
3
|
+
Version: 1.2.2.dev3
|
|
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/
|
|
@@ -19,17 +19,19 @@ Classifier: Operating System :: MacOS :: MacOS X
|
|
|
19
19
|
Classifier: Operating System :: Microsoft :: Windows
|
|
20
20
|
Classifier: Operating System :: POSIX
|
|
21
21
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
22
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
23
22
|
Classifier: Programming Language :: Python :: 3.10
|
|
24
23
|
Classifier: Programming Language :: Python :: 3.11
|
|
25
24
|
Classifier: Programming Language :: Python :: 3.12
|
|
26
25
|
Classifier: Programming Language :: Python :: 3.13
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
27
27
|
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
28
28
|
Classifier: Topic :: Security
|
|
29
29
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
30
30
|
Classifier: Topic :: Utilities
|
|
31
|
-
Requires-Python: >=3.
|
|
32
|
-
Requires-Dist: dissect-cstruct>=4.
|
|
31
|
+
Requires-Python: >=3.10
|
|
32
|
+
Requires-Dist: dissect-cstruct>=4.7
|
|
33
|
+
Requires-Dist: dissect-executable>=1.11
|
|
34
|
+
Requires-Dist: dissect-util>=3.24
|
|
33
35
|
Requires-Dist: lark
|
|
34
36
|
Provides-Extra: c2
|
|
35
37
|
Requires-Dist: flow-record; extra == 'c2'
|
|
@@ -99,7 +101,7 @@ If you want to install the latest pre-release version, use the ``--pre`` flag::
|
|
|
99
101
|
|
|
100
102
|
$ pip install --pre dissect.cobaltstrike
|
|
101
103
|
|
|
102
|
-
**dissect.cobaltstrike** requires Python 3.
|
|
104
|
+
**dissect.cobaltstrike** requires Python 3.10 or later.
|
|
103
105
|
|
|
104
106
|
Documentation
|
|
105
107
|
-------------
|
|
@@ -27,7 +27,7 @@ If you want to install the latest pre-release version, use the ``--pre`` flag::
|
|
|
27
27
|
|
|
28
28
|
$ pip install --pre dissect.cobaltstrike
|
|
29
29
|
|
|
30
|
-
**dissect.cobaltstrike** requires Python 3.
|
|
30
|
+
**dissect.cobaltstrike** requires Python 3.10 or later.
|
|
31
31
|
|
|
32
32
|
Documentation
|
|
33
33
|
-------------
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '1.2.2.dev3'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 2, 2, 'dev3')
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/beacon.py
RENAMED
|
@@ -369,7 +369,7 @@ def iter_beacon_config_blocks(
|
|
|
369
369
|
# Try XorEncoded files first as they are more common
|
|
370
370
|
if not found and xordecode:
|
|
371
371
|
try:
|
|
372
|
-
fxor = cast(BinaryIO, XorEncodedFile.from_file(fobj))
|
|
372
|
+
fxor = cast("BinaryIO", XorEncodedFile.from_file(fobj))
|
|
373
373
|
for xorkey in xor_keys:
|
|
374
374
|
for config_block in find_beacon_config_bytes(fxor, xorkey):
|
|
375
375
|
found = True
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/pe.py
RENAMED
|
@@ -8,6 +8,10 @@ import io
|
|
|
8
8
|
import logging
|
|
9
9
|
from typing import BinaryIO, Optional, Tuple
|
|
10
10
|
|
|
11
|
+
from dissect.executable import PE
|
|
12
|
+
from dissect.executable.pe.c_pe import c_pe
|
|
13
|
+
from dissect.util.stream import OverlayStream, RangeStream
|
|
14
|
+
|
|
11
15
|
from dissect import cstruct
|
|
12
16
|
|
|
13
17
|
logger = logging.getLogger(__name__)
|
|
@@ -174,6 +178,39 @@ pestruct.load(PE_DEF)
|
|
|
174
178
|
DOSHEADER_X64 = bytes.fromhex("554889e54881")
|
|
175
179
|
DOSHEADER_X86 = bytes.fromhex("e8000000005b")
|
|
176
180
|
|
|
181
|
+
NT_SIGNATURE = c_pe.IMAGE_NT_SIGNATURE.to_bytes(4, "little")
|
|
182
|
+
OS2_SIGNATURE = c_pe.IMAGE_OS2_SIGNATURE.to_bytes(4, "little")
|
|
183
|
+
IMAGE_DOS_SIGNATURE = c_pe.IMAGE_DOS_SIGNATURE.to_bytes(2, "little")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class LenientPE(PE):
|
|
187
|
+
"""Wrapper around :class:`dissect.executable.PE` that is lenient towards invalid PE signatures.
|
|
188
|
+
|
|
189
|
+
This is accomplished by using an OverlayStream to patch the PE signature when it's invalid.
|
|
190
|
+
|
|
191
|
+
It currently overlays the following fixes:
|
|
192
|
+
|
|
193
|
+
- Invalid PE signature (not "PE\0\0")
|
|
194
|
+
- Invalid MZ signature (not "MZ")
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
def __init__(self, fh: BinaryIO) -> None:
|
|
198
|
+
fh.seek(0)
|
|
199
|
+
mz_header = c_pe.IMAGE_DOS_HEADER(fh)
|
|
200
|
+
overlay = OverlayStream(fh, size=fh.seek(0, io.SEEK_END))
|
|
201
|
+
if mz_header.e_magic != c_pe.IMAGE_DOS_SIGNATURE:
|
|
202
|
+
logger.debug("Patching invalid MZ signature: %r -> %r", mz_header.e_magic, IMAGE_DOS_SIGNATURE)
|
|
203
|
+
overlay.add(0, IMAGE_DOS_SIGNATURE)
|
|
204
|
+
fh = overlay
|
|
205
|
+
|
|
206
|
+
fh.seek(mz_header.e_lfanew, io.SEEK_SET)
|
|
207
|
+
signature = fh.read(4)
|
|
208
|
+
if signature not in (NT_SIGNATURE, OS2_SIGNATURE):
|
|
209
|
+
logger.debug("Patching invalid PE signature: %r -> %r", signature, NT_SIGNATURE)
|
|
210
|
+
overlay.add(mz_header.e_lfanew, NT_SIGNATURE)
|
|
211
|
+
fh = overlay
|
|
212
|
+
super().__init__(fh)
|
|
213
|
+
|
|
177
214
|
|
|
178
215
|
def find_mz_offset(fh: BinaryIO, start_offset: int = 0, maxrange: int = 1024) -> Optional[int]:
|
|
179
216
|
"""Find and return the start offset of a valid IMAGE_DOS_HEADER or ``None`` if it cannot be found.
|
|
@@ -226,35 +263,15 @@ def find_compile_stamps(
|
|
|
226
263
|
Tuple with ``(IMAGE_FILE_HEADER.TimeDateStamp, IMAGE_EXPORT_DIRECTORY.TimeDateStamp)``.
|
|
227
264
|
Either tuple values can be ``None`` if it's not found.
|
|
228
265
|
"""
|
|
229
|
-
mz_offset = find_mz_offset(fh, start_offset=start_offset, maxrange=maxrange)
|
|
230
|
-
if mz_offset is None:
|
|
231
|
-
return (None, None)
|
|
232
|
-
|
|
233
266
|
compile_stamp = None
|
|
234
267
|
export_stamp = None
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if image.Machine == pestruct.IMAGE_FILE_MACHINE_AMD64:
|
|
243
|
-
optional_header = pestruct.IMAGE_OPTIONAL_HEADER64(fh)
|
|
244
|
-
else:
|
|
245
|
-
optional_header = pestruct.IMAGE_OPTIONAL_HEADER(fh)
|
|
246
|
-
export_dd = optional_header.DataDirectory[pestruct.IMAGE_DIRECTORY_ENTRY_EXPORT]
|
|
247
|
-
sections = [pestruct.IMAGE_SECTION_HEADER(fh) for _ in range(image.NumberOfSections)]
|
|
248
|
-
ds = None
|
|
249
|
-
for section in sections:
|
|
250
|
-
if section.VirtualAddress <= export_dd.VirtualAddress < (section.VirtualAddress + section.VirtualSize):
|
|
251
|
-
ds = section
|
|
252
|
-
break
|
|
253
|
-
if ds is not None:
|
|
254
|
-
offset = export_dd.VirtualAddress - ds.VirtualAddress + ds.PointerToRawData + mz_offset
|
|
255
|
-
fh.seek(offset)
|
|
256
|
-
export_dir = pestruct.IMAGE_EXPORT_DIRECTORY(fh)
|
|
257
|
-
export_stamp = export_dir.TimeDateStamp
|
|
268
|
+
|
|
269
|
+
mz_offset = find_mz_offset(fh, start_offset=start_offset, maxrange=maxrange)
|
|
270
|
+
if mz_offset is not None:
|
|
271
|
+
pe = LenientPE(RangeStream(fh, offset=mz_offset, size=None))
|
|
272
|
+
compile_stamp = int(pe.timestamp.timestamp())
|
|
273
|
+
export_stamp = int(pe.exports.timestamp.timestamp()) if pe.exports else None
|
|
274
|
+
|
|
258
275
|
return (compile_stamp, export_stamp)
|
|
259
276
|
|
|
260
277
|
|
|
@@ -118,7 +118,7 @@ class XorEncodedFile(io.RawIOBase):
|
|
|
118
118
|
logger.debug(f"Found common nonce offset: {offset} ({count})")
|
|
119
119
|
found_nonce_offset = offset
|
|
120
120
|
xf = cls(fh, nonce_offset=found_nonce_offset)
|
|
121
|
-
if pe.find_mz_offset(cast(BinaryIO, xf)) is not None:
|
|
121
|
+
if pe.find_mz_offset(cast("BinaryIO", xf)) is not None:
|
|
122
122
|
xf.seek(0)
|
|
123
123
|
return xf
|
|
124
124
|
raise ValueError(f"MZ header not found for: {fh}")
|
|
@@ -7,12 +7,16 @@ The easiest way to install ``dissect.cobaltstrike`` is to use **pip**:
|
|
|
7
7
|
|
|
8
8
|
$ pip install dissect.cobaltstrike
|
|
9
9
|
|
|
10
|
-
Python 3.
|
|
10
|
+
Python 3.10 or higher is required and it has the following dependencies:
|
|
11
11
|
|
|
12
12
|
* dissect.cstruct_ - for structure parsing
|
|
13
|
+
* dissect.executable_ - for PE parsing
|
|
14
|
+
* dissect.util_ - for useful file stream functions
|
|
13
15
|
* lark_ - for parsing malleable c2 profiles
|
|
14
16
|
|
|
15
17
|
.. _dissect.cstruct: https://github.com/fox-it/dissect.cstruct
|
|
18
|
+
.. _dissect.executable: https://github.com/fox-it/dissect.executable
|
|
19
|
+
.. _dissect.util: https://github.com/fox-it/dissect.util
|
|
16
20
|
.. _lark: https://github.com/lark-parser/lark
|
|
17
21
|
|
|
18
22
|
The following pip `extras` flavours are provided as well:
|
|
@@ -79,12 +83,18 @@ run tests with `checksum8` in the name including verbose and stdout logging:
|
|
|
79
83
|
Linting
|
|
80
84
|
-------
|
|
81
85
|
|
|
82
|
-
For linting
|
|
86
|
+
For linting, run:
|
|
83
87
|
|
|
84
88
|
.. code-block:: shell
|
|
85
89
|
|
|
86
90
|
$ tox -e lint
|
|
87
91
|
|
|
92
|
+
If there are any linting issues you can try to "fix" them using:
|
|
93
|
+
|
|
94
|
+
.. code-block:: shell
|
|
95
|
+
|
|
96
|
+
$ tox -e fix
|
|
97
|
+
|
|
88
98
|
Documentation
|
|
89
99
|
-------------
|
|
90
100
|
|
|
@@ -6,14 +6,16 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
name = "dissect.cobaltstrike"
|
|
7
7
|
dynamic = ["version"]
|
|
8
8
|
description = "a Python library for dissecting Cobalt Strike related data"
|
|
9
|
-
requires-python = ">=3.
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
10
|
license = {text = "MIT License"}
|
|
11
11
|
readme = "README.rst"
|
|
12
12
|
authors = [
|
|
13
13
|
{name = "Yun Zheng Hu", email = "hu@fox-it.com"},
|
|
14
14
|
]
|
|
15
15
|
dependencies = [
|
|
16
|
-
"dissect.cstruct >= 4.
|
|
16
|
+
"dissect.cstruct >= 4.7",
|
|
17
|
+
"dissect.executable >= 1.11",
|
|
18
|
+
"dissect.util >= 3.24",
|
|
17
19
|
"lark",
|
|
18
20
|
]
|
|
19
21
|
keywords = ["dissect", "cobaltstrike", "beacon", "parser", "parsing", "lark", "cstruct"]
|
|
@@ -28,11 +30,11 @@ classifiers = [
|
|
|
28
30
|
"Operating System :: MacOS :: MacOS X",
|
|
29
31
|
"Operating System :: Microsoft :: Windows",
|
|
30
32
|
"Programming Language :: Python :: 3 :: Only",
|
|
31
|
-
"Programming Language :: Python :: 3.9",
|
|
32
33
|
"Programming Language :: Python :: 3.10",
|
|
33
34
|
"Programming Language :: Python :: 3.11",
|
|
34
35
|
"Programming Language :: Python :: 3.12",
|
|
35
36
|
"Programming Language :: Python :: 3.13",
|
|
37
|
+
"Programming Language :: Python :: 3.14",
|
|
36
38
|
"Topic :: Security",
|
|
37
39
|
"Topic :: Utilities",
|
|
38
40
|
"Topic :: Scientific/Engineering :: Information Analysis",
|
|
@@ -75,6 +77,41 @@ docs = [
|
|
|
75
77
|
"dissect.cobaltstrike[full]",
|
|
76
78
|
]
|
|
77
79
|
|
|
80
|
+
[dependency-groups]
|
|
81
|
+
c2 = [
|
|
82
|
+
"flow.record",
|
|
83
|
+
"pycryptodome",
|
|
84
|
+
"httpx",
|
|
85
|
+
]
|
|
86
|
+
pcap = [
|
|
87
|
+
"pyshark",
|
|
88
|
+
{include-group = "c2"},
|
|
89
|
+
]
|
|
90
|
+
full = [
|
|
91
|
+
{include-group = "c2"},
|
|
92
|
+
{include-group = "pcap"},
|
|
93
|
+
"rich",
|
|
94
|
+
]
|
|
95
|
+
test = [
|
|
96
|
+
"pytest",
|
|
97
|
+
"pytest-cov",
|
|
98
|
+
"pytest-httpserver",
|
|
99
|
+
{include-group = "full"},
|
|
100
|
+
]
|
|
101
|
+
lint = [
|
|
102
|
+
"ruff",
|
|
103
|
+
]
|
|
104
|
+
docs = [
|
|
105
|
+
"sphinx",
|
|
106
|
+
"sphinx_rtd_theme>=2.0",
|
|
107
|
+
"sphinx-autoapi",
|
|
108
|
+
"sphinx-copybutton",
|
|
109
|
+
"sphinx-argparse-cli",
|
|
110
|
+
"ipython",
|
|
111
|
+
"pickleshare",
|
|
112
|
+
{include-group = "full"},
|
|
113
|
+
]
|
|
114
|
+
|
|
78
115
|
[project.scripts]
|
|
79
116
|
beacon-artifact = "dissect.cobaltstrike.artifact:main"
|
|
80
117
|
beacon-dump = "dissect.cobaltstrike.beacon:main"
|
|
@@ -130,9 +130,7 @@ def test_setting_useragent_edgecase():
|
|
|
130
130
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
131
131
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
132
132
|
00 00 00 00 00 00
|
|
133
|
-
""".replace(
|
|
134
|
-
"\n", ""
|
|
135
|
-
)
|
|
133
|
+
""".replace("\n", "")
|
|
136
134
|
config = beacon.BeaconConfig(bytes.fromhex(data))
|
|
137
135
|
|
|
138
136
|
ua = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; Win64; x64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)" # noqa: E501
|
|
@@ -290,7 +288,7 @@ def test_main(capfdbinary, request, fixture, options, ret, stdout, stderr):
|
|
|
290
288
|
def test_beacon_public_key(beacon_x86_file):
|
|
291
289
|
bconfig = beacon.BeaconConfig.from_file(beacon_x86_file)
|
|
292
290
|
assert bconfig.public_key == bytes.fromhex(
|
|
293
|
-
"30819f300d06092a864886f70d010101050003818d0030818902818100c9bc2d82418688a2e4f8d645ca54dacce652ef725189444fa2def7acfaf0b40000d45933eceb80e7fc1e1e2a540a3e96c2ffc22026369d0ff07da59898f1752593876f69a80b763042abbb92c52f8a85556c5f8e8b052060231684e007a866fc010f69f4c1d79e236b1b90cbc3861bf9b3a366cf5dac02d39519dafc717dece50203010001" # noqa:
|
|
291
|
+
"30819f300d06092a864886f70d010101050003818d0030818902818100c9bc2d82418688a2e4f8d645ca54dacce652ef725189444fa2def7acfaf0b40000d45933eceb80e7fc1e1e2a540a3e96c2ffc22026369d0ff07da59898f1752593876f69a80b763042abbb92c52f8a85556c5f8e8b052060231684e007a866fc010f69f4c1d79e236b1b90cbc3861bf9b3a366cf5dac02d39519dafc717dece50203010001" # noqa: E501
|
|
294
292
|
)
|
|
295
293
|
assert hashlib.sha256(bconfig.public_key).hexdigest() == bconfig.settings["SETTING_PUBKEY"]
|
|
296
294
|
|
|
@@ -62,7 +62,7 @@ def test_c2profile_generator():
|
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
def test_value_to_string():
|
|
65
|
-
val = b"\
|
|
65
|
+
val = b"\xca\xfe\xba\xbe"
|
|
66
66
|
assert c2profile.value_to_string(val) == '"\\xca\\xfe\\xba\\xbe"'
|
|
67
67
|
val = b"\x00\x22\x27"
|
|
68
68
|
assert c2profile.value_to_string(val) == '"\\x00\\"\'"'
|
|
@@ -16,7 +16,17 @@ commands =
|
|
|
16
16
|
[testenv:lint]
|
|
17
17
|
deps = pre-commit
|
|
18
18
|
skip_install = true
|
|
19
|
-
commands =
|
|
19
|
+
commands =
|
|
20
|
+
pre-commit run codespell --all-files
|
|
21
|
+
pre-commit run ruff-lint --all-files
|
|
22
|
+
pre-commit run ruff-format --all-files
|
|
23
|
+
|
|
24
|
+
[testenv:fix]
|
|
25
|
+
deps = pre-commit
|
|
26
|
+
skip_install = true
|
|
27
|
+
commands =
|
|
28
|
+
pre-commit run ruff-fix --all-files
|
|
29
|
+
pre-commit run ruff-format --all-files
|
|
20
30
|
|
|
21
31
|
[testenv:build]
|
|
22
32
|
skip_install = true
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# file generated by setuptools-scm
|
|
2
|
-
# don't change, don't track in version control
|
|
3
|
-
|
|
4
|
-
__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
|
|
5
|
-
|
|
6
|
-
TYPE_CHECKING = False
|
|
7
|
-
if TYPE_CHECKING:
|
|
8
|
-
from typing import Tuple
|
|
9
|
-
from typing import Union
|
|
10
|
-
|
|
11
|
-
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
12
|
-
else:
|
|
13
|
-
VERSION_TUPLE = object
|
|
14
|
-
|
|
15
|
-
version: str
|
|
16
|
-
__version__: str
|
|
17
|
-
__version_tuple__: VERSION_TUPLE
|
|
18
|
-
version_tuple: VERSION_TUPLE
|
|
19
|
-
|
|
20
|
-
__version__ = version = '1.2.2.dev2'
|
|
21
|
-
__version_tuple__ = version_tuple = (1, 2, 2, 'dev2')
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/.github/workflows/dissect-ci.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/__init__.py
RENAMED
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/artifact.py
RENAMED
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/c2.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/c_c2.py
RENAMED
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/client.py
RENAMED
|
File without changes
|
|
@@ -23,9 +23,10 @@ import logging
|
|
|
23
23
|
from dataclasses import dataclass
|
|
24
24
|
from typing import TYPE_CHECKING, BinaryIO
|
|
25
25
|
|
|
26
|
-
from dissect.cobaltstrike.utils import grouper, u32be, xor
|
|
27
26
|
from dissect.cstruct import cstruct
|
|
28
27
|
|
|
28
|
+
from dissect.cobaltstrike.utils import grouper, u32be, xor
|
|
29
|
+
|
|
29
30
|
if TYPE_CHECKING:
|
|
30
31
|
from collections.abc import Iterator
|
|
31
32
|
|
|
@@ -172,7 +173,6 @@ def payload_checksum(data: bytes) -> int:
|
|
|
172
173
|
|
|
173
174
|
def iter_guardrail_configs_with_beacon(fh: BinaryIO) -> Iterator[GuardrailMetadata]:
|
|
174
175
|
for grconfig in iter_guardrail_configs(fh):
|
|
175
|
-
|
|
176
176
|
# Unmask the beacon config, static single byte xor key should be 0x2E unless modified beacon
|
|
177
177
|
# The beacon config is still masked with the environmental key
|
|
178
178
|
grconfig.beacon_xor_key = b"\x2e" # we currently only support the XOR default key
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/pcap.py
RENAMED
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/utils.py
RENAMED
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/dissect/cobaltstrike/version.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/c2profile-grammar.rst
RENAMED
|
File without changes
|
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/codespell-ignore-words.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/images/beacon-client.png
RENAMED
|
File without changes
|
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/images/teamserver_ls.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/scripts/example_client.rst
RENAMED
|
File without changes
|
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/structures/c2_def.rst
RENAMED
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/structures/cs_def.rst
RENAMED
|
File without changes
|
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/structures/index.rst
RENAMED
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/structures/pe_def.rst
RENAMED
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/tools/beacon-artifact.rst
RENAMED
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/tools/beacon-client.rst
RENAMED
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/tools/beacon-dump.rst
RENAMED
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/tools/beacon-pcap.rst
RENAMED
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/tools/beacon-xordecode.rst
RENAMED
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/tools/c2profile-dump.rst
RENAMED
|
File without changes
|
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/docs/tutorials/index.rst
RENAMED
|
File without changes
|
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/scripts/checksum8-accesslogs.py
RENAMED
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/scripts/dump_beacon_keys.py
RENAMED
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/scripts/example_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dissect_cobaltstrike-1.2.2.dev2 → dissect_cobaltstrike-1.2.2.dev3}/tests/test_guardrails.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|