mirror of
https://github.com/casterbyte/Sara.git
synced 2025-07-10 14:44:30 +02:00
sara v1.0
This commit is contained in:
parent
0833d202d5
commit
d9f23b2998
5 changed files with 508 additions and 656 deletions
240
README.md
240
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.
|
||||
|
||||

|
||||
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, <caster@exploit.org>
|
||||
Pseudonym: Caster
|
||||
Version: 1.1
|
||||
RouterOS Security Inspector. Designed for Security Professionals
|
||||
|
||||
Author: Magama Bazarov, <caster@exploit.org>
|
||||
```
|
||||
|
||||
# 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, <caster@exploit.org>
|
||||
|
||||
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, <caster@exploit.org>
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
424
sara.py
Normal file
424
sara.py
Normal file
|
@ -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, <caster@exploit.org>\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()
|
12
setup.py
12
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',
|
||||
)
|
488
vex.py
488
vex.py
|
@ -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, <caster@exploit.org>")
|
||||
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()
|
Loading…
Add table
Add a link
Reference in a new issue