166 lines
No EOL
5.4 KiB
Python
166 lines
No EOL
5.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
MikroTik Plain API Authentication Tester
|
|
Tests credentials using plain (non-SSL) MikroTik API protocol on port 8728
|
|
"""
|
|
|
|
import socket
|
|
import sys
|
|
|
|
class MikrotikPlainAPITester:
|
|
def __init__(self, host, port=8728):
|
|
self.host = host
|
|
self.port = port
|
|
self.sock = None
|
|
|
|
def connect(self):
|
|
"""Establish plain connection to MikroTik router"""
|
|
try:
|
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
self.sock.settimeout(10)
|
|
self.sock.connect((self.host, self.port))
|
|
return True
|
|
except Exception as e:
|
|
print(f"Connection failed: {e}")
|
|
return False
|
|
|
|
def disconnect(self):
|
|
"""Close the connection"""
|
|
if self.sock:
|
|
self.sock.close()
|
|
|
|
def encode_length(self, length):
|
|
"""Encode length for MikroTik API protocol"""
|
|
if length <= 0x7F:
|
|
return bytes([length])
|
|
elif length <= 0x3FFF:
|
|
return bytes([((length >> 8) & 0xFF) | 0x80, length & 0xFF])
|
|
elif length <= 0x1FFFFF:
|
|
return bytes([((length >> 16) & 0xFF) | 0xC0,
|
|
(length >> 8) & 0xFF,
|
|
length & 0xFF])
|
|
elif length <= 0xFFFFFFF:
|
|
return bytes([((length >> 24) & 0xFF) | 0xE0,
|
|
(length >> 16) & 0xFF,
|
|
(length >> 8) & 0xFF,
|
|
length & 0xFF])
|
|
else:
|
|
return bytes([0xF0,
|
|
(length >> 24) & 0xFF,
|
|
(length >> 16) & 0xFF,
|
|
(length >> 8) & 0xFF,
|
|
length & 0xFF])
|
|
|
|
def decode_length(self):
|
|
"""Decode length from MikroTik API protocol"""
|
|
c = self.sock.recv(1)[0]
|
|
|
|
if (c & 0x80) == 0x00:
|
|
return c
|
|
elif (c & 0xC0) == 0x80:
|
|
return ((c & ~0xC0) << 8) + self.sock.recv(1)[0]
|
|
elif (c & 0xE0) == 0xC0:
|
|
data = self.sock.recv(2)
|
|
return ((c & ~0xE0) << 16) + (data[0] << 8) + data[1]
|
|
elif (c & 0xF0) == 0xE0:
|
|
data = self.sock.recv(3)
|
|
return ((c & ~0xF0) << 24) + (data[0] << 16) + (data[1] << 8) + data[2]
|
|
elif (c & 0xF8) == 0xF0:
|
|
data = self.sock.recv(4)
|
|
return (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]
|
|
|
|
def write_word(self, word):
|
|
"""Send a word to the router"""
|
|
word_bytes = word.encode('utf-8')
|
|
self.sock.send(self.encode_length(len(word_bytes)))
|
|
self.sock.send(word_bytes)
|
|
|
|
def read_word(self):
|
|
"""Read a word from the router"""
|
|
length = self.decode_length()
|
|
if length == 0:
|
|
return ""
|
|
return self.sock.recv(length).decode('utf-8', 'ignore')
|
|
|
|
def write_sentence(self, words):
|
|
"""Send a sentence (list of words) to the router"""
|
|
for word in words:
|
|
self.write_word(word)
|
|
self.write_word("")
|
|
|
|
def read_sentence(self):
|
|
"""Read a sentence from the router"""
|
|
sentence = []
|
|
while True:
|
|
word = self.read_word()
|
|
if word == "":
|
|
break
|
|
sentence.append(word)
|
|
return sentence
|
|
|
|
def test_login(self, username, password):
|
|
"""Test login credentials"""
|
|
if not self.connect():
|
|
return False
|
|
|
|
try:
|
|
# Send login command
|
|
self.write_sentence(["/login", f"=name={username}", f"=password={password}"])
|
|
|
|
# Read response
|
|
response = self.read_sentence()
|
|
|
|
if response and response[0] == "!done":
|
|
return True
|
|
else:
|
|
return False
|
|
except Exception as e:
|
|
print(f"Login test error: {e}")
|
|
return False
|
|
finally:
|
|
self.disconnect()
|
|
|
|
def main():
|
|
host = "10.250.2.2"
|
|
port = 8728 # Plain API port
|
|
|
|
# Common MikroTik default passwords
|
|
credentials = [
|
|
("admin", ""), # Empty password
|
|
("admin", "admin"), # admin/admin
|
|
("admin", "password"), # admin/password
|
|
("admin", "123456"), # admin/123456
|
|
("admin", "mikrotik"), # admin/mikrotik
|
|
("admin", "router"), # admin/router
|
|
("admin", "default"), # admin/default
|
|
]
|
|
|
|
print(f"Testing MikroTik Plain API authentication on {host}:{port}")
|
|
print("=" * 60)
|
|
|
|
tester = MikrotikPlainAPITester(host, port)
|
|
|
|
for username, password in credentials:
|
|
pwd_display = f'"{password}"' if password else "(empty)"
|
|
print(f"Testing {username}:{pwd_display}...", end=" ")
|
|
|
|
if tester.test_login(username, password):
|
|
print(f"✓ SUCCESS!")
|
|
print(f"\n*** WORKING CREDENTIALS FOUND ***")
|
|
print(f"Host: {host}")
|
|
print(f"Port: {port} (Plain API)")
|
|
print(f"Username: {username}")
|
|
print(f"Password: {pwd_display}")
|
|
return
|
|
else:
|
|
print("✗ Failed")
|
|
|
|
print("\nNo working credentials found with common defaults.")
|
|
print("\nNext steps:")
|
|
print("1. Device likely has custom password")
|
|
print("2. Consider hardware reset if you own the device")
|
|
print("3. Check device label for default credentials")
|
|
print("4. Try WinBox which might show more info")
|
|
|
|
if __name__ == "__main__":
|
|
main() |