Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
538 commits
Select commit Hold shift + click to select a range
09342c1
Merge pull request #1375 from Pennyw0rth/neff-add-agents.md
NeffIsBack Aug 24, 2026
8a7edb5
Merge branch 'main' into fix/ldap-groups-memberof-fallback
NeffIsBack Aug 24, 2026
b7ba0f9
Adjust query such that we don't query all objects and don't fail on e…
NeffIsBack Aug 24, 2026
4a2c8d4
Make output logic clear and add dn fallback
NeffIsBack Aug 24, 2026
473e333
update args pvk
Aug 25, 2026
cb7d22c
modify output_file and counter management
Aug 25, 2026
c5ab84a
added lsa quiet mode
Aug 25, 2026
1d06afc
only rely on kdcHost for backupkey recovery
Aug 25, 2026
8e4fc22
move it closer to dpapi stuff, easier to read
Aug 25, 2026
01536dc
ruff
Aug 25, 2026
92e61ef
Fix the open file to append instead of overwrite
Aug 25, 2026
0de1fc8
change function name to log_secret to make it clearer
Aug 25, 2026
a5fe3ba
fix(vnc): bound the connection so an unresponsive server cannot hang …
Marshall-Hallenbeck Aug 25, 2026
c013e02
Add --cat argument for nfs
NeffIsBack Aug 25, 2026
e9c2c66
Merge pull request #1377 from Pennyw0rth/neff-add-nfs-cat
NeffIsBack Aug 25, 2026
6f7e0a8
Restrict dploot to v3
NeffIsBack Aug 25, 2026
fc970ca
Merge pull request #1378 from Pennyw0rth/neff-fix-dploot-version
NeffIsBack Aug 25, 2026
fd9ebe9
Merge pull request #1376 from Pennyw0rth/fix/vnc-connection-timeout
Marshall-Hallenbeck Aug 25, 2026
a819614
add get_file
zblurx Aug 26, 2026
4711814
add sam and lsa dump
zblurx Aug 26, 2026
ab4a44c
update poetry lock
zblurx Aug 26, 2026
b6f20e4
ruff
zblurx Aug 26, 2026
ee9f59e
refacto namespace loading
zblurx Aug 27, 2026
3c52ecb
create wmi RemoteOperation class
zblurx Aug 27, 2026
65f4714
add ntds dump
zblurx Aug 27, 2026
48cc6b0
added --use-snapshot-id arg
zblurx Aug 27, 2026
34b7a35
ruff
zblurx Aug 27, 2026
78d607b
fix issue with default shadow_id value
zblurx Aug 27, 2026
0d2af8b
added display message when dump LSA
zblurx Aug 28, 2026
b114c64
showing machine keys if collected
zblurx Aug 28, 2026
2bdc7e5
Merge pull request #1362 from amikuss/fix/ldap-groups-memberof-fallback
NeffIsBack Aug 30, 2026
ff3d493
Merge branch 'main' into dpapi_upgrade
NeffIsBack Aug 30, 2026
a9b8cf8
add highlight in log_secret and set output_file in init
zblurx Aug 31, 2026
7308add
Merge branch 'dpapi_upgrade' of github.com:Pennyw0rth/NetExec into dp…
zblurx Aug 31, 2026
42fe6b4
remove print
zblurx Aug 31, 2026
6ee4802
fix dploot_connection upgrade debug log message
zblurx Aug 31, 2026
961f4e5
enhance use of credentials in db
zblurx Aug 31, 2026
4bf5abd
ruff
zblurx Aug 31, 2026
9817aad
Add --exclude-hosts and --skip-self overwriting nxc.conf file
Aug 31, 2026
cb351cf
Add support for IPv6
Aug 31, 2026
db807bb
Merge branch 'main' into add_exclude_options
Dfte Aug 31, 2026
9e94f58
simplify the logic and fix regression
mpgn Aug 31, 2026
978270a
Add default value to --trust-keys argument
mpgn Aug 31, 2026
5f53ae9
Formatting
NeffIsBack Sep 1, 2026
6dc9e9d
Make IP exclusion list additive instead of overwriting itself
NeffIsBack Sep 1, 2026
a7e55ff
Formatting
NeffIsBack Sep 1, 2026
fc30724
Add IPv6 self exclusion
NeffIsBack Sep 1, 2026
c0536cc
Add comment back
NeffIsBack Sep 1, 2026
336738b
Move target parsing to helper functions
NeffIsBack Sep 1, 2026
ac84b7d
Formatting
NeffIsBack Sep 1, 2026
2fbbd24
Add netifaces to lock files
NeffIsBack Sep 1, 2026
883921d
Ensure that excluded hosts are valid IPs
NeffIsBack Sep 1, 2026
5800ec5
Add prompt if exclusion exists but target is hostname
NeffIsBack Sep 1, 2026
ee0e488
Merge pull request #1368 from Pennyw0rth/neff-add-shadow-creds
NeffIsBack Sep 2, 2026
28e1ad1
Fix handling of empty SMB share folder
x-way May 7, 2026
2e96341
ssh: distinguish 'key type rejected by server' from invalid passphras…
Diogo-Damasceno Sep 3, 2026
2fc0465
Merge branch 'main' into dpapi_upgrade
zblurx Sep 3, 2026
ce33ac0
Fix #1385: derive nxcdb hosts CSV header from the protocol's HostsTable
Diogo-Damasceno Sep 4, 2026
59d29c8
Fix #1349: skip non-SearchResultEntry objects in LDAP --query
Diogo-Damasceno Sep 4, 2026
26e2007
rdp module: reuse the wmi protocol connection instead of a second DCO…
XiaoliChan Sep 5, 2026
eee763c
bitlocker module: reuse the wmi protocol connection instead of a seco…
XiaoliChan Sep 5, 2026
7f5f869
Merge pull request #1204 from Dfte/add_exclude_options
NeffIsBack Sep 6, 2026
766c221
Trim down on info logs so we can see only valid hosts
NeffIsBack Sep 6, 2026
c988463
Provide information about excluded hosts more verbosly
NeffIsBack Sep 6, 2026
f1e6b38
Add clear command to shadow-creds module
NeffIsBack Sep 6, 2026
e969f23
Merge pull request #1403 from Pennyw0rth/neff-add-clean
NeffIsBack Sep 6, 2026
d640fb7
Merge pull request #1233 from x-way/fix-empty-dir-spider
NeffIsBack Sep 6, 2026
4ae0dd7
Fix spelling mistakes across multiple files
Sep 9, 2026
ccef3a7
Merge pull request #1408 from r4vanan/main
NeffIsBack Sep 9, 2026
5e95c34
revert impacket dep to the original repo
zblurx Sep 9, 2026
887941f
Merge branch 'main' into wmi_dump
zblurx Sep 9, 2026
0de205c
Fix lock files
NeffIsBack Sep 9, 2026
4671234
Formatting
NeffIsBack Sep 9, 2026
7fd40a3
Formatting
NeffIsBack Sep 9, 2026
b328661
Formatting
NeffIsBack Sep 9, 2026
0cc365c
Formatting
NeffIsBack Sep 9, 2026
8230e9a
fix from update of impacket
mpgn Sep 9, 2026
98d0549
Respect history for TDO trust keys
Goultarde Sep 9, 2026
6699b40
Merge branch 'main' into feature/trust-keys
mpgn Sep 9, 2026
8073a79
update lock files
mpgn Sep 9, 2026
444cea8
Merge pull request #1190 from Pennyw0rth/feat/smb-get-folder
Marshall-Hallenbeck Sep 9, 2026
90efb2a
Merge pull request #1321 from Goultarde/feature/trust-keys
mpgn Sep 9, 2026
c612a13
Change CRLF to LF
NeffIsBack Sep 9, 2026
5d9ea3a
Merge pull request #1410 from Pennyw0rth/neff-fix-encoding
NeffIsBack Sep 9, 2026
4026155
Merge branch 'main' into wmi-reuse-fix
NeffIsBack Sep 9, 2026
c72f2ca
Merge pull request #1390 from XiaoliChan/wmi-reuse-fix
NeffIsBack Sep 9, 2026
ff987ee
Fix false positives on ldap signing and cbt checks when NTLM is disabled
Natasha-B Sep 10, 2026
dde1482
Fix conditional check for signing_required
mpgn Sep 11, 2026
e6deadf
Merge pull request #1413 from Natasha-B/fix-ldap-check
mpgn Sep 11, 2026
c3122a9
Add generic exception handler for ConnectionRefused
NeffIsBack Sep 11, 2026
6c29f3c
Fix error handling of impacket/rpcrt
NeffIsBack Sep 11, 2026
40abafc
Add some padding for user descriptions
NeffIsBack Sep 11, 2026
6b5f557
Remove useless share listing
NeffIsBack Sep 11, 2026
a7626dd
Improve ftp host banner and session closing
NeffIsBack Sep 11, 2026
5823cdb
Add ftp --cat command
NeffIsBack Sep 11, 2026
32469fe
Fix bloodhound collection if signing and cbt is enforced
NeffIsBack Sep 11, 2026
a922755
Fix crash if NTLM is unavailable
NeffIsBack Sep 11, 2026
333ec79
lock file hash
NeffIsBack Sep 11, 2026
44cc9f1
Ruff
NeffIsBack Sep 11, 2026
44267c8
Recover the description field of deleted objects
Fabrizzio53 Sep 11, 2026
317840f
Remove unused functions and imports
NeffIsBack Sep 12, 2026
9249bb3
Fix remoteng execution
NeffIsBack Sep 12, 2026
4e2c0a6
Rename 'context' to 'connection' so it is in line with modules
NeffIsBack Sep 12, 2026
164f35d
Catch Exception if software is not installed
NeffIsBack Sep 12, 2026
e5b7a5f
Merge branch 'main' into dpapi_upgrade
NeffIsBack Sep 12, 2026
f1078b6
Use dploot pypi version
NeffIsBack Sep 12, 2026
490d739
Formatting
NeffIsBack Sep 13, 2026
ee3dd5e
Formatting
NeffIsBack Sep 13, 2026
a601726
Fixing wrongfully string decoding
NeffIsBack Sep 13, 2026
d3c3ca9
Merge pull request #1259 from Dfte/refactor_list_database
NeffIsBack Sep 13, 2026
0b3f805
Change negative expression logging message
NeffIsBack Sep 13, 2026
aecf64d
uv lock update
mpgn Sep 13, 2026
7bd7788
Merge pull request #1327 from Pennyw0rth/dpapi_upgrade
mpgn Sep 13, 2026
db63325
Merge branch 'main' into add_hash_dump_mssql
NeffIsBack Sep 13, 2026
d5e0478
Formatting
NeffIsBack Sep 13, 2026
8525495
Merge branch 'main' into add_mssql_database_listing
NeffIsBack Sep 13, 2026
310979a
Merge branch 'main' into neff-fixes
NeffIsBack Sep 13, 2026
81e53ec
Merge pull request #1419 from Pennyw0rth/neff-fixes
NeffIsBack Sep 13, 2026
3746e0d
Catch LDAP connection errors while checking for signing
rtpt-romankarwacik Sep 14, 2026
d4cdc6c
Merge pull request #1421 from rtpt-romankarwacik/ldap_signing_except
NeffIsBack Sep 14, 2026
d542b20
fix logging origin in dpapi related modules
zblurx Sep 14, 2026
5b83b43
Merge branch 'main' into wmi_dump
zblurx Sep 14, 2026
e193097
adapt LSA dump to dpapi upgrade
zblurx Sep 14, 2026
4fd0531
check filesize and warn user if too big
zblurx Sep 14, 2026
47af3a0
Formatting
NeffIsBack Sep 15, 2026
df9c322
Update lock files
NeffIsBack Sep 15, 2026
a43afce
Merge pull request #1423 from Pennyw0rth/dpapi_logging_fix
mpgn Sep 15, 2026
11d9151
Revert "[DPAPI] Fix logging origin in dpapi related modules"
mpgn Sep 15, 2026
85aa348
Merge pull request #1426 from Pennyw0rth/revert-1423-dpapi_logging_fix
mpgn Sep 15, 2026
5f5aa26
Revert "Revert "[DPAPI] Fix logging origin in dpapi related modules""
mpgn Sep 15, 2026
615a501
uv lock update with newer correct time
mpgn Sep 15, 2026
22840bf
Merge pull request #1427 from Pennyw0rth/dpapi_logging_fix_v2
mpgn Sep 15, 2026
60b3a5d
SSH: catch TimeoutError during login with a clean fail message
MRX-72 Sep 18, 2026
4f6ef43
edit e2e_commands
termanix Sep 18, 2026
3299d94
misleading naming
termanix Sep 18, 2026
99c65e8
Add NTLM Auth for getKerberosTGT
termanix Sep 18, 2026
ff52856
Add NT hash usage on example line
termanix Sep 18, 2026
efb0f30
Comment out shadow-cred and resetnightmare
termanix Sep 19, 2026
325cd1c
removed sys library, sys.exit swapped to return false
termanix Sep 19, 2026
9eb36e4
Merge pull request #1428 from MRX-72/fix-ssh-timeout-error
NeffIsBack Sep 19, 2026
57c5af5
edit e2e
termanix Sep 19, 2026
03ae02e
empty strings fallback to first defined line from getKerberosTGT
termanix Sep 19, 2026
f0e0cc6
Merge branch 'main' into patch-1
termanix Sep 19, 2026
76486f1
removed double strings from getKerberosTGT
termanix Sep 19, 2026
475a841
Merge branch 'patch-1' of https://github.com/termanix/NetExec into pa…
termanix Sep 19, 2026
a0a37b7
Merge pull request #1429 from termanix/patch-1
NeffIsBack Sep 19, 2026
3808655
Add big timeout for backup-operator module for larger hives
NeffIsBack Sep 20, 2026
b4ce916
Merge branch 'main' into fix/sanitize-ntlm-hostname
NeffIsBack Sep 20, 2026
e63a43c
Improve DNS check to be compliant with referenced RFCs
NeffIsBack Sep 20, 2026
818985b
Rename sanitizing function
NeffIsBack Sep 20, 2026
12ebe0d
Merge branch 'main' into main
NeffIsBack Sep 20, 2026
3a7bf59
Merge pull request #1433 from Pennyw0rth/neff-fix-backup-operator
NeffIsBack Sep 20, 2026
af92759
Merge pull request #1257 from Dfte/add_hash_dump_mssql
NeffIsBack Sep 22, 2026
f55edf3
support big files download
zblurx Sep 22, 2026
1920b3b
Sort imports
NeffIsBack Sep 22, 2026
421bd67
Remove unnecessary init
NeffIsBack Sep 22, 2026
5532240
Simplify module args
NeffIsBack Sep 22, 2026
3e0cc70
Simplify action selection
NeffIsBack Sep 22, 2026
9bfa036
Remove unused import
NeffIsBack Sep 22, 2026
fc3fb87
First review pass simplifying logic
NeffIsBack Sep 22, 2026
f1726b0
Simplify logic
NeffIsBack Sep 22, 2026
a8dec3c
Merge branch 'main' into add_mssql_database_listing
NeffIsBack Sep 22, 2026
fdb60ae
Merge pull request #1258 from Dfte/add_mssql_database_listing
NeffIsBack Sep 22, 2026
e3d8682
Improve module description
NeffIsBack Sep 22, 2026
b9535e6
Formatting
NeffIsBack Sep 22, 2026
26568e4
Reach legacy RDP servers by allowing weak TLS ciphers
azoxlpf Sep 22, 2026
2f1c3e0
Added live boot key extraction to backup_operator
abdo210xl Sep 22, 2026
643c937
Merge pull request #1440 from azoxlpf/fix/rdp-legacy-tls-864
NeffIsBack Sep 22, 2026
ef3d4c8
Update aardwolf dependency version
mpgn Sep 22, 2026
27a3a3a
Merge pull request #1442 from Pennyw0rth/mpgn-patch-1
mpgn Sep 22, 2026
cd956dd
ruff
zblurx Sep 23, 2026
72abb07
Merge pull request #1441 from abdo210xl/backup-operator-bootkey
mpgn Sep 23, 2026
c8394ad
Add patch by @azoxlpf fixing SSH error detection if pass auth is disa…
NeffIsBack Sep 23, 2026
fb0fbc1
Merge branch 'main' into fix/ssh-key-type-rejected
NeffIsBack Sep 23, 2026
ed9fb8b
Formatting
NeffIsBack Sep 23, 2026
18a2bfa
Remove unused import
NeffIsBack Sep 23, 2026
1b3f75b
Add patch by @azoxlpf fixing nxcdb export headers
NeffIsBack Sep 23, 2026
bd87cc2
Remove tests file
NeffIsBack Sep 23, 2026
83df61b
Merge pull request #1384 from Diogo-Damasceno/fix/ssh-key-type-rejected
NeffIsBack Sep 23, 2026
4134e68
Merge pull request #1387 from Diogo-Damasceno/fix/nxcdb-host-csv-header
NeffIsBack Sep 23, 2026
c375b3b
Add DNS sanitization function
NeffIsBack Sep 23, 2026
a995133
Add path sanitizer
NeffIsBack Sep 23, 2026
23be541
Add comments
NeffIsBack Sep 23, 2026
c812041
Apply path sanitization
NeffIsBack Sep 23, 2026
88c4963
Sanitize manual NTLM challenge parsing
NeffIsBack Sep 24, 2026
25076be
Add tests
NeffIsBack Sep 24, 2026
736ca36
Add AGENTS.md instructions
NeffIsBack Sep 24, 2026
150f323
Add DNS sanitization
NeffIsBack Sep 24, 2026
5e31253
Sanitize SQL statements
NeffIsBack Sep 24, 2026
31ca909
Add tests
NeffIsBack Sep 24, 2026
654f3c1
Sanitize SMB input
NeffIsBack Sep 24, 2026
bde3705
Sanitize SMB download folder
NeffIsBack Sep 24, 2026
87d34ad
Preserve remote filename while sanitizing local file name
NeffIsBack Sep 24, 2026
a47d07e
Preserve remote filename while sanitizing local file name
NeffIsBack Sep 24, 2026
641f99d
Merge pull request #736 from Fabrizzio53/main
NeffIsBack Sep 25, 2026
752c43f
Delete tests
NeffIsBack Sep 27, 2026
ba64b23
Add patch by @azoxlpf fixing --query when receiving non-searchresult …
NeffIsBack Sep 27, 2026
74c93d1
Merge pull request #1388 from Diogo-Damasceno/fix/ldap-query-non-entr…
NeffIsBack Sep 27, 2026
4187fa7
Prevent sql injection
NeffIsBack Sep 27, 2026
91b0964
Prevent path traversal attacks
NeffIsBack Sep 27, 2026
910415c
Losen DNS sanitization to allow weird windows dns names
NeffIsBack Sep 27, 2026
13d00e4
Losen filename restrictions to allow curly braces and fix tests
NeffIsBack Sep 27, 2026
d8bc2f7
Remove bullshit tests
NeffIsBack Sep 27, 2026
e056c5a
Enforce AI usage rules, also trim down a bit
NeffIsBack Sep 27, 2026
226a2f4
Merge pull request #1446 from Pennyw0rth/neff-improve-PR-template
NeffIsBack Sep 27, 2026
bb7afd4
Fix lock file
NeffIsBack Sep 27, 2026
6555186
Fix new task format
NeffIsBack Sep 27, 2026
71c36e9
Fix new task format for task killing
NeffIsBack Sep 27, 2026
4702580
Merge pull request #1447 from Pennyw0rth/neff-fix-tasklist-kill
NeffIsBack Sep 27, 2026
61bec86
change logic in wmi read_file
zblurx Sep 30, 2026
641ba4d
ruff
zblurx Sep 30, 2026
59bfc04
Merge branch 'main' into wmi_dump
zblurx Sep 30, 2026
b37469c
feat: switch VSS method to remoteSSWMI
Sep 30, 2026
3e0e37e
Merge pull request #1444 from Pennyw0rth/fix-path-traversals
NeffIsBack Sep 30, 2026
07aef24
fix error with shadow copy filesize
zblurx Oct 1, 2026
064374c
Merge branch 'wmi_dump' of github.com:Pennyw0rth/NetExec into wmi_dump
zblurx Oct 1, 2026
d688285
Merge branch 'main' into main
NeffIsBack Oct 1, 2026
ea761c1
Sanitize filename
NeffIsBack Oct 2, 2026
7a199c2
Formatting
NeffIsBack Oct 2, 2026
8adf9cb
Merge pull request #1448 from snowpeacock/main
NeffIsBack Oct 2, 2026
0ded751
Wording
NeffIsBack Oct 2, 2026
9592f69
Remove py3.10
NeffIsBack Oct 2, 2026
8f2fa96
Ruff fixes
NeffIsBack Oct 2, 2026
535f26d
Update lock files
NeffIsBack Oct 2, 2026
609f03a
Remove py3.10
NeffIsBack Oct 2, 2026
9404844
Merge pull request #1450 from Pennyw0rth/neff-remove-py3.10
NeffIsBack Oct 2, 2026
7135d89
fix rclone padding and library
BepuSun Oct 4, 2026
ec6d41f
fix rclone padding and library
BepuSun Oct 4, 2026
0c21bf9
Ketp AES.block_size in rclone.py
BepuSun Oct 4, 2026
306bf29
Merge pull request #1453 from BepuSun/fix_rclone
NeffIsBack Oct 4, 2026
f0c40cd
fix raisechild module
mpgn Oct 10, 2026
f8b53ae
update lock files
mpgn Oct 10, 2026
43c569a
update lock files
mpgn Oct 10, 2026
c666586
Merge pull request #1461 from Pennyw0rth/raisechild
mpgn Oct 11, 2026
9ec65be
Merge pull request #1379 from Pennyw0rth/wmi_dump
mpgn Oct 11, 2026
90f2d9c
Merge branch 'main' into fix/rdp-pwned-gui-access
mpgn Oct 11, 2026
f5d2c88
bump aardwolf
mpgn Oct 11, 2026
93afe3e
fix ruff
mpgn Oct 11, 2026
e9fd573
Merge pull request #1348 from azoxlpf/fix/rdp-pwned-gui-access
mpgn Oct 11, 2026
ca96761
Merge remote-tracking branch 'upstream/main' into rdp-exec-reliabilit…
mpgn Oct 11, 2026
1d8296c
fix ruff
mpgn Oct 11, 2026
893ff6f
fix(rdp): non-NLA fallback for auth_only exec check + builtin Timeout…
mpgn Oct 11, 2026
a419bd5
Merge pull request #1329 from Adamkadaban/rdp-exec-reliability-final
mpgn Oct 11, 2026
f7835c3
Add simple visual separater
NeffIsBack Oct 11, 2026
28e5851
Merge pull request #1462 from Pennyw0rth/neff-improve-mssql-output
NeffIsBack Oct 11, 2026
0602ef4
deps: replace certipy-ad git fork with certipy-nxc from PyPI
mpgn Oct 14, 2026
6c312cd
deps: declare certipy-nxc exclude-newer override in pyproject so CI k…
mpgn Oct 14, 2026
89a698d
Merge pull request #1468 from Pennyw0rth/certipy-nxc
mpgn Oct 14, 2026
9ad4964
Sync with upstream Pennyw0rth/NetExec (536 commits)
aconite33 Oct 15, 2026
53007f3
fix: resolve ruff lint errors from merge
aconite33 Oct 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@
Please include a summary of the change and which issue is fixed, or what the enhancement does.
List any dependencies that are required for this change.

If you have used AI in any form, please state the tool you used (e.g. Claude Code, Cursor, Amp) along with the extent that the work was AI-assisted. See the project's AI policy for more details: https://github.com/Pennyw0rth/NetExec/blob/main/AI_POLICY.md
### AI Assistance
If you have used AI in any form, please state the tool you used (e.g. Claude Code, Cursor, Amp) along with the extent that the work was AI-assisted.

Note that if you have used AI, you:
- MUST test the changes against a live system
- MUST include a screenshot of such a test (e.g. with a before and after screenshot when submitting a bug fix)
- MUST fill out this PR template by hand, see the project's AI policy: [AI_POLICY.md](https://github.com/Pennyw0rth/NetExec/blob/main/AI_POLICY.md)

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

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
Expand All @@ -16,7 +22,8 @@ Insert an "x" inside the brackets for relevant items (do not delete options)
- [ ] 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
Please provide guidance on what setup is needed to test the introduced changes, such as your locally running machine Python version & OS, as well as the target(s) you tested against, including software versions.
Please provide guidance on what setup is needed to test the introduced changes, such as the target(s) you tested against or software versions.

In particular:
- Bug Fix: Please provide a short description on how to trigger the bug, to make the bug reproducable for the reviewer.
- Added Feature/Enhancement: Please specify what setup is needed in order to test the changes, such as:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macOS-latest, macos-26-intel, windows-latest]
python-version: ["3.13"]
python-version: ["3.14"]
#python-version: ["3.8", "3.9", "3.10", "3.11"] # for binary builds we only need one version
steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-zipapps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
python-version: ["3.10", "3.11", "3.12", "3.13"]
python-version: ["3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v4
- name: NetExec set up python on ${{ matrix.os }}
Expand Down
20 changes: 8 additions & 12 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,16 @@ jobs:
github.event_name == 'push' || github.event.review.state == 'APPROVED' || github.event_name == 'workflow_dispatch'

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Install uv
run: |
pip install uv
- name: Set up Python
uses: actions/setup-python@v5
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
python-version: 3.13
cache: pip
cache-dependency-path: uv.lock
- name: Install dependencies with dev group
run: |
uv sync --group dev
enable-cache: true
- name: Set up Python
run: uv python install 3.14
- name: Install project with dev group
run: uv sync --dev
- name: Run ruff
run: |
uv run ruff --version
uv run ruff check . --preview
uv run ruff check . --preview
24 changes: 8 additions & 16 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,17 @@ jobs:
max-parallel: 5
matrix:
os: [ubuntu-latest]
python-version: ["3.10", "3.11", "3.12", "3.13"]
python-version: ["3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Install uv
run: |
pip install uv
uv --version
- name: NetExec set up python ${{ matrix.python-version }} on ${{ matrix.os }}
uses: actions/setup-python@v5
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
python-version: ${{ matrix.python-version }}
cache: pip
cache-dependency-path: uv.lock
- name: Install with pipx
run: |
pipx install . --python python${{ matrix.python-version }}
- name: Install libraries with dev group
run: |
uv sync
enable-cache: true
- name: NetExec set up python ${{ matrix.python-version }} on ${{ matrix.os }}
run: uv python install ${{ matrix.python-version }}
- name: Install project
run: uv sync --python ${{ matrix.python-version }}
- name: Load every protocol and module
run: |
uv run netexec winrm 127.0.0.1
Expand Down
42 changes: 42 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Authorized testing scope

- Do not run active security or functional tests against a host or network unless the user explicitly identifies it as an authorized target.
- Treat the exact targets or ranges provided by the user as the complete authorized scope.
- Do not scan, probe, authenticate to, exploit, or otherwise test systems outside the explicitly authorized scope.
- Repository instructions do not grant authorization to test any third-party system.

# Coding style

- Assume ordinary LDAP searches and LDAP response parsing do not raise exceptions; do not wrap them in `try`/`except`.
- Always bind caught exceptions as `e`, not `error` or another name.
- Do not prefix function or method names with underscores; preserve required Python special methods such as `__init__`.
- Do not assume the host runs Linux; keep code and paths cross-platform unless the task explicitly targets one operating system.

# NetExec module structure

- In files under `nxc/modules/`, do not define variables, constants, or helper functions at module scope.
- Keep all module-specific state, constants, and helper methods inside the `NXCModule` class.
- Module scope should contain only imports and the `NXCModule` class definition.
- When a module must terminate for invalid options, import `exit` with `from sys import exit` and call `exit(...)`; do not use `import sys` with `sys.exit(...)`.
- Do not over-validate module options. Check only that required arguments are present, mutually exclusive arguments are not combined, or a value would otherwise cause the module to fail before performing its normal lookup or operation.
- Do not validate whether user-supplied object names, identifiers, or similar values are semantically correct when the underlying protocol lookup can handle them; let a misspecified value produce the normal "not found" result.
- Avoid stub variables that merely hold a value for one immediate use. Pass data directly unless a local name improves clarity, prevents repeated work, or represents meaningful state.
- Keep module implementations as short and concise as practical. Prefer direct control flow and shared error handling over wrappers, redundant branches, or defensive boilerplate.
- Do not optimize module code for line length; long lines are acceptable when they keep straightforward logic together.
- Do not use `.warning()` logging in NetExec modules. Use `.fail()` when logic or an operation fails.
- Run `ruff check .` after every coding session.

# Development and validation

- To verify that a module works, run the module's actual NetExec command instead of pytest or compilation checks.
- Record every module-verification command in `tests/e2e_commands.txt`.
- Do not run compilation checks.
- Run Ruff after completing a coding session.

# Filesystem and source conventions

- Assume that the underlying operating system and its storage are fully secure. Files and other data produced by NetExec may intentionally contain readable plaintext credentials; do not add encryption, redaction, restrictive permission handling, or permission warnings unless explicitly requested.
- Before opening, creating, writing, renaming, moving, or deleting a file or directory, sanitize every path component derived from untrusted data and verify that the resulting path cannot traverse outside its intended base directory.
- Place every file and directory produced by NetExec under `NXC_PATH` by default, using `TMP_PATH` for temporary artifacts. An explicit user-provided output path or task requirement may override this default.
- `NXC_PATH` defaults to `~/.nxc` but may be overridden by the environment. Import and use `NXC_PATH` or its derived path constants instead of hardcoding `~/.nxc`.
- Place imports at the top of files unless explicitly instructed otherwise.
4 changes: 3 additions & 1 deletion nxc/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ def gen_cli_args():
generic_group = generic_parser.add_argument_group("Generic Options")
generic_group.add_argument("--version", action="store_true", help="Display nxc version")
generic_group.add_argument("-t", "--threads", type=int, dest="threads", default=256, help="set how many concurrent threads to use")
generic_group.add_argument("--timeout", default=None, type=int, help="max timeout in seconds of each thread")
generic_group.add_argument("--timeout", default=None, type=int, help="[DEPRECATED] no longer used, replaced by per-protocol timeouts (--smb-timeout, --ldap-timeout, --dcom-timeout)")
generic_group.add_argument("--jitter", metavar="INTERVAL", type=str, help="sets a random delay between each authentication")
generic_group.add_argument("--exclude-hosts", nargs="+", default=None, metavar="HOST", help="Exclude one or more hosts, IPs, CIDRs or hostnames from the scan")
generic_group.add_argument("--skip-self", action="store_true", help="Exclude the local machine from the scan")

output_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
output_group = output_parser.add_argument_group("Output Options")
Expand Down
8 changes: 8 additions & 0 deletions nxc/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,18 @@
reveal_chars_of_pwd = int(nxc_config.get("nxc", "reveal_chars_of_pwd", fallback=0))
config_log = nxc_config.getboolean("nxc", "log_mode", fallback=False)
host_info_colors = literal_eval(nxc_config.get("nxc", "host_info_colors", fallback=["green", "red", "yellow", "cyan"]))
check_guest_account = nxc_config.getboolean("nxc", "check_guest_account", fallback=False)
display_dc = nxc_config.getboolean("nxc", "display_dc", fallback=True)
skip_self = nxc_config.getboolean("nxc", "skip_self", fallback=False)
exclude_hosts = literal_eval(nxc_config.get("nxc", "exclude_hosts", fallback=[]))
if not isinstance(exclude_hosts, list):
nxc_logger.error(f"exclude_hosts must be a 'list', got '{type(exclude_hosts).__name__}'. Exiting.")
exit(1)

# Read stealth_label from user config first, then default config
stealth_label = nxc_config.get("nxc", "stealth_label").strip("'\"") if nxc_config.has_option("nxc", "stealth_label") else nxc_default_config.get("nxc", "stealth_label", fallback="C$ Access!").strip("'\"")


if len(host_info_colors) != 4:
nxc_logger.error("Config option host_info_colors must have 4 values! Using default values.")
host_info_colors = nxc_default_config.get("nxc", "host_info_colors")
Expand Down
24 changes: 16 additions & 8 deletions nxc/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
from nxc.config import pwned_label
from nxc.config import stealth_label
from nxc.helpers.logger import highlight
from nxc.loaders.moduleloader import ModuleLoader
from nxc.helpers.path import sanitize_path_component
from nxc.loaders.moduleloader import ModuleLoader, ModuleOptionsError
from nxc.logger import nxc_logger, NXCAdapter
from nxc.context import Context
from nxc.paths import NXC_PATH
Expand Down Expand Up @@ -149,6 +150,7 @@ def __init__(self, args, db, target):
self.args.use_kcache or
self.args.aesKey or
(hasattr(self.args, "delegate") and self.args.delegate) or
(hasattr(self.args, "generate_st") and self.args.generate_st) or
(hasattr(self.args, "no_preauth_targets") and self.args.no_preauth_targets))
self.aesKey = None if not self.args.aesKey else self.args.aesKey[0]
self.use_kcache = None if not self.args.use_kcache else self.args.use_kcache
Expand All @@ -175,12 +177,14 @@ def __init__(self, args, db, target):
if self.kerberos:
self.host = self.hostname

self.logger.info(f"Socket info: host={self.host}, hostname={self.hostname}, kerberos={self.kerberos}, ipv6={self.is_ipv6}, link-local ipv6={self.is_link_local_ipv6}")
self.logger.debug(f"Socket info: host={self.host}, hostname={self.hostname}, kerberos={self.kerberos}, ipv6={self.is_ipv6}, link-local ipv6={self.is_link_local_ipv6}")

try:
self.proto_flow()
except FileNotFoundError as e:
self.logger.error(f"File not found error on target {target}: {e}")
except ConnectionRefusedError as e:
self.logger.error(f"Target {target} refused the connection: {e}")
except Exception as e:
if "ERROR_DEPENDENT_SERVICES_RUNNING" in str(e):
self.logger.error(f"Exception while calling proto_flow() on target {target}: {e}")
Expand Down Expand Up @@ -242,20 +246,20 @@ def proto_flow(self):
self.logger.debug("Kicking off proto_flow")
self.proto_logger()
if not self.create_conn_obj():
self.logger.info(f"Failed to create connection object for target {self.host}, exiting...")
self.logger.debug(f"Failed to create connection object for target {self.host}, exiting...")
else:
self.logger.debug("Created connection object")
self.logger.info(f"Successfully created connection object for target '{self.host}'")
self.enum_host_info()

# Construct the output file template using os.path.join for OS compatibility
base_log_dir = os.path.join(NXC_PATH, "logs")
filename_pattern = f"{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}".replace(":", "-")
filename_pattern = sanitize_path_component(f"{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}", max_bytes=220)
self.output_file_template = os.path.join(base_log_dir, "{output_folder}", filename_pattern)
# Default output filename for logs
self.output_filename = os.path.join(base_log_dir, filename_pattern)

self.print_host_info()
if self.login() or (self.username == "" and self.password == ""):
if self.login() or (self.username == "" and self.password == "" and self.protocol != "mssql"):
self.logger.debug("Calling command arguments")
self.call_cmd_args()
if self.args.module:
Expand Down Expand Up @@ -335,7 +339,7 @@ def over_fail_limit(self, username):
if self.failed_logins == self.args.fail_limit:
return True

if username in user_failed_logins and self.args.ufail_limit == user_failed_logins[username]: # noqa: SIM103
if username in user_failed_logins and self.args.ufail_limit == user_failed_logins[username]: # ruff: ignore[needless-bool]
return True

return False
Expand Down Expand Up @@ -605,5 +609,9 @@ def load_modules(self):
self.modules = []

for module_path in self.module_paths:
module = loader.init_module(module_path)
try:
module = loader.init_module(module_path)
except ModuleOptionsError as e:
self.logger.debug(f"Skipping module: {e}")
continue
self.modules.append(module)
4 changes: 4 additions & 0 deletions nxc/data/nxc.conf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ audit_mode =
reveal_chars_of_pwd = 0
log_mode = False
host_info_colors = ["green", "red", "yellow", "cyan"]
check_guest_account = False
display_dc = True
skip_self = False
exclude_hosts = []

[BloodHound]
bh_enabled = False
Expand Down
11 changes: 7 additions & 4 deletions nxc/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,10 @@ def clear_database(self):
self.db_execute(table.delete())

def db_execute(self, *args):
self.lock.acquire()
res = self.sess.execute(*args)
self.lock.release()
return res
with self.lock:
res = self.sess.execute(*args)
return res.freeze()() if res.returns_rows else res

def get_keys(self, key_id=None, cred_id=None):
"""Return an empty list by default. Protocols that support keys (e.g. SSH) override this."""
return []
10 changes: 5 additions & 5 deletions nxc/helpers/bloodhound.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def add_user_bh(user, domain, logger, config):
with driver.session().begin_transaction() as tx:
for user_info in users_owned:
distinguished_name = "".join([f"DC={dc}," for dc in user_info["domain"].split(".")]).rstrip(",")
domain_query = tx.run(f"MATCH (d:Domain) WHERE d.distinguishedname STARTS WITH '{distinguished_name}' RETURN d").data()
domain_query = tx.run("MATCH (d:Domain) WHERE d.distinguishedname STARTS WITH $distinguished_name RETURN d", distinguished_name=distinguished_name).data()
if not domain_query:
logger.debug(f"Domain {user_info['domain']} not found in BloodHound. Falling back to domainless query.")
_add_without_domain(user_info, tx, logger)
Expand All @@ -68,14 +68,14 @@ def _add_with_domain(user_info, domain, tx, logger):
user_owned = f"{user_info['username']}@{domain}"
account_type = "User"

result = tx.run(f"MATCH (c:{account_type} {{name:'{user_owned}'}}) RETURN c").data()
result = tx.run(f"MATCH (c:{account_type} {{name: $user_owned}}) RETURN c", user_owned=user_owned).data()

if len(result) == 0:
logger.fail("Account not found in the BloodHound database.")
return
if result[0]["c"].get("owned") in (False, None):
logger.debug(f"MATCH (c:{account_type} {{name:'{user_owned}'}}) SET c.owned=True RETURN c.name AS name")
result = tx.run(f"MATCH (c:{account_type} {{name:'{user_owned}'}}) SET c.owned=True RETURN c.name AS name").data()[0]
result = tx.run(f"MATCH (c:{account_type} {{name: $user_owned}}) SET c.owned=True RETURN c.name AS name", user_owned=user_owned).data()[0]
logger.highlight(f"Node {result['name']} successfully set as owned in BloodHound")


Expand All @@ -87,7 +87,7 @@ def _add_without_domain(user_info, tx, logger):
user_owned = user_info["username"]
account_type = "User"

result = tx.run(f"MATCH (c:{account_type}) WHERE c.name STARTS WITH '{user_owned}' RETURN c").data()
result = tx.run(f"MATCH (c:{account_type}) WHERE c.name STARTS WITH $user_owned RETURN c", user_owned=user_owned).data()

if len(result) == 0:
logger.fail("Account not found in the BloodHound database.")
Expand All @@ -97,5 +97,5 @@ def _add_without_domain(user_info, tx, logger):
return
elif result[0]["c"].get("owned") in (False, None):
logger.debug(f"MATCH (c:{account_type} {{name:'{result[0]['c']['name']}'}}) SET c.owned=True RETURN c.name AS name")
result = tx.run(f"MATCH (c:{account_type} {{name:'{result[0]['c']['name']}'}}) SET c.owned=True RETURN c.name AS name").data()[0]
result = tx.run(f"MATCH (c:{account_type} {{name: $user_owned}}) SET c.owned=True RETURN c.name AS name", user_owned=result[0]["c"]["name"]).data()[0]
logger.highlight(f"Node {result['name']} successfully set as owned in BloodHound")
Loading
Loading