Skip to content

Commit 383f9f5

Browse files
authored
Merge pull request #2 from Tw1sm/bug/duplicate-clr-assembly
Bug/duplicate clr assembly
2 parents 498d052 + 5d8a9a2 commit 383f9f5

6 files changed

Lines changed: 57 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
# Changelog
2+
## [v0.1.2] - 09/21/2023
3+
### Fixed
4+
- Issue #1
5+
- When using `clr` module, if custom assembly already exists under a different name `pysqlrecon` would previously log the error and exit
6+
- Now it deletes the offending assembly and tries creation again
7+
28
## [v0.1.1] - 09/03/2023
39
- Initial commit

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "pysqlrecon"
3-
version = "0.1.1"
3+
version = "0.1.2"
44
description = "Offensive MSSQL Python toolkit"
55
authors = ["Matt Creel <mcreel31@gmail.com>"]
66
readme = "README.md"

pysqlrecon/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.1.0'
1+
__version__ = '0.1.2'

pysqlrecon/lib/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from typing import Any
33
from rich.table import Table
44

5+
from pysqlrecon.lib.exceptions import DuplicateAssemblyError
56
from pysqlrecon.logger import logger, console
67
from pysqlrecon.lib.sqlagent import SqlAgentMixin
78
from pysqlrecon.lib.clr import ClrMixin
@@ -11,6 +12,10 @@
1112

1213
class PySqlRecon(SqlAgentMixin, ClrMixin, ModuleMixin, QueryMixin):
1314

15+
# https://learn.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors-6000-to-6999?view=sql-server-ver16
16+
DUPLICATE_ASM_ERROR = 6285
17+
18+
1419
def __init__(self, target, domain, username, password, port, link, impersonate,
1520
db, hashes, aesKey, kerberos, no_pass, dc_ip, windows_auth) -> None:
1621

@@ -205,9 +210,15 @@ def print_replies(self) -> None:
205210
for keys in list(self.ms_sql.replies.keys()):
206211
for i, key in enumerate(self.ms_sql.replies[keys]):
207212
if key['TokenType'] == tds.TDS_ERROR_TOKEN:
213+
error_num = key['Number']
208214
error = "(%s): Line %d: %s" % (key['ServerName'].decode('utf-16le'), key['LineNumber'], key['MsgText'].decode('utf-16le'))
209215
self.lastError = tds.SQLErrorException("ERROR: Line %d: %s" % (key['LineNumber'], key['MsgText'].decode('utf-16le')))
210216
logger.error(error)
217+
218+
# handle duplicate assembly error
219+
if error_num == PySqlRecon.DUPLICATE_ASM_ERROR:
220+
raise DuplicateAssemblyError(error)
221+
211222
exit()
212223

213224
elif key['TokenType'] == tds.TDS_INFO_TOKEN:

pysqlrecon/lib/exceptions.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import re
2+
3+
4+
class DuplicateAssemblyError(Exception):
5+
"""
6+
Exception raised for creation of a CLR assembly that already exsists
7+
"""
8+
9+
def __init__(self, message):
10+
self.assembly_name = ""
11+
12+
# extract the name of the assembly from the message
13+
match = re.search(r'name "(.*?)"', message)
14+
if match:
15+
self.assembly_name = match.group(1)

pysqlrecon/modules/clr.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from pathlib import Path
55
from hashlib import sha512
66

7+
from pysqlrecon.lib.exceptions import DuplicateAssemblyError
78
from pysqlrecon.logger import logger
89
from pysqlrecon.lib import PySqlRecon
910

@@ -17,8 +18,8 @@
1718
@app.callback(invoke_without_command=True)
1819
def main(
1920
ctx: typer.Context,
20-
dll: Path = typer.Option(None, "--dll", dir_okay=False, readable=True, help=".NET DLL to load into stored procedure"),
21-
function: str = typer.Option(None, "--function", help="Function within .NET DLL to execute")):
21+
dll: Path = typer.Option(..., "--dll", dir_okay=False, readable=True, help=".NET DLL to load into stored procedure"),
22+
function: str = typer.Option(..., "--function", help="Function within .NET DLL to execute")):
2223

2324
pysqlrecon: PySqlRecon = ctx.obj['pysqlrecon']
2425

@@ -86,7 +87,8 @@ def main(
8687

8788
#####
8889
# Create the custom assembly
89-
pysqlrecon.create_asm(asm_name, dll_bytes)
90+
create_assembly(pysqlrecon, asm_name, dll_bytes, function)
91+
9092
if not pysqlrecon.check_assembly(asm_name):
9193
logger.error("Failed to create custom assembly")
9294
logger.info("Cleaning up...")
@@ -122,4 +124,21 @@ def main(
122124
pysqlrecon.delete_tasm_resources(asm_name, function)
123125

124126
pysqlrecon.disconnect()
125-
127+
128+
129+
# recursive func to create assembly and handle duplicate assmebly error by deleting and re-creating
130+
# fix for https://github.com/Tw1sm/PySQLRecon/issues/1
131+
def create_assembly(pysqlrecon, asm_name, dll_bytes, function):
132+
try:
133+
pysqlrecon.create_asm(asm_name, dll_bytes)
134+
135+
except DuplicateAssemblyError as e:
136+
logger.warning("Duplicate assembly detected - will try to delete and re-create")
137+
138+
pysqlrecon.delete_tasm_resources(e.assembly_name, function)
139+
logger.info(f"Deleted the offending duplicate assembly '{e.assembly_name}'")
140+
141+
logger.info(f"Attempting to re-create assembly with name '{asm_name}'")
142+
pysqlrecon.create_asm(asm_name, dll_bytes)
143+
144+

0 commit comments

Comments
 (0)