Skip to content

Fix all kinds of security issues - #1444

Open
NeffIsBack wants to merge 23 commits into
mainfrom
fix-path-traversals
Open

NeffIsBack wants to merge 23 commits into
mainfrom
fix-path-traversals

Conversation

@NeffIsBack

@NeffIsBack NeffIsBack commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Description

Based on #1243 but got too much so here is a proper description of what happened.

This handles all kinds of security issues:

  • Sanitizing the source input on protocol level, focused on the input in enum_host_info() as first line of defense against poisoned data
  • Sanitizing sinks where we e.g. write to file or download with a hostname in the file name. This should prevent path traversals as second line of defense
  • Uses prepared statements in SQL stuff like accessing the neo4j database in bloodhound cause why not, better be safe
  • Should prevent path traversals when downloading folders
  • Adds an instruction to the AGENTS.md file so that future PRs hopefully respect sinks and sanitize file names

Codex with gpt 5.6-sol on ultra was used to fix the issues. Have manually reviewed (and fixed where AI was stupid) the code.

Type of change

Insert an "x" inside the brackets for relevant items (do not delete options)

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Deprecation of feature or functionality
  • This change requires a documentation update
  • This requires a third party update (such as Impacket, Dploot, lsassy, etc)
  • This PR was created with the assistance of AI (list what type of assistance, tool(s)/model(s) in the description)

Setup guide for the review

TEST EVERYTHING

Screenshots (if appropriate):

Example of basic path operations still working:
image
image

Checklist:

Insert an "x" inside the brackets for completed and relevant items (do not delete options)

  • I have ran Ruff against my changes (poetry: poetry run ruff check ., use --fix to automatically fix what it can)
  • I have added or updated the tests/e2e_commands.txt file if necessary (new modules or features are required to be added to the e2e tests)
  • If reliant on changes of third party dependencies, such as Impacket, dploot, lsassy, etc, I have linked the relevant PRs in those projects
  • I have linked relevant sources that describes the added technique (blog posts, documentation, etc)
  • I have performed a self-review of my own code (not an AI review)
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (PR here: https://github.com/Pennyw0rth/NetExec-Wiki)

TristanInSec and others added 21 commits May 16, 2026 16:17
Strip non-alphanumeric characters (except hyphens and dots) from
server-provided NTLM hostname before use in file paths or content.

Prevents:
- Path traversal via ../ in hostname (file creation outside ~/.nxc/logs/)
- DoS via null byte (ValueError crash in open())
- DoS via { characters (KeyError crash in str.format())
- Newline injection in --generate-hosts-file output
- Affects: SMB, RDP, VNC, WinRM, MSSQL credential dump and screenshot paths
Log a warning showing the original and sanitized hostname so users
are alerted to potential non-compliant implementations or rogue servers.
Move the hostname sanitization regex from an inline check in
connection.py into a reusable sanitize_hostname() function in
nxc/helpers/misc.py, and apply it at the source in each protocol's
enum_host_info where hostnames are received from NTLM/server data.

Protocols covered: SMB, WinRM, WMI, MSSQL, RDP.

Addresses review feedback on PR #1243.
@NeffIsBack NeffIsBack added the bug-fix This Pull Request fixes a bug label Sep 24, 2026

@Marshall-Hallenbeck Marshall-Hallenbeck left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't test it but the code looks good.

@NeffIsBack

Copy link
Copy Markdown
Member Author

Ironically the LLM didn't find and patch the security issue in spider_plus yet. Gonna fix that tomorrow and double check that everything we know of has been caught.

@mpgn

mpgn commented Sep 25, 2026

Copy link
Copy Markdown
Collaborator

are we all sure about all split(".", 1)[1] ?

@NeffIsBack

Copy link
Copy Markdown
Member Author

are we all sure about all split(".", 1)[1] ?

It's probably unnecessary since we are always taking the first element anyway (and I can revert it if you want), but I think it is supposed to prevent breaking the domain apart where it should remain one blob.

@mpgn

mpgn commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

One of the new tests in this PR currently fails: test_smb_hosts_file_sanitizes_at_sink_without_mutating_values.

The --generate-hosts-file sink in print_host_info() writes self.hostname / self.targetDomain directly, without sanitizing at the sink, so it relies entirely on source-level sanitization. The test feeds raw values and shows the newline injection is still possible at the sink — the single hosts line gets split into several:

$ uv run pytest "tests/test_sanitize.py::test_smb_hosts_file_sanitizes_at_sink_without_mutating_values" -vv

>       assert (tmp_path / "hosts").read_text().splitlines() == ["192.0.2.1     .._.._HOST_._evil__ .._.._HOST_"]
E       AssertionError: assert ['192.0.2.1     ../../HOST', '.[evil]', ' ../../HOST', ''] == ['192.0.2.1     .._.._HOST_._evil__ .._.._HOST_']
E         At index 0 diff: '192.0.2.1     ../../HOST' != '192.0.2.1     .._.._HOST_._evil__ .._.._HOST_'
E         Left contains 3 more items, first extra item: '.[evil]'
E         Full diff:
E         - ['192.0.2.1     .._.._HOST_._evil__ .._.._HOST_']
E         + ['192.0.2.1     ../../HOST', '.[evil]', ' ../../HOST', '']

FAILED tests/test_sanitize.py::test_smb_hosts_file_sanitizes_at_sink_without_mutating_values
1 failed in 1.10s

In the real flow the source is sanitized in enum_host_info(), so the generated hosts file is safe in practice — but the second (sink) layer of defense the PR advertises is missing for this specific sink (same applies to the --generate-krb5-file block, which writes self.domain raw). Sanitizing at the write site (e.g. sanitize_dns(self.hostname, ...) / sanitize_dns(self.targetDomain, ...) before building the line) would make this test pass and close the gap.

The other 73 tests in tests/test_sanitize.py pass, and ruff check is clean on the changed files.

(review by opus 4.8 ^^)

@NeffIsBack

NeffIsBack commented Sep 25, 2026 •

Copy link
Copy Markdown
Member Author

Yeah sol also really really wanted me to sanitize this part, but I refused so far because this is kind of the entire point of sanitizing the sources. The krb/host file sanitization added a LOT of really uggly code. I will recheck

@mpgn

mpgn commented Sep 25, 2026

Copy link
Copy Markdown
Collaborator

in this case this is just the test that fail, maybe remove it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug-fix This Pull Request fixes a bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants