Skip to content

Commit 3517b82

Browse files
authored
[PATCH] Bug fixes and logging enhancements
Merge pull request #13 from MatrixEditor/dev/patch-errors
2 parents 1c91092 + 3426c6f commit 3517b82

File tree

23 files changed

+186
-80
lines changed

23 files changed

+186
-80
lines changed

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,14 @@ Take a look at the [Documentation on GitHub-Pages](https://matrixeditor.github.i
5656

5757
## License
5858

59-
Distributed under the MIT License. See LICENSE for more information.
59+
Distributed under the MIT License. See LICENSE for more information.
60+
61+
## Disclaimer
62+
63+
**Dementor** is intended only for lawful educational purposes: learning, testing
64+
in your own lab, or assessments on systems where you have explicit written
65+
authorization. You agree not to use this software to access, damage, interfere
66+
with, or exfiltrate data from systems for which you do not have permission.
67+
We make no promises about safety, completeness, or fitness for any purpose. Use
68+
at your own risk. If you discover a vulnerability, please follow responsible
69+
disclosure by using the private disclosing feature offered by GitHub.

dementor/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@
1818
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1919
# SOFTWARE.
2020

21-
__version__ = "1.0.0.dev12"
21+
__version__ = "1.0.0.dev13"
2222
__author__ = "MatrixEditor"

dementor/database.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ def add_auth(
207207
username_text = "(blank)"
208208

209209
full_name = (
210-
f" for [b]{username_text}[/]/[b]{markup.escape(domain)}[/]"
210+
f" for [b]{markup.escape(domain)}[/]/[b]{username_text}[/]"
211211
if domain
212212
else f" for [b]{username_text}[/]"
213213
)

dementor/log/hexdump.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright (c) 2025-Present MatrixEditor
2+
#
3+
# Permission is hereby granted, free of charge, to any person obtaining a copy
4+
# of this software and associated documentation files (the "Software"), to deal
5+
# in the Software without restriction, including without limitation the rights
6+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
# copies of the Software, and to permit persons to whom the Software is
8+
# furnished to do so, subject to the following conditions:
9+
#
10+
# The above copyright notice and this permission notice shall be included in all
11+
# copies or substantial portions of the Software.
12+
#
13+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
# SOFTWARE.
20+
21+
# just reusing impacket's hexdump
22+
from impacket.structure import hexdump
23+
24+
__all__ = ["hexdump"]

dementor/log/logger.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from dementor.config.toml import TomlConfig, Attribute as A
3535
from dementor.log import dm_print, dm_console
3636

37+
3738
class LoggingConfig(TomlConfig):
3839
_section_ = "Log"
3940
_fields_ = [
@@ -234,11 +235,10 @@ def init_logfile(session) -> None:
234235
if not config.log_enable:
235236
return
236237

237-
workspace = pathlib.Path(session.workspace_path)
238-
workspace /= config.log_dir or "logs"
239-
workspace.mkdir(parents=True, exist_ok=True)
240-
log_name = f"log_{util.now()}.log"
241-
dm_logger.add_logfile(str(workspace / log_name))
238+
log_dir: pathlib.Path = session.resolve_path(config.log_dir or "logs")
239+
log_dir.mkdir(parents=True, exist_ok=True)
240+
log_name = f"dm_log-{util.now()}.log"
241+
dm_logger.add_logfile(str(log_dir / log_name))
242242

243243

244244
class ProtocolLoggerMixin:

dementor/protocols/http.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ def send_wpad_script(self):
328328
else:
329329
script = self.server.render_page("wpad.dat")
330330
if self.config.http_wpad_enabled and not script:
331-
self.logger.error("WPAD enabled but script not configured")
331+
self.logger.fail("WPAD enabled but script not configured")
332332
return self.send_error(HTTPStatus.NOT_FOUND)
333333

334334
self.logger.success("Serving WPAD script to client")
@@ -381,7 +381,7 @@ def auth_ntlm(self, token, logger, scheme=None):
381381
message = ntlm.NTLM_HTTP.get_instance(f"NTLM {token}")
382382
except Exception:
383383
# invalid value
384-
logger.error(f"Invalid negotiate authentication: {token}")
384+
logger.fail(f"Invalid negotiate authentication: {token}")
385385
self.send_error(HTTPStatus.INTERNAL_SERVER_ERROR, "Internal Server Error")
386386
return
387387

@@ -414,7 +414,7 @@ def auth_ntlm(self, token, logger, scheme=None):
414414
self.finish_request(logger)
415415

416416
case _:
417-
logger.error(f"Invalid negotiate authentication: {token}")
417+
logger.fail(f"Invalid negotiate authentication: {token}")
418418
self.send_error(
419419
HTTPStatus.INTERNAL_SERVER_ERROR, "Internal Server Error"
420420
)
@@ -437,7 +437,7 @@ def auth_basic(self, token, logger):
437437
try:
438438
username, password = base64.b64decode(token).decode().split(":", 1)
439439
except ValueError:
440-
logger.error(f"Invalid basic authentication: {token}")
440+
logger.fail(f"Invalid basic authentication: {token}")
441441
self.send_error(HTTPStatus.INTERNAL_SERVER_ERROR, "Internal Server Error")
442442
return
443443

@@ -532,9 +532,12 @@ def server_bind(self):
532532
ThreadingHTTPServer.server_bind(self)
533533

534534
def finish_request(self, request, client_address) -> None:
535-
self.RequestHandlerClass(
536-
self.config, self.server_config, request, client_address, self
537-
)
535+
try:
536+
self.RequestHandlerClass(
537+
self.config, self.server_config, request, client_address, self
538+
)
539+
except ConnectionResetError:
540+
pass
538541

539542
def render_error(
540543
self, code: int, message: str | None = None, explain: str | None = None

dementor/protocols/imap.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ def handle_data(self, data, transport) -> None:
220220
tag, cmd, *args = shlex.split(line)
221221
self.seq_id = tag
222222
except ValueError:
223-
self.logger.debug(f"Unknown command: {line!r}")
223+
self.logger.debug(f"Unknown command: {line.encode()!r}")
224224
self.bad("Invalid command")
225225
continue
226226

@@ -231,7 +231,7 @@ def handle_data(self, data, transport) -> None:
231231
except StopHandler:
232232
break
233233
else:
234-
self.logger.debug(f"Unknown command: {line!r}")
234+
self.logger.debug(f"Unknown command: {line.encode()!r}")
235235
# 7.1.5. BYE Response
236236
self._push("BYE Unknown command", seq=False)
237237
break
@@ -240,7 +240,7 @@ def recv_line(self, size: int) -> str | None:
240240
data = self.rfile.readline(size)
241241
if data:
242242
text = data.decode("utf-8", errors="replace").strip()
243-
self.logger.debug(repr(text), is_client=True)
243+
self.logger.debug(repr(data), is_client=True)
244244
return text
245245

246246
# implementation

dementor/protocols/ipp.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,9 @@ def setup_proto_logger(self):
210210
)
211211

212212
def send_response(self, code: int, message=None, document=None) -> None:
213+
path = getattr(self, "path", "<invalid>")
213214
self.logger.debug(
214-
markup.escape(f"{self.command} {self.path} {code}"), is_server=True
215+
markup.escape(f"{self.command} {path} {code}"), is_server=True
215216
)
216217
if not hasattr(self, "_headers_buffer"):
217218
self._headers_buffer = []
@@ -421,6 +422,9 @@ def server_bind(self) -> None:
421422
return super().server_bind()
422423

423424
def finish_request(self, request, client_address) -> None:
424-
self.RequestHandlerClass(
425-
self.config, self.server_config, request, client_address, self
426-
)
425+
try:
426+
self.RequestHandlerClass(
427+
self.config, self.server_config, request, client_address, self
428+
)
429+
except ConnectionError:
430+
pass

dementor/protocols/ldap.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from dementor.config.toml import TomlConfig, Attribute as A
4040
from dementor.config.session import SessionConfig
4141
from dementor.config.util import get_value
42+
from dementor.log import hexdump
4243
from dementor.log.logger import ProtocolLogger
4344
from dementor.servers import (
4445
ThreadingTCPServer,
@@ -245,7 +246,7 @@ def handle_sasl_GSS_SPNEGO(self, message, bind_auth):
245246
# we expect this to be a NTLM message
246247
data = bytes(bind_auth["credentials"])
247248
if not data.startswith(b"NTLMSSP"):
248-
self.logger.debug(f"Unsupported SASL mechanism: {data.hex()}")
249+
self.logger.debug(f"Unsupported SASL mechanism:\n{hexdump.hexdump(data)}")
249250
return False
250251

251252
# negotiate message will have message type 0x01
@@ -346,7 +347,10 @@ def recv(self, size: int) -> LDAPMessage | None:
346347
try:
347348
message, _ = BERDecoder.decode(data, asn1Spec=LDAPMessage())
348349
except Exception as e:
349-
self.logger.error(f"Failed to decode LDAP packet. Original data (hex): {data.hex()}")
350+
self.logger.fail("Received invalid LDAP - terminating connection...")
351+
self.logger.debug(
352+
f"Invalid LDAP packet: {str(e.__class__)}\n{hexdump.hexdump(data)}"
353+
)
350354
return
351355

352356
return message

dementor/protocols/msrpc/rpc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def handle_data(self, data, transport) -> None:
166166
try:
167167
header = rpcrt.MSRPCHeader(data)
168168
except struct.error:
169-
self.logger.error(f"Could not parse MSRPC header. Received packet: {data.hex()}")
169+
self.logger.fail("Data is not a valid MSRPC header, closing connection")
170170
return
171171

172172
match header["type"]:

0 commit comments

Comments
 (0)