authfinder 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ Copyright 2026 Khael
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: authfinder
3
+ Version: 1.0.0
4
+ Summary: Execute commands across Windows systems using multiple RCE methods (WinRM, SMB, WMI, RDP, SSH, MSSQL)
5
+ Author: Khael
6
+ Project-URL: Homepage, https://github.com/KhaelK138/authfinder
7
+ Project-URL: Repository, https://github.com/KhaelK138/authfinder
8
+ Project-URL: Issues, https://github.com/KhaelK138/authfinder/issues
9
+ Keywords: security,pentest,windows,remote-execution,winrm,smb,wmi,rdp
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Information Technology
12
+ Classifier: Intended Audience :: System Administrators
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Security
20
+ Classifier: Topic :: System :: Systems Administration
21
+ Requires-Python: >=3.8
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Provides-Extra: dev
25
+ Requires-Dist: build; extra == "dev"
26
+ Requires-Dist: twine; extra == "dev"
27
+ Dynamic: license-file
28
+
29
+ # AuthFinder
30
+
31
+ A tool for executing commands across multiple Windows systems using various remote execution methods. Automatically tries multiple techniques until one succeeds, based on return codes and output. Makes executing commands given credentials a hell of a lot easier.
32
+
33
+ Big thanks to NetExec, Impacket, and Evil-Winrm, as this tool just essentially acts as a wrapper around those (making it more of a script, I suppose).
34
+
35
+ ## Features
36
+
37
+ - **Multiple RCE Methods**: Automatically tries various Windows remote execution techniques:
38
+ - WinRM (HTTP/HTTPS)
39
+ - PSExec (Impacket)
40
+ - SMBExec (NetExec)
41
+ - WMI (NetExec)
42
+ - AtExec (Impacket)
43
+ - RDP (NetExec)
44
+ - SSH (NetExec)
45
+ - MSSQL (Impacket)
46
+ - **Multi-threaded**: Execute commands across multiple hosts simultaneously
47
+ - **Automatic Pass-the-Hash**: Just paste the NTLM hash as the credential
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ pipx install authfinder
53
+ ```
54
+
55
+ ### External Dependencies
56
+
57
+ This tool requires the following external tools to be installed:
58
+
59
+ ```bash
60
+ # Impacket (for PSExec, AtExec, MSSQL)
61
+ pipx install impacket
62
+
63
+ # NetExec (for SMBExec, WMI, RDP, SSH)
64
+ pipx install git+https://github.com/Pennyw0rth/NetExec
65
+
66
+ # Evil-WinRM (for WinRM)
67
+ gem install evil-winrm
68
+ ```
69
+
70
+ ## Usage
71
+
72
+ ### Basic Usage
73
+
74
+ ```bash
75
+ # Execute command on single host
76
+ authfinder 192.168.1.10 administrator Password123 whoami
77
+
78
+ # Execute across IP range of 192.168.1.1 to 192.168.1.50
79
+ authfinder 192.168.1.1-50 admin Pass123 "net user"
80
+
81
+ # Use hash instead of password
82
+ authfinder 10.0.0.1-10 admin :{32-bit-hash} whoami
83
+ ```
84
+
85
+ ### IP Range Format
86
+
87
+ Supports various formats:
88
+ - Single IP: `192.168.1.10`
89
+ - Range: `192.168.1.1-254`
90
+ - Multiple ranges: `10.0.1-5.10-20` (expands to all combinations)
91
+ - File with IP ranges: `targets.txt`
92
+
93
+ ### Credential File Format
94
+
95
+ Create a text file with alternating username/password lines:
96
+
97
+ ```
98
+ administrator
99
+ Password123!
100
+ admin
101
+ Pass123
102
+ backup_admin
103
+ :aad3b435b51404eeaad3b435b51404ee
104
+ ```
105
+
106
+ Lines starting with `#` are treated as comments. For NT hashes, use them directly as the password.
107
+
108
+ ## Command-Line Options
109
+
110
+ ```
111
+ Options:
112
+ -v Verbose output (shows all tool attempts)
113
+ -o Show successful command output (WARNING: may trigger AV)
114
+ -f <file> Use credential file instead of single username/password
115
+ --threads <n> Number of concurrent threads (default: 10)
116
+ --tools <list> Comma-separated list of tools to try in order
117
+ --timeout <seconds> Command timeout in seconds (default: 15)
118
+ --run-all Run all tools instead of stopping at first success
119
+ --skip-portscan Skip port scanning and attempt all tools
120
+ ```
121
+
122
+
123
+ ## Todo
124
+
125
+ Add kerberos support lol
126
+ - Requires supporting hostnames and configuring `/etc/krb5.conf` for tools like evil-winrm
127
+
128
+ ## License
129
+
130
+ MIT License - see LICENSE file for details
131
+
132
+ ## Disclaimer
133
+
134
+ This tool is intended for authorized security assessments only. Ensure you have proper authorization before using this tool on any systems you do not own or have explicit permission to test.
@@ -0,0 +1,106 @@
1
+ # AuthFinder
2
+
3
+ A tool for executing commands across multiple Windows systems using various remote execution methods. Automatically tries multiple techniques until one succeeds, based on return codes and output. Makes executing commands given credentials a hell of a lot easier.
4
+
5
+ Big thanks to NetExec, Impacket, and Evil-Winrm, as this tool just essentially acts as a wrapper around those (making it more of a script, I suppose).
6
+
7
+ ## Features
8
+
9
+ - **Multiple RCE Methods**: Automatically tries various Windows remote execution techniques:
10
+ - WinRM (HTTP/HTTPS)
11
+ - PSExec (Impacket)
12
+ - SMBExec (NetExec)
13
+ - WMI (NetExec)
14
+ - AtExec (Impacket)
15
+ - RDP (NetExec)
16
+ - SSH (NetExec)
17
+ - MSSQL (Impacket)
18
+ - **Multi-threaded**: Execute commands across multiple hosts simultaneously
19
+ - **Automatic Pass-the-Hash**: Just paste the NTLM hash as the credential
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ pipx install authfinder
25
+ ```
26
+
27
+ ### External Dependencies
28
+
29
+ This tool requires the following external tools to be installed:
30
+
31
+ ```bash
32
+ # Impacket (for PSExec, AtExec, MSSQL)
33
+ pipx install impacket
34
+
35
+ # NetExec (for SMBExec, WMI, RDP, SSH)
36
+ pipx install git+https://github.com/Pennyw0rth/NetExec
37
+
38
+ # Evil-WinRM (for WinRM)
39
+ gem install evil-winrm
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ ### Basic Usage
45
+
46
+ ```bash
47
+ # Execute command on single host
48
+ authfinder 192.168.1.10 administrator Password123 whoami
49
+
50
+ # Execute across IP range of 192.168.1.1 to 192.168.1.50
51
+ authfinder 192.168.1.1-50 admin Pass123 "net user"
52
+
53
+ # Use hash instead of password
54
+ authfinder 10.0.0.1-10 admin :{32-bit-hash} whoami
55
+ ```
56
+
57
+ ### IP Range Format
58
+
59
+ Supports various formats:
60
+ - Single IP: `192.168.1.10`
61
+ - Range: `192.168.1.1-254`
62
+ - Multiple ranges: `10.0.1-5.10-20` (expands to all combinations)
63
+ - File with IP ranges: `targets.txt`
64
+
65
+ ### Credential File Format
66
+
67
+ Create a text file with alternating username/password lines:
68
+
69
+ ```
70
+ administrator
71
+ Password123!
72
+ admin
73
+ Pass123
74
+ backup_admin
75
+ :aad3b435b51404eeaad3b435b51404ee
76
+ ```
77
+
78
+ Lines starting with `#` are treated as comments. For NT hashes, use them directly as the password.
79
+
80
+ ## Command-Line Options
81
+
82
+ ```
83
+ Options:
84
+ -v Verbose output (shows all tool attempts)
85
+ -o Show successful command output (WARNING: may trigger AV)
86
+ -f <file> Use credential file instead of single username/password
87
+ --threads <n> Number of concurrent threads (default: 10)
88
+ --tools <list> Comma-separated list of tools to try in order
89
+ --timeout <seconds> Command timeout in seconds (default: 15)
90
+ --run-all Run all tools instead of stopping at first success
91
+ --skip-portscan Skip port scanning and attempt all tools
92
+ ```
93
+
94
+
95
+ ## Todo
96
+
97
+ Add kerberos support lol
98
+ - Requires supporting hostnames and configuring `/etc/krb5.conf` for tools like evil-winrm
99
+
100
+ ## License
101
+
102
+ MIT License - see LICENSE file for details
103
+
104
+ ## Disclaimer
105
+
106
+ This tool is intended for authorized security assessments only. Ensure you have proper authorization before using this tool on any systems you do not own or have explicit permission to test.
@@ -0,0 +1,3 @@
1
+ """authfinder: Execute commands across Windows systems using multiple RCE methods"""
2
+
3
+ __version__ = "1.0.0"
@@ -0,0 +1,541 @@
1
+ #!/usr/bin/env python3
2
+ import subprocess
3
+ import os
4
+ import base64
5
+ import sys
6
+ import shlex
7
+ from concurrent.futures import ThreadPoolExecutor, as_completed
8
+ import threading
9
+ import argparse
10
+ import shutil
11
+ import socket
12
+
13
+ EXEC_TIMEOUT = 20
14
+ RDP_TIMEOUT = 45
15
+ MAX_THREADS = 10
16
+
17
+ VERBOSE = False
18
+ OUTPUT = False
19
+ RUN_ALL = False
20
+ SKIP_PORTSCAN = False
21
+ TOOLS_SPECIFIED = False
22
+
23
+ VALID_TOOLS = ["winrm", "smbexec", "wmi", "ssh", "mssql", "psexec", "atexec", "rdp"]
24
+ NXC_TOOLS = {"smbexec", "wmi", "ssh", "rdp"}
25
+
26
+ IMPACKET_PREFIX = "impacket-" # or "" for .py suffix
27
+ NXC_CMD = "nxc"
28
+ WINRM_CMD = "evil-winrm"
29
+
30
+ print_lock = threading.Lock()
31
+
32
+ def colorize(line):
33
+ line = line.replace("[-]", "\033[31m[-]\033[0m")
34
+ line = line.replace("[+]", "\033[32m[+]\033[0m")
35
+ return line
36
+
37
+ def vprint(msg):
38
+ if VERBOSE:
39
+ with print_lock:
40
+ print(colorize(msg))
41
+
42
+ def oprint(msg):
43
+ # don't want to duplicate output, so check if verbose is enabled
44
+ if OUTPUT and not VERBOSE:
45
+ with print_lock:
46
+ print(colorize(msg))
47
+
48
+ def safe_print(msg):
49
+ with print_lock:
50
+ print(colorize(msg))
51
+
52
+ def parse_ip_range(ip_range):
53
+ parts = ip_range.split('.')
54
+ if len(parts) != 4:
55
+ raise SystemExit("Invalid IP range format")
56
+
57
+ def expand(part):
58
+ vals = []
59
+ for section in part.split(','):
60
+ if '-' in section:
61
+ s, e = map(int, section.split('-'))
62
+ vals.extend(range(s, e + 1))
63
+ else:
64
+ vals.append(int(section))
65
+ return vals
66
+
67
+ expanded = [expand(p) for p in parts]
68
+ return [f"{a}.{b}.{c}.{d}"
69
+ for a in expanded[0]
70
+ for b in expanded[1]
71
+ for c in expanded[2]
72
+ for d in expanded[3]]
73
+
74
+ def is_nthash(credential):
75
+ cred = credential.lstrip(':').replace("'", "")
76
+ if len(cred) == 32:
77
+ try:
78
+ int(cred, 16)
79
+ return True
80
+ except ValueError:
81
+ return False
82
+ return False
83
+
84
+
85
+ def load_credential_file(path):
86
+ """
87
+ Load credentials from file with newline-separated format:
88
+ <user1>
89
+ <user1_password>
90
+ <user2>
91
+ <user2_password>
92
+ ...
93
+
94
+ Blank lines and lines starting with # are ignored.
95
+ For hashes, use the hash directly as the password line.
96
+ """
97
+ try:
98
+ with open(path, "r", encoding="utf-8") as f:
99
+ lines = [line.rstrip("\n\r") for line in f]
100
+ except Exception as e:
101
+ print(f"Error: cannot read credential file '{path}': {e}")
102
+ sys.exit(1)
103
+ creds = []
104
+
105
+ filtered = []
106
+ for line in lines:
107
+ stripped = line.strip()
108
+ if stripped and not stripped.startswith("#"):
109
+ filtered.append(line)
110
+
111
+ if len(filtered) % 2 != 0:
112
+ raise SystemExit(f"Credential file has odd number of lines ({len(filtered)}). Expected pairs of user/password.")
113
+
114
+ for i in range(0, len(filtered), 2):
115
+ user = filtered[i].strip()
116
+ cred = filtered[i + 1]
117
+ creds.append((user, cred))
118
+
119
+ return creds
120
+
121
+ def normalize_tool_name(name):
122
+ """Normalize tool name aliases to canonical form."""
123
+ name = name.lower().strip()
124
+ if name in ("evilwinrm", "evil-winrm"):
125
+ return "winrm"
126
+ return name
127
+
128
+
129
+ def parse_tools_list(tools_str):
130
+ """Parse comma-separated list of tools, validating each one."""
131
+ tools = []
132
+ for t in tools_str.split(','):
133
+ normalized = normalize_tool_name(t)
134
+ if normalized not in VALID_TOOLS:
135
+ print(f"Error: Invalid tool '{t}'. Valid options: {', '.join(VALID_TOOLS)}")
136
+ sys.exit(1)
137
+ if normalized not in tools:
138
+ tools.append(normalized)
139
+ return tools
140
+
141
+ def check_port(ip, port, timeout=1):
142
+ """Check if a port is open on the given IP."""
143
+ try:
144
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
145
+ sock.settimeout(timeout)
146
+ result = sock.connect_ex((ip, port))
147
+ sock.close()
148
+ return result == 0
149
+ except:
150
+ return False
151
+
152
+ def scan_ports_for_tools(ip, tool_list):
153
+ """
154
+ Scan ports for given tools and return viable tools.
155
+ For winrm, checks port 5985 for winrm and 5986 for winrm-ssl.
156
+ Returns tuple of (viable_tools, open_ports)
157
+ """
158
+ viable_tools = []
159
+ open_ports = []
160
+
161
+ TOOL_PORTS = {"psexec": 445, "smbexec": 445, "atexec": 445, "wmi": 135, "rdp": 3389, "mssql": 1433, "ssh": 22, "winrm": 5985,"winrm-ssl": 5986}
162
+
163
+ tools_to_check = tool_list if tool_list else VALID_TOOLS
164
+
165
+ for tool in tools_to_check:
166
+ # Check both winrm ports
167
+ if tool == "winrm":
168
+ if check_port(ip, 5985):
169
+ viable_tools.append("winrm")
170
+ if 5985 not in open_ports:
171
+ open_ports.append(5985)
172
+ if check_port(ip, 5986):
173
+ viable_tools.append("winrm-ssl")
174
+ if 5986 not in open_ports:
175
+ open_ports.append(5986)
176
+ elif tool in TOOL_PORTS:
177
+ port = TOOL_PORTS[tool]
178
+ if check_port(ip, port):
179
+ viable_tools.append(tool)
180
+ if port not in open_ports:
181
+ open_ports.append(port)
182
+
183
+ return viable_tools, open_ports
184
+
185
+ def build_cmd(tool, user, target, credential, command):
186
+ b64 = base64.b64encode(command.encode("utf-16le")).decode()
187
+ use_hash = is_nthash(credential)
188
+ hash_val = credential.lstrip(':')
189
+
190
+ # For nxc tools, add --no-output unless -o was passed
191
+ nxc_output_flag = "" if OUTPUT else " --no-output"
192
+
193
+ # Impacket tools
194
+ if tool == "psexec":
195
+ cmd = impacket_cmd("psexec")
196
+ return (f"{cmd} -hashes :{hash_val} \"{user}\"@{target} 'powershell -enc {b64}'"
197
+ if use_hash else
198
+ f"{cmd} \"{user}\":{credential}@{target} 'powershell -enc {b64}'")
199
+
200
+ if tool == "mssql":
201
+ cmd = impacket_cmd("mssqlclient")
202
+ return (f"{cmd} -hashes :{hash_val} \"{user}\"@{target} -windows-auth -command 'enable_xp_cmdshell' -command 'xp_cmdshell powershell -enc {b64}'"
203
+ if use_hash else
204
+ f"{cmd} \"{user}\":{credential}@{target} -windows-auth -command 'enable_xp_cmdshell' -command 'xp_cmdshell powershell -enc {b64}'")
205
+
206
+ if tool == "atexec":
207
+ cmd = impacket_cmd("atexec")
208
+ return (f"{cmd} -hashes :{hash_val} \"{user}\"@{target} 'powershell -enc {b64}'"
209
+ if use_hash else
210
+ f"{cmd} \"{user}\":{credential}@{target} 'powershell -enc {b64}'")
211
+
212
+ # winrm handling - both regular and SSL variants
213
+ # yes I know nxc has a winrm module which can oneshot commands, but evil-winrm has proved itself more dependable
214
+ if tool == "winrm":
215
+ return (f"echo 'powershell -enc {b64}' | {WINRM_CMD} -i {target} -u \"{user}\" -H {hash_val}"
216
+ if use_hash else
217
+ f"echo 'powershell -enc {b64}' | {WINRM_CMD} -i {target} -u \"{user}\" -p {credential}")
218
+
219
+ if tool == "winrm-ssl":
220
+ return (f"echo 'powershell -enc {b64}' | {WINRM_CMD} -i {target} -u \"{user}\" -H {hash_val} --ssl"
221
+ if use_hash else
222
+ f"echo 'powershell -enc {b64}' | {WINRM_CMD} -i {target} -u \"{user}\" -p {credential} --ssl")
223
+
224
+ # NXC tools
225
+ if tool == "smbexec":
226
+ return (f"{NXC_CMD} smb {target} -H {hash_val} -u \"{user}\" -X 'powershell -enc {b64}' --exec-method smbexec{nxc_output_flag}"
227
+ if use_hash else
228
+ f"{NXC_CMD} smb {target} -p {credential} -u \"{user}\" -X 'powershell -enc {b64}' --exec-method smbexec{nxc_output_flag}")
229
+
230
+ if tool == "wmi":
231
+ # we don't actually need to pass the --no-output here, as defender won't catch it regardless it seems
232
+ # additionally, adding --no-output makes it very difficult to differentiate between command execution and a successful authentication w/o execution
233
+ return (f"{NXC_CMD} wmi {target} -H {hash_val} -u \"{user}\" -x 'powershell -enc {b64}'"
234
+ if use_hash else
235
+ f"{NXC_CMD} wmi {target} -p {credential} -u \"{user}\" -x 'powershell -enc {b64}'")
236
+
237
+ if tool == "ssh":
238
+ return f"{NXC_CMD} ssh {target} -p {credential} -u \"{user}\" -x 'powershell -enc {b64}'{nxc_output_flag}"
239
+
240
+ if tool == "rdp":
241
+ return (f"{NXC_CMD} rdp {target} -u \"{user}\" -H {hash_val} -X 'powershell -enc {b64}'{nxc_output_flag}"
242
+ if use_hash else
243
+ f"{NXC_CMD} rdp {target} -u \"{user}\" -p {credential} -X 'powershell -enc {b64}'{nxc_output_flag}")
244
+
245
+ raise Exception(f"Unknown tool: {tool}")
246
+
247
+ def run_chain(user, ip, credential, command, tool_list=None):
248
+ chain = tool_list if tool_list else VALID_TOOLS
249
+
250
+ # test both winrm types
251
+ if TOOLS_SPECIFIED:
252
+ expanded_chain = []
253
+ for tool in chain:
254
+ if tool == "winrm":
255
+ expanded_chain.extend(["winrm", "winrm-ssl"])
256
+ elif tool not in expanded_chain: # Avoid duplicates
257
+ expanded_chain.append(tool)
258
+ chain = expanded_chain
259
+
260
+ for tool in chain:
261
+ # Can't pass the hash with SSH
262
+ if tool == "ssh" and is_nthash(credential):
263
+ safe_print(f" [-] Skipping SSH for {ip}: cannot pass the hash.")
264
+ continue
265
+
266
+ if tool == "rdp" and NXC_CMD == "crackmapexec":
267
+ safe_print(f" [-] Skipping RDP for {ip}: crackmapexec does not support running commands via RDP.")
268
+ continue
269
+
270
+ if tool == "mssql":
271
+ safe_print(f"[*] Attempting to enable xp_cmdshell on {ip}...")
272
+
273
+ cmd = build_cmd(tool, user, ip, credential, command)
274
+ safe_print(f"[*] Trying {tool}: {cmd}")
275
+
276
+ try:
277
+ timeout = RDP_TIMEOUT if tool == "rdp" else EXEC_TIMEOUT
278
+ result = subprocess.run(cmd, shell=True, timeout=timeout, capture_output=True)
279
+ rc = result.returncode
280
+ out = result.stdout.decode("utf-8", errors="ignore")
281
+ vprint(f"[v] Output for {tool} on {ip} (rc={rc}):")
282
+ if not out or out == '':
283
+ vprint(f"(no output)")
284
+ else:
285
+ vprint(out)
286
+
287
+ except subprocess.TimeoutExpired:
288
+ safe_print(f" [-] For {ip}: {tool} timed out.")
289
+ continue
290
+
291
+ # psexec can have "[-]" in stdout if some shares are writeable and others aren't
292
+ if tool == "psexec":
293
+ if "Found writable share" in out:
294
+ if "Stopping service" in out:
295
+ # psexec succeeded and exited (sometimes with rc 1!)
296
+ if RUN_ALL:
297
+ # need to run all tools, even if we succeeded
298
+ safe_print(f" [+] Success! With command: {cmd}")
299
+ oprint(out)
300
+ continue
301
+ return (tool, out, cmd)
302
+ else:
303
+ # if "Stopping service" not detected, AV likely caught binary, so it hangs
304
+ safe_print(f" [-] For {ip}: {tool} auth succeeded, but timed out likely due to AV.")
305
+ continue
306
+ else:
307
+ # made it through psexec, but no writeable shares found (will return rc 0)
308
+ safe_print(f" [-] For {ip}: {tool} failed.")
309
+ continue
310
+
311
+ if tool == "rdp":
312
+ if "[-] Clipboard" in out:
313
+ safe_print(f" \033[33m[!]\033[0m For {ip}: {tool} succeeded as {user} with {credential}, but failed to initialize clipboard and run command. Try manually using RDP.")
314
+ continue
315
+ elif "unrecognized arguments" in out:
316
+ safe_print(f" [-] For {ip}: {tool} failed. NetExec is out of date; 'nxc rdp' doesn't support '-X'. Please reinstall netexec to use RDP.")
317
+ continue
318
+ elif "[-]" in out:
319
+ safe_print(f" [-] For {ip}: {tool} failed.")
320
+ continue
321
+
322
+ if tool in NXC_TOOLS or tool == "atexec":
323
+ if '[-]' in out:
324
+ if "Could not retrieve" in out:
325
+ safe_print(f" \033[33m[!]\033[0m For {ip}: {tool} AUTHENTICATION succeeded as {user} with {credential}, but likely failed to run command. Try running without -o to avoid tripping AV.")
326
+ # nxc will return 0 if the tool succeeded but auth failed
327
+ else:
328
+ safe_print(f" [-] For {ip}: {tool} failed.")
329
+ continue
330
+ if '[+]' in out and 'Executed command' not in out:
331
+ safe_print(f" \033[33m[!]\033[0m For {ip}: {tool} AUTHENTICATION succeeded as {user} with {credential}, but seemingly failed to run command. Does the user have the necessary permissions?")
332
+ continue
333
+ if rc == 0 and out == "":
334
+ # nxc tools will sometimes just fail silently
335
+ safe_print(f" [-] For {ip}: {tool} failed.")
336
+ continue
337
+
338
+ if tool == "mssql" and "ERROR" in out:
339
+ safe_print(f" [-] For {ip}: {tool} failed.")
340
+ continue
341
+
342
+ # one-shotting using evil-winrm results in a return code of 1
343
+ if rc == 0 or (tool in ("winrm", "winrm-ssl") and rc == 1 and "NoMethodError" in out):
344
+ if RUN_ALL:
345
+ # need to run all tools, even if we succeeded
346
+ safe_print(f" [+] Success! With command: {cmd}")
347
+ oprint(out)
348
+ continue
349
+ return (tool, out, cmd)
350
+
351
+ safe_print(f" [-] For {ip}: {tool} failed.")
352
+
353
+ return None
354
+
355
+ def execute_on_ip(username, ip, credential, command, tool_list=None):
356
+
357
+ if SKIP_PORTSCAN:
358
+ safe_print(f"[*] Skipping portscan for {ip} (--skip-portscan enabled)")
359
+ viable_tools = tool_list if tool_list else VALID_TOOLS
360
+ else:
361
+ safe_print(f"[*] Checking applicable tools for {ip} via portscan")
362
+ viable_tools, open_ports = scan_ports_for_tools(ip, tool_list)
363
+
364
+ if VERBOSE:
365
+ vprint(f"[v] Open ports for {ip}: {sorted(open_ports)}")
366
+
367
+ if not viable_tools:
368
+ safe_print(f"[-] No required ports open for {ip}. Either it's not up, or the target is firewalled. If you want to try anyway, use --skip-portscan.")
369
+ return (ip, None)
370
+
371
+ display_tools = ["winrm" if t == "winrm-ssl" else t for t in viable_tools]
372
+ display_tools = list(dict.fromkeys(display_tools))
373
+ safe_print(f" \033[34m[i]\033[0m Viable tools found for {ip} based on portscan: {', '.join(display_tools)}")
374
+
375
+ result = run_chain(username, ip, credential, command, viable_tools)
376
+
377
+ if RUN_ALL:
378
+ safe_print(f"[*] All tools successfully run for {ip} with {username}.")
379
+ return (ip, None)
380
+
381
+ if result is None:
382
+ safe_print(f"[-] All tools failed for {ip} with {username}.")
383
+ return (ip, None)
384
+
385
+ tool, out, cmd = result
386
+ safe_print(f" [+] Success! With command: {cmd}")
387
+ if tool == "mssql":
388
+ safe_print(f"\033[33m[!] WARNING: MSSQL used for command execution; xp_cmdshell is currently enabled on {ip}. \033[0m")
389
+ oprint(out)
390
+ if not RUN_ALL:
391
+ return (ip, tool)
392
+
393
+ def parse_args():
394
+ parser = argparse.ArgumentParser(
395
+ description="Execute commands across an IP range using multiple Windows RCE methods",
396
+ formatter_class=argparse.RawTextHelpFormatter,
397
+ usage="%(prog)s ip_range username credential command [-h] [-v] [-o] [--threads NUM_THREADS] [--timeout TIMEOUT_SECONDS] [--tools LIST] [--run-all] [--skip-portscan] [-f CRED_FILE]"
398
+ )
399
+
400
+ parser.add_argument("-v", action="store_true", help="Verbose output")
401
+ parser.add_argument("-o", action="store_true", help="Show successful command output")
402
+ parser.add_argument("--threads", metavar="NUM_THREADS", type=int, default=10, help="Number of concurrent threads")
403
+ parser.add_argument("--timeout", metavar="TIMEOUT_SECONDS", type=int, default=15, help="Number of seconds before commands timeout")
404
+ parser.add_argument("--tools", metavar="LIST", help="Comma-separated list of tools to try")
405
+ parser.add_argument("--run-all", action="store_true", help="Run all tools, often running the desired command multiple times")
406
+ parser.add_argument("--skip-portscan", action="store_true", help="Skip port scanning and attempt all tools")
407
+ parser.add_argument("-f", "--file", metavar="CRED_FILE", help="Credential file (newline-separated user/password pairs)")
408
+
409
+ parser.add_argument("ip_range", help="IP range (e.g., 192.168.1.1-254)")
410
+ parser.add_argument("username", nargs="?", help="Username")
411
+ parser.add_argument("credential", nargs="?", help="Password or NT hash")
412
+ parser.add_argument("command", nargs="*", help="Command to run (default: whoami)")
413
+
414
+ args = parser.parse_args()
415
+
416
+ if args.file and (args.username or args.credential):
417
+ parser.error("Cannot specify username/password when using -f")
418
+
419
+ if not args.file and (not args.username or not args.credential):
420
+ parser.error("Must supply either -f FILE or username + credential")
421
+
422
+ return args
423
+
424
+
425
+
426
+ def check_dependencies():
427
+ """Check if required tools are installed."""
428
+ global IMPACKET_PREFIX, NXC_CMD, WINRM_CMD
429
+
430
+ # Check impacket (either impacket-psexec or psexec.py)
431
+ r1 = shutil.which("impacket-psexec")
432
+ r2 = shutil.which("psexec.py")
433
+ if r1:
434
+ IMPACKET_PREFIX = "impacket-"
435
+ elif r2:
436
+ IMPACKET_PREFIX = ""
437
+ else:
438
+ print("[-] impacket not found. Install with: pipx install impacket")
439
+ sys.exit(1)
440
+
441
+ # Check nxc/crackmapexec
442
+ r1 = shutil.which("nxc")
443
+ r2 = shutil.which("netexec")
444
+ r3 = shutil.which("crackmapexec")
445
+ if r1:
446
+ NXC_CMD = "nxc"
447
+ elif r2:
448
+ NXC_CMD = "netexec"
449
+ elif r3:
450
+ NXC_CMD = "crackmapexec"
451
+ else:
452
+ print("[-] netexec not found. Install with: pipx install git+https://github.com/Pennyw0rth/NetExec")
453
+ sys.exit(1)
454
+
455
+ # Check evil-winrm
456
+ if shutil.which("evil-winrm"):
457
+ WINRM_CMD = "evil-winrm"
458
+ else:
459
+ # default in exegol
460
+ base = "/usr/local/rvm/gems"
461
+ for d in os.listdir(base):
462
+ if d.endswith("@evil-winrm"):
463
+ WINRM_CMD = f"{base}/{d}/wrappers/evil-winrm"
464
+ if not WINRM_CMD:
465
+ print("[-] evil-winrm not found. Please install with gem install evil-winrm")
466
+ sys.exit(1)
467
+
468
+ def impacket_cmd(tool):
469
+ """Return the correct impacket command name based on install type."""
470
+ if IMPACKET_PREFIX:
471
+ return f"impacket-{tool}"
472
+ return f"{tool}.py"
473
+
474
+ def main():
475
+ global VERBOSE, OUTPUT, MAX_THREADS, EXEC_TIMEOUT, RUN_ALL, SKIP_PORTSCAN, TOOLS_SPECIFIED
476
+
477
+ check_dependencies()
478
+
479
+ args = parse_args()
480
+
481
+ VERBOSE = args.v
482
+ OUTPUT = args.o
483
+ MAX_THREADS = args.threads
484
+ EXEC_TIMEOUT = args.timeout
485
+ RUN_ALL = args.run_all
486
+ SKIP_PORTSCAN = args.skip_portscan
487
+
488
+ if args.tools:
489
+ tool_list = parse_tools_list(args.tools)
490
+ TOOLS_SPECIFIED = True
491
+ print(f"[*] Using tools: {', '.join(tool_list)}")
492
+ else:
493
+ tool_list = None
494
+
495
+ if args.skip_portscan:
496
+ print("\033[33m[!] Port scanning disabled (--skip-portscan). All tools will be attempted.\033[0m")
497
+
498
+ command = " ".join(args.command) if args.command else "whoami"
499
+
500
+ if args.file:
501
+ credential_list = load_credential_file(args.file)
502
+ else:
503
+ credential_list = [(args.username, args.credential)]
504
+
505
+ if args.ip_range.endswith('.txt'):
506
+ ips = []
507
+ with open(args.ip_range) as f:
508
+ for line in f:
509
+ line = line.strip()
510
+ if line and not line.startswith('#'):
511
+ ips.extend(parse_ip_range(line))
512
+ else:
513
+ ips = parse_ip_range(args.ip_range)
514
+
515
+ print(f"[*] Loaded {len(credential_list)} credential set(s)")
516
+ print(f"[*] Processing {len(ips)} IPs with {MAX_THREADS} threads...")
517
+
518
+ if not OUTPUT:
519
+ print("\033[33m[!] Output Disabled. Run with -o to see successful command output\033[0m")
520
+ else:
521
+ print("-" * 20)
522
+ print("\033[33m[!] WARNING: Output Enabled. This WILL trip AV for certain tools\033[0m")
523
+ print("-" * 20)
524
+
525
+ futures = []
526
+ with ThreadPoolExecutor(max_workers=MAX_THREADS) as executor:
527
+ for ip in ips:
528
+ for (user, cred) in credential_list:
529
+ cred = shlex.quote(cred)
530
+ futures.append(
531
+ executor.submit(execute_on_ip, user, ip, cred, command, tool_list)
532
+ )
533
+
534
+ for future in as_completed(futures):
535
+ try:
536
+ future.result()
537
+ except Exception as e:
538
+ safe_print(f"[!] Exception: {e}")
539
+
540
+ if __name__ == "__main__":
541
+ main()
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: authfinder
3
+ Version: 1.0.0
4
+ Summary: Execute commands across Windows systems using multiple RCE methods (WinRM, SMB, WMI, RDP, SSH, MSSQL)
5
+ Author: Khael
6
+ Project-URL: Homepage, https://github.com/KhaelK138/authfinder
7
+ Project-URL: Repository, https://github.com/KhaelK138/authfinder
8
+ Project-URL: Issues, https://github.com/KhaelK138/authfinder/issues
9
+ Keywords: security,pentest,windows,remote-execution,winrm,smb,wmi,rdp
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Information Technology
12
+ Classifier: Intended Audience :: System Administrators
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Security
20
+ Classifier: Topic :: System :: Systems Administration
21
+ Requires-Python: >=3.8
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Provides-Extra: dev
25
+ Requires-Dist: build; extra == "dev"
26
+ Requires-Dist: twine; extra == "dev"
27
+ Dynamic: license-file
28
+
29
+ # AuthFinder
30
+
31
+ A tool for executing commands across multiple Windows systems using various remote execution methods. Automatically tries multiple techniques until one succeeds, based on return codes and output. Makes executing commands given credentials a hell of a lot easier.
32
+
33
+ Big thanks to NetExec, Impacket, and Evil-Winrm, as this tool just essentially acts as a wrapper around those (making it more of a script, I suppose).
34
+
35
+ ## Features
36
+
37
+ - **Multiple RCE Methods**: Automatically tries various Windows remote execution techniques:
38
+ - WinRM (HTTP/HTTPS)
39
+ - PSExec (Impacket)
40
+ - SMBExec (NetExec)
41
+ - WMI (NetExec)
42
+ - AtExec (Impacket)
43
+ - RDP (NetExec)
44
+ - SSH (NetExec)
45
+ - MSSQL (Impacket)
46
+ - **Multi-threaded**: Execute commands across multiple hosts simultaneously
47
+ - **Automatic Pass-the-Hash**: Just paste the NTLM hash as the credential
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ pipx install authfinder
53
+ ```
54
+
55
+ ### External Dependencies
56
+
57
+ This tool requires the following external tools to be installed:
58
+
59
+ ```bash
60
+ # Impacket (for PSExec, AtExec, MSSQL)
61
+ pipx install impacket
62
+
63
+ # NetExec (for SMBExec, WMI, RDP, SSH)
64
+ pipx install git+https://github.com/Pennyw0rth/NetExec
65
+
66
+ # Evil-WinRM (for WinRM)
67
+ gem install evil-winrm
68
+ ```
69
+
70
+ ## Usage
71
+
72
+ ### Basic Usage
73
+
74
+ ```bash
75
+ # Execute command on single host
76
+ authfinder 192.168.1.10 administrator Password123 whoami
77
+
78
+ # Execute across IP range of 192.168.1.1 to 192.168.1.50
79
+ authfinder 192.168.1.1-50 admin Pass123 "net user"
80
+
81
+ # Use hash instead of password
82
+ authfinder 10.0.0.1-10 admin :{32-bit-hash} whoami
83
+ ```
84
+
85
+ ### IP Range Format
86
+
87
+ Supports various formats:
88
+ - Single IP: `192.168.1.10`
89
+ - Range: `192.168.1.1-254`
90
+ - Multiple ranges: `10.0.1-5.10-20` (expands to all combinations)
91
+ - File with IP ranges: `targets.txt`
92
+
93
+ ### Credential File Format
94
+
95
+ Create a text file with alternating username/password lines:
96
+
97
+ ```
98
+ administrator
99
+ Password123!
100
+ admin
101
+ Pass123
102
+ backup_admin
103
+ :aad3b435b51404eeaad3b435b51404ee
104
+ ```
105
+
106
+ Lines starting with `#` are treated as comments. For NT hashes, use them directly as the password.
107
+
108
+ ## Command-Line Options
109
+
110
+ ```
111
+ Options:
112
+ -v Verbose output (shows all tool attempts)
113
+ -o Show successful command output (WARNING: may trigger AV)
114
+ -f <file> Use credential file instead of single username/password
115
+ --threads <n> Number of concurrent threads (default: 10)
116
+ --tools <list> Comma-separated list of tools to try in order
117
+ --timeout <seconds> Command timeout in seconds (default: 15)
118
+ --run-all Run all tools instead of stopping at first success
119
+ --skip-portscan Skip port scanning and attempt all tools
120
+ ```
121
+
122
+
123
+ ## Todo
124
+
125
+ Add kerberos support lol
126
+ - Requires supporting hostnames and configuring `/etc/krb5.conf` for tools like evil-winrm
127
+
128
+ ## License
129
+
130
+ MIT License - see LICENSE file for details
131
+
132
+ ## Disclaimer
133
+
134
+ This tool is intended for authorized security assessments only. Ensure you have proper authorization before using this tool on any systems you do not own or have explicit permission to test.
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ authfinder/__init__.py
5
+ authfinder/authfinder.py
6
+ authfinder.egg-info/PKG-INFO
7
+ authfinder.egg-info/SOURCES.txt
8
+ authfinder.egg-info/dependency_links.txt
9
+ authfinder.egg-info/entry_points.txt
10
+ authfinder.egg-info/requires.txt
11
+ authfinder.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ authfinder = authfinder.authfinder:main
@@ -0,0 +1,4 @@
1
+
2
+ [dev]
3
+ build
4
+ twine
@@ -0,0 +1 @@
1
+ authfinder
@@ -0,0 +1,45 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "authfinder"
7
+ version = "1.0.0"
8
+ description = "Execute commands across Windows systems using multiple RCE methods (WinRM, SMB, WMI, RDP, SSH, MSSQL)"
9
+ readme = "README.md"
10
+ authors = [
11
+ {name = "Khael"}
12
+ ]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Intended Audience :: Information Technology",
16
+ "Intended Audience :: System Administrators",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.8",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Topic :: Security",
24
+ "Topic :: System :: Systems Administration",
25
+ ]
26
+ keywords = ["security", "pentest", "windows", "remote-execution", "winrm", "smb", "wmi", "rdp"]
27
+ requires-python = ">=3.8"
28
+ dependencies = []
29
+
30
+ [project.optional-dependencies]
31
+ dev = [
32
+ "build",
33
+ "twine",
34
+ ]
35
+
36
+ [project.scripts]
37
+ authfinder = "authfinder.authfinder:main"
38
+
39
+ [project.urls]
40
+ Homepage = "https://github.com/KhaelK138/authfinder"
41
+ Repository = "https://github.com/KhaelK138/authfinder"
42
+ Issues = "https://github.com/KhaelK138/authfinder/issues"
43
+
44
+ [tool.setuptools]
45
+ packages = ["authfinder"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+