Control a Kali Linux Laptop from Windows 11: SSH, Hermes, Remote Desktop and Screen Mirroring
This tutorial turns a Windows 11 laptop into the control seat for a physical Kali Linux laptop on the same home network. You get passwordless SSH, Hermes Agent running commands on Kali, a separate remote desktop with XRDP, and a live mirror of the laptop’s real screen with x11vnc.
I use this setup on my own machines, and I repeat it every time I add a laptop. The steps below are the ones I follow, with my own addresses, names and keys replaced by example values. A companion tutorial covers the same setup for an Ubuntu laptop: Control an Ubuntu Laptop from Windows 11.
0. Before you start
Section titled “0. Before you start”0.1 The values this tutorial uses
Section titled “0.1 The values this tutorial uses”Every command in this tutorial uses the example values below, so you can copy it as it is. These are not your values. Replace each one with your own before you run a command.
| What | Value in this tutorial | How to find yours |
|---|---|---|
| Home network | 192.168.50.0/24 |
ipconfig on Windows |
| Router (gateway and DNS) | 192.168.50.1 |
ipconfig → Default Gateway |
| Windows laptop address | 192.168.50.10 |
ipconfig → Wi-Fi → IPv4 Address |
| Kali laptop address (static) | 192.168.50.20 |
You choose it in Phase 1 |
| Wi-Fi connection name on Kali | HomeWiFi |
nmcli connection show --active on Kali |
| Kali Wi-Fi interface | wlan0 |
ip -4 -br addr on Kali |
| Kali account | labuser |
whoami on Kali |
| Kali hostname | lab-Kali |
hostname on Kali |
| Windows account folder | C:\Users\Lab User |
echo $env:USERPROFILE in PowerShell |
| SSH alias, key file and Hermes profile | kali-lab, kali_lab, kali-lab |
You choose them in Phases 4 and 6 |
| Hermes gateway name | Kali Lab |
You choose it in Phase 6 |
| LM Studio API port | 1234 (LM Studio’s default) |
LM Studio → Developer → Local Server |
| Folder for the screen launcher and its icon | C:\RemoteAccess |
You choose it in Phase 7 |
The example Windows account folder contains a space on purpose. Many real account folders have one, and a space breaks a path that is not in quotes.
A short Your values note marks each command that holds a value from this table.
0.2 Name consistency
Section titled “0.2 Name consistency”- The SSH alias is
kali-lab(hyphen). - The key file is
kali_lab(underscore). - The login user is
labuser. - The Hermes profile that drives Kali is also named
kali-lab.
These are four different things. Most errors during this setup come from mixing them up (Phase 10).
0.3 Ports and services
Section titled “0.3 Ports and services”| Port | Service | Where it listens | Reached from Windows by |
|---|---|---|---|
| TCP 22 | OpenSSH | Kali, all interfaces | ssh kali-lab, Hermes (both modes) |
| TCP 3389 | xrdp | Kali, all interfaces | Remote Desktop app (mstsc) |
| TCP 5900 | x11vnc (on demand) | Kali localhost only | SSH tunnel → localhost:5901 on Windows |
| TCP 1234 | LM Studio API | Windows | Kali’s Hermes (Mode B model) |
0.4 What you need, and the versions this was tested with
Section titled “0.4 What you need, and the versions this was tested with”- A Windows 11 laptop with Hermes Desktop installed and signed in to a model provider.
- A physical Kali Linux laptop with the default Xfce desktop, and an administrator account (a member of the
sudogroup). - Both laptops on the same home Wi-Fi network, which you control.
- LM Studio on Windows, only for Mode B.
- PowerShell on Windows. Windows PowerShell 5.1 and PowerShell 7 both work for this tutorial.
| Machine | Software | Tested version |
|---|---|---|
| Windows | OpenSSH client | OpenSSH_for_Windows_9.5p2 |
| Windows | Hermes Desktop (Microsoft Store) | package NousResearchInc.HermesAgent, agent v0.22.5 |
| Windows | LM Studio | 0.4.26 |
| Windows | TigerVNC viewer | 1.16.2 |
| Kali | Kali GNU/Linux Rolling | 2026.3 |
| Kali | Desktop stack | Xfce 4.20, LightDM 1.33, xrdp 0.10.6, xorgxrdp 0.10.5, dbus-x11 |
| Kali | OpenSSH server | 10.6p1 |
| Kali | Other | x11vnc 0.9.17, tmux 3.7c, ufw installed but inactive |
| Kali | Hermes Agent (Mode B) | v0.22.5, git install in ~/.hermes/hermes-agent |
Administrator-account warning: because labuser is an administrator, commands Hermes runs as labuser can reach everything this account can, and some commands can request sudo.
- Do not configure passwordless unrestricted
sudo. - Do not put the
sudopassword in Hermes prompts or configuration. - Keep command-approval safeguards enabled.
This tutorial uses one account for everything: SSH, Hermes, RDP and VNC all use labuser.
1. Understand the connections
Section titled “1. Understand the connections”1.1 Architecture
Section titled “1.1 Architecture”flowchart LR
subgraph W["Windows 11 (192.168.50.10)"]
HD["Hermes Desktop"]
PA["Profile kali-lab<br/>(agent on Windows)"]
PD["Profile default<br/>(commands on Windows)"]
RDP["Remote Desktop app<br/>mstsc.exe"]
VV["TigerVNC viewer<br/>localhost:5901"]
LMS["LM Studio API :1234"]
end
subgraph K["Kali laptop (192.168.50.20, user labuser)"]
SSHD["OpenSSH :22"]
HK["Hermes on Kali<br/>(Mode B)"]
XR["xrdp :3389<br/>separate desktop :10"]
XV["x11vnc localhost:5900<br/>real screen :0"]
end
HD --> PA
HD --> PD
PA -- "Mode A: ssh commands (key kali_lab)" --> SSHD
HD -. "Mode B: SSH tunnel + dashboard" .-> HK
HK -- "model requests" --> LMS
RDP -- "Kali password of labuser" --> XR
VV -- "SSH tunnel (key)" --> XV
SSH, Remote Desktop, and VNC are separate paths:
- SSH carries Hermes traffic (both modes) and the VNC tunnel.
- XRDP gives you a separate graphical session.
- x11vnc shows the laptop’s actual screen.
None of the graphical paths gives Hermes mouse-and-keyboard control of the GUI; Hermes works through the terminal.
1.2 Choose how Hermes should run
Section titled “1.2 Choose how Hermes should run”flowchart TD
Q{"Where should the Hermes agent run?"}
Q -->|"On Windows; commands on Kali"| A["Mode A<br/>Gateway: This device<br/>Profile: kali-lab (terminal.backend = ssh)"]
Q -->|"On Kali; Desktop is only the window"| B["Mode B<br/>Gateway: Kali Lab (registered SSH)<br/>Hermes installed on Kali"]
A --> A2["Model + agent on the fast laptop<br/>commands run as labuser on Kali"]
B --> B2["Agent, sessions, memory on Kali<br/>model via LM Studio on Windows"]
- Mode A is the recommended mode when the Windows laptop is the more powerful machine: Windows is the brain, and the Kali laptop is the hands. It is configured in a separate
kali-labprofile, so thedefaultprofile keeps running commands on Windows. A per-profile setup also stops commands meant for Windows from running on Kali by accident. - Mode B: Desktop opens the Hermes installed on Kali through the registered SSH gateway Kali Lab.
1.3 Choose how to see Kali’s screen
Section titled “1.3 Choose how to see Kali’s screen”| You want | Use | Phase |
|---|---|---|
| Your own separate Kali desktop (works even if nobody is at the laptop) | XRDP + Windows Remote Desktop | 5 |
| The same screen that is on the laptop’s display (like a VM console) | x11vnc + TigerVNC over SSH (Open-KaliScreen.ps1) |
7.4 |
| VMware Workstation console | Only for Kali running as a VM on Windows, which is a different Kali | 7.2 |
Phase 1: Confirm the LAN and set Kali’s IP address
Section titled “Phase 1: Confirm the LAN and set Kali’s IP address”1.1 Confirm Windows’ address
Section titled “1.1 Confirm Windows’ address”On the Windows laptop, open PowerShell and run:
ipconfigGet-NetConnectionProfileFind the adapter named Wi-Fi. Example output:
Wireless LAN adapter Wi-Fi: IPv4 Address. . . . . . . . . . . : 192.168.50.10 Subnet Mask . . . . . . . . . . . : 255.255.255.0 Default Gateway . . . . . . . . . : 192.168.50.1Get-NetConnectionProfile must show Wi-Fi → Private. Firewall rules in this tutorial apply to the Private profile only.
Do not use the other adapters. If VirtualBox or VMware is installed, ipconfig also lists their virtual adapters: VirtualBox Host-Only (usually 192.168.56.1), and VMware’s VMnet1 and VMnet8 (VMware picks their subnets at install). The Kali laptop cannot reach these virtual networks. LM Studio, for example, can report “Reachable at 192.168.56.1:1234”; that is the VirtualBox adapter, not the LAN. Use the Wi-Fi address.
1.2 Inspect Kali’s current network
Section titled “1.2 Inspect Kali’s current network”On the Kali laptop itself, open a terminal:
ip -4 -br addrip routenmcli connection show --activenmcli device statusRecord the active interface, connection profile name, current subnet, default gateway, and DNS servers. To inspect connection and DNS details:
nmcli device showresolvectl status # if unavailable: cat /etc/resolv.confExample output:
wlan0 UP 192.168.50.20/24default via 192.168.50.1 dev wlan0 proto static metric 600active connection: HomeWiFi (802-11-wireless, wlan0)1.3 Make Kali use a static address through Kali itself (no router access needed)
Section titled “1.3 Make Kali use a static address through Kali itself (no router access needed)”There are two methods. Use one method for the active connection, not both.
Before setting a manual address, check whether 192.168.50.20 is already assigned to Kali. If it is not, verify that no other device uses it. A ping with no reply does not prove an address is unused. A static address set on Kali is not reserved in the router. If the address falls inside the router’s DHCP range, another device could receive it later; if that ever happens, pick an address outside the DHCP pool.
Method A: Kali desktop Network Settings
Section titled “Method A: Kali desktop Network Settings”-
Open Kali’s Settings → Network (labels vary by desktop version).
-
Select the active Wi-Fi network (
HomeWiFiin this tutorial) and open its settings (gear icon). -
Open IPv4.
-
Change the method from Automatic (DHCP) to Manual.
-
Enter:
Field Value Address 192.168.50.20Netmask 255.255.255.0Gateway 192.168.50.1DNS 192.168.50.1 -
Save and reconnect if required. Continue to step 1.4.
Method B: Kali terminal with NetworkManager nmcli
Section titled “Method B: Kali terminal with NetworkManager nmcli”Find the exact connection profile name, then apply the manual address:
nmcli connection show --activeCON="HomeWiFi" # use your exact connection namenmcli connection show "$CON" # optional: review current settings first
sudo nmcli connection modify "$CON" \ ipv4.method manual \ ipv4.addresses "192.168.50.20/24" \ ipv4.gateway "192.168.50.1" \ ipv4.dns "192.168.50.1"
sudo nmcli connection up "$CON"Confirm what is stored:
nmcli -g ipv4.method,ipv4.addresses,ipv4.gateway,ipv4.dns,connection.autoconnect connection show "$CON"# expected: manual / 192.168.50.20/24 / 192.168.50.1 / 192.168.50.1 / yes1.4 Verify Kali networking before continuing
Section titled “1.4 Verify Kali networking before continuing”On Kali:
ip -4 -br addrip routeping -c 4 192.168.50.1 # LAN routingping -c 4 1.1.1.1 # internet reachabilitygetent hosts kali.org # DNSIf any test fails, correct the address, prefix, gateway, or DNS before continuing. Then reboot and confirm the address persists:
sudo reboot# after restart:ip -4 -br addr # wlan0 must show 192.168.50.20/241.5 Test Windows-to-Kali reachability
Section titled “1.5 Test Windows-to-Kali reachability”On Windows PowerShell:
ping 192.168.50.20Test-NetConnection 192.168.50.20 -Port 22 # after Phase 3A successful ping with TcpTestSucceeded : False on port 22 means the network is fine but SSH is not running yet. Phase 3 fixes it.
Phase 2: Verify the existing Kali account
Section titled “Phase 2: Verify the existing Kali account”Do not create another user. On Kali, signed in as labuser:
whoamiidsudo -vsudo -lgetent passwd labuser | cut -d: -f7Expected:
whoami→labuser.id→ uid 1000, groups includesudoandadm.admletslabuserread/var/log/xrdp*.logand the system journal without sudo, which is handy for troubleshooting.- The login shell is
/usr/bin/zsh, Kali’s default. This matters in Phase 6B: non-interactive SSH commands only read~/.zshenv.
If your account is not already an administrator, do not change account permissions as part of this network setup; review the account first.
Phase 3: Install and start the SSH server on Kali
Section titled “Phase 3: Install and start the SSH server on Kali”3.1 Install OpenSSH Server
Section titled “3.1 Install OpenSSH Server”On Kali, as labuser:
sudo apt updatesudo apt install -y openssh-serversudo systemctl enable --now sshCheck the service and listening port:
systemctl is-enabled ssh; systemctl is-active ssh # enabled / activessh -V # OpenSSH_10.6p1 Debian-1ss -ltn | grep ':22 ' # LISTEN 0.0.0.0:22 and [::]:22The stock Kali sshd_config works unchanged (UsePAM yes, KbdInteractiveAuthentication no, X11Forwarding yes, AcceptEnv LANG LC_* COLORTERM NO_COLOR). Key login and password login are both allowed by default.
OpenSSH 10 behaviour to know: sshd penalises a source address that keeps failing authentication (PerSourcePenalties). After repeated bad logins (for example, a Hermes Desktop entry with a wrong user name), the Kali journal shows srclimit_penalise: 192.168.50.10/32: activating ipv4 penalty .... sshd then drops every new SSH connection from Windows for a while, including ones that are configured correctly. Fix the bad client and wait about a minute (Phase 10).
3.2 Firewall
Section titled “3.2 Firewall”sudo ufw status verbose # on a default Kali: Status: inactive (ufw installed, not enabled)With UFW inactive, nothing else is needed. If you enable UFW later, allow the Windows laptop first:
sudo ufw allow from 192.168.50.10 to any port 22 proto tcpsudo ufw allow from 192.168.50.10 to any port 3389 proto tcpVNC needs no rule: it listens on localhost and travels inside SSH. Do not enable a default-deny policy without reviewing existing lab rules. Never create router port-forwarding for SSH, RDP, or VNC; keep them on the trusted LAN or behind a VPN.
Phase 4: Configure Windows SSH key authentication and the kali-lab alias
Section titled “Phase 4: Configure Windows SSH key authentication and the kali-lab alias”This phase makes this command work without a password or passphrase:
ssh kali-lab4.1 Check whether the key already exists
Section titled “4.1 Check whether the key already exists”On Windows, open PowerShell (normal, not administrator):
New-Item -ItemType Directory -Force "$env:USERPROFILE\.ssh" | Out-NullTest-Path "$env:USERPROFILE\.ssh\kali_lab"Test-Path "$env:USERPROFILE\.ssh\kali_lab.pub"If both exist, keep using them. Do not overwrite an existing key. If ssh-keygen asks ...kali_lab already exists. Overwrite (y/n)?, answer n. Answering y replaces the key, and Kali rejects the new one until you install the new .pub again.
Create the key only if it does not exist:
ssh-keygen -t ed25519 -f "$env:USERPROFILE\.ssh\kali_lab"Press Enter twice at the passphrase prompts, so the key has no passphrase. Hermes connects with BatchMode=yes and cannot type a passphrase. The Windows ssh-agent service, which could hold an unlocked key, is Disabled by default (step 4.6). The key is protected by your Windows account instead.
The files are:
kali_lab: private key. Never share it, copy it to Kali, or paste it into chat.kali_lab.pub: public key. This is what goes on Kali.
Check its fingerprint. The output is one line like 256 SHA256:<43 characters> lab user@winpc (ED25519); the comment at the end is your Windows user and computer name:
ssh-keygen -lf "$env:USERPROFILE\.ssh\kali_lab.pub"4.2 Add the public key to the existing account on Kali
Section titled “4.2 Add the public key to the existing account on Kali”On Windows, print the public key:
Get-Content "$env:USERPROFILE\.ssh\kali_lab.pub"Copy the single line starting with ssh-ed25519 and ending with your Windows user and computer name (lab user@winpc in this tutorial). On Kali, as labuser:
mkdir -p ~/.ssh && chmod 700 ~/.sshtouch ~/.ssh/authorized_keysnano ~/.ssh/authorized_keys # paste the line on its own line; keep any existing lineschmod 600 ~/.ssh/authorized_keysVerify:
ls -ld ~/.ssh # drwx------ labuser labuserls -l ~/.ssh/authorized_keys # -rw------- labuser labuserawk '{print $1, $NF}' ~/.ssh/authorized_keys # ssh-ed25519 user@winpc (1 key)If the file is group-writable (mode 664), tighten it to 600. sshd accepts 664 only while your account’s group has no other members, so do not rely on it.
4.3 Verify Kali’s SSH server fingerprint
Section titled “4.3 Verify Kali’s SSH server fingerprint”On Kali (the .pub host key is world-readable, so no sudo is needed):
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub# 256 SHA256:<your Kali host key fingerprint> root@lab-Kali (ED25519)When Windows first connects, it shows ED25519 key fingerprint is SHA256:.... Accept only if it matches. If it ever changes unexpectedly, stop and investigate.
4.4 Create the Windows OpenSSH config alias
Section titled “4.4 Create the Windows OpenSSH config alias”Open the config file:
notepad "$env:USERPROFILE\.ssh\config"Add this block and keep any other Host entries you already have:
Host kali-lab 192.168.50.20 HostName 192.168.50.20 User labuser Port 22 IdentityFile ~/.ssh/kali_lab IdentitiesOnly yesWhy both names are on the Host line:
ssh kali-labworks as the short alias.- Anything that connects to the raw IP also gets user
labuserand keykali_lab. That includes Hermes Desktop’s gateway, Hermes’ SSH backend, and the screen-mirror script. - With only the alias on the
Hostline, Desktop entries that use the IP fall back to the default keys and Kali rejects them.
Rules to remember:
- Alias matching is case-sensitive.
Kali-labdoes not matchkali-lab(ssh -G labuser@Kali-labshowshostname kali-laband the default keys). kali_lab(underscore) is not an alias:ssh kali_lab→Could not resolve hostname kali_lab.- The user part is always
labuser. A file path is never a user name.
Confirm how OpenSSH resolves both names:
ssh -G kali-lab | Select-String '^(user|hostname|port|identityfile|identitiesonly) 'ssh -G 192.168.50.20 | Select-String '^(user|hostname|identityfile) 'Expected for both: user labuser, hostname 192.168.50.20, port 22, identityfile ~/.ssh/kali_lab, identitiesonly yes.
4.5 Test SSH using the exact alias
Section titled “4.5 Test SSH using the exact alias”ssh kali-lab "hostname && whoami && id && pwd"ssh -o BatchMode=yes kali-lab "hostname && whoami && pwd"Expected: lab-Kali, labuser, the groups, /home/labuser. The BatchMode=yes test is the important one: it fails instead of prompting, exactly like Hermes. If it prints the three lines, Hermes can use the key.
To see which key was accepted:
ssh -v kali-lab "hostname" 2>&1 | Select-String 'Offering|Server accepts|Authenticated'# Offering public key: C:\Users\Lab User/.ssh/kali_lab ED25519 SHA256:...# Server accepts key: ...kali_lab...# Authenticated to 192.168.50.20 ([192.168.50.20]:22) using "publickey".Interactive session: ssh kali-lab gives the ┌──(labuser㉿lab-Kali)-[~] prompt; leave with exit. Do not continue to Hermes until both tests pass.
4.6 The Windows ssh-agent (not needed here)
Section titled “4.6 The Windows ssh-agent (not needed here)”Get-Service ssh-agent | Select-Object Status, StartType # default: Stopped / DisabledWith a passphrase-less key, no agent is needed. Running Set-Service/Start-Service ssh-agent in a normal PowerShell fails with Access is denied and Cannot start service, and ssh-add then reports Error connecting to agent. That is expected, not a fault.
Only if you later switch to a passphrase-protected key, run this in an Administrator PowerShell:
Set-Service -Name ssh-agent -StartupType AutomaticStart-Service ssh-agentThen, in your normal PowerShell, run ssh-add "$env:USERPROFILE\.ssh\kali_lab". Hermes must run in the same Windows session that holds the agent.
Phase 5: Configure Kali’s graphical desktop for Remote Desktop (XRDP)
Section titled “Phase 5: Configure Kali’s graphical desktop for Remote Desktop (XRDP)”XRDP gives Windows its own, separate Xfce desktop on the Kali laptop (display :10), using the same labuser account and its Linux password. It does not show what is on the laptop’s screen; for that, see Phase 7.4.
5.1 Confirm Xfce is installed
Section titled “5.1 Confirm Xfce is installed”On Kali:
echo "$XDG_CURRENT_DESKTOP" # XFCE (in a local desktop terminal)dpkg -s kali-desktop-xfce >/dev/null 2>&1 && echo "Kali Xfce is installed" || echo "Kali Xfce is not installed"Xfce is Kali’s default desktop. Install XRDP, the Xorg backend, and the D-Bus helpers:
sudo apt install -y xrdp xorgxrdp dbus-x11Only if Kali has no desktop at all (large download):
sudo apt install -y kali-desktop-xfce xorg xrdp xorgxrdp dbus-x115.2 Configure the XRDP session for your account
Section titled “5.2 Configure the XRDP session for your account”Many recipes use a one-line .xsession that contains just startxfce4. With that file, the Remote Desktop window closes immediately after the password whenever you are also logged in on the laptop. Use this file instead (as labuser on Kali):
cat > ~/.xsession <<'EOF'#!/bin/sh# XRDP desktop. Unsetting the shared user-bus address makes startxfce4# launch a private D-Bus, so this session doesn't collide with a local Xfce login.unset DBUS_SESSION_BUS_ADDRESSexec startxfce4EOFchmod 755 ~/.xsessionls -l ~/.xsession # -rwxr-xr-x labuser labuserWhy this is needed:
- Kali’s systemd gives each user one shared D-Bus.
/etc/X11/Xsession.d/20dbus_xdg-runtimepoints every X session at/run/user/1000/bus(1000 is the first user’s ID). - When
labuseris already logged in on the laptop’s screen, that local Xfce owns the session-manager names on the bus. - A second Xfce started by XRDP finds them taken and quits within a second.
/var/log/xrdp-sesman.logshows:andWindow manager (pid ..., display 10) finished normally in 1 secsWindow manager (pid ..., display 10) exited quickly (1 secs). This could indicate a window manager config problem/var/log/xrdp.logshowsXorg server closed connection. - Windows then simply closes the RDP window.
Unsetting DBUS_SESSION_BUS_ADDRESS makes startxfce4 start a private D-Bus for the RDP desktop, so both desktops coexist: RDP connects while labuser stays logged in locally.
The startup chain is /etc/xrdp/startwm.sh → /etc/X11/Xsession → ~/.xsession. Leave startwm.sh unmodified.
5.3 Start XRDP and inspect the configuration
Section titled “5.3 Start XRDP and inspect the configuration”sudo systemctl enable --now xrdpsystemctl is-enabled xrdp; systemctl is-active xrdp xrdp-sesman # enabled / active activess -ltn | grep ':3389' # LISTEN *:3389id xrdp # groups include ssl-certDefault settings (stock files, unchanged):
| File | Setting | Value | Meaning |
|---|---|---|---|
/etc/xrdp/xrdp.ini |
port |
3389 |
Standard RDP port (some Kali recipes use 3390; this one does not) |
security_layer / crypt_level |
negotiate / high |
TLS negotiated with mstsc | |
max_bpp |
32 |
Full colour | |
| sessions offered | [Xorg], [Xvnc], [vnc-any], [neutrinordp-any] |
Always pick Xorg | |
/etc/xrdp/sesman.ini |
KillDisconnected / DisconnectedTimeLimit |
false / 0 |
Closing the RDP window keeps the desktop running; reconnecting resumes it |
Policy |
Default |
One session per user, resumed on reconnect | |
AllowRootLogin |
true |
Optional hardening: set false (sudo edit + sudo systemctl restart xrdp) |
If the xrdp user is in the ssl-cert group, no TLS key-permission fix is needed. If logs ever show a certificate permission error, run sudo adduser xrdp ssl-cert && sudo systemctl restart xrdp.
5.4 Set or verify your account password for RDP
Section titled “5.4 Set or verify your account password for RDP”XRDP uses the Linux account password, not the SSH key. To change it, run on Kali as labuser:
passwdA wrong password shows as login failed on the xrdp screen, and as pam_authenticate failed / AUTHFAIL: user=labuser in /var/log/xrdp-sesman.log.
5.5 Connect from Windows Remote Desktop
Section titled “5.5 Connect from Windows Remote Desktop”Test the port:
Test-NetConnection 192.168.50.20 -Port 3389 # TcpTestSucceeded : TrueThen:
- Press
Win + R, typemstsc, and press Enter. - Computer:
192.168.50.20(your Kali address). Click Connect and accept the certificate prompt (xrdp’s self-signed certificate). - On the xrdp login screen, set Session: Xorg. Choosing Xvnc fails with
X server failed to start(waitforx: Unable to open display :10) because no VNC server backend is installed for xrdp. - Username:
labuser(your Kali account). Password: the Kali account password.
For an older laptop, start at a moderate resolution: Show Options → Display, for example 1280 × 800.
5.6 Do you have to log out of the laptop before Windows connects?
Section titled “5.6 Do you have to log out of the laptop before Windows connects?”No, not with the .xsession from step 5.2.
- SSH sessions never require logging out; one account can have many.
- XRDP opens a separate desktop and works while you are logged in on the laptop.
- Without the 5.2 file, it does not: the RDP window closes right after the password.
Fallback if an RDP login ever closes immediately again: on the laptop, choose Log Out (locking the screen is not enough), then reconnect.
Ending vs. disconnecting:
- Closing the RDP window only disconnects. The remote desktop keeps running (
KillDisconnected=false), and the next connection resumes it. - To end it, use Log Out inside the RDP desktop.
Phase 6: Connect Hermes to Kali
Section titled “Phase 6: Connect Hermes to Kali”There are two setups. Pick by where the agent should run (section 1.2), or set up both.
Option A: Agent on Windows, commands on Kali (profile kali-lab)
Section titled “Option A: Agent on Windows, commands on Kali (profile kali-lab)”The agent and its model run on Windows; only terminal commands go to Kali as labuser. It is configured in the Hermes profile kali-lab, used from Desktop’s This device gateway.
A.1 Verify the Windows Hermes installation
Section titled “A.1 Verify the Windows Hermes installation”Open a fresh PowerShell:
Get-Command hermeshermes --version # Hermes Agent v0.22.5 · stablehermes profile listhermes doctorssh -V # OpenSSH_for_Windows_9.5p2The Microsoft Store (MSIX) Desktop install keeps its data in %LOCALAPPDATA%\hermes; each profile has its own config.yaml and .env:
| Profile | Config | Secrets/env |
|---|---|---|
default |
%LOCALAPPDATA%\hermes\config.yaml |
%LOCALAPPDATA%\hermes\.env |
kali-lab |
%LOCALAPPDATA%\hermes\profiles\kali-lab\config.yaml |
%LOCALAPPDATA%\hermes\profiles\kali-lab\.env |
If every CLI command prints ⚠ install out of sync (venv: out of sync with uv.lock), followed by a note that this desktop-app-managed install must rebuild the artifact, it comes from the Store-managed runtime and does not block anything; updating Hermes Desktop clears it. If hermes doctor flags .env file missing for default, create it with hermes setup.
A.2 Create the kali-lab profile and set its terminal backend to SSH
Section titled “A.2 Create the kali-lab profile and set its terminal backend to SSH”hermes profile create kali-lab --clone-from default --no-alias ` --description "Agent on Windows; terminal commands run on the Kali laptop over SSH"hermes -p kali-lab config set terminal.backend sshhermes config set terminal.backend local # keep 'default' on Windows--clone-from defaultcopies your default profile’s model and settings into the new profile. Skip the first command if the profile already exists.--no-aliasskips the wrapper command, so you call the profile withhermes -p kali-lab ....-p kali-lab(or--profile kali-lab) targets that profile’s home. Without it, the command changesdefault.
A.3 Add the SSH variables to the profile’s .env
Section titled “A.3 Add the SSH variables to the profile’s .env”notepad "$env:LOCALAPPDATA\hermes\profiles\kali-lab\.env"Add these lines, keeping any existing ones and without duplicating a variable:
# SSH terminal backend: this profile's commands run on the Kali laptop as 'labuser'TERMINAL_SSH_HOST=192.168.50.20TERMINAL_SSH_USER=labuserTERMINAL_SSH_PORT=22TERMINAL_SSH_KEY="C:/Users/Lab User/.ssh/kali_lab"Notes:
- Quotes are required around the key path when your Windows account folder contains a space, as
Lab Userdoes. Use forward slashes and the private key, not.pub. TERMINAL_SSH_HOSTandTERMINAL_SSH_USERare required. Port and key are optional, but are set explicitly here.- Use the IP, not the alias, in
TERMINAL_SSH_HOST. The IP does not depend on alias spelling, and thanks to step 4.4 it resolves to the same user and key anyway.
Verify without exposing other secrets:
hermes -p kali-lab config get terminal.backend # sshhermes config get terminal.backend # localSelect-String -Path "$env:LOCALAPPDATA\hermes\profiles\kali-lab\.env" -Pattern '^TERMINAL_SSH_'How the backend works (per the Hermes configuration docs): Hermes connects with BatchMode=yes and StrictHostKeyChecking=accept-new, captures the remote login environment once, and runs each command as a fresh bash -c in the tracked working directory. During a session it also copies a fixed set of Hermes cache folders (documents, images, audio, screenshots, attachments, and similar) to Kali, and syncs changes back. Other cache folders, such as cache/partials, are never copied.
A.4 Restart and test
Section titled “A.4 Restart and test”Restart the Hermes gateway so Desktop reloads the profile:
hermes gateway restarthermes gateway statusTest from PowerShell (one-shot, uses the profile’s model):
hermes -p kali-lab chat -Q -q "Use your terminal tool to run: hostname && whoami && pwd && grep PRETTY_NAME /etc/os-release. Reply with only the output."Expected output:
lab-Kalilabuser/home/labuserPRETTY_NAME="Kali GNU/Linux Rolling"Test in Desktop:
- In the Sessions sidebar, select the gateway This device.
- In the profile rail, select kali-lab.
- Ask the same question.
The same lines must come back. With profile default, the answer is the Windows host instead, which is intended.
If the agent reports an SSH error, test from the same Windows account first:
ssh -o BatchMode=yes 192.168.50.20 "hostname && whoami && pwd"If this works but Hermes does not, check:
terminal.backendof the selected profile;- that profile’s
.env; - the quotes around the key path;
- whether Kali is penalising Windows after failed logins (Phase 3.1).
Option B: Agent on Kali through the registered SSH gateway Kali Lab
Section titled “Option B: Agent on Kali through the registered SSH gateway Kali Lab”Desktop opens a tunnel to Kali, starts Kali’s Hermes dashboard, and the agent runs there. Its sessions, memory, skills, model configuration, and files belong to the Hermes home of labuser on Kali (/home/labuser/.hermes).
B.1 Install Hermes on Kali
Section titled “B.1 Install Hermes on Kali”On Kali, as labuser (never with sudo):
curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bashOpen a new terminal (or run source ~/.zshrc, because Kali’s default shell is zsh, not bash), then:
command -v hermes # /home/labuser/.local/bin/hermeshermes --version # Hermes Agent v0.22.5 ... Install directory: /home/labuser/.hermes/hermes-agent Install method: githermes doctor~/.local/bin/hermes is a small wrapper that runs ~/.hermes/hermes-agent/.hermes/bin/hermes.
B.2 Make hermes visible to non-interactive SSH (required)
Section titled “B.2 Make hermes visible to non-interactive SSH (required)”Desktop runs its probe as a non-interactive SSH command, and for zsh that reads only ~/.zshenv. Kali adds ~/.local/bin to PATH in ~/.zprofile, which only login shells read. Without the fix below, the probe cannot find hermes, and Test fails with “Hermes is not installed on the remote host”. Fix it as labuser:
cat >> ~/.zshenv <<'EOF'# Make ~/.local/bin (hermes, pipx tools) visible to non-interactive SSH commands toocase ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) export PATH="$HOME/.local/bin:$PATH" ;; esacEOFVerify from Windows:
ssh kali-lab "command -v hermes && hermes --version"# Hermes Agent v0.22.5 ... · stableAlternatively, leave PATH alone and enter /home/labuser/.local/bin/hermes in the connection’s Hermes path field (B.4). The ~/.zshenv line also helps every other ssh kali-lab "..." command.
B.3 Give Kali’s Hermes a model (LM Studio on Windows)
Section titled “B.3 Give Kali’s Hermes a model (LM Studio on Windows)”Point Kali’s Hermes at LM Studio on the Windows laptop (~/.hermes/config.yaml on Kali):
model: default: liquid/lfm2-1.2b provider: custom:lmstudio base_url: http://192.168.50.10:1234/v1This works only while LM Studio serves on the LAN.
1. LM Studio (Windows):
- Open Developer → Local Server and check the port (
1234by default). - Turn on Serve on Local Network.
- Load the model.
- Start the server, and keep it running while you use Mode B.
If LM Studio offers API authentication, enabling it is recommended; then store the key in Kali’s Hermes provider settings, never in shared files.
2. Windows firewall (Administrator PowerShell, once):
New-NetFirewallRule -DisplayName "LM Studio API - Home Wi-Fi" -Direction Inbound -Action Allow ` -Protocol TCP -LocalPort 1234 -RemoteAddress 192.168.50.0/24 -Profile PrivateGet-NetFirewallRule -DisplayName "LM Studio API - Home Wi-Fi" | Format-List DisplayName, Enabled, ProfileWhen LM Studio first runs, Windows can also create two lm studio.exe allow rules for the Public profile. They are not needed on your Private home Wi-Fi. Disabling them is optional hardening: Get-NetFirewallRule -DisplayName 'lm studio.exe' | Disable-NetFirewallRule (Administrator).
3. Confirm LM Studio is listening on the LAN interface (Windows):
Get-NetTCPConnection -State Listen -LocalPort 1234 | Format-Table LocalAddress, LocalPort0.0.0.0 or your Wi-Fi address (192.168.50.10) is good. 127.0.0.1 means Serve on Local Network is off.
4. Test from Kali, and use the exact model id:
curl -s http://192.168.50.10:1234/v1/models # lists loaded models; note the "id"curl -s http://192.168.50.10:1234/v1/chat/completions -H "Content-Type: application/json" \ -d '{"model":"liquid/lfm2-1.2b","messages":[{"role":"user","content":"Reply with exactly: OK"}]}'curl printing nothing (or HTTP 000) means LM Studio is not running or not serving on the LAN. To change Kali’s model or provider, run ssh -t kali-lab hermes model.
The model name must be an id that /v1/models returns. If Hermes asks for a model that LM Studio has not loaded, LM Studio logs Model with identifier '...' not found. Requests to /api/show in the LM Studio log mean the provider is set to Ollama, not LM Studio/custom. Hermes’ agent workflows also need a model with a large context window and real tool-calling support (check the current Hermes quickstart for the minimum context). Small models may chat fine but fail at tools.
B.4 The Desktop gateway entry
Section titled “B.4 The Desktop gateway entry”In Hermes Desktop: Settings → Gateways → Registered gateways (also the plug button in the profile rail, or Ctrl+K → “Gateways”). Keep exactly one Kali entry:
| Field | Value |
|---|---|
| Kind | SSH |
| Name | Kali Lab |
| SSH host | labuser@192.168.50.20:22 |
| Hermes path (optional) | blank: auto-detect works after B.2. Otherwise /home/labuser/.local/bin/hermes. |
| SSH key (if the editor shows one) | ~/.ssh/kali_lab |
Save connection, then Test and wait for Reachable. Test checks both the HTTP and WebSocket legs, so a pass means chat will work.
Desktop stores the entry in %APPDATA%\Hermes\connections.json. It can store a default key path such as ~/.ssh/id_rsa, even when that file does not exist on your PC. The connection still works, because the Host kali-lab 192.168.50.20 block (step 4.4) adds IdentityFile ~/.ssh/kali_lab. Keep that config block, or set the key field to ~/.ssh/kali_lab.
Remove every broken Kali entry. Typical broken entries are:
kali_lab: underscore, not an alias.Kali-labwith user~/ssh/config: a path in the user field, and a capitalised alias.192.168.50.20with user~/.ssh/kali_lab: the key path in the user field.
Desktop retries every saved entry. Their bad logins trip Kali’s OpenSSH penalty (Phase 3.1), and that breaks even the working connections. Use the trash button on each broken entry.
B.5 Which backend the Desktop window itself boots
Section titled “B.5 Which backend the Desktop window itself boots”Separately from the registry, the connection-mode control at the top of Settings → Gateways decides which backend Desktop’s window boots into. It is stored in %APPDATA%\Hermes\connection.json.
| Connection mode | What happens at launch |
|---|---|
SSH → labuser@192.168.50.20 |
Desktop starts Kali’s Hermes over the tunnel. Log: [ssh] connection spawned dashboard: Hermes Agent v0.22.5 ... at /home/labuser/.local/bin/hermes, then [boot] Remote Hermes backend is ready. |
| Local | Desktop starts the Windows runtime; Kali is reached via Mode A or by switching to Kali Lab. |
Either way, the Sessions sidebar’s gateway selector lets you switch between This device (Mode A with profile kali-lab) and Kali Lab (Mode B). Make primary sets the fallback gateway; At startup, return to Sessions on the last-used gateway reopens the last one used.
If the connection mode points at a bad host, or at a laptop that is off, Desktop fails at launch with Desktop boot failed: Could not reach <host> over SSH.
B.6 Understand what changes when you switch to Kali Lab
Section titled “B.6 Understand what changes when you switch to Kali Lab”Once you open the Kali Lab gateway, you are talking to the Hermes backend installed on Kali:
- Its sessions, memory, skills, provider configuration, files, and command execution belong to the Hermes home of
labuseron Kali. - Its model is whatever Kali’s Hermes is configured for (B.3), not the Windows profile’s model.
- It is not the Windows agent using a remote terminal; that is Mode A.
To return to the Windows-hosted agent, switch the gateway back to This device and select profile kali-lab.
Phase 7: Remote Desktop, VMware, and screen mirroring
Section titled “Phase 7: Remote Desktop, VMware, and screen mirroring”This phase separates four things that are often confused:
- RDP to the physical Kali laptop.
- What VMware can and cannot do.
- A Kali VM inside VMware.
- Seeing the physical laptop’s real screen.
7.1 Kali is a separate physical laptop (this tutorial)
Section titled “7.1 Kali is a separate physical laptop (this tutorial)”Use XRDP (Phase 5) for a separate desktop, or x11vnc (7.4) for the real screen. Quick check from Windows:
- Kali is powered on and on the same Wi-Fi.
ping 192.168.50.20succeeds.Test-NetConnection 192.168.50.20 -Port 3389→TcpTestSucceeded : True.- Run
mstsc→192.168.50.20→ session Xorg →labuser+ Kali password.
7.2 If you actually mean Kali running as a VM inside VMware Workstation on Windows
Section titled “7.2 If you actually mean Kali running as a VM inside VMware Workstation on Windows”Use this only for a Kali virtual machine hosted on the Windows laptop. That is a different Kali instance, not the physical laptop.
Method 1: Use the VMware console (most direct)
Section titled “Method 1: Use the VMware console (most direct)”-
Open VMware Workstation and select the Kali VM.
-
Adjust VM → Settings → Display if needed, then VM → Power On.
-
Sign in on the VM console.
-
For auto-resize and clipboard, install the guest tools inside the VM (not on the physical laptop):
sudo apt update && sudo apt install -y open-vm-tools open-vm-tools-desktop && sudo reboot
Method 2: Use Windows Remote Desktop to reach the Kali VM
Section titled “Method 2: Use Windows Remote Desktop to reach the Kali VM”- Power off the VM, then open VM → Settings → Network Adapter.
- Choose the network mode:
- Bridged: the VM gets its own LAN address from the router.
- NAT (VMnet8) or Host-only (VMnet1): only this Windows host can reach it directly.
- Tick Connected and Connect at power on, then start the VM.
- Inside the VM:
ip -4 -br addrandsudo systemctl status xrdp --no-pager. - On Windows:
Test-NetConnection <VM_GUEST_IP> -Port 3389, thenmstsc→<VM_GUEST_IP>.
Never give a bridged VM 192.168.50.20; that address belongs to the physical laptop.
7.3 Which method applies to you?
Section titled “7.3 Which method applies to you?”| Goal | Best method | Address used by Windows |
|---|---|---|
| Separate desktop on the physical Kali laptop | XRDP + mstsc |
192.168.50.20 |
| The physical laptop’s real screen | x11vnc + TigerVNC via SSH (7.4) | localhost:5901 (tunnel to Kali) |
| Console of a Kali VM on this Windows PC | VMware Workstation console | none |
| RDP into a Kali VM | XRDP inside the VM + mstsc |
the VM’s own IP |
7.4 See and control the physical laptop’s real screen (like a VM console)
Section titled “7.4 See and control the physical laptop’s real screen (like a VM console)”This is the replacement for “VMware remote connect”. x11vnc shares Kali’s live display :0 (the screen on the laptop):
- It listens on Kali’s localhost only.
- It is reached through an SSH tunnel with your key, so nothing new is exposed on the network.
- It starts on demand and stops when you close the viewer.
One-time setup:
# Kali, as labusersudo apt install -y x11vncx11vnc -storepasswd # optional. Pressing Enter (empty) stores no password.# Windowswinget install TigerVNC.TigerVNC # installs C:\Program Files\TigerVNC\vncviewer.exeNew-Item -ItemType Directory -Force 'C:\RemoteAccess' | Out-Nullnotepad 'C:\RemoteAccess\Open-KaliScreen.ps1' # paste the launcher from Appendix A.6, then saveWith an empty password, ~/.vnc/passwd is not created, and the launcher automatically uses -nopw. That is acceptable because x11vnc binds to Kali’s localhost, and the only way in is the SSH key. If you later set a VNC password (VNC uses only its first 8 characters), the launcher detects ~/.vnc/passwd and requires it.
Use (whenever you are logged in on the laptop’s own screen):
- Double-click the desktop shortcut Kali Screen (Appendix B), or
- run
powershell -ExecutionPolicy Bypass -File C:\RemoteAccess\Open-KaliScreen.ps1(add-FullScreenfor full screen).
In the viewer, F8 opens TigerVNC’s menu (full screen on/off, Ctrl-Alt-Del, disconnect).
Fit-to-screen (why a simple launcher shows only the top-left of Kali, zoomed):
The numbers below come from our example laptops: a 4K Windows laptop at 200 % scaling, and a Kali desktop of 2560×1440.
| Fact | Example value |
|---|---|
| Kali desktop sent by x11vnc | 2560×1440 |
| Windows display | 3840×2400 at 200 % scaling |
| TigerVNC DPI awareness | unaware: Windows enlarges its whole window 2× |
| Space TigerVNC sees (work area) | 1920×1152 units |
TigerVNC has no client-side scaling option (its -RemoteResize would change the laptop’s real resolution, so it isn’t used). Shown at 1:1 in 1920×1152 units and then enlarged 2×, Kali’s 2560×1440 screen overflows, leaving only the top-left part visible.
The launcher therefore measures the usable area in TigerVNC’s own (DPI-unaware) units and passes x11vnc -scale <fraction>, keeping the aspect ratio:
- Windowed:
min((1920−16)/2560, (1152−40)/1440)= 0.7438, so Kali arrives as 1904×1071 and the whole screen fits. -FullScreen:min(1920/2560, 1200/1440)= 0.75, so 1920×1080.
x11vnc maps mouse and keyboard to the scaled image automatically. The image is resampled twice (shrunk by x11vnc, then enlarged by Windows), so it is slightly soft, but it shows UI elements at about the same size as on the Kali laptop itself.
What the launcher does (full script in Appendix A.6):
- One SSH call reads whether
~/.vnc/passwdexists and Kali’s screen size (xdpyinfo -display :0). If Kali is unreachable, or nobody is logged in on the laptop, it stops with a clear message. - It computes the fit scale as above.
- It runs
ssh -L 5901:localhost:5900 kali-lab "x11vnc -display :0 -auth guess -localhost -rfbport 5900 <-nopw | -rfbauth ~/.vnc/passwd> -scale <fraction> -once -timeout 60 -noxdamage". - It waits for x11vnc to print
PORT=5900. It deliberately does not probe the port, because a probe would use up the single-onceclient. - It opens TigerVNC on
localhost::5901(plus-FullScreenif requested) and waits until you close it. - It kills the local
ssh, then stops x11vnc on Kali withpkill -u labuser -f '[x]11vnc -display :0 ...'. Killing the local ssh alone leaves x11vnc running, because there is no terminal to deliver a hang-up; the[x]keepspkillfrom matching its own command line. - Any failure appears in a Windows message box titled Kali Screen, because the shortcut runs without a console.
How to check that it works:
- The viewer shows the laptop’s entire current screen, with nothing cut off at the right or bottom.
- After you close the viewer,
ssh kali-lab "pgrep -a x11vnc || echo none"printsnone.
Limits:
- If nobody is logged in on the laptop (LightDM greeter), there is no desktop to share. Use XRDP instead.
- If the laptop’s screen is locked, you see the lock screen; unlock it with your Kali password.
Phase 8: Restrict access and preserve a safe recovery path
Section titled “Phase 8: Restrict access and preserve a safe recovery path”8.1 Keep SSH, RDP, VNC, LM Studio, and Hermes off the public internet
Section titled “8.1 Keep SSH, RDP, VNC, LM Studio, and Hermes off the public internet”Do not forward TCP 22, 3389, 5900, or the LM Studio port from the router. Keep them on the trusted home LAN (Windows Wi-Fi profile Private), and use a properly secured VPN for access from outside. Do not expose any Hermes web interface, gateway, or dashboard directly to the internet.
8.2 Avoid unrestricted sudo shortcuts
Section titled “8.2 Avoid unrestricted sudo shortcuts”Hermes inherits all of your account’s access, so keep these boundaries:
- Do not add
labuserto new groups just to make one command work. - Never configure
NOPASSWD: ALLfor convenience. - Never put the Kali sudo password in prompts, skills, scripts, or
.envfiles. - Review commands before execution, especially destructive ones, package installs, network changes, and scans.
- Limit security testing to systems and targets you own or are explicitly authorised to test.
8.3 Keep keys and Hermes secrets private
Section titled “8.3 Keep keys and Hermes secrets private”-
Never share the private key
%USERPROFILE%\.ssh\kali_labor put it in a repository; onlykali_lab.pubbelongs on Kali. -
Keep private:
%LOCALAPPDATA%\hermes\.env%LOCALAPPDATA%\hermes\profiles\kali-lab\.env- Kali’s
~/.hermes/.envandauth.json %APPDATA%\Hermes\connection.jsonandconnections.json, which hold adopted gateway tokens
When you share troubleshooting output, show only
TERMINAL_SSH_*lines or field names, never the whole file. -
Stop if Kali’s host key fingerprint changes unexpectedly (4.3).
-
Mode A copies some Hermes cache folders to Kali during sessions (A.3). Treat Kali as part of your trusted environment.
Phase 9: Verify the complete workflow
Section titled “Phase 9: Verify the complete workflow”Run these in order before any security work.
9.1 Confirm both LAN addresses and ports
Section titled “9.1 Confirm both LAN addresses and ports”ipconfig # Wi-Fi 192.168.50.10Test-NetConnection 192.168.50.20 -Port 22 # TrueTest-NetConnection 192.168.50.20 -Port 3389 # TrueOn Kali: ip -4 -br addr → wlan0 ... 192.168.50.20/24.
9.2 Confirm the SSH alias and account
Section titled “9.2 Confirm the SSH alias and account”ssh -o BatchMode=yes kali-lab "hostname && whoami && id && pwd && grep PRETTY_NAME /etc/os-release"Expected: lab-Kali, labuser, groups including sudo, /home/labuser, Kali GNU/Linux Rolling.
9.3 Confirm the selected Hermes mode
Section titled “9.3 Confirm the selected Hermes mode”Mode A:
hermes -p kali-lab config get terminal.backend # sshhermes -p kali-lab chat -Q -q "Use your terminal tool to run: hostname && whoami && pwd. Reply with only the output."In Desktop, gateway This device, profile kali-lab: ask the same question. Expect lab-Kali, labuser, /home/labuser.
Mode B: start the LM Studio server first. In Desktop, open Kali Lab (Test → Reachable) and ask the agent to run hostname, whoami, pwd, cat /etc/os-release.
Never infer the active mode from the look of the chat window; check the command output.
9.4 Create a test results directory on Kali
Section titled “9.4 Create a test results directory on Kali”mkdir -p ~/pentest-resultsprintf 'Kali remote execution is working\n' > ~/pentest-results/connection-test.txtFrom Windows:
ssh kali-lab "cat ~/pentest-results/connection-test.txt"scp kali-lab:pentest-results/report.txt "$env:USERPROFILE\Downloads\" # copy a real report back9.5 Use tmux for long-running terminal work
Section titled “9.5 Use tmux for long-running terminal work”Install tmux if it is missing (sudo apt install -y tmux). As labuser:
tmux new -s lab # start# detach: Ctrl+B, then Dtmux attach -t lab # reattach laterA tmux session survives SSH disconnects; GUI programs and ordinary background processes may not.
9.6 Confirm both graphical paths
Section titled “9.6 Confirm both graphical paths”- Stay logged in on the laptop. Run
mstsc→192.168.50.20→ Xorg →labuser. The Xfce desktop must open and stay open. - Double-click Kali Screen on the desktop (or run
C:\RemoteAccess\Open-KaliScreen.ps1). TigerVNC must show the laptop’s entire current screen, with nothing cut off at the right or bottom. Close it; afterwardsssh kali-lab "pgrep -a x11vnc || echo none"should printnone.
Phase 10: Troubleshooting
Section titled “Phase 10: Troubleshooting”10.1 Common errors, with cause and fix
Section titled “10.1 Common errors, with cause and fix”| Symptom / exact message | Cause | Fix |
|---|---|---|
Test-NetConnection ... -Port 22 → TcpTestSucceeded : False (ping OK); ssh: connect to host 192.168.50.20 port 22: Connection refused |
sshd not installed or not running | Phase 3.1 |
ssh: Could not resolve hostname kali-lab before the config exists |
No Host kali-lab block yet |
Phase 4.4 |
ssh: Could not resolve hostname kali_lab: No such host is known. |
Underscore: kali_lab is the key file, not an alias |
Use kali-lab |
ssh -G kali_lab shows your Windows user name, hostname kali_lab, and the default identity files |
Checked a name that matches no Host block |
ssh -G kali-lab |
Could not resolve hostname kali-lab for Kali-lab |
Alias matching is case-sensitive | Lower-case kali-lab, or use the IP |
...kali_lab already exists. Overwrite (y/n)? |
Key already present | Answer n. If overwritten, reinstall the new .pub on Kali (4.2). |
Set-Service ... Access is denied, Start-Service ... Cannot start service ssh-agent, ssh-add: Error connecting to agent |
Agent service is Disabled and needs Administrator; not needed for a passphrase-less key | Ignore (4.6) |
hermes doctor: ~/AppData/Local/hermes/.env file missing |
default profile has no .env |
Create it (hermes setup), or add the needed lines |
CLI prefix ⚠ install out of sync (venv: out of sync with uv.lock) |
Store-managed Desktop runtime | Harmless; update Hermes Desktop |
Desktop log [ssh] connect to kali_lab:22 failed ... Could not resolve hostname kali_lab, then Desktop boot failed: Could not reach kali_lab over SSH |
Gateway / connection mode used kali_lab |
SSH host labuser@192.168.50.20:22 (B.4, B.5) |
Desktop log connect to ~/.ssh/kali_lab@192.168.50.20:22 failed (kind=auth-failed) ... Permission denied (publickey,password) |
Key path typed into the user part | User labuser; key in the key field |
Desktop log connect to ~/ssh/config@Kali-lab:22 failed ... Could not resolve hostname kali-lab |
A path in the user field, and capital K |
Delete the entry |
Desktop log Connection closed by 192.168.50.20 port 22; Kali journal srclimit_penalise ... attempted authentication by invalid user / drop connection ... penalty |
OpenSSH 10 penalty after bad logins (for example, user ~ from a path in the user field) |
Delete broken entries, wait about 1 min. Check: ssh kali-lab "journalctl -u ssh --since '-10 min' | grep -i penalt" |
| Gateway Test: “Hermes is not installed on the remote host” although it is installed | zsh non-interactive SSH reads only ~/.zshenv; ~/.local/bin not on PATH |
B.2, or Hermes path /home/labuser/.local/bin/hermes |
Desktop log Using bundled payload ..., readiness probe ... exceeded its budget (4.0s) |
Informational only | None |
Kali agent: Model with identifier '...' not found; LM Studio: unexpected /api/show |
Model id not loaded; Ollama-style provider | Use an id from /v1/models; provider LM Studio/custom (B.3) |
curl http://192.168.50.10:1234/v1/models from Kali prints nothing / HTTP 000 |
LM Studio server stopped, or not on the LAN interface | Start the server with Serve on Local Network (B.3) |
| LM Studio shows “Reachable at 192.168.56.1:1234” | That is the VirtualBox Host-Only adapter | Use your Wi-Fi address, 192.168.50.10:1234 in this tutorial |
| Agent “ran commands” but output is from Windows | Wrong profile or gateway selected | Gateway This device + profile kali-lab (Mode A), or Kali Lab (Mode B) |
RDP: password accepted, then the window closes immediately. sesman: Window manager ... exited quickly (1 secs); xrdp: Xorg server closed connection; ~/.xsession-errors: X server already running on display :10.0 |
Same-user local Xfce owns the shared D-Bus; the RDP Xfce quits | .xsession from 5.2, or log out on the laptop |
RDP: login failed; sesman pam_authenticate failed: Authentication failure, AUTHFAIL: user=labuser |
Wrong Kali password | Use your Linux account password (5.4) |
RDP: X server failed to start, waitforx: Unable to open display :10, log shows Starting X server ... Xvnc :10 |
Session type Xvnc selected | Select Xorg |
| Mirror shows only the top-left ~⅓ of Kali, zoomed in | TigerVNC is DPI-unaware, so Windows (200 %) enlarges it 2×; Kali’s screen doesn’t fit, and TigerVNC can’t scale | Use the launcher in Appendix A.6, which passes x11vnc a -scale fit (7.4) |
| Message box “Kali’s screen (:0) is not available” | Nobody is logged in on the laptop (greeter shown), so there is no :0 desktop for your account |
Log in on the laptop, or use RDP (Phase 5) |
| Message box “Cannot reach Kali over SSH (kali-lab)” | Kali off, asleep, off the Wi-Fi, or SSH penalty | Test-NetConnection 192.168.50.20 -Port 22; Phase 3.1 |
| Message box “x11vnc/SSH stopped early” | x11vnc not installed, or a stale x11vnc on port 5900 | sudo apt install -y x11vnc; ssh kali-lab "pkill -u labuser -f '[x]11vnc'" |
| Shortcut shows a generic icon | Icon file moved or deleted | Keep C:\RemoteAccess\Open-KaliScreen.ico; recreate the shortcut (Appendix B) |
ssh kali-lab "pkill -f 'x11vnc ...'" exits with code 255 |
pkill -f matched and killed its own SSH shell |
Use the [x]11vnc pattern |
10.2 General symptoms
Section titled “10.2 General symptoms”| Symptom | What to check |
|---|---|
| Kali no longer has its static address after reboot | nmcli -g ipv4.method,ipv4.addresses connection show HomeWiFi; another active profile; IP conflict |
| SSH times out | Same Wi-Fi? Router client isolation? Kali awake? Current Kali IP? |
Permission denied (publickey) |
User labuser; key kali_lab; the line in ~/.ssh/authorized_keys; ~/.ssh 700 and the file 600 |
ssh kali-lab works but the Hermes backend fails |
Selected profile’s terminal.backend and .env; quoted key path; SSH penalty |
| RDP refused | systemctl is-active xrdp; ss -ltn | grep 3389; firewall |
| RDP black screen | Xfce installed; ~/.xsession content and mode 755; ~/.xsession-errors |
| VMware VM unreachable via RDP | VM powered on; adapter mode; IP taken from inside the VM; xrdp active in the VM |
10.3 Where the logs are
Section titled “10.3 Where the logs are”| Machine | Log | Command (no sudo needed for a member of adm) |
|---|---|---|
| Kali | SSH | journalctl -u ssh -n 100 --no-pager |
| Kali | XRDP session manager | tail -n 50 /var/log/xrdp-sesman.log |
| Kali | XRDP | tail -n 50 /var/log/xrdp.log |
| Kali | RDP desktop startup | tail -n 50 ~/.xsession-errors, ~/.xorgxrdp.10.log |
| Kali | Hermes on Kali | ~/.hermes/logs/ (agent.log, gateway.log, errors.log, gui.log) |
| Kali | Listening ports | ss -ltn | grep -E ':(22|3389|5900)\b' |
| Windows | Hermes Desktop | %LOCALAPPDATA%\hermes\logs\desktop.log; search [ssh] and [boot] |
| Windows | Hermes agent/gateway | %LOCALAPPDATA%\hermes\logs\agent.log, gateway.log, errors.log |
Read just the Desktop SSH lines:
Select-String -Path "$env:LOCALAPPDATA\hermes\logs\desktop.log" -Pattern '\[ssh\]|\[boot\]' | Select-Object -Last 30 | ForEach-Object LineAppendix A: Configuration reference
Section titled “Appendix A: Configuration reference”A.1 Windows %USERPROFILE%\.ssh\config (Kali block)
Section titled “A.1 Windows %USERPROFILE%\.ssh\config (Kali block)”Host kali-lab 192.168.50.20 HostName 192.168.50.20 User labuser Port 22 IdentityFile ~/.ssh/kali_lab IdentitiesOnly yesA.2 Windows Hermes profiles
Section titled “A.2 Windows Hermes profiles”| Profile | terminal.backend |
TERMINAL_SSH_* in its .env |
|---|---|---|
default |
local |
none needed |
kali-lab |
ssh |
HOST=192.168.50.20, USER=labuser, PORT=22, KEY="C:/Users/Lab User/.ssh/kali_lab" |
A.3 Hermes Desktop (%APPDATA%\Hermes)
Section titled “A.3 Hermes Desktop (%APPDATA%\Hermes)”| File | Content (non-secret fields) |
|---|---|
connections.json |
the gateway registry: local (This device) and kali-lab → label Kali Lab, host 192.168.50.20, user labuser |
connection.json |
the window backend: mode=local, or mode=ssh with host 192.168.50.20 and user labuser |
A.4 Kali ~/.xsession (mode 755)
Section titled “A.4 Kali ~/.xsession (mode 755)”#!/bin/sh# XRDP desktop. Unsetting the shared user-bus address makes startxfce4# launch a private D-Bus, so this session doesn't collide with a local Xfce login.unset DBUS_SESSION_BUS_ADDRESSexec startxfce4A.5 Kali ~/.zshenv
Section titled “A.5 Kali ~/.zshenv”# Make ~/.local/bin (hermes, pipx tools) visible to non-interactive SSH commands toocase ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) export PATH="$HOME/.local/bin:$PATH" ;; esacA.6 Windows C:\RemoteAccess\Open-KaliScreen.ps1 (the screen launcher, with fit-to-screen)
Section titled “A.6 Windows C:\RemoteAccess\Open-KaliScreen.ps1 (the screen launcher, with fit-to-screen)”<# Open-KaliScreen.ps1 - view and control the Kali laptop's REAL screen (display :0) from Windows, like a VM console.
How it works: SSH (your kali-lab key) starts x11vnc on Kali bound to Kali's localhost only, forwards it through the SSH tunnel to this PC, and opens TigerVNC. Nothing is exposed on the network. x11vnc exits when you close the viewer (or if no viewer connects within 60 s).
Fit to screen: TigerVNC has no client-side scaling and is not DPI-aware, so Windows enlarges it by the display scale (for example 200%). The script measures the usable screen area in those same units and asks x11vnc to shrink Kali's picture to fit before sending it, so the whole Kali screen is always visible.
One-time prerequisites: Kali : sudo apt install -y x11vnc x11vnc -storepasswd (optional; empty = no VNC password) Windows: winget install TigerVNC.TigerVNC
Requirement at use time: your Kali account must be logged in on Kali's own screen. Usage: powershell -ExecutionPolicy Bypass -File C:\RemoteAccess\Open-KaliScreen.ps1 [-FullScreen] (or the "Kali Screen" desktop shortcut). In the viewer, F8 opens the menu.#>param( [string]$SshHost = 'kali-lab', [string]$KaliUser = 'labuser', [int] $LocalPort = 5901, [int] $TimeoutSec = 30, [switch]$FullScreen)$ErrorActionPreference = 'Stop'Add-Type -AssemblyName System.Windows.FormsAdd-Type -Namespace KaliScreen -Name Dpi -MemberDefinition @'[DllImport("user32.dll")] public static extern IntPtr SetThreadDpiAwarenessContext(IntPtr context);'@
try { $viewer = @("$env:ProgramFiles\TigerVNC\vncviewer.exe", (Get-Command vncviewer.exe -ErrorAction SilentlyContinue).Source) | Where-Object { $_ -and (Test-Path $_) } | Select-Object -First 1 if (-not $viewer) { throw 'TigerVNC viewer not found. Install it: winget install TigerVNC.TigerVNC' }
# One SSH round-trip: is a VNC password set, and how large is Kali's screen (:0)? $info = ssh.exe -o BatchMode=yes -o ConnectTimeout=8 $SshHost 'test -f ~/.vnc/passwd && echo pw; xdpyinfo -display :0 2>/dev/null | awk ''/dimensions/{print $2}''' if ($LASTEXITCODE -eq 255) { throw "Cannot reach Kali over SSH ($SshHost). Is the Kali laptop on and on the Wi-Fi?" } $dims = $info | Where-Object { $_ -match '^\d+x\d+$' } | Select-Object -First 1 if (-not $dims) { throw "Kali's screen (:0) is not available. Log in as '$KaliUser' on the Kali laptop itself first, or use Remote Desktop (mstsc) instead." } $remoteW, $remoteH = [int[]]($dims -split 'x')
# Usable area in the same DPI-unaware units TigerVNC sees (for example 1920x1152 at 200% on a 3840x2400 display). $prev = [KaliScreen.Dpi]::SetThreadDpiAwarenessContext([IntPtr](-1)) # DPI_AWARENESS_CONTEXT_UNAWARE $screen = [System.Windows.Forms.Screen]::PrimaryScreen $area = if ($FullScreen) { $screen.Bounds } else { $screen.WorkingArea } [void][KaliScreen.Dpi]::SetThreadDpiAwarenessContext($prev) $availW = $area.Width - $(if ($FullScreen) { 0 } else { 16 }) # window borders $availH = $area.Height - $(if ($FullScreen) { 0 } else { 40 }) # title bar + borders $scale = [Math]::Min(1.0, [Math]::Min($availW / $remoteW, $availH / $remoteH)) $scaleArg = if ($scale -lt 1) { '-scale ' + $scale.ToString('0.0000', [Globalization.CultureInfo]::InvariantCulture) } else { '' }
$log = Join-Path $env:TEMP "kali-x11vnc-$PID.out" $errLog = "$log.err" # Use the VNC password if one was set with 'x11vnc -storepasswd'; otherwise run without one. # Either way x11vnc only listens on Kali's localhost, so the SSH key is the real gate. $vncAuth = if ($info -contains 'pw') { '-rfbauth ~/.vnc/passwd' } else { '-nopw' } $remote = "x11vnc -display :0 -auth guess -localhost -rfbport 5900 $vncAuth $scaleArg -once -timeout 60 -noxdamage" $sshArgs = "-o BatchMode=yes -o ExitOnForwardFailure=yes -L ${LocalPort}:localhost:5900 $SshHost `"$remote`""
$ssh = Start-Process ssh.exe -ArgumentList $sshArgs -WindowStyle Hidden -PassThru ` -RedirectStandardOutput $log -RedirectStandardError $errLog try { # x11vnc prints "PORT=5900" once it is listening; wait for that instead of a fixed sleep, # and don't probe the port (a probe would count as the single -once client). $deadline = (Get-Date).AddSeconds($TimeoutSec) while (-not (Select-String -Path $log -Pattern '^PORT=' -Quiet -ErrorAction SilentlyContinue)) { if ($ssh.HasExited) { throw "x11vnc/SSH stopped early.`n$(Get-Content $errLog -Raw)" } if ((Get-Date) -gt $deadline) { throw "x11vnc did not start within $TimeoutSec s.`n$(Get-Content $errLog -Raw)" } Start-Sleep -Milliseconds 500 } $viewerArgs = @("localhost::$LocalPort") if ($FullScreen) { $viewerArgs = @('-FullScreen') + $viewerArgs } Start-Process $viewer -ArgumentList $viewerArgs -Wait } finally { if (-not $ssh.HasExited) { $ssh.Kill() } # Killing the local ssh does not stop x11vnc on Kali (no tty, so no hangup signal); stop it explicitly. # '[x]11vnc' matches x11vnc but not this pkill's own shell command line. ssh.exe -o BatchMode=yes $SshHost "pkill -u $KaliUser -f '[x]11vnc -display :0 -auth guess -localhost -rfbport 5900'" 2>$null Remove-Item $log, $errLog -ErrorAction SilentlyContinue }}catch { # The desktop shortcut runs this without a console, so show failures in a message box. [void][System.Windows.Forms.MessageBox]::Show($_.Exception.Message, 'Kali Screen', 'OK', 'Error') exit 1}Appendix B: Desktop shortcut for the screen mirror (with a Kali icon)
Section titled “Appendix B: Desktop shortcut for the screen mirror (with a Kali icon)”A one-click Kali Screen icon on the Windows desktop that runs C:\RemoteAccess\Open-KaliScreen.ps1. Launching it opens the fitted viewer, and closing the viewer stops x11vnc on Kali.
| Item | Value |
|---|---|
| Shortcut file | Kali Screen.lnk in your Desktop folder |
| Target | C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe |
| Arguments | -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "C:\RemoteAccess\Open-KaliScreen.ps1" |
| Start in | C:\RemoteAccess |
| Run | Minimized |
| Icon | C:\RemoteAccess\Open-KaliScreen.ico |
Any .ico file works for the icon. Download a Kali icon from a source whose license lets you use it, and save it as C:\RemoteAccess\Open-KaliScreen.ico.
B.1 Create it with PowerShell (normal PowerShell, not Administrator)
Section titled “B.1 Create it with PowerShell (normal PowerShell, not Administrator)”# 1. Keep the icon next to the script, so cleaning Downloads never breaks the shortcutCopy-Item "$env:USERPROFILE\Downloads\kali.ico" 'C:\RemoteAccess\Open-KaliScreen.ico'
# 2. Create the desktop shortcut$lnkPath = Join-Path ([Environment]::GetFolderPath('Desktop')) 'Kali Screen.lnk'$sh = New-Object -ComObject WScript.Shell$lnk = $sh.CreateShortcut($lnkPath)$lnk.TargetPath = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"$lnk.Arguments = '-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "C:\RemoteAccess\Open-KaliScreen.ps1"'$lnk.WorkingDirectory = 'C:\RemoteAccess'$lnk.IconLocation = 'C:\RemoteAccess\Open-KaliScreen.ico,0'$lnk.WindowStyle = 7 # 7 = minimized$lnk.Description = "View and control the Kali laptop's real screen (x11vnc over SSH)"$lnk.Save()
# 3. Check it$c = $sh.CreateShortcut($lnkPath); $c.TargetPath; $c.Arguments; $c.IconLocationOptional full-screen shortcut: repeat step 2 with the name Kali Screen (Full).lnk and the arguments ending in -File "C:\RemoteAccess\Open-KaliScreen.ps1" -FullScreen. In the viewer, F8 → Full screen toggles it at any time.
B.2 Or create it by hand (no commands)
Section titled “B.2 Or create it by hand (no commands)”- Right-click the desktop → New → Shortcut.
- Type the location of the item:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "C:\RemoteAccess\Open-KaliScreen.ps1" - Click Next, name it
Kali Screen, then click Finish. - Right-click the new shortcut → Properties → Shortcut tab:
- Start in:
C:\RemoteAccess - Run: Minimized
- Change Icon… → Browse… →
C:\RemoteAccess\Open-KaliScreen.ico→ OK → OK.
- Start in:
- Optional: right-click → Pin to Start or Pin to taskbar.
B.3 Notes
Section titled “B.3 Notes”- No visible console.
-WindowStyle Hiddenplus Run: Minimized means only the TigerVNC window appears. If something fails (Kali off, nobody logged in on the laptop, x11vnc missing), the script shows a Kali Screen message box with the reason instead (troubleshooting table, Phase 10.1). - Execution policy.
-ExecutionPolicy Bypassapplies only to this launch; the system’s PowerShell policy is unchanged. - Moving files. If you move
Open-KaliScreen.ps1or the.ico, update the shortcut’s target/icon, or recreate it with B.1. - Stale icon. If Windows keeps showing an old or blank icon after you change the
.ico, runie4uinit.exe -showto refresh the icon cache.
Final checklist
Section titled “Final checklist”The setup is complete when four things work:
ssh kali-labworks without prompts.- The selected Hermes mode returns
lab-Kali/labuserfrom Kali. - RDP opens a desktop.
- The mirror shows the laptop’s real screen.
Only after these should you add penetration-testing tasks.
Official documentation
Section titled “Official documentation”- Hermes Desktop: Desktop and remote backend concepts.
- Hermes Desktop: Connecting to many Hermes instances: registered SSH gateways,
user@host:22format, Hermes path, Test. - Hermes Agent configuration:
terminal.backend: ssh,TERMINAL_SSH_*, remote state sync. - Hermes Agent profiles: per-profile homes and
hermes -p. - Hermes Agent security: remote execution and secrets.
- Hermes Agent installation: Linux installer and per-user layout.
- Kali Linux: SSH configuration
- Kali Linux: Setting up RDP with Xfce
- XRDP project
- x11vnc (Debian manual page)
- TigerVNC
- LM Studio: OpenAI-compatible API and serving on the network
- Microsoft: Windows Remote Desktop clients
- Broadcom: Virtual Network Editor in VMware Workstation: bridged, NAT, host-only.
- Broadcom: Shared VM feature removed in Workstation 16.2.0
- Broadcom: VMware Remote Console (VMRC): vSphere VMs only.