authfinder 1.0.0__py3-none-any.whl → 1.1.0__py3-none-any.whl
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.
- authfinder/__init__.py +2 -2
- authfinder/authfinder.py +45 -22
- {authfinder-1.0.0.dist-info → authfinder-1.1.0.dist-info}/METADATA +8 -5
- authfinder-1.1.0.dist-info/RECORD +8 -0
- {authfinder-1.0.0.dist-info → authfinder-1.1.0.dist-info}/WHEEL +1 -1
- authfinder-1.0.0.dist-info/RECORD +0 -8
- {authfinder-1.0.0.dist-info → authfinder-1.1.0.dist-info}/entry_points.txt +0 -0
- {authfinder-1.0.0.dist-info → authfinder-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {authfinder-1.0.0.dist-info → authfinder-1.1.0.dist-info}/top_level.txt +0 -0
authfinder/__init__.py
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"""authfinder: Execute commands across Windows systems using multiple RCE methods"""
|
|
1
|
+
"""authfinder: Execute commands across Windows and Linux systems using multiple RCE methods"""
|
|
2
2
|
|
|
3
|
-
__version__ = "1.
|
|
3
|
+
__version__ = "1.1.0"
|
authfinder/authfinder.py
CHANGED
|
@@ -19,6 +19,7 @@ OUTPUT = False
|
|
|
19
19
|
RUN_ALL = False
|
|
20
20
|
SKIP_PORTSCAN = False
|
|
21
21
|
TOOLS_SPECIFIED = False
|
|
22
|
+
LINUX_MODE = False
|
|
22
23
|
|
|
23
24
|
VALID_TOOLS = ["winrm", "smbexec", "wmi", "ssh", "mssql", "psexec", "atexec", "rdp"]
|
|
24
25
|
NXC_TOOLS = {"smbexec", "wmi", "ssh", "rdp"}
|
|
@@ -235,6 +236,9 @@ def build_cmd(tool, user, target, credential, command):
|
|
|
235
236
|
f"{NXC_CMD} wmi {target} -p {credential} -u \"{user}\" -x 'powershell -enc {b64}'")
|
|
236
237
|
|
|
237
238
|
if tool == "ssh":
|
|
239
|
+
if LINUX_MODE:
|
|
240
|
+
b64 = base64.b64encode(command.encode("utf-8")).decode()
|
|
241
|
+
return f"{NXC_CMD} ssh {target} -p {credential} -u \"{user}\" -x 'echo {b64} | base64 -d | $0'{nxc_output_flag}"
|
|
238
242
|
return f"{NXC_CMD} ssh {target} -p {credential} -u \"{user}\" -x 'powershell -enc {b64}'{nxc_output_flag}"
|
|
239
243
|
|
|
240
244
|
if tool == "rdp":
|
|
@@ -327,6 +331,10 @@ def run_chain(user, ip, credential, command, tool_list=None):
|
|
|
327
331
|
else:
|
|
328
332
|
safe_print(f" [-] For {ip}: {tool} failed.")
|
|
329
333
|
continue
|
|
334
|
+
if tool == "ssh" and 'Linux - Shell' in out and not LINUX_MODE:
|
|
335
|
+
safe_print(f" \033[33m[!]\033[0m For {ip}: {tool} AUTHENTICATION succeeded as {user} with {credential}, but this seems like a Linux machine, so the command didn't run.")
|
|
336
|
+
safe_print(" \033[33m[!]\033[0m Use \033[33m--linux\033[0m to run command across Linux machines.")
|
|
337
|
+
continue
|
|
330
338
|
if '[+]' in out and 'Executed command' not in out:
|
|
331
339
|
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
340
|
continue
|
|
@@ -335,9 +343,13 @@ def run_chain(user, ip, credential, command, tool_list=None):
|
|
|
335
343
|
safe_print(f" [-] For {ip}: {tool} failed.")
|
|
336
344
|
continue
|
|
337
345
|
|
|
338
|
-
if tool == "mssql"
|
|
339
|
-
|
|
340
|
-
|
|
346
|
+
if tool == "mssql":
|
|
347
|
+
if "The EXECUTE permission was denied" in out:
|
|
348
|
+
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?")
|
|
349
|
+
continue
|
|
350
|
+
if "ERROR" in out:
|
|
351
|
+
safe_print(f" [-] For {ip}: {tool} failed.")
|
|
352
|
+
continue
|
|
341
353
|
|
|
342
354
|
# one-shotting using evil-winrm results in a return code of 1
|
|
343
355
|
if rc == 0 or (tool in ("winrm", "winrm-ssl") and rc == 1 and "NoMethodError" in out):
|
|
@@ -406,6 +418,8 @@ def parse_args():
|
|
|
406
418
|
parser.add_argument("--skip-portscan", action="store_true", help="Skip port scanning and attempt all tools")
|
|
407
419
|
parser.add_argument("-f", "--file", metavar="CRED_FILE", help="Credential file (newline-separated user/password pairs)")
|
|
408
420
|
|
|
421
|
+
parser.add_argument("--linux", action="store_true", help="Linux-only mode - automates SSH, ignores other tools")
|
|
422
|
+
|
|
409
423
|
parser.add_argument("ip_range", help="IP range (e.g., 192.168.1.1-254)")
|
|
410
424
|
parser.add_argument("username", nargs="?", help="Username")
|
|
411
425
|
parser.add_argument("credential", nargs="?", help="Password or NT hash")
|
|
@@ -417,7 +431,7 @@ def parse_args():
|
|
|
417
431
|
parser.error("Cannot specify username/password when using -f")
|
|
418
432
|
|
|
419
433
|
if not args.file and (not args.username or not args.credential):
|
|
420
|
-
parser.error("Must supply either -f FILE or username
|
|
434
|
+
parser.error("Must supply either -f FILE or username and credential")
|
|
421
435
|
|
|
422
436
|
return args
|
|
423
437
|
|
|
@@ -434,7 +448,7 @@ def check_dependencies():
|
|
|
434
448
|
IMPACKET_PREFIX = "impacket-"
|
|
435
449
|
elif r2:
|
|
436
450
|
IMPACKET_PREFIX = ""
|
|
437
|
-
|
|
451
|
+
elif not LINUX_MODE:
|
|
438
452
|
print("[-] impacket not found. Install with: pipx install impacket")
|
|
439
453
|
sys.exit(1)
|
|
440
454
|
|
|
@@ -461,7 +475,7 @@ def check_dependencies():
|
|
|
461
475
|
for d in os.listdir(base):
|
|
462
476
|
if d.endswith("@evil-winrm"):
|
|
463
477
|
WINRM_CMD = f"{base}/{d}/wrappers/evil-winrm"
|
|
464
|
-
if not WINRM_CMD:
|
|
478
|
+
if not WINRM_CMD and not LINUX_MODE:
|
|
465
479
|
print("[-] evil-winrm not found. Please install with gem install evil-winrm")
|
|
466
480
|
sys.exit(1)
|
|
467
481
|
|
|
@@ -472,7 +486,7 @@ def impacket_cmd(tool):
|
|
|
472
486
|
return f"{tool}.py"
|
|
473
487
|
|
|
474
488
|
def main():
|
|
475
|
-
global VERBOSE, OUTPUT, MAX_THREADS, EXEC_TIMEOUT, RUN_ALL, SKIP_PORTSCAN, TOOLS_SPECIFIED
|
|
489
|
+
global VERBOSE, OUTPUT, MAX_THREADS, EXEC_TIMEOUT, RUN_ALL, SKIP_PORTSCAN, TOOLS_SPECIFIED, LINUX_MODE
|
|
476
490
|
|
|
477
491
|
check_dependencies()
|
|
478
492
|
|
|
@@ -480,28 +494,17 @@ def main():
|
|
|
480
494
|
|
|
481
495
|
VERBOSE = args.v
|
|
482
496
|
OUTPUT = args.o
|
|
483
|
-
MAX_THREADS = args.threads
|
|
497
|
+
MAX_THREADS = args.threads if args.threads > 0 else 1
|
|
484
498
|
EXEC_TIMEOUT = args.timeout
|
|
485
499
|
RUN_ALL = args.run_all
|
|
486
500
|
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"
|
|
501
|
+
LINUX_MODE = args.linux
|
|
499
502
|
|
|
500
503
|
if args.file:
|
|
501
504
|
credential_list = load_credential_file(args.file)
|
|
502
505
|
else:
|
|
503
506
|
credential_list = [(args.username, args.credential)]
|
|
504
|
-
|
|
507
|
+
|
|
505
508
|
if args.ip_range.endswith('.txt'):
|
|
506
509
|
ips = []
|
|
507
510
|
with open(args.ip_range) as f:
|
|
@@ -512,12 +515,32 @@ def main():
|
|
|
512
515
|
else:
|
|
513
516
|
ips = parse_ip_range(args.ip_range)
|
|
514
517
|
|
|
518
|
+
if len(ips) < MAX_THREADS:
|
|
519
|
+
MAX_THREADS = len(ips)
|
|
520
|
+
|
|
515
521
|
print(f"[*] Loaded {len(credential_list)} credential set(s)")
|
|
516
522
|
print(f"[*] Processing {len(ips)} IPs with {MAX_THREADS} threads...")
|
|
523
|
+
|
|
524
|
+
if args.linux:
|
|
525
|
+
if args.tools:
|
|
526
|
+
print("\033[31m[!] Tools (--tools) cannot be specified alongside Linux-mode (--linux), as only SSH is supported. Continuing with SSH...\033[0m")
|
|
527
|
+
args.tools = "ssh"
|
|
528
|
+
|
|
529
|
+
if args.tools:
|
|
530
|
+
tool_list = parse_tools_list(args.tools)
|
|
531
|
+
TOOLS_SPECIFIED = True
|
|
532
|
+
print(f"[*] Using tools: {', '.join(tool_list)}")
|
|
533
|
+
else:
|
|
534
|
+
tool_list = None
|
|
535
|
+
|
|
536
|
+
if args.skip_portscan:
|
|
537
|
+
print("\033[33m[!] Port scanning disabled (--skip-portscan). All tools will be attempted.\033[0m")
|
|
538
|
+
|
|
539
|
+
command = " ".join(args.command) if args.command else "whoami"
|
|
517
540
|
|
|
518
541
|
if not OUTPUT:
|
|
519
542
|
print("\033[33m[!] Output Disabled. Run with -o to see successful command output\033[0m")
|
|
520
|
-
|
|
543
|
+
elif not LINUX_MODE:
|
|
521
544
|
print("-" * 20)
|
|
522
545
|
print("\033[33m[!] WARNING: Output Enabled. This WILL trip AV for certain tools\033[0m")
|
|
523
546
|
print("-" * 20)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: authfinder
|
|
3
|
-
Version: 1.
|
|
4
|
-
Summary: Execute commands across Windows systems using multiple RCE methods (WinRM, SMB, WMI, RDP, SSH, MSSQL)
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Execute commands across Windows and Linux systems using multiple RCE methods (WinRM, SMB, WMI, RDP, SSH, MSSQL)
|
|
5
5
|
Author: Khael
|
|
6
6
|
Project-URL: Homepage, https://github.com/KhaelK138/authfinder
|
|
7
7
|
Project-URL: Repository, https://github.com/KhaelK138/authfinder
|
|
@@ -28,7 +28,7 @@ Dynamic: license-file
|
|
|
28
28
|
|
|
29
29
|
# AuthFinder
|
|
30
30
|
|
|
31
|
-
A tool for executing commands across
|
|
31
|
+
A tool for executing commands across Windows (and Linux) 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
32
|
|
|
33
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
34
|
|
|
@@ -45,6 +45,7 @@ Big thanks to NetExec, Impacket, and Evil-Winrm, as this tool just essentially a
|
|
|
45
45
|
- MSSQL (Impacket)
|
|
46
46
|
- **Multi-threaded**: Execute commands across multiple hosts simultaneously
|
|
47
47
|
- **Automatic Pass-the-Hash**: Just paste the NTLM hash as the credential
|
|
48
|
+
- **Linux Support**: Use `--linux` to attempt to run commands across linux machines instead, via SSH
|
|
48
49
|
|
|
49
50
|
## Installation
|
|
50
51
|
|
|
@@ -78,7 +79,7 @@ authfinder 192.168.1.10 administrator Password123 whoami
|
|
|
78
79
|
# Execute across IP range of 192.168.1.1 to 192.168.1.50
|
|
79
80
|
authfinder 192.168.1.1-50 admin Pass123 "net user"
|
|
80
81
|
|
|
81
|
-
# Use
|
|
82
|
+
# Use nthash instead of password
|
|
82
83
|
authfinder 10.0.0.1-10 admin :{32-bit-hash} whoami
|
|
83
84
|
```
|
|
84
85
|
|
|
@@ -86,6 +87,7 @@ authfinder 10.0.0.1-10 admin :{32-bit-hash} whoami
|
|
|
86
87
|
|
|
87
88
|
Supports various formats:
|
|
88
89
|
- Single IP: `192.168.1.10`
|
|
90
|
+
- Multi-IP: `192.168.1.15,17,29,153`
|
|
89
91
|
- Range: `192.168.1.1-254`
|
|
90
92
|
- Multiple ranges: `10.0.1-5.10-20` (expands to all combinations)
|
|
91
93
|
- File with IP ranges: `targets.txt`
|
|
@@ -100,7 +102,7 @@ Password123!
|
|
|
100
102
|
admin
|
|
101
103
|
Pass123
|
|
102
104
|
backup_admin
|
|
103
|
-
:
|
|
105
|
+
:12345678123456781234567812345678
|
|
104
106
|
```
|
|
105
107
|
|
|
106
108
|
Lines starting with `#` are treated as comments. For NT hashes, use them directly as the password.
|
|
@@ -117,6 +119,7 @@ Options:
|
|
|
117
119
|
--timeout <seconds> Command timeout in seconds (default: 15)
|
|
118
120
|
--run-all Run all tools instead of stopping at first success
|
|
119
121
|
--skip-portscan Skip port scanning and attempt all tools
|
|
122
|
+
--linux Enables Linux-only mode, which uses SSH and ignores other tools
|
|
120
123
|
```
|
|
121
124
|
|
|
122
125
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
authfinder/__init__.py,sha256=VHsLEb_v1vOh7ztHlnIfTE5ADM0lXPqC9vK5Oac5wl8,121
|
|
2
|
+
authfinder/authfinder.py,sha256=-hPWugovB5v7q9vt-axjgEXx0AYFHL_3IFdCiqQmZVc,22654
|
|
3
|
+
authfinder-1.1.0.dist-info/licenses/LICENSE,sha256=Kw_kDNp7vsEauRb1Al1Urf1pe0EZWYB0HGK8PvT4FV0,1060
|
|
4
|
+
authfinder-1.1.0.dist-info/METADATA,sha256=2lh46fk3BvbTye3IF09bXpB86CyoX6Mmo1-SixwkPAw,4614
|
|
5
|
+
authfinder-1.1.0.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
6
|
+
authfinder-1.1.0.dist-info/entry_points.txt,sha256=9ChuQL6PNJlPHW_hSXvoFffcHCbpE0M8RHk8cz7yaIo,58
|
|
7
|
+
authfinder-1.1.0.dist-info/top_level.txt,sha256=tBEVwAMMMn0YcpoXt_lO1xiT-uznaCGrp7184swWocg,11
|
|
8
|
+
authfinder-1.1.0.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
authfinder/__init__.py,sha256=b2jBb6R4cuiX1PxJY3rYvF_ur_hCDqsYnC326whCgFA,111
|
|
2
|
-
authfinder/authfinder.py,sha256=kd1cB3oxBDK_XzftviRwOMBAlNV8R52OLLxK5TWxyig,21155
|
|
3
|
-
authfinder-1.0.0.dist-info/licenses/LICENSE,sha256=Kw_kDNp7vsEauRb1Al1Urf1pe0EZWYB0HGK8PvT4FV0,1060
|
|
4
|
-
authfinder-1.0.0.dist-info/METADATA,sha256=KdyyXzqJNunFBs5aX6SMb3RzeOLo4b0LAuOxI4TVRB4,4367
|
|
5
|
-
authfinder-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
6
|
-
authfinder-1.0.0.dist-info/entry_points.txt,sha256=9ChuQL6PNJlPHW_hSXvoFffcHCbpE0M8RHk8cz7yaIo,58
|
|
7
|
-
authfinder-1.0.0.dist-info/top_level.txt,sha256=tBEVwAMMMn0YcpoXt_lO1xiT-uznaCGrp7184swWocg,11
|
|
8
|
-
authfinder-1.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|