Skip to content

Commit 0850909

Browse files
authored
Merge pull request #14 from Tw1sm/feature/sccm-modules
Add SCCM Modules
2 parents 9ebe1a2 + 424f986 commit 0850909

18 files changed

+770
-10
lines changed

CHANGELOG.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
11
# Changelog
2+
## [v0.3.0] - 08/05/2024
3+
### Added
4+
- SCCM modules from [SQLRecon](https://github.com/skahwah/SQLRecon?tab=readme-ov-file#sccm-modules)
5+
- `addadmin`
6+
- `credentials`
7+
- `logons`
8+
- `removeadmin`
9+
- `sites`
10+
- `taskdata`
11+
- `tasklist`
12+
- `users`
13+
214
## [v0.2.1] - 07/26/2024
315
### Fixed
416
- Issue [#12](https://github.com/Tw1sm/PySQLRecon/issues/12)
@@ -7,7 +19,6 @@
719
### Added
820
- `sample` module to retrive table data without manual SQL query
921

10-
1122
## [v0.1.4] - 02/03/2024
1223
### Fixed
1324
- Issue [#9](https://github.com/Tw1sm/PySQLRecon/issues/9)

README.md

+27-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ PySQLRecon
99

1010
PySQLRecon is a Python port of the awesome [SQLRecon](https://github.com/skahwah/SQLRecon) project by [@sanjivkawa](https://twitter.com/sanjivkawa). See the [commands](#commands) section for a list of capabilities.
1111

12+
[Post](https://tw1sm.substack.com/p/takeover-1-with-pysqlrecon) demonstrating SCCM TAKEOVER-1 with PySQLRecon.
13+
1214
## Install
1315
PySQLRecon can be installed with `pip3 install pysqlrecon` or by cloning this repository and running `pip3 install .`
1416

@@ -39,7 +41,8 @@ links [NORM] Enumerate linked servers [I,L]
3941
olecmd [PRIV] Execute a system command using OLE automation procedures [I,L]
4042
query [NORM] Execute a custom SQL query [I,L]
4143
rows [NORM] Get the count of rows in a table [I,L]
42-
sample [NORM] Query a sample of table data [I,L]
44+
sample [NORM] Query a sample of table data [I,L]
45+
sccm [SUBM] Submodule for SCCM specific commands
4346
search [NORM] Search a table for a column name [I,L]
4447
smb [NORM] Coerce NetNTLM auth via xp_dirtree [I,L]
4548
tables [NORM] Enumerate tables within a database [I,L]
@@ -48,6 +51,19 @@ whoami [NORM] Gather logged in user, mapped user and roles [I,L]
4851
xpcmd [PRIV] Execute a system command using xp_cmdshell [I,L]
4952
```
5053

54+
### SCCM Commands
55+
SCCM commands can be found by running `pysqlrecon [OPTIONS] sccm -h` (required global flags will need to be specified for this to work - see [usage](#usage))
56+
```
57+
addadmin [PRIV] Elevate an account to Full Administrator [I]
58+
credentials [NORM] Display encrypted credentials [I]
59+
logons [NORM] Display SCCM clients and last logged on user [I]
60+
removeadmin [PRIV] Remove elevated account or elevated privileges [I]
61+
sites [NORM] Gather SCCM site info [I]
62+
taskdata [NORM] Decrypt task sequences [I]
63+
tasklist [NORM] Display task sequences [I]
64+
users [NORM] Enumerate SCCM users [I]
65+
```
66+
5167
## Usage
5268
PySQLRecon has global options (available to any command), with some commands introducing additional flags. All global options must be specified *before* the command name:
5369
```
@@ -70,7 +86,14 @@ Target execution of a PySQLRecon command on a linked server (instead of the SQL
7086

7187
Impersonate a user account while running a PySQLRecon command with the `--impersonate` flag.
7288

73-
`--link` and `--impersonate` and incompatible.
89+
`--link` and `--impersonate` are incompatible.
90+
91+
### Usage with `ntlmrelayx`
92+
PySQLRecon can be used with `proxychains` to take advantage of relayed authentication targeting a `mssql://` service. Due to the way ntlmrelayx sessions work, the `--database` parameter will not be respected when running PySQLRecon (the relay session will always be connected to the master database). This can come into play especially when using SCCM modules, which require the site database to be sepecified. To fix this, first change the database context using the `query` module (this will persist across any subsequent PySQLRecon usage, with the same relay session). Example:
93+
```
94+
proxychains4 pysqlrecon -t <target> -d <DOMAIN> -u <username> -p FAKE query --query 'use new_db_name'
95+
```
96+
You can now run modules/queries that target resources within that specifc database, even without specifying `--database`, from the same `ntlmrelayx` session.
7497

7598
## Development
7699
pysqlrecon uses Poetry to manage dependencies. Install from source and setup for development with:
@@ -85,7 +108,7 @@ poetry run pysqlrecon --help
85108
PySQLRecon is easily extensible - see the template and instructions in [resources](resources/command_template/)
86109

87110
### TODO
88-
- [ ] Add SQLRecon SCCM commands
111+
- [x] Add SQLRecon SCCM commands
89112
- [ ] Add Azure SQL DB support?
90113

91114
## References and Credits
@@ -94,3 +117,4 @@ PySQLRecon is easily extensible - see the template and instructions in [resource
94117
- [https://securityintelligence.com/x-force/databases-beware-abusing-microsoft-sql-server-with-sqlrecon/](https://securityintelligence.com/x-force/databases-beware-abusing-microsoft-sql-server-with-sqlrecon/)
95118
- [https://gist.github.com/skahwah/a585e176e4a5cf319b0c759637f5c410](https://gist.github.com/skahwah/a585e176e4a5cf319b0c759637f5c410)
96119
- Also checkout [MSSqlPwner](https://github.com/ScorpionesLabs/MSSqlPwner) for other offensive MSSQL capabilities written in Python
120+
- [PXEThief](https://github.com/MWR-CyberSec/PXEThief)

poetry.lock

+43-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "pysqlrecon"
3-
version = "0.2.1"
3+
version = "0.3.0"
44
description = "Offensive MSSQL Python toolkit"
55
authors = ["Matt Creel <[email protected]>"]
66
readme = "README.md"
@@ -12,6 +12,7 @@ python = "^3.11"
1212
rich = "^12.5.1"
1313
typer = "^0.6.1"
1414
impacket = "^0.11.0"
15+
pycryptodome = "^3.20.0"
1516

1617
[tool.poetry.group.dev.dependencies]
1718
ruff = "^0.0.235"

pysqlrecon/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.2.1'
1+
__version__ = '0.3.0'

pysqlrecon/__main__.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def main(
4545

4646
# Misc Options
4747
debug: bool = typer.Option(False, '--debug', help='Turn DEBUG output ON', rich_help_panel='Misc Options'),
48-
basic_tables: bool = typer.Option(False, '--basic-tables', help='Use simple ASCII table output', rich_help_panel='Misc Options'),
48+
basic_tables: bool = typer.Option(False, '--basic-tables', help='Use simple ASCII table output (avoids truncation)', rich_help_panel='Misc Options'),
4949
quiet: bool = typer.Option(False, '--quiet', help='Hide the banner', rich_help_panel='Misc Options')):
5050

5151
if not quiet:
@@ -57,6 +57,10 @@ def main(
5757
logger.warning("Cannot use --impersonate and --link together")
5858
exit()
5959

60+
if not sql_auth and domain is None:
61+
logger.warning("Windows authentication requires a domain specified with -d/--domain")
62+
exit()
63+
6064
# accesing a link may require Kerberos auth
6165
if link is not None and kerberos is False:
6266
logger.warning("Querying a linked server may require specifying Kerberos authentication")

pysqlrecon/lib/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
from pysqlrecon.lib.clr import ClrMixin
99
from pysqlrecon.lib.module import ModuleMixin
1010
from pysqlrecon.lib.query import QueryMixin
11+
from pysqlrecon.lib.sccm import SccmMixin
1112

1213

13-
class PySqlRecon(SqlAgentMixin, ClrMixin, ModuleMixin, QueryMixin):
14+
class PySqlRecon(SqlAgentMixin, ClrMixin, ModuleMixin, QueryMixin, SccmMixin):
1415

1516
# https://learn.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors-6000-to-6999?view=sql-server-ver16
1617
DUPLICATE_ASM_ERROR = 6285

pysqlrecon/lib/sccm.py

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
from Crypto.Cipher import DES3
2+
from hashlib import sha1
3+
import struct
4+
5+
from pysqlrecon.logger import logger
6+
7+
8+
class SccmMixin:
9+
10+
#
11+
# https://github.com/skahwah/SQLRecon/blob/main/SQLRecon/SQLRecon/modules/SCCM.cs#L844
12+
#
13+
@staticmethod
14+
def decode_data(encrypted_blob):
15+
16+
data_len = struct.unpack_from('<I', encrypted_blob, 52)[0]
17+
encrypted_data = encrypted_blob[64:64 + data_len]
18+
logger.debug(f"Encrypted data length: {data_len}")
19+
20+
hash_base = encrypted_blob[4:44]
21+
22+
logger.debug(f"Hash Base: [ {', '.join([str(byte) for byte in hash_base])} ]")
23+
24+
key = SccmMixin.aes_des_key_derivation(hash_base)[:24]
25+
logger.debug(f"Derived Key: [ {', '.join([str(byte) for byte in key])} ]")
26+
27+
iv = bytes([0] * 8)
28+
cipher = DES3.new(key, DES3.MODE_CBC, iv)
29+
decrypted = cipher.decrypt(encrypted_data)
30+
31+
# Remove PKCS7 padding
32+
padding_len = decrypted[-1]
33+
if isinstance(padding_len, int) and padding_len > 0 and padding_len <= 8:
34+
decrypted = decrypted[:-padding_len]
35+
36+
try:
37+
decoded_string = decrypted.decode('utf-16-le')
38+
except UnicodeDecodeError as e:
39+
print(f"Decoding error: {e}")
40+
raise
41+
42+
return decoded_string
43+
44+
45+
#
46+
# https://github.com/MWR-CyberSec/PXEThief/blob/main/media_variable_file_cryptography.py#L23
47+
#
48+
@staticmethod
49+
def aes_des_key_derivation(password):
50+
key_sha1 = sha1(password).digest()
51+
52+
b0 = b""
53+
for x in key_sha1:
54+
b0 += bytes((x ^ 0x36,))
55+
56+
b1 = b""
57+
for x in key_sha1:
58+
b1 += bytes((x ^ 0x5c,))
59+
60+
# pad remaining bytes with the appropriate value
61+
b0 += b"\x36"*(64 - len(b0))
62+
b1 += b"\x5c"*(64 - len(b1))
63+
64+
b0_sha1 = sha1(b0).digest()
65+
b1_sha1 = sha1(b1).digest()
66+
67+
return b0_sha1 + b1_sha1
68+
69+
70+
@staticmethod
71+
def convert_sid_to_binary(sid_string):
72+
# Split the SID string into its components
73+
sid_parts = sid_string.split('-')
74+
75+
# Extract the revision level (first part after 'S')
76+
revision = int(sid_parts[1])
77+
78+
# Extract the identifier authority (next part)
79+
identifier_authority = int(sid_parts[2])
80+
81+
# Extract the sub authorities (remaining parts)
82+
sub_authorities = [int(part) for part in sid_parts[3:]]
83+
84+
# Create the binary representation
85+
# Start with revision and sub-authority count
86+
sid_binary = struct.pack('B', revision) + struct.pack('B', len(sub_authorities))
87+
88+
# Handle identifier authority
89+
if identifier_authority > 0xFFFFFFFF:
90+
sid_binary += struct.pack('>Q', identifier_authority)[2:]
91+
else:
92+
sid_binary += b'\x00\x00' + struct.pack('>I', identifier_authority)
93+
94+
# Add each sub authority
95+
for sub_authority in sub_authorities:
96+
sid_binary += struct.pack('<I', sub_authority)
97+
98+
return sid_binary
99+

pysqlrecon/modules/__init__.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
enablexp, disablexp, xpcmd, enablerpc, disablerpc, enableole,\
44
disableole, enableclr, disableclr, olecmd, agentstatus, agentcmd, \
55
clr, adsi, sample
6+
from pysqlrecon.modules import sccm
67

78
__all__ = [
89
checkrpc,
@@ -34,5 +35,8 @@
3435
olecmd,
3536
agentstatus,
3637
agentcmd,
37-
adsi
38+
adsi,
39+
40+
# sccm submodule
41+
sccm
3842
]

pysqlrecon/modules/sccm/__init__.py

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import typer
2+
3+
from pysqlrecon.modules.sccm import users, sites, credentials, logons, tasklist, taskdata, \
4+
addadmin, removeadmin
5+
6+
__all__ = [
7+
addadmin,
8+
credentials,
9+
logons,
10+
removeadmin,
11+
sites,
12+
taskdata,
13+
tasklist,
14+
users
15+
]
16+
17+
COMMAND_NAME = "sccm"
18+
HELP = "[blue][SUBM][/] Submodule for SCCM specific commands"
19+
20+
21+
app = typer.Typer(add_completion=False,
22+
rich_markup_mode='rich',
23+
context_settings={'help_option_names': ['-h', '--help']},
24+
pretty_exceptions_show_locals=False
25+
)
26+
27+
for command in __all__:
28+
app.add_typer(
29+
command.app,
30+
name=command.COMMAND_NAME,
31+
help=command.HELP
32+
)
33+

0 commit comments

Comments
 (0)