Projects STRLCPY routeros-scanner Commits 9a19177f
🤬
  • query nvd code

    the code for the creation and update of the CVE data file (assets/mikrotik_cpe_match.json)
  • Loading...
  • noafru committed 2 years ago
    9a19177f
    1 parent f5ce17bd
  • ■ ■ ■ ■ ■ ■
    README.md
    1 1  # RouterOS Scanner
    2 2   
     3 +<img src='https://github.com/microsoft/routeros-scanner/blob/main/assets/img/section52.png' img align='right' width='100' height='100'/>
     4 + 
    3 5  Forensics tool for Mikrotik devices. Search for suspicious properties and weak security points that need to be fixed on the router.
    4 6   
    5 7  This tool’s functionalities include the following:
    skipped 22 lines
    28 30  `-ps` | The password of the given user name (empty password by default) | Optional
    29 31  `-J` | Print the results as json format (prints txt format by default) | Optional
    30 32  `-concise` | Print a shortened text output focusing on recommendations and suspicious data | Optional
     33 +`-update` | Update the CVE Json file (the file is updated automatically if it hasn't been updated in the last month)| Optional
    31 34   
    32 35  ### Executing examples:
    33 36   ./main.py -i 192.168.88.1 -u admin
    skipped 6 lines
    40 43  1. raw data - all the data we search in.
    41 44  2. suspicious - things we found out as suspicious - should be checked if they are legitimate or malicious.
    42 45  3. recommendation - things we found out as weak security points and recommendations for fixing them.
     46 + 
     47 +## More info & solution:
     48 +Researchers developed this forensic tool while investigating how MikroTik devices are used in Trickbot C2 infrastructure.
     49 +You can read more about the research [here](https://www.microsoft.com/security/blog/2022/03/16/uncovering-trickbots-use-of-iot-devices-in-command-and-control-infrastructure/)
     50 + 
     51 +[Microsoft Defender for IoT](https://azure.microsoft.com/en-us/services/iot-defender/#overview) is an agentless network-layer security solution that allows
     52 +organizations to continuously monitor and discover assets, detect threats, and manage vulnerabilities in their IoT/OT
     53 +and Industrial Control Systems (ICS) devices, on-premises and in Azure-connected environments.
    43 54   
    44 55  ## Contributing
    45 56   
    skipped 20 lines
  • ■ ■ ■ ■ ■ ■
    commands/version.py
    skipped 1 lines
    2 2  # Licensed under the MIT License.
    3 3  import traceback
    4 4  import re
     5 +import os
    5 6  import sys
    6 7   
    7 8  from commands.basecommand import BaseCommand
    8 9  from nvd import CVEValidator
     10 + 
     11 +CVES_PATH = './assets/mikrotik_cpe_match.json'
    9 12   
    10 13   
    11 14  class Version(BaseCommand):
    skipped 24 lines
    36 39   recommendation = []
    37 40   
    38 41   try:
    39  - if version:
    40  - cve = CVEValidator('./assets/mikrotik_cpe_match.json')
     42 + if version and os.path.isfile(CVES_PATH):
     43 + cve = CVEValidator(CVES_PATH)
    41 44   ver_cves = cve.check_version(version)
    42 45   if ver_cves:
    43 46   sus_version = ver_cves
    skipped 9 lines
  • ■ ■ ■ ■ ■ ■
    main.py
    skipped 2 lines
    3 3   
    4 4  import argparse
    5 5  import json
     6 +import traceback
    6 7  import paramiko
     8 +import os
     9 +import datetime
     10 +import sys
     11 +import re
    7 12   
    8 13  from commands.fwnat import FWNat
    9 14  from commands.dns import DNS
    skipped 5 lines
    15 20  from commands.socks import Socks
    16 21  from commands.users import Users
    17 22  from commands.version import Version
     23 +import query_nvd
     24 + 
     25 +CVES_PATH = './assets/mikrotik_cpe_match.json'
    18 26   
    19 27   
    20 28  def main(args):
    21 29   all_data = {}
    22 30   commands = [Version(), Scheduler(), Files(), FWNat(), Proxy(), Socks(), DNS(), Users(), Ports(), FW()]
    23 31   
    24  - print(f'Mikrotik ip address: {args.ip}\n')
     32 + if args.update or is_cves_file_updated():
     33 + update_cves()
     34 + 
     35 + print(f'** Mikrotik ip address: {args.ip}\n')
    25 36   
    26 37   with paramiko.SSHClient() as ssh_client:
    27 38   ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    skipped 24 lines
    52 63   data = res[command][item]
    53 64   print(f'\t\t{data}')
    54 65   
     66 +def is_cves_file_updated():
     67 + if os.path.isfile(CVES_PATH):
     68 + month_ago = datetime.datetime.now() - datetime.timedelta(30)
     69 + modification_date = datetime.datetime.fromtimestamp(os.path.getmtime(CVES_PATH))
     70 + need_update = month_ago > modification_date
     71 + if need_update:
     72 + print("** The CVE data file hasn't been update in the last month", file = sys.stderr)
     73 + else:
     74 + print("** There is no CVE data file", file = sys.stderr)
     75 + need_update = True
     76 + 
     77 + return need_update
     78 + 
     79 +def update_cves():
     80 + print(f'** Trying to update the CVE json file', file = sys.stderr)
     81 + try:
     82 + ci = query_nvd.CVEsInterface()
     83 + 
     84 + ver_cves = ci.nist_api('mikrotik', 'routeros')
     85 + strip_no_routeros_version(ver_cves)
     86 + 
     87 + if ver_cves:
     88 + with open('./assets/mikrotik_cpe_match.json', 'w') as fjson:
     89 + fjson.write(json.dumps(ver_cves, indent=4))
     90 + print("** The CVE update process succeeded", file=sys.stderr)
     91 + else:
     92 + print(
     93 + "** The CVE update process failed, if an older version of the json file is available, the program will use it, else the program won't return CVE data.",
     94 + file=sys.stderr)
     95 + except Exception:
     96 + print(f"** The CVE update process failed, if an older version of the json file is available, the program will use it, else the program won't return CVE data.\n"
     97 + f" The exception message:\n {traceback.format_exc()}", file = sys.stderr)
     98 + 
     99 +def strip_no_routeros_version(cves):
     100 + for cve in cves:
     101 + for versions in cves[cve]:
     102 + for ver in list(versions):
     103 + if not is_routeros_version(versions[ver]):
     104 + versions.pop(ver)
     105 + 
     106 +def is_routeros_version(ver):
     107 + return re.match(r'\d{1,2}\.\d{1,2}(\.\d{1,2})?', ver)
     108 + 
    55 109   
    56 110  if __name__ == '__main__':
    57 111   parser = argparse.ArgumentParser()
    skipped 3 lines
    61 115   parser.add_argument('-ps', '--password', help='The password of the given user name', default='')
    62 116   parser.add_argument('-J', help='Print the results as json format', action='store_true')
    63 117   parser.add_argument('-concise', help='Print out only suspicious items and recommendations', action='store_true')
     118 + parser.add_argument('-update', help='Update the CVE Json file', action='store_true')
    64 119   args = parser.parse_args()
    65 120   
    66 121   main(args)
    skipped 1 lines
  • ■ ■ ■ ■ ■ ■
    query_nvd.py
     1 +# Copyright (c) Microsoft Corporation.
     2 +# Licensed under the MIT License.
     3 + 
     4 +import requests
     5 +import collections
     6 +import sys
     7 +from retry import retry
     8 + 
     9 + 
     10 +class NvdApiError(Exception):
     11 + """Exception raised for errors in the API request """
     12 + def __init__(self, params, msg):
     13 + self.message = f"Received an error during the API process with the following params: {params}. The API message: {msg}"
     14 + super().__init__(self.message)
     15 + 
     16 +class hashabledict(dict):
     17 + def __key(self):
     18 + summ = []
     19 + for k in sorted(self):
     20 + v = self[k]
     21 + if type(v) == dict:
     22 + summ.append((k, hashabledict(v)))
     23 + elif type(v) == list:
     24 + summ.append((k, tuple(sorted(str(v)))))
     25 + else:
     26 + summ.append((k, v))
     27 + return tuple(summ)
     28 + 
     29 + def __hash__(self):
     30 + return hash(self.__key())
     31 + 
     32 + def __eq__(self, other):
     33 + return self.__key() == other.__key()
     34 + 
     35 + 
     36 +class CVEsInterface():
     37 + def __init__(self):
     38 + self._ver_cves = collections.defaultdict(list)
     39 + 
     40 + @retry((NvdApiError, Exception), tries=3, delay=2)
     41 + def _web_api_query(self, url, params=None):
     42 + response = requests.get(f"{url}", params=params, timeout=10)
     43 + if response.status_code == 200:
     44 + response = response.json()
     45 + return response
     46 + else:
     47 + msg = ""
     48 + if "message" in response.json().keys():
     49 + msg = response.json()["message"]
     50 + raise NvdApiError(params, msg)
     51 + 
     52 + def nist_api(self, vendor, product):
     53 + resultsPerPage = 500
     54 + 
     55 + totalResults = self.get_cves(product, vendor, resultsPerPage, 0)
     56 + 
     57 + for cur_index in range(resultsPerPage, totalResults, resultsPerPage):
     58 + self.get_cves(product, vendor, resultsPerPage, cur_index)
     59 + 
     60 + return self._ver_cves
     61 + 
     62 + def get_cves(self, product, vendor, resultsPerPage, cur_index):
     63 + total_results = 0
     64 + response = self._web_api_query("https://services.nvd.nist.gov/rest/json/cves/1.0?",
     65 + params={"keyword": product, "resultsPerPage": resultsPerPage,
     66 + "startIndex": cur_index})
     67 + if response:
     68 + self._convert_to_ranges(response["result"]["CVE_Items"], vendor, product)
     69 + total_results = response["totalResults"]
     70 + return total_results
     71 + 
     72 + def _convert_to_ranges(self, all_cves_data, vendor, product):
     73 + for cve_data in all_cves_data:
     74 + cve = cve_data["cve"]['CVE_data_meta']['ID']
     75 + 
     76 + if cve in self._ver_cves.keys():
     77 + continue
     78 + 
     79 + if 'configurations' not in cve_data:
     80 + print (f'ERROR: No configurations {cve}', file = sys.stderr)
     81 + else:
     82 + if 'nodes' not in cve_data['configurations']:
     83 + print (f'ERROR: No nodes {cve}', file = sys.stderr)
     84 + else:
     85 + versions = []
     86 + for node in cve_data['configurations']['nodes']:
     87 + if node['operator'] != 'OR':
     88 + print(f'DEBUG: No handling for OR operator in node, the following CVE needs to be implemented: {cve}', file=sys.stderr)
     89 + else:
     90 + for cpe_match in node['cpe_match']:
     91 + cpe_res = hashabledict()
     92 + if 'cpe23Uri' in cpe_match:
     93 + if not f'{vendor}:{product}' in cpe_match['cpe23Uri']:
     94 + continue
     95 + 
     96 + if 'versionStartIncluding' in cpe_match:
     97 + cpe_res['start_including'] = cpe_match['versionStartIncluding']
     98 + if 'versionEndIncluding' in cpe_match:
     99 + cpe_res['end_including'] = cpe_match['versionEndIncluding']
     100 + if 'versionStartExcluding' in cpe_match:
     101 + cpe_res['start_excluding'] = cpe_match['versionStartExcluding']
     102 + if 'versionEndExcluding' in cpe_match:
     103 + cpe_res['end_excluding'] = cpe_match['versionEndExcluding']
     104 + 
     105 + if 'cpe23Uri' in cpe_match:
     106 + exact_ver = cpe_match['cpe23Uri'].partition(f'cpe:2.3:o:{vendor}:{product}:')[2].partition(':')[0]
     107 + if exact_ver not in (['*', '']):
     108 + cpe_res['exact'] = exact_ver
     109 + 
     110 + if cpe_res:
     111 + versions.append(cpe_res)
     112 + if versions:
     113 + self._ver_cves[cve] = list(set(versions))
     114 + 
     115 + 
     116 + 
     117 + 
Please wait...
Page is in error, reload to recover