9

I've just completed OverTheWire Bandit wargame, level 18.

That was a surprise. Here are the instructions for this level.

The password for the next level is stored in a file readme in the homedirectory. Unfortunately, someone has modified .bashrc to log you out when you log in with SSH.

I was thinking of a way to have the shell skip sourcing .bashrc. But before I found out a solution, I got tempted into simply starting an SFTP connection with the server. I didn't expect it to work. But it did. And I would like to know why. I thought SFTP runs over SSH.

For clarity, when I SSH into the server, I get kicked out, as expected.

1
  • 1
    SFTP does run over SSH. bash can also run over SSH. But SFTP does not run over bash. (note that I am using "run over" loosely here) Commented Oct 1, 2016 at 9:50

3 Answers 3

13

On bash in general

Bash's design with respect to startup files is rather peculiar. Bash loads .bashrc in two unrelated circumstances:

  • When it's an interactive shell, except when it's a login shell (and except when it's invoked as sh). This is why .bash_profile typically loads .bashrc.
  • When bash is not interactive nor a login shell nor invoked as sh but given a command to execute with -c and SHLVL is unset or less or equal to 1, and one of the following is true:

    • If standard input is a socket. In practice, this mostly happens when bash is invoked by rshd, i.e. when running rsh remotehost.example.com somecommand.
    • If activated at compile time (which is the case on some distributions, such as Debian and derivatives), if one of the environment variables SSH_CLIENT or SSH2_CLIENT is defined. In practice, this means that bash is invoked by sshd, i.e. ssh remotehost.example.com somecommand.
      If you don't know how bash was compiled, you can find out whether this option was set by checking whether the binary contains the string SSH_CLIENT:

      strings /bin/bash | grep SSH_CLIENT
      

On SSH in general

When you execute a command through the SSH protocol, the command is passed over the wire as a string. The string is executed by the remote shell. When you run ssh example.com somecommand, if the remote user's login shell is /bin/bash, the SSH server runs /bin/bash -c somecommand. There is no way to bypass the login shell. This permits restricted login shells, for example to allow only file copying and not general command execution.

There is one exception: the SSH protocol allows the client to request a specific subsystem. If the client requests the sftp subsystem, then by default the OpenSSH server invokes the program /usr/lib/openssh/sftp-server (the location may vary) via the user's login shell. But it can also be configured to run an internal SFTP server through the line

Subsystem sftp internal-sftp

in the sshd_config file. In the case of the internal SFTP server, and only in this case, the user's login shell is bypassed.

For this challenge

In the case of OverTheWire Bandit 18, .bashrc contains

…
# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac
…
echo 'Byebye !'
exit 0

So you can solve this level by doing anything that causes bash not to be interactive.

As you discovered, SFTP works.
But ssh [email protected] cat readme would also work.
As would echo 'cat readme' | ssh [email protected].
And pressing Ctrl+C at the right time during an interactive login would also work: it would interrupt bash, so the .bashrc would not be completely executed. Bash takes macroscopic time to start up, so while this doesn't work reliably, it can be done in practice.

3
  • 2
    I don't think SFTP runs bash at all, not just non-interactively. Commented Oct 1, 2016 at 9:51
  • @immibis I believe you are correct about that. I have used SFTP to exchange files on a user account which was configured with some application as the login shell rather than a "real" shell.
    – kasperd
    Commented Oct 1, 2016 at 16:47
  • @immibis SFTP doesn't run bash. But unless the SFTP server is internal, sshd runs the user's login shell which runs sftp-server. Hence, if the login shell doesn't interpret the string /usr/lib/openssh/sftp-server as “run the command /usr/lib/openssh/sftp-server”, you can't use SFTP. Commented Oct 1, 2016 at 21:38
5

The .bashrc is only executed when you start a shell.

The SSH server can be configured not to start a shell for the SFTP protocol by providing the command Subsystem internal-sftp in the server's sshd_config file.

Conjecture: it's likely that this is how the OverTheWire Bandit wargame is actually configured rather than an adjustment to .bashrc because otherwise the same restriction for ssh would also block the sftp server command.

3
  • 2
    Running SFTP does start a shell. The SSH daemon runs /path/to/shell -c /path/to/sftp-server where /path/to/shell is the user's login shell. If the user's login shell is bash, then .bashrc may be executed depending on how bash was compiled. It's always executed when bash is executed by rshd (yes, even for a non-interactive shell, bash startup is weird) and a compile-time option extends this to sshd. Commented Sep 30, 2016 at 21:59
  • I went and checked. Good guess, but in fact it isn't so subtle. The exit in .bashrc is only executed if bash is interactive. Commented Oct 1, 2016 at 0:06
  • @Gilles, with Subsystem sftp internal-sftp and echo No.; exit 1 in my .bashrc I get no ssh access to the server but sftp still works. (And then of course it's potentially possible to overwrite or remove the .bashrc. Permissions notwithstanding.) The ssh connection attempt fails whether interactive or not. On the other hand, if I have Subsystem sftp /usr/lib/openssh/sftp-server then the SFTP service also fails when .bashrc is mangled. Commented Oct 2, 2016 at 16:44
4

sftp uses the same credentials, but does not run an interactive shell on the remote machine.

An administrator can prevent an sftp user from running ssh, e.g., as described in How to restrict users to SFTP only instead of SSH

You must log in to answer this question.