# SSH Authorized Keys ForcedCommand and Wrapper Audit

- **URL:** https://hackindex.io/services/ssh/post-exploitation/ssh-authorized-keys-forcedcommand-audit
- **Path:** SSH > Post-Exploitation
- **Up:** [Post-Exploitation](https://hackindex.io/services/ssh/post-exploitation.md)
- **Published:** 2026-01-03
- **Updated:** 2026-01-03

Once you have access, check whether SSH access control is being altered via `authorized_keys` options or server-side forced commands. This is not about adding persistence. It is about detecting it, documenting it, and understanding why SSH behavior differs per user.

SSH keys are not just authentication tokens. The `authorized_keys` format supports per-key options that restrict what a key can do after it authenticates. A `command=` option forces the session to run a single command regardless of what the client requests. A `from=` option limits which source IPs can use the key. `no-pty` blocks terminal allocation, `no-port-forwarding` kills tunneling, and `environment=` injects variables into the session. These restrictions matter for post-exploitation: they tell you whether a compromised key gives you a full shell or just a constrained channel.

## Check per-user authorized_keys options

The risk pattern is `authorized_keys` entries with restrictive or unexpected options, especially `command=`. Those options change what happens when a key authenticates. A deployment key with `no-pty` but no `command=` still allows non-interactive exec. A key with `command="/usr/local/bin/backup.sh"` gives you that script and nothing else, unless you can exploit the script itself.

On a host where you have filesystem access:

```bash
grep -nE 'command=|from=|permitopen=|no-pty|no-port-forwarding|environment=' ~/.ssh/authorized_keys 2>/dev/null
```

If you can enumerate other users after privilege gain, check all authorized_keys files on the system. Service accounts and CI/CD identities often carry keys with `command=` restrictions or tight `from=` allowlists. An unexpected `environment=` line can leak credential paths or application secrets.

```bash
sudo find /home -maxdepth 3 -type f -name authorized_keys -print 2>/dev/null
sudo find /home -maxdepth 3 -type f -name authorized_keys -exec grep -nE 'command=|SSH_ORIGINAL_COMMAND|permitopen=|no-pty' {} \; 2>/dev/null
```

If you find `command=`, capture the full line in notes. That is the control point. Note the exact command path, any arguments, and whether `SSH_ORIGINAL_COMMAND` is referenced inside the script. Scripts that pass `SSH_ORIGINAL_COMMAND` to a shell are often exploitable for command injection.

Per-key options to audit in `authorized_keys`:

- `command=` forces a single command on authentication
- `from=` restricts which source IPs can use the key
- `no-pty` blocks terminal allocation
- `no-port-forwarding` disables SSH tunneling
- `environment=` injects variables into the session

## Check sshd server-side forced command policy

Server-wide forced commands override interactive shells and can make SSH sessions behave inconsistently across users. A `ForceCommand` directive in `sshd_config` applies globally unless scoped to a `Match` block. This is more restrictive than per-key options because it cannot be bypassed by modifying `authorized_keys`. `AuthorizedKeysCommand` means the server fetches authorized keys dynamically from a script or external source instead of the filesystem, which is common in LDAP-integrated or cloud SSH setups.

```bash
sudo sshd -T 2>/dev/null | grep -iE 'forcecommand|authorizedkeyscommand|permittty|allowtcpforwarding'
```

Also check the raw config and any `Match` blocks. Match blocks apply directives to specific users, groups, or source addresses. A `Match User deploy ForceCommand /usr/bin/git-shell` entry means the deploy account is jailed to git operations. If you have code execution as that user, you are working inside git-shell, not a standard shell. Match blocks that combine `ForceCommand` with `AllowTcpForwarding no` and `PermitTTY no` are the standard pattern for locked-down deployment identities.

```bash
sudo grep -nE '^(Match|ForceCommand|AuthorizedKeysCommand|AllowTcpForwarding|PermitTTY|PermitTunnel|PasswordAuthentication|PubkeyAuthentication)' /etc/ssh/sshd_config 2>/dev/null
```

## References

- [sshd_config(5) manual page](https://man.openbsd.org/sshd_config) - ForceCommand, Match blocks, AuthorizedKeysCommand, and forwarding controls.
- [ssh(1) manual page](https://man.openbsd.org/ssh) - Client behavior when forced commands and restricted sessions are in play.
