From d9f23b2998c8a29d9b31ea4a774f9a41218b3810 Mon Sep 17 00:00:00 2001 From: casterbyte Date: Sat, 17 Aug 2024 20:00:26 +0500 Subject: [PATCH] sara v1.0 --- README.md | 240 ++++++++--------------- banner/banner.png | Bin 14092 -> 0 bytes sara.py | 424 ++++++++++++++++++++++++++++++++++++++++ setup.py | 12 +- vex.py | 488 ---------------------------------------------- 5 files changed, 508 insertions(+), 656 deletions(-) delete mode 100644 banner/banner.png create mode 100644 sara.py delete mode 100644 vex.py diff --git a/README.md b/README.md index 02421f6..5f1caf4 100644 --- a/README.md +++ b/README.md @@ -1,187 +1,103 @@ -# Vex: RouterOS Security Inspector +# Sara: RouterOS Security Inspector - -Autonomous RouterOS configuration analyzer to find security issues. No networking required, only read configurations. - -![](/banner/banner.png) +It is a autonomous RouterOS configuration analyzer for finding security issues on MikroTik hardware. ``` -Vex: RouterOS Security Inspector -Designed for security engineers + _____ + / ____| + | (___ __ _ _ __ __ _ + \___ \ / _` | '__/ _` | + ____) | (_| | | | (_| | + |_____/ \__,_|_| \__,_| v1.0 -Author: Magama Bazarov, -Pseudonym: Caster -Version: 1.1 + RouterOS Security Inspector. Designed for Security Professionals + + Author: Magama Bazarov, ``` -# Disclaimer +# Mechanism -The tool is intended solely for analyzing the security of RouterOS hardware. The author is not responsible for any damage caused by using this tool +This tool is written in Python 3 and uses regular expressions to look for specific values in configurations to detect a problem. As of v1.0, the tool performs 20 security checks, including: -------------- -# Operating +1. **SMB Service Detection**: Identifies if the SMB service is enabled, which may expose the device to vulnerabilities like CVE-2018-7445; -It is written in Python 3 and its work is based on looking for certain elements in configurations that may indicate RouterOS network security issues. The search for suspicious elements is performed using regular expressions. +2. **RMI Services Analysis**: Examines active Remote Management Interface (RMI) services such as Telnet, FTP, SSH, and others. The tool warns about unsafe services and provides recommendations for securing them; -The tool performs 18 tests: - -``` -1. Displays information about RouterOS version, device model, serial number -2. Checks the settings of neighbor discovery protocols -3. Checks the status of the Bandwidth Server -4. Checks DNS & DDNS settings -5. Checking the UPnP status -6. Checking SSH status -7. Checking for SOCKS -8. Checking the status of ROMON -9. Check MAC Telnet Server -10. Check MAC Winbox Server -11. Check MAC Ping Server -12. Verifying VRRP authentication -13. Checking SNMP settings -14. OSPF Security check -15. Checking password requirements settings -16. Checking the PoE status -17. Checking SMB activity -18. Checking RMI interfaces -``` - -> Warning: For a complete RouterOS check, it is recommended to export the configuration using `export verbose` to unload the entire configuration - --------- +3. **UPnP Status Check**: Detects if Universal Plug and Play (UPnP) is enabled, which can open up the network to unauthorized access; +4. **WiFi Configuration Review**: Analyzes WiFi settings for vulnerabilities, including insecure authentication methods, enabled WPS, and PMKID exposure; +5. **DNS Configuration Review**: Checks DNS settings, looking for remote DNS requests being allowed and the absence of DNS over HTTPS (DoH); +6. **Dynamic DNS (DDNS) Status**: Identifies if DDNS is enabled, which might expose your network to unnecessary risks; +7. **Power over Ethernet (PoE) Settings Review**: Analyzes PoE configurations to ensure power management does not pose risks to connected devices; +8. **Protected RouterBOOT Check**: Ensures that Protected RouterBOOT is enabled, preventing unauthorized changes to the bootloader settings; +9. **SOCKS Proxy Detection**: Identifies if a SOCKS proxy is enabled, which could indicate a compromised device; +10. **Bandwidth Server Check**: Detects if the Bandwidth Server is enabled, which could lead to unwanted traffic on the network; +11. **OSPF Interface Analysis**: Examines OSPF interface settings for missing passive mode and authentication, both of which are crucial for securing OSPF communications; +12. **VRRP Interface Analysis**: Checks for VRRP interfaces that lack proper authentication, potentially exposing the network to Man-in-the-Middle (MITM) attacks; +13. **Discovery Protocols Configuration**: Reviews the settings for network discovery protocols, ensuring they are limited to trusted interfaces; +14. **User Password Policy Check**: Analyzes user password policies to ensure they meet security best practices; +15. **SSH Strong Crypto Detection**: Detects if SSH is configured with weak cryptography, providing advice on how to secure it; +16. **Connection Tracking Status**: Reviews the connection tracking settings, advising on when it might be beneficial to disable it; +17. **RoMON Status Check**: Detects if RoMON is enabled, highlighting the need for careful management to prevent unauthorized access to other RouterOS devices; +18. **MAC Server Settings Review**: Analyzes MAC Server and MAC Winbox settings, recommending restrictions to enhance security; +19. **SNMP Analysis**: Identifies the use of default or weak SNMP community strings, which could lead to information gathering attacks; +20. **Port Forwarding Rules Check**: Detects port forwarding rules (dst-nat), warning about potential exposure of internal services to the internet. # Usage -```bash -caster@kali:~$ sudo apt install git python3-colorama -caster@kali:~$ git clone https://github.com/casterbyte/Vex -caster@kali:~$ cd Vex/ -caster@kali:~/Vex$ sudo python3 setup.py install -caster@kali:~$ vex -``` - -``` -sage: vex.py [-h] --config CONFIG - -Vex: RouterOS Security Inspector - -options: - -h, --help show this help message and exit - --config CONFIG Path to the RouterOS configuration file -``` - -To perform a configuration analysis, you must supply the RouterOS configuration file as input. This is done with the `--config` argument: +To install Sara: ```bash -caster@kali:~$ vex --config routeros.conf +caster@kali:~$ sudo apt install python3-colorama git +caster@kali:~$ git clone https://github.com/casterbyte/Sara +caster@kali:~/Sara$ sudo python3 setup.py install +caster@kali:~$ sara + + _____ + / ____| + | (___ __ _ _ __ __ _ + \___ \ / _` | '__/ _` | + ____) | (_| | | | (_| | + |_____/ \__,_|_| \__,_| v1.0 + + RouterOS Security Inspector. Designed for Security Professionals + + Author: Magama Bazarov, + + It's recommended to provide a configuration file exported using the 'export verbose' command + +usage: sara [-h] --config-file CONFIG_FILE +sara: error: the following arguments are required: --config-file ``` -Here is an example of the analyzed config: +Sara uses just one argument, it is the name/path to the RouterOS configuration file: -``` -[*] Config Analyzing... ------------------------------- -[+] Device Information: -[*] Software ID: BGM1-F15F -[*] Model: C52iG-5HaxD2HaxD -[*] Serial Number: XGB15HBGP01 ------------------------------- -[+] Discovery Protocols: -[!] Warning: Discovery protocols are enabled on all interfaces -[*] Impact: Information Gathering ------------------------------- -[+] Bandwidth Server: -[!] Warning: Bandwidth Server is enabled -[*] Impact: Potential misuse for traffic analysis and network performance degradation ------------------------------- -[+] DNS Settings: -[!] Warning: Router is configured as a DNS server -[*] Impact: DNS Flood -[*] Recommendation: Consider closing this port from the internet to avoid unwanted traffic ------------------------------- -[+] DDNS Settings: -[!] Warning: Dynamic DNS is enabled -[*] Impact: Exposure to dynamic IP changes and potential unauthorized access ------------------------------- -[+] UPnP Settings: -[!] Warning: UPnP is enabled -[*] Impact: Potential unauthorized port forwarding and security risks ------------------------------- -[+] SSH Strong Crypto: -[!] Warning: SSH strong crypto is disabled (strong-crypto=no) -[*] Impact: Less secure SSH connections -[*] Recommendation: Enable strong crypto (strong-crypto=yes) for enhanced security. This will use stronger encryption, HMAC algorithms, larger DH primes, and disallow weaker ones ------------------------------- -[+] SOCKS Settings: -[!] Warning: SOCKS proxy is enabled -[*] Impact: Potential unauthorized access and misuse of network resources -[*] Recommendation: Disable SOCKS proxy or ensure it is properly secured. SOCKS can be used maliciously if RouterOS is compromised ------------------------------- -[+] ROMON Settings: -[!] Warning: ROMON is enabled -[*] Impact: ROMON can be a jump point to other MikroTik devices and should be monitored carefully -[*] Recommendation: Monitor ROMON activities and ensure proper security measures are in place ------------------------------- -[+] MAC Ping Server Settings: -[!] Warning: MAC Ping Server is enabled -[*] Impact: Possible unwanted traffic ------------------------------- -[+] VRRP Authentication Settings: -[!] Warning: VRRP interface 'vrrp1' has no authentication -[*] Impact: Potential unauthorized access and manipulation of VRRP settings -[*] Recommendation: Configure authentication for VRRP interfaces to prevent unauthorized access -[!] Warning: VRRP interface 'vrrp3' has no authentication -[*] Impact: Potential unauthorized access and manipulation of VRRP settings -[*] Recommendation: Configure authentication for VRRP interfaces to prevent unauthorized access ------------------------------- -[+] SNMP: -[!] Warning: SNMP community 'public' is in use -[*] Impact: Information Gathering -[*] Recommendation: Change the community name to something more secure -[!] Warning: SNMP community 'private' is in use -[*] Impact: Information Gathering -[*] Recommendation: Change the community name to something more secure ------------------------------- -[+] OSPF Interface Templates Check: -[!] Warning: OSPF interface 'home' is not set to passive -[!] Warning: OSPF interface 'home' has no authentication -[*] Impact: Potential unauthorized access and network disruption -[*] Recommendation: Configure authentication and passive mode for OSPF interfaces to enhance security -[!] Warning: OSPF interface 'ether1' is not set to passive -[!] Warning: OSPF interface 'ether1' has no authentication -[*] Impact: Potential unauthorized access and network disruption -[*] Recommendation: Configure authentication and passive mode for OSPF interfaces to enhance security -[!] Warning: OSPF interface 'ether3' is not set to passive -[!] Warning: OSPF interface 'ether3' has no authentication -[*] Impact: Potential unauthorized access and network disruption -[*] Recommendation: Configure authentication and passive mode for OSPF interfaces to enhance security ------------------------------- -[+] Password Strength Requirements: -[!] Warning: No minimum password complexity or length requirements -[*] Recommendation: Set minimum password complexity and length requirements to enhance security ------------------------------- -[+] PoE Settings: -[!] Warning: PoE is set to auto-on -[*] Impact: There is a risk of damaging connected devices by unexpectedly supplying power to the port -[*] Recommendation: Review and set PoE settings appropriately ------------------------------- -[+] RMI Interfaces Status: -[*] Telnet is enabled - Consider disabling for security reasons -[*] FTP is enabled - Consider disabling for security reasons -[*] WWW (HTTP) is enabled -[*] SSH is enabled -[*] WWW-SSL (HTTPS) is enabled -[*] API is enabled - Consider disabling for security reasons -[*] Winbox is enabled -[*] API-SSL is enabled - Consider disabling for security reasons -[!] Recommendation: Restrict access to RMI only from trusted subnets +```bash +caster@kali:~$ sara --config-file routeros.txt + + _____ + / ____| + | (___ __ _ _ __ __ _ + \___ \ / _` | '__/ _` | + ____) | (_| | | | (_| | + |_____/ \__,_|_| \__,_| v1.0 + + RouterOS Security Inspector. Designed for Security Professionals + + Author: Magama Bazarov, + + It's recommended to provide a configuration file exported using the 'export verbose' command + +[*] Analyzing the configuration file: /mnt/hgfs/Development/Sara/routeros.txt (36.38 KB) + +[+] Device Information + [*] RouterOS Version: X.XX.X + [*] Model: XXXX-XXXXXXXXXX + [*] Serial Number: XXXXXXXXXXX ``` # Outro -The tool is updated and maintained, suggestions: caster@exploit.org - - +Sara will be maintained and updated, suggestions: caster@exploit.org diff --git a/banner/banner.png b/banner/banner.png deleted file mode 100644 index 4257e7ba2c9e828097b3a4e13ea237fca09bdcf1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14092 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 L{$j%U8xQ^$nQnK? diff --git a/sara.py b/sara.py new file mode 100644 index 0000000..063ab49 --- /dev/null +++ b/sara.py @@ -0,0 +1,424 @@ +#!/usr/bin/env python3 + +import argparse +import re +import colorama +from colorama import Fore, Style + +# Colorama +colorama.init(autoreset=True) + +def banner(): + banner_text = r""" + _____ + / ____| + | (___ __ _ _ __ __ _ + \___ \ / _` | '__/ _` | + ____) | (_| | | | (_| | + |_____/ \__,_|_| \__,_| v1.0 +""" + print(banner_text) + print(" " + Fore.YELLOW + "RouterOS Security Inspector. Designed for Security Professionals\n") + print(" " + Fore.YELLOW + "Author: " + Style.RESET_ALL + "Magama Bazarov, \n") + print(" " + "It's recommended to provide a configuration file exported using the 'export verbose' command") + print() + +def parse_arguments(): + parser = argparse.ArgumentParser() + parser.add_argument('--config-file', required=True, help='Path to the RouterOS configuration file') + return parser.parse_args() + +def extract_device_info(config_data): + version_pattern = r"#.*by RouterOS (\S+)" + model_pattern = r"# model = (\S+)" + serial_pattern = r"# serial number = (\S+)" + + version_match = re.search(version_pattern, config_data) + model_match = re.search(model_pattern, config_data) + serial_match = re.search(serial_pattern, config_data) + + if version_match and model_match and serial_match: + routeros_version = version_match.group(1) + model = model_match.group(1) + serial_number = serial_match.group(1) + + print(f"{Style.BRIGHT}[+] Device Information{Style.RESET_ALL}") + print(f"{Fore.YELLOW} [*] RouterOS Version: {routeros_version}{Style.RESET_ALL}") + print(f"{Fore.YELLOW} [*] Model: {model}{Style.RESET_ALL}") + print(f"{Fore.YELLOW} [*] Serial Number: {serial_number}{Style.RESET_ALL}") + print() + +def check_smb_enabled(config_data): + smb_pattern = r"/ip smb\s+set.*enabled=yes" + match = re.search(smb_pattern, config_data) + if match: + print(f"{Style.BRIGHT}[+] Checking SMB{Style.RESET_ALL}") + print(f"{Fore.YELLOW} [!] Warning: SMB service is enabled. Are you sure you want to do this? Also possible CVE-2018-7445{Style.RESET_ALL}") + print(f"{Fore.GREEN} [*] Solution: Turn off SMB or if you need it, filter access to it{Style.RESET_ALL}") + print() + +def check_rmi_services(config_data): + rmi_services = { + 'telnet': r"set telnet address=.* disabled=(no|yes)", + 'ftp': r"set ftp address=.* disabled=(no|yes)", + 'www': r"set www address=.* disabled=(no|yes)", + 'ssh': r"set ssh address=.* disabled=(no|yes)", + 'www-ssl': r"set www-ssl address=.* disabled=(no|yes)", + 'api': r"set api address=.* disabled=(no|yes)", + 'winbox': r"set winbox address=.* disabled=(no|yes)", + 'api-ssl': r"set api-ssl address=.* disabled=(no|yes)", + } + + active_services = [] + unsafe_services = ['ftp', 'www', 'telnet'] + caution_services = ['www-ssl', 'winbox', 'ssh'] + + for service, pattern in rmi_services.items(): + match = re.search(pattern, config_data) + if match: + disabled_status = re.search(r"disabled=(no|yes)", match.group(0)).group(1) + if disabled_status == 'no': + active_services.append(service) + + if active_services: + print(f"{Style.BRIGHT}[+] Checking RMI Services{Style.RESET_ALL}") + + unsafe_active_services = [s for s in active_services if s in unsafe_services] + if unsafe_active_services: + print(f"{Fore.RED} [!] Warning: The following RMI services are enabled and may be unsafe: {', '.join(unsafe_active_services)}.{Style.RESET_ALL}") + + caution_active_services = [s for s in active_services if s in caution_services] + if caution_active_services: + print(f"{Fore.YELLOW} [!] Caution: The following RMI services are enabled: {', '.join(caution_active_services)}.{Style.RESET_ALL}") + + api_active_services = [s for s in active_services if s in ['api', 'api-ssl']] + if api_active_services: + print(f"{Fore.YELLOW} [!] Note: The following RMI services are enabled and might be susceptible to brute force attacks: {', '.join(api_active_services)}.{Style.RESET_ALL}") + + if unsafe_active_services or caution_active_services or api_active_services: + print(f"{Fore.GREEN} [*] Solution: Disable the above RMI services if they are not required for security.{Style.RESET_ALL}") + print(f"{Fore.CYAN} [*] Tip: Restrict access to enabled services to trusted subnets only.{Style.RESET_ALL}") + print() + +def check_upnp_enabled(config_data): + upnp_pattern = r"/ip upnp\s+set.*enabled=yes" + match = re.search(upnp_pattern, config_data) + if match: + print(f"{Style.BRIGHT}[+] Checking UPnP{Style.RESET_ALL}") + print(f"{Fore.YELLOW} [!] Warning: UPnP is enabled. This can expose your network to various security risks, including unauthorized access.{Style.RESET_ALL}") + print(f"{Fore.GREEN} [*] Solution: Disable UPnP unless absolutely necessary, and ensure your firewall is properly configured.{Style.RESET_ALL}") + print() + +def check_wifi_settings(config_data): + wifi_patterns = [ + r"/interface wifi\s+set \[ find default-name=(.*?) \]\s+(.*?)(?=set \[ find default-name=|\Z)", + r"/interface wireless\s+set \[ find default-name=(.*?) \]\s+(.*?)(?=set \[ find default-name=|\Z)", + r"/interface wifiwave2\s+set \[ find default-name=(.*?) \]\s+(.*?)(?=set \[ find default-name=|\Z)" + ] + wps_pattern = r"wps=(push-button|disable)" + pmkid_pattern = r"disable-pmkid=(no|yes)" + auth_pattern = r"security\.authentication-types=(wpa-psk|wpa2-psk)" + + for wifi_pattern in wifi_patterns: + wifi_matches = re.findall(wifi_pattern, config_data, re.DOTALL) + + if wifi_matches: + print(f"{Style.BRIGHT}[+] Checking WiFi Settings{Style.RESET_ALL}") + + for interface, settings in wifi_matches: + wps_match = re.search(wps_pattern, settings) + pmkid_match = re.search(pmkid_pattern, settings) + auth_match = re.search(auth_pattern, settings) + + if wps_match and wps_match.group(1) == 'push-button': + print(f"{Fore.YELLOW} [!] Warning: WPS is enabled on interface {interface}. WPS Pin code can be cracked, brute-forced.{Style.RESET_ALL}") + + if pmkid_match and pmkid_match.group(1) == 'no': + print(f"{Fore.YELLOW} [!] Warning: PMKID is enabled on interface {interface}. PMKID is easy to bruteforce.{Style.RESET_ALL}") + + if auth_match: + auth_type = auth_match.group(1) + if auth_type in ['wpa-psk', 'wpa2-psk']: + print(f"{Fore.YELLOW} [!] Warning: Interface {interface} is using insecure authentication method '{auth_type}'. WPA/WPA2-PSK are long gone, use WPA2-E, WPA3.{Style.RESET_ALL}") + + print() + +def check_dns_settings(config_data): + dns_pattern = r"/ip dns\s+set\s+(.*?)\s+(?=\/ip dns|\Z)" + allow_remote_requests_pattern = r"allow-remote-requests=(yes|no)" + use_doh_server_pattern = r"use-doh-server=\"(.*?)\"" + + dns_matches = re.findall(dns_pattern, config_data, re.DOTALL) + + if dns_matches: + print(f"{Style.BRIGHT}[+] Checking DNS Settings{Style.RESET_ALL}") + + for settings in dns_matches: + allow_remote_requests_match = re.search(allow_remote_requests_pattern, settings) + if allow_remote_requests_match and allow_remote_requests_match.group(1) == 'yes': + print(f"{Fore.YELLOW} [!] Warning: Router is configured to allow remote DNS requests. Close the DNS UDP/53 port from the Internet.{Style.RESET_ALL}") + + use_doh_server_match = re.search(use_doh_server_pattern, settings) + if use_doh_server_match and use_doh_server_match.group(1) == '': + print(f"{Fore.YELLOW} [!] Note: DNS over HTTPS (DoH) is not configured. Consider configuring a DoH server for improved privacy.{Style.RESET_ALL}") + + print() + +def check_ddns_enabled(config_data): + pattern = r"/ip cloud\s+set.*ddns-enabled=yes" + match = re.search(pattern, config_data) + + if match: + print(f"{Style.BRIGHT}[+] Checking DDNS Configuration{Style.RESET_ALL}") + print(f"{Fore.YELLOW} [!] Warning: DDNS is enabled. Are you sure you need it?{Style.RESET_ALL}") + print(f"{Fore.GREEN} [*] Solution: Disable DDNS if it is not required.{Style.RESET_ALL}") + print() + +def check_poe_settings(config_data): + poe_pattern = r"/interface ethernet\s+set \[ find default-name=(.*?) \]\s+(.*?)(?=set \[ find default-name=|\Z)" + poe_status_pattern = r"poe-out=(auto-on|forced-on)" + + poe_matches = re.findall(poe_pattern, config_data, re.DOTALL) + + found_poe = False + + for interface, settings in poe_matches: + poe_status_match = re.search(poe_status_pattern, settings) + if poe_status_match: + if not found_poe: + print(f"{Style.BRIGHT}[+] Checking PoE Settings{Style.RESET_ALL}") + found_poe = True + poe_status = poe_status_match.group(1) + if poe_status in ['auto-on', 'forced-on']: + print(f"{Fore.YELLOW} [!] Warning: PoE is enabled on interface {interface} with setting '{poe_status}'. This could supply power to connected devices and potentially damage them if not properly managed.{Style.RESET_ALL}") + + if found_poe: + print() + +def check_protected_routerboot(config_data): + pattern = r"protected-routerboot=(enabled|disabled)" + match = re.search(pattern, config_data) + + if match and match.group(1) == "disabled": + print(f"{Style.BRIGHT}[+] Checking Protected RouterBOOT{Style.RESET_ALL}") + print(f"{Fore.RED} [!] Warning: Protected RouterBOOT is disabled. This may allow unauthorized changes to the bootloader settings.{Style.RESET_ALL}") + print(f"{Fore.GREEN} [*] Solution: Enable Protected RouterBOOT to prevent unauthorized access to the bootloader.{Style.RESET_ALL}") + print() + +def check_socks_enabled(config_data): + pattern = r"/ip socks\s+set.*enabled=yes" + match = re.search(pattern, config_data) + + if match: + print(f"{Style.BRIGHT}[+] Checking SOCKS Proxy{Style.RESET_ALL}") + print(f"{Fore.YELLOW} [!] Warning: SOCKS Proxy is enabled. The presence of SOCKS may indicate that the device has been compromised.{Style.RESET_ALL}") + print(f"{Fore.GREEN} [*] Solution: Disable SOCKS Proxy if it is not required.{Style.RESET_ALL}") + print() + +def check_bandwidth_server_enabled(config_data): + pattern = r"/tool bandwidth-server\s+set.*enabled=yes" + match = re.search(pattern, config_data) + + if match: + print(f"{Style.BRIGHT}[+] Checking Bandwidth Server{Style.RESET_ALL}") + print(f"{Fore.YELLOW} [!] Warning: Bandwidth Server is enabled. Possible unwanted traffic.{Style.RESET_ALL}") + print(f"{Fore.GREEN} [*] Solution: Disable the Bandwidth Server if it is not required.{Style.RESET_ALL}") + print() + +def check_ospf_interfaces(config_data): + ospf_pattern = r'add\s[^\n]*?interfaces=([\w-]+)[^\n]*' + matches = re.findall(ospf_pattern, config_data) + + if matches: + print(f"{Style.BRIGHT}[+] Checking OSPF Interfaces{Style.RESET_ALL}") + for match in matches: + interface_block = re.search(rf'add[^\n]*?interfaces={match}[^\n]*', config_data).group(0) + missing_passive = 'passive' not in interface_block + missing_auth = 'auth=' not in interface_block + + if missing_passive: + print(f"{Fore.YELLOW} [!] Warning: OSPF interface '{match}' is not passive. Without passive interfaces, an attacker can hear an OSPF Hello on the air and connect to an OSPF network.{Style.RESET_ALL}") + print(f"{Fore.GREEN} [*] Solution: Consider configuring the interface '{match}' as passive to limit OSPF traffic only to necessary interfaces.{Style.RESET_ALL}") + + if missing_auth: + print(f"{Fore.YELLOW} [!] Warning: OSPF interface '{match}' does not have authentication configured. Without authentication, an attacker can connect to an OSPF network.{Style.RESET_ALL}") + print(f"{Fore.CYAN} [*] Tip: When configuring OSPF authentication, use strong passwords, as OSPF password bruteforcing is still possible.{Style.RESET_ALL}") + print(f"{Fore.GREEN} [*] Solution: Configure authentication on the interface '{match}' to secure OSPF.{Style.RESET_ALL}") + print() + +def check_vrrp_interfaces(config_data): + vrrp_pattern = r'add\s[^\n]*?name=([\w-]+)[^\n]*' + matches = re.findall(vrrp_pattern, config_data) + + if matches: + found_issue = False + for match in matches: + interface_block = re.search(rf'add[^\n]*?name={match}[^\n]*', config_data).group(0) + missing_auth = 'authentication=none' in interface_block + + if missing_auth: + if not found_issue: + print(f"{Style.BRIGHT}[+] Checking VRRP Interfaces{Style.RESET_ALL}") + found_issue = True + print(f"{Fore.YELLOW} [!] Warning: VRRP interface '{match}' does not have proper authentication configured (authentication=none). An attacker can spoof the VRRP and conduct MITM.{Style.RESET_ALL}") + print(f"{Fore.CYAN} [*] Fact: Only the 2 version of VRRP supports authentication configuration. If you use AH - it uses HMAC-MD5.{Style.RESET_ALL}") + print(f"{Fore.GREEN} [*] Solution: Configure authentication on the interface '{match}' to secure VRRP.{Style.RESET_ALL}") + + if found_issue: + print() + +def check_discovery_protocols(config_data): + pattern = r"/ip neighbor discovery-settings\s+set.*discover-interface-list=all.*protocol=([\w,]+)" + match = re.search(pattern, config_data) + + if match: + active_protocols = match.group(1) + print(f"{Style.BRIGHT}[+] Checking Discovery Protocols{Style.RESET_ALL}") + print(f"{Fore.RED} [!] Warning: Discovery Protocols are enabled on all interfaces (discover-interface-list=all). This could expose detailed information about your device to the network.{Style.RESET_ALL}") + print(f"{Fore.YELLOW} [!] Active protocols: {active_protocols}{Style.RESET_ALL}") + print(f"{Fore.GREEN} [*] Solution: Limit the discovery protocols to specific interfaces or disable them if not required to enhance security.{Style.RESET_ALL}") + print() + +def check_user_password_policies(config_data): + pattern = r"/user settings\s+set.*minimum-categories=0.*minimum-password-length=0" + match = re.search(pattern, config_data) + + if match: + print(f"{Style.BRIGHT}[+] Checking User Password Policies{Style.RESET_ALL}") + print(f"{Fore.RED} [!] Warning: Password policies are not properly configured. Both minimum password categories and minimum password length are set to 0.{Style.RESET_ALL}") + print(f"{Fore.GREEN} [*] Solution: Set a higher minimum password length and require at least one or more character categories (e.g., uppercase, lowercase, numbers, special characters) for better security.{Style.RESET_ALL}") + print() + +def check_ssh_strong_crypto(config_data): + pattern = r"/ip ssh\s+set.*strong-crypto=no" + match = re.search(pattern, config_data) + + if match: + print(f"{Style.BRIGHT}[+] Checking SSH Strong Crypto{Style.RESET_ALL}") + print(f"{Fore.YELLOW} [!] Warning: SSH is configured with 'strong-crypto=no'. This reduces the security of SSH connections by allowing weaker encryption algorithms.{Style.RESET_ALL}") + print(f"{Fore.GREEN} [*] Solution: Set 'strong-crypto=yes' to enhance security. This will: {Style.RESET_ALL}") + print(f"{Fore.YELLOW} - Use stronger encryption, HMAC algorithms, and larger DH primes while disallowing weaker ones.{Style.RESET_ALL}") + print(f"{Fore.YELLOW} - Prefer 256-bit and 192-bit encryption instead of 128 bits.{Style.RESET_ALL}") + print(f"{Fore.YELLOW} - Disable null encryption.{Style.RESET_ALL}") + print(f"{Fore.YELLOW} - Prefer sha256 for hashing instead of sha1.{Style.RESET_ALL}") + print(f"{Fore.YELLOW} - Disable md5.{Style.RESET_ALL}") + print(f"{Fore.YELLOW} - Use a 2048-bit prime for Diffie Hellman exchange instead of 1024-bit.{Style.RESET_ALL}") + print() + +def check_connection_tracking(config_data): + pattern = r"/ip firewall connection tracking\s+set.*enabled=(auto|yes)" + match = re.search(pattern, config_data) + + if match: + enabled_value = match.group(1) + print(f"{Style.BRIGHT}[+] Checking Connection Tracking{Style.RESET_ALL}") + print(f"{Fore.YELLOW} [!] Connection Tracking is currently set to '{enabled_value}'.{Style.RESET_ALL}") + print(f"{Fore.GREEN} [*] Advice: If this device is being used as a transit router, you might consider disabling Connection Tracking to improve performance. However, proceed with caution as it can affect certain network features.{Style.RESET_ALL}") + print() + + +def check_romon_enabled(config_data): + pattern = r"/tool romon\s+set.*enabled=yes" + match = re.search(pattern, config_data) + + if match: + print(f"{Style.BRIGHT}[+] Checking RoMON Settings{Style.RESET_ALL}") + print(f"{Fore.YELLOW} [!] Warning: RoMON is enabled. If you are using RoMON, you should carefully manage its settings, as an attacker might use it to gain access to other RouterOS devices.{Style.RESET_ALL}") + print(f"{Fore.GREEN} [*] Advice: Regularly review RoMON configurations and ensure that only authorized devices can use RoMON.{Style.RESET_ALL}") + print() + +def check_mac_server_settings(config_data): + mac_server_pattern = r"/tool mac-server\s+set.*allowed-interface-list=all" + mac_winbox_pattern = r"/tool mac-server mac-winbox\s+set.*allowed-interface-list=all" + mac_ping_pattern = r"/tool mac-server ping\s+set.*enabled=yes" + + mac_server_match = re.search(mac_server_pattern, config_data) + mac_winbox_match = re.search(mac_winbox_pattern, config_data) + mac_ping_match = re.search(mac_ping_pattern, config_data) + + if mac_server_match or mac_winbox_match or mac_ping_match: + print(f"{Style.BRIGHT}[+] Checking MAC Server Settings{Style.RESET_ALL}") + + if mac_server_match: + print(f"{Fore.YELLOW} [!] Warning: MAC Server is allowed on all interfaces (allowed-interface-list=all). This compromises the security of the Winbox interface.{Style.RESET_ALL}") + + if mac_winbox_match: + print(f"{Fore.YELLOW} [!] Warning: MAC Winbox is allowed on all interfaces (allowed-interface-list=all). This compromises the security of the Winbox interface.{Style.RESET_ALL}") + + if mac_ping_match: + print(f"{Fore.YELLOW} [!] Warning: MAC Ping is enabled. Possible unwanted traffic.{Style.RESET_ALL}") + + print(f"{Fore.GREEN} [*] Solution: Limit MAC server and MAC Winbox to specific trusted interfaces, and disable MAC Ping if it is not required.{Style.RESET_ALL}") + print() + +def check_snmp_communities(config_data): + public_pattern = r'/snmp community[\s\S]*?(set|add)[\s\S]*?name=public' + private_pattern = r'/snmp community[\s\S]*?(set|add)[\s\S]*?name=private' + + public_match = re.search(public_pattern, config_data) + private_match = re.search(private_pattern, config_data) + + if public_match or private_match: + print(f"{Style.BRIGHT}[+] Checking SNMP Communities{Style.RESET_ALL}") + + if public_match: + print(f"{Fore.YELLOW} [!] Warning: SNMP community 'public' is in use. Possible Information Gathering attack vector by bruteforcing community string.{Style.RESET_ALL}") + + if private_match: + print(f"{Fore.YELLOW} [!] Warning: SNMP community 'private' is in use. Possible Information Gathering attack vector by bruteforcing community string.{Style.RESET_ALL}") + + print(f"{Fore.GREEN} [*] Solution: Change the SNMP community names to something more secure, and restrict SNMP access to trusted IP addresses only.{Style.RESET_ALL}") + print() + +def check_port_forwarding_rules(config_data): + nat_pattern = r"add\s+action=dst-nat(?:\s+\S+)*\s+to-addresses=\S+(?:\s+\S+)*\s+to-ports=\S+" + matches = re.findall(nat_pattern, config_data, re.DOTALL) + + if matches: + print(f"{Style.BRIGHT}[+] Checking Port Forwarding (dst-nat){Style.RESET_ALL}") + for match in matches: + print(f"{Fore.YELLOW} [!] Warning: Port forwarding detected:{Style.RESET_ALL} {match.strip()}. This may expose your internal network to the internet.") + print(f"{Fore.CYAN} [!] Risk: Using port forwarding reduces the level of network security. A device exposed to the internet via port forwarding can be hacked, putting the internal infrastructure at risk.") + print(f"{Fore.GREEN} [*] Solution: It's better to avoid port forwarding in favor of VPN servers for accessing the internal infrastructure from outside.{Style.RESET_ALL}") + print() + + +def main(): + banner() + args = parse_arguments() + config_file = args.config_file + + try: + with open(config_file, 'r') as file: + config_data = file.read() + print(f"{Style.BRIGHT}[*] Analyzing the configuration file: {config_file} ({round(len(config_data)/1024, 2)} KB){Style.RESET_ALL}\n") + extract_device_info(config_data) + check_smb_enabled(config_data) + check_rmi_services(config_data) + check_upnp_enabled(config_data) + check_wifi_settings(config_data) + check_dns_settings(config_data) + check_ddns_enabled(config_data) + check_poe_settings(config_data) + check_protected_routerboot(config_data) + check_socks_enabled(config_data) + check_bandwidth_server_enabled(config_data) + check_ospf_interfaces(config_data) + check_vrrp_interfaces(config_data) + check_discovery_protocols(config_data) + check_user_password_policies(config_data) + check_ssh_strong_crypto(config_data) + check_connection_tracking(config_data) + check_romon_enabled(config_data) + check_mac_server_settings(config_data) + check_snmp_communities(config_data) + check_port_forwarding_rules(config_data) + + except FileNotFoundError: + print(f"{Fore.RED}Error: The file '{config_file}' was not found.{Style.RESET_ALL}") + except Exception as e: + print(f"{Fore.RED}Error: An unexpected error occurred: {e}{Style.RESET_ALL}") + +# Проверка на запуск скрипта +if __name__ == "__main__": + main() diff --git a/setup.py b/setup.py index 41a6675..7b69ce7 100644 --- a/setup.py +++ b/setup.py @@ -1,23 +1,23 @@ from setuptools import setup, find_packages setup( - name="vex", - version="1.1", - url="https://github.com/casterbyte/vex", + name="sara", + version="1.0", + url="https://github.com/casterbyte/sara", author="Magama Bazarov", author_email="caster@exploit.org", - scripts=['vex.py'], + scripts=['sara.py'], description="RouterOS Security Inspector", long_description=open('README.md').read(), long_description_content_type='text/markdown', license="Apache-2.0", - keywords=['network security', 'mikrotik', 'routeros'], + keywords=['mikrotik', 'routeros', 'config analyzer'], packages=find_packages(), install_requires=[ 'colorama', ], entry_points={ - "console_scripts": ["vex = vex:main"], + "console_scripts": ["sara = sara:main"], }, python_requires='>=3.11', ) \ No newline at end of file diff --git a/vex.py b/vex.py deleted file mode 100644 index f33b7a2..0000000 --- a/vex.py +++ /dev/null @@ -1,488 +0,0 @@ -#!/usr/bin/env python3 - -import re -import argparse -from colorama import init, Fore, Style - -# Colorama -init(autoreset=True) - -# Banner -banner = ''' - .%. .%. - :@@@: .%@@- - :@@+@@: .%@*@@- - :@@- -@@:.@@= .@@= - :@@: :@@@@= .@@- - :@@: =@@+ .@@= - :@@: :@@@@- .@@= - -@@: -@@-:@@= .%@+ - -@@: =@@- :@@= .%@+ - =@@@@@@@@@@@@@@@@@@@@@@@@@@@@= - @@% @@* - *@# #@# - #@# *@# - #@* +@% - %@@@@@@@@@@@@@@@@@@@@% -''' - -print(banner) -print(" Vex: RouterOS Security Inspector") -print(" Designed for security engineers\n") -print(" For documentation visit: " + "https://github.com/casterbyte/Vex\n") -print(" " + Fore.YELLOW + "Author: " + Style.RESET_ALL + "Magama Bazarov, ") -print(" " + Fore.YELLOW + "Pseudonym: " + Style.RESET_ALL + "Caster") -print(" " + Fore.YELLOW + "Version: " + Style.RESET_ALL + "1.1") -print(" " + Fore.WHITE + "CAUTION: " + Fore.YELLOW + "For the tool to work correctly, use the RouterOS configuration from using the" + Fore.WHITE + " export verbose" + Fore.YELLOW + " command\n") - -# Device Info -def extract_info(config_content): - info_found = False - info = [] - - version_pattern = r'# .* by RouterOS ([\d.]+)' - software_id_pattern = r'# software id = (\S+)' - model_pattern = r'# model = (\S+)' - serial_number_pattern = r'# serial number = (\S+)' - - version = re.search(version_pattern, config_content) - software_id = re.search(software_id_pattern, config_content) - model = re.search(model_pattern, config_content) - serial_number = re.search(serial_number_pattern, config_content) - - if version: - info.append(f"{Style.BRIGHT + Fore.WHITE}[*] RouterOS Version: {Style.BRIGHT + Fore.YELLOW}{version.group(1)}{Style.RESET_ALL}") - info_found = True - if software_id: - info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Software ID: {Style.BRIGHT + Fore.YELLOW}{software_id.group(1)}{Style.RESET_ALL}") - info_found = True - if model: - info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Model: {Style.BRIGHT + Fore.YELLOW}{model.group(1)}{Style.RESET_ALL}") - info_found = True - if serial_number: - info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Serial Number: {Style.BRIGHT + Fore.YELLOW}{serial_number.group(1)}{Style.RESET_ALL}") - info_found = True - - if info_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] Device Information:{Style.RESET_ALL}") - for line in info: - print(line) - -# Discovery -def check_discovery_settings(config_content): - discovery_found = False - discovery_info = [] - - discovery_pattern = r'/ip neighbor discovery-settings[\s\S]*?set discover-interface-list=all' - if re.search(discovery_pattern, config_content): - discovery_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}Discovery protocols are enabled on all interfaces{Style.RESET_ALL}") - discovery_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}Information Gathering{Style.RESET_ALL}") - discovery_found = True - - if discovery_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] Discovery Protocols:{Style.RESET_ALL}") - for line in discovery_info: - print(line) - -# Bandwidth Server -def check_bandwidth_server(config_content): - bandwidth_found = False - bandwidth_info = [] - - bandwidth_pattern = r'/tool bandwidth-server[\s\S]*?set[\s\S]*?enabled=yes' - if re.search(bandwidth_pattern, config_content): - bandwidth_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}Bandwidth Server is enabled{Style.RESET_ALL}") - bandwidth_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}Potential misuse for traffic analysis and network performance degradation{Style.RESET_ALL}") - bandwidth_found = True - - if bandwidth_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] Bandwidth Server:{Style.RESET_ALL}") - for line in bandwidth_info: - print(line) - -# DNS Check -def check_dns_settings(config_content): - dns_found = False - dns_info = [] - - dns_pattern = r'/ip dns[\s\S]*?set[\s\S]*?allow-remote-requests=yes' - if re.search(dns_pattern, config_content): - dns_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}Router is configured as a DNS server{Style.RESET_ALL}") - dns_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}DNS Flood{Style.RESET_ALL}") - dns_info.append(f"{Style.BRIGHT + Fore.GREEN}[*] Recommendation: {Style.BRIGHT + Fore.GREEN}Consider closing this port from the internet to avoid unwanted traffic{Style.RESET_ALL}") - dns_found = True - - if dns_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] DNS Settings:{Style.RESET_ALL}") - for line in dns_info: - print(line) - -# UPnP Check -def check_upnp_settings(config_content): - upnp_found = False - upnp_info = [] - - upnp_pattern = r'/ip upnp[\s\S]*?set[\s\S]*?enabled=(\w+)' - match = re.search(upnp_pattern, config_content) - if match and match.group(1) == 'yes': - upnp_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}UPnP is enabled{Style.RESET_ALL}") - upnp_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}Potential unauthorized port forwarding and security risks{Style.RESET_ALL}") - upnp_found = True - else: - upnp_info.append(f"{Style.BRIGHT + Fore.GREEN}[*] UPnP is not enabled{Style.RESET_ALL}") - - if upnp_found or not upnp_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] UPnP Settings:{Style.RESET_ALL}") - for line in upnp_info: - print(line) - -# DDNS Check -def check_ddns_settings(config_content): - ddns_found = False - ddns_info = [] - - ddns_pattern = r'/ip cloud[\s\S]*?set[\s\S]*?ddns-enabled=yes' - if re.search(ddns_pattern, config_content): - ddns_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}Dynamic DNS is enabled{Style.RESET_ALL}") - ddns_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}Exposure to dynamic IP changes and potential unauthorized access{Style.RESET_ALL}") - ddns_found = True - - if ddns_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] DDNS Settings:{Style.RESET_ALL}") - for line in ddns_info: - print(line) - -# SSH Strong Crypto -def check_ssh_settings(config_content): - ssh_found = False - ssh_info = [] - - ssh_pattern = r'/ip ssh[\s\S]*?set[\s\S]*?strong-crypto=no' - if re.search(ssh_pattern, config_content): - ssh_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}SSH strong crypto is disabled (strong-crypto=no){Style.RESET_ALL}") - ssh_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}Less secure SSH connections{Style.RESET_ALL}") - ssh_info.append(f"{Style.BRIGHT + Fore.GREEN}[*] Recommendation: {Style.BRIGHT + Fore.GREEN}Enable strong crypto (strong-crypto=yes) for enhanced security. This will use stronger encryption, HMAC algorithms, larger DH primes, and disallow weaker ones{Style.RESET_ALL}") - ssh_found = True - - if ssh_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] SSH Strong Crypto:{Style.RESET_ALL}") - for line in ssh_info: - print(line) - -# SOCKS Check -def check_socks_settings(config_content): - socks_found = False - socks_info = [] - - socks_pattern = r'/ip socks[\s\S]*?set[\s\S]*?enabled=(\w+)' - match = re.search(socks_pattern, config_content) - if match and match.group(1) == 'yes': - socks_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}SOCKS proxy is enabled{Style.RESET_ALL}") - socks_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}Potential unauthorized access and misuse of network resources{Style.RESET_ALL}") - socks_info.append(f"{Style.BRIGHT + Fore.GREEN}[*] Recommendation: {Style.BRIGHT + Fore.GREEN}Disable SOCKS proxy or ensure it is properly secured. SOCKS can be used maliciously if RouterOS is compromised{Style.RESET_ALL}") - socks_found = True - else: - socks_info.append(f"{Style.BRIGHT + Fore.GREEN}[*] SOCKS proxy is not enabled{Style.RESET_ALL}") - - if socks_found or not socks_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] SOCKS Settings:{Style.RESET_ALL}") - for line in socks_info: - print(line) - -# VRRP -def check_vrrp_authentication(config_content): - vrrp_found = False - vrrp_info = [] - - vrrp_pattern = r'add\s[^\n]*?authentication=none[^\n]*?name=([\w-]+)' - matches = re.findall(vrrp_pattern, config_content) - - if matches: - for interface_name in matches: - vrrp_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}VRRP interface '{interface_name}' has no authentication{Style.RESET_ALL}") - vrrp_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}Potential unauthorized access and manipulation of VRRP settings{Style.RESET_ALL}") - vrrp_info.append(f"{Style.BRIGHT + Fore.GREEN}[*] Recommendation: {Style.BRIGHT + Fore.GREEN}Configure authentication for VRRP interfaces to prevent unauthorized access{Style.RESET_ALL}") - vrrp_found = True - - if vrrp_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] VRRP Authentication Settings:{Style.RESET_ALL}") - for line in vrrp_info: - print(line) -# ROMON -def check_romon(config_content): - romon_info = [] - - romon_pattern = r'/tool romon[\s\S]*?set[\s\S]*?enabled=yes' - if re.search(romon_pattern, config_content): - romon_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}ROMON is enabled{Style.RESET_ALL}") - romon_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}ROMON can be a jump point to other MikroTik devices and should be monitored carefully{Style.RESET_ALL}") - romon_info.append(f"{Style.BRIGHT + Fore.GREEN}[*] Recommendation: {Style.BRIGHT + Fore.GREEN}Monitor ROMON activities and ensure proper security measures are in place{Style.RESET_ALL}") - else: - romon_info.append(f"{Style.BRIGHT + Fore.GREEN}[*] ROMON is not enabled{Style.RESET_ALL}") - - if romon_info: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] ROMON Settings:{Style.RESET_ALL}") - for line in romon_info: - print(line) - -# MAC Telnet Server -def check_mac_server(config_content): - mac_server_found = False - mac_server_info = [] - - mac_server_pattern = r'/tool mac-server[\s\S]*?set[\s\S]*?allowed-interface-list=all' - if re.search(mac_server_pattern, config_content): - mac_server_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}MAC Telnet server is active on all interfaces{Style.RESET_ALL}") - mac_server_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}This reduces the security of the Winbox interface. Filter access{Style.RESET_ALL}") - mac_server_found = True - - if mac_server_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] MAC Server Settings:{Style.RESET_ALL}") - for line in mac_server_info: - print(line) - -# MAC Winbox Server -def check_mac_winbox_server(config_content): - mac_winbox_found = False - mac_winbox_info = [] - - mac_winbox_pattern = r'/tool mac-server mac-winbox[\s\S]*?set[\s\S]*?allowed-interface-list=all' - if re.search(mac_winbox_pattern, config_content): - mac_winbox_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}MAC Winbox Server is accessible on all interfaces{Style.RESET_ALL}") - mac_winbox_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}This reduces the security of the Winbox interface. Filter access{Style.RESET_ALL}") - mac_winbox_found = True - - if mac_winbox_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] MAC Winbox Server Settings:{Style.RESET_ALL}") - for line in mac_winbox_info: - print(line) - -# MAC Ping Server -def check_mac_ping_server(config_content): - mac_ping_found = False - mac_ping_info = [] - - mac_ping_pattern = r'/tool mac-server ping[\s\S]*?set[\s\S]*?enabled=yes' - if re.search(mac_ping_pattern, config_content): - mac_ping_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}MAC Ping Server is enabled{Style.RESET_ALL}") - mac_ping_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}Possible unwanted traffic{Style.RESET_ALL}") - mac_ping_found = True - - if mac_ping_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] MAC Ping Server Settings:{Style.RESET_ALL}") - for line in mac_ping_info: - print(line) - - - -# SNMP Community Check -def check_snmp_community(config_content): - snmp_found = False - snmp_info = [] - - public_pattern = r'/snmp community[\s\S]*?set[\s\S]*?name=public' - private_pattern = r'/snmp community[\s\S]*?set[\s\S]*?name=private' - - public_match = re.search(public_pattern, config_content) - private_match = re.search(private_pattern, config_content) - - if public_match: - snmp_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}SNMP community 'public' is in use{Style.RESET_ALL}") - snmp_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}Information Gathering{Style.RESET_ALL}") - snmp_info.append(f"{Style.BRIGHT + Fore.GREEN}[*] Recommendation: {Style.BRIGHT + Fore.GREEN}Change the community name to something more secure{Style.RESET_ALL}") - snmp_found = True - - if private_match: - snmp_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}SNMP community 'private' is in use{Style.RESET_ALL}") - snmp_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}Information Gathering{Style.RESET_ALL}") - snmp_info.append(f"{Style.BRIGHT + Fore.GREEN}[*] Recommendation: {Style.BRIGHT + Fore.GREEN}Change the community name to something more secure{Style.RESET_ALL}") - snmp_found = True - - if snmp_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] SNMP:{Style.RESET_ALL}") - for line in snmp_info: - print(line) - -# OSPF Check -def check_ospf_templates(config_content): - ospf_found = False - ospf_info = [] - - ospf_pattern = r'add\s[^\n]*?interfaces=([\w-]+)[^\n]*' - matches = re.findall(ospf_pattern, config_content) - - for match in matches: - interface_block = re.search(rf'add[^\n]*?interfaces={match}[^\n]*', config_content).group(0) - missing_passive = 'passive' not in interface_block - missing_auth = 'auth=' not in interface_block - - if missing_passive or missing_auth: - if missing_passive: - ospf_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}OSPF interface '{match}' is not set to passive{Style.RESET_ALL}") - if missing_auth: - ospf_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}OSPF interface '{match}' has no authentication{Style.RESET_ALL}") - ospf_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}Potential unauthorized access and network disruption{Style.RESET_ALL}") - ospf_info.append(f"{Style.BRIGHT + Fore.GREEN}[*] Recommendation: {Style.BRIGHT + Fore.GREEN}Configure authentication and passive mode for OSPF interfaces to enhance security{Style.RESET_ALL}") - ospf_found = True - - if ospf_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] OSPF Interface Templates Check:{Style.RESET_ALL}") - for line in ospf_info: - print(line) - -# User Settings Check (Pass Length) -def check_user_settings(config_content): - user_settings_found = False - user_settings_info = [] - - user_settings_pattern = r'/user settings[\s\S]*?set[\s\S]*?minimum-categories=0[\s\S]*?minimum-password-length=0' - if re.search(user_settings_pattern, config_content): - user_settings_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}No minimum password complexity or length requirements{Style.RESET_ALL}") - user_settings_info.append(f"{Style.BRIGHT + Fore.GREEN}[*] Recommendation: {Style.BRIGHT + Fore.YELLOW}Set minimum password complexity and length requirements to enhance security{Style.RESET_ALL}") - user_settings_found = True - - if user_settings_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] Password Strength Requirements:{Style.RESET_ALL}") - for line in user_settings_info: - print(line) - -# PoE Check -def check_poe_settings(config_content): - poe_found = False - poe_info = [] - - poe_auto_on_pattern = r'/interface ethernet[\s\S]*?poe-out=auto-on' - poe_forced_on_pattern = r'/interface ethernet[\s\S]*?poe-out=forced-on' - - poe_auto_on_match = re.search(poe_auto_on_pattern, config_content) - poe_forced_on_match = re.search(poe_forced_on_pattern, config_content) - - if poe_auto_on_match: - poe_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}PoE is set to auto-on{Style.RESET_ALL}") - poe_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}There is a risk of damaging connected devices by unexpectedly supplying power to the port{Style.RESET_ALL}") - poe_info.append(f"{Style.BRIGHT + Fore.GREEN}[*] Recommendation: {Style.BRIGHT + Fore.YELLOW}Review and set PoE settings appropriately{Style.RESET_ALL}") - poe_found = True - - if poe_forced_on_match: - poe_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}PoE is set to forced-on{Style.RESET_ALL}") - poe_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}There is a significant risk of damaging connected devices by unexpectedly supplying power to the port{Style.RESET_ALL}") - poe_info.append(f"{Style.BRIGHT + Fore.GREEN}[*] Recommendation: {Style.BRIGHT + Fore.YELLOW}Review and set PoE settings appropriately{Style.RESET_ALL}") - poe_found = True - - if poe_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] PoE Settings:{Style.RESET_ALL}") - for line in poe_info: - print(line) - -# SMB Check -def check_smb_settings(config_content): - smb_found = False - smb_info = [] - - smb_section_pattern = r'/ip smb\s*set\s.*?enabled=(\w+)' - match = re.search(smb_section_pattern, config_content) - - if match and match.group(1) == 'yes': - smb_info.append(f"{Style.BRIGHT + Fore.RED}[!] Warning: {Style.BRIGHT + Fore.YELLOW}SMB is enabled{Style.RESET_ALL}") - smb_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Impact: {Style.BRIGHT + Fore.YELLOW}Reading files, potential CVE-2018-7445{Style.RESET_ALL}") - smb_info.append(f"{Style.BRIGHT + Fore.WHITE}[*] Recommendation: {Style.BRIGHT + Fore.GREEN}Are you sure you want SMB? If you don't need it, turn it off. Be careful{Style.RESET_ALL}") - smb_found = True - - if smb_found: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] SMB Settings:{Style.RESET_ALL}") - for line in smb_info: - print(line) - -# Service Check -def check_services(config_content): - services_info = [] - - # Patterns for each service - service_patterns = { - "Telnet": r'/ip service[\s\S]*?set telnet address=.* disabled=(\S+)', - "FTP": r'/ip service[\s\S]*?set ftp address=.* disabled=(\S+)', - "WWW (HTTP)": r'/ip service[\s\S]*?set www address=.* disabled=(\S+)', - "SSH": r'/ip service[\s\S]*?set ssh address=.* disabled=(\S+)', - "WWW-SSL (HTTPS)": r'/ip service[\s\S]*?set www-ssl address=.* disabled=(\S+)', - "API": r'/ip service[\s\S]*?set api address=.* disabled=(\S+)', - "Winbox": r'/ip service[\s\S]*?set winbox address=.* disabled=(\S+)', - "API-SSL": r'/ip service[\s\S]*?set api-ssl address=.* disabled=(\S+)', - } - - for service, pattern in service_patterns.items(): - match = re.search(pattern, config_content) - if match: - status = match.group(1) - if status == 'no': - if service in ["Telnet", "FTP", "API", "API-SSL"]: - services_info.append(f"{Style.BRIGHT + Fore.RED}[*] {service} is enabled{Style.RESET_ALL} - {Style.BRIGHT + Fore.GREEN}Consider disabling for security reasons{Style.RESET_ALL}") - else: - services_info.append(f"{Style.BRIGHT + Fore.YELLOW}[*] {service} is enabled{Style.RESET_ALL}") - else: - services_info.append(f"{Style.BRIGHT + Fore.GREEN}[*] {service} is disabled{Style.RESET_ALL}") - else: - services_info.append(f"{Style.BRIGHT + Fore.RED}[!] {service} configuration not found{Style.RESET_ALL}") - - if services_info: - print(f"{Fore.CYAN}" + "-" * 30 + Style.RESET_ALL) - print(f"{Fore.CYAN}[+] RMI Interfaces Status:{Style.RESET_ALL}") - for line in services_info: - print(line) - - # General recommendation - print(f"{Style.BRIGHT + Fore.GREEN}[!] Recommendation:{Style.RESET_ALL} {Style.BRIGHT + Fore.GREEN}Restrict access to RMI only from trusted subnets{Style.RESET_ALL}") - -# Main -def main(): - parser = argparse.ArgumentParser(description="Vex: RouterOS Security Inspector") - parser.add_argument("--config", required=True, type=str, help="Path to the RouterOS configuration file") - args = parser.parse_args() - - config_file = args.config - - print(f"{Fore.GREEN}[*] Config Analyzing...{Style.RESET_ALL}") - try: - with open(config_file, 'r') as file: - config_content = file.read() - extract_info(config_content) - check_discovery_settings(config_content) - check_bandwidth_server(config_content) - check_dns_settings(config_content) - check_ddns_settings(config_content) - check_upnp_settings(config_content) - check_ssh_settings(config_content) - check_socks_settings(config_content) - check_romon(config_content) - check_mac_server(config_content) - check_mac_winbox_server(config_content) - check_mac_ping_server(config_content) - check_vrrp_authentication(config_content) - check_snmp_community(config_content) - check_ospf_templates(config_content) - check_user_settings(config_content) - check_poe_settings(config_content) - check_smb_settings(config_content) - check_services(config_content) - except Exception as e: - print(f"{Fore.RED}Error reading file: {e}{Style.RESET_ALL}") - -if __name__ == "__main__": - main()