Skip to content

SSH

SSH is arguably the most common way to get a remote shell. We have already covered using basic SSH commands to connect to a server, in this article we are going to look at SSH in a bit more detail.

We will look at some of the useful flags that SSH accepts, using SSH Keys, details of server configuration, and how this can effect access. Finally we are going to take a look at SSH tunnels, and how we can use them to bypass firewalls.

Important

We will be focusing on OpenSSH, this is probably the most common SSH server on Linux systems and is also the default included in Windows 10.

Other SSH servers it may be worth looking at are Dropbear, this is common on busybox based systems (routers, embedded devices)

Useful SSH Client Commands

We all know the usual syntax to connect to SSH as a given user

ssh user@device

#For example connecting to a sever
ssh dang@evil.org

# Or an IP address
ssh dang@127.0.0.1

There are some other useful SSH flags that we can use when creating our connections.

Verbose mode -v

Verbose mode is incredibly useful for debugging. I use it a lot when having the inevitable problems with SSH keys

$ ssh -v kali@192.168.253.130

OpenSSH_8.2p1 Ubuntu-4ubuntu0.1, OpenSSL 1.1.1f  31 Mar 2020
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.conf matched no files
debug1: /etc/ssh/ssh_config line 21: Applying options for *
debug1: Connecting to 192.168.253.130 [192.168.253.130] port 22.
debug1: Connection established.

---- 8<  SNIP -----

debug1: Authentications that can continue: publickey,password
debug1: Trying private key: /home/dang/.ssh/id_dsa
Authenticated to 192.168.253.130 ([192.168.253.130]:22).
debug1: channel 0: new [client-session]
debug1: Requesting no-more-sessions@openssh.com
Running Commands

We can also ask the SSH service to run a command when we comment,
while this may not be especially useful in most situations, it can be helpful when to try and bypass things like restricted shell environments.

We can ask ssh to run a command by specifing it. For example we can ask the system to run the which command

$ ssh kali@192.168.253.130 which netcat
/usr/bin/netcat

Or we could try to bypass a restricted shell, by asking SSH to run another type of shell

$ ssh kali@192.168.253.130 /bin/sh
ls
6005
ComsecStack
Derrick

You notice when we try to start a different type of shell we still get a restricted output (for example there is no prompt, and things like history don't work). This is because we are running the shell from the SSH process, rather than an pseudoterminal PTY environment.

In this case we can ask SSH to try to allocate a new TTY to the process using the -t flag

In the example below, we get a more familiar version of /bin/sh

PS C:\Users\dang> ssh kali@192.168.253.130 -t /bin/sh
$ ls
6005         Derrick  Documents  env  Music          passwords.txt~  Pictures  sqlmapdem  Videos
ComsecStack  Desktop  Downloads  git  passwords.txt  payloads        Public    Templates
$

Note

We can also pass arguments to the commands. Something that might be useful here when escaping restricted shells is to pass the --noprofile flag, that will attempt to load the shell without any profile settings (for example restricted shell configuration)

PS C:\Users\dang> ssh kali@192.168.253.130 -t /bin/bash --noprofile
kali@kali:~$
Editing Files Remotely

If you use a proper text editor4 you can also use the power of SSH to edit files on the remote machine5.

M-x M-f  /ssh:remote:~/file

That covers some of the useful flags we can pass to the SSH client. There are other useful ones that we will need to use, for example for tunnelling or deal with SSH keys, but I will cover them later in the relevant section.

Copying Files using SCP

We can also use the SCP command to copy files to and from our remote machine.

Note

There are probably better ways for transferring lots of files (rsync rocks), SCP is quite useful if we want to try and send a config file or expliot code to the server. Or if we want to exfiltrate some data

The syntax for SCP is pretty much the same as the usual cp command.

For example to copy a file from to the remote you could use

$ scp <file> <user>@<address>:<path to destination>

#For example to copy the file evil.txt to the home directory

$ scp evil.txt dang@192.168.253.130:~

To copy a file FROM a remote machine we can flip the syntax around

$ scp <user>@<address>:<path>  <local path>

#For exmaple, copy /etc/passwd to your homedir

$ scp dang@192.168.253.130:/etc/passwd  ~/passwd.txt

If we need to transfer multiple files, we can use the -r flag.

For example to transfer the Bleh directory between machines we could use

$ scp -r bleh  dang@192.168.253.130:~

SSH Keys

One nice feature of SSH is it allows us to use PKI to have a "passwordless"1 approach to connection. This means that, rather than type our password each time, we can use a public / private key pair to authenticate with the server.

You have almost certainly used this to authenticate with GitHub2 and we can also use the same approach on our own servers.

Generating an SSH key

The first step here is to generate a ssh key pair on our own system.

We have a choice of key types that we can use

  • RSA: Uses traditional RSA (the large prime version). This was the standard until open SSH 6.5. While its got a much larger key size, it may be a good idea to have one of these as its compatible with most SSH systems

  • Elliptic Curve: The ed25519 format uses a more modern elliptic curve to generate the keys. These keys are smaller, and mathematically more secure than the RSA versions.

    However, it might not work with all systems as the key format is still not supported everywhere.

Note

I have a couple of sets of SSH keys.

  • The "Real" set (so GitHub and the like), is the ed25519 elliptic curve.
  • The Hacking set is plain old RSA.

While I know that leaving my public key lying around should be OK, it makes me feel better that its a burner.

It also means that I can keep the RSA key size low (I don't really care about how secure it is) as its only used to connect to HTB boxes.

We can generate the a key pair using the ssh-keygen command (these commands also work nicely on windows):

For an ED25519 based key pair we use:

dang@DESKTOP-KJDVQ2J:~$ ssh-keygen -t ed25519
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/dang/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/dang/.ssh/id_ed25519
Your public key has been saved in /home/dang/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:UOBdJtYfWg91mvhjdyvwwin6sGY6qzGlAdCjMQldnc4 dang@DESKTOP-KJDVQ2J
The key's randomart image is:
+--[ED25519 256]--+
|++ ....o+.o  .. .|
|= +  .o+ +. +. + |
| = . oo .  +.+o  |
|. .   E.  . ...  |
|   . .  S  .  + o|
|    +     . +. oo|
|   +   . . + o . |
|    o. o+ . . .  |
|   ..o*o..       |
+----[SHA256]-----+

For RSA we use the -t rsa flag we can also specify a key size using the -b option

#Generate a 2048 byte RSA key
ssh-keygen -t rsa -b 2048

Once we have generated a key pair we will have the following on our system (by default it ~/.ssh/)

  • The Public Key, name.pub (for example id_rsa.pub) that we place on the remote server
  • The Private Key name (for example id_rsa) that we don't share with anyone else.

Using an SSH Key pair with services like GitHub

With services like GitHub we can now upload the PUBLIC key to the server and use it for authentication.

dang@DESKTOP-KJDVQ2J:~$ cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPA06n0aoTcsFza0nh/TC0TOFuR7d+Rej6ZugV6Ut/bi dang@DESKTOP-KJDVQ2J

Adding a GitHub key

Adding our SSH Key to a remote server

To get key based authentication on a remote server we also need to add the PUBLIC key to it. Obviously the server will need to have an SSH service running on it for this to work.

For OpenSSH the public key part lives in a users .ssh/authorized_keys file.

First create the ssh directory if it doesn't exist

mkdir ~/.ssh
chmod 700 ~/.ssh

Then create (or modify) the authorized_keys file to add your key to it

dang@ArchVM ~$ echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPA06n0aoTcsFza0nh/TC0TOFuR7d+Rej6ZugV6Ut/bi dang@DESKTOP-KJDVQ2J" >> ~/.ssh/authorized_keys

#And check its there
dang@ArchVM ~$ cat ~/.ssh/authorized_keys
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPA06n0aoTcsFza0nh/TC0TOFuR7d+Rej6ZugV6Ut/bi dang@DESKTOP-KJDVQ2J

Important

SSH can get really funny about the permissions (for good reason, otherwise anyone might start adding public keys to the authorized_keys file)

You need to make sure that the following permissions (or tighter) are set

  • Users Home folder: Not Group / Other writeable (755)
  • .ssh folder: User RWX access (700)
  • .ssh/authorized_keys: (644)

If you have setup the key file, but SSH keeps prompting you for a password the best way to check the permissions is with the -v flag. This allows you to see if the expected key is being sent to the server and rejected.

dang@DESKTOP-KJDVQ2J:~$ ssh -v dang@192.168.253.138
OpenSSH_8.2p1 Ubuntu-4ubuntu0.1, OpenSSL 1.1.1f  31 Mar 2020
debug1: Reading configuration data /etc/ssh/ssh_config
....
debug1: Offering public key: /home/dang/.ssh/id_ed25519 ED25519 SHA256:UOBdJtYfWg91mvhjdyvwwin6sGY6qzGlAdCjMQldnc4
debug1: Authentications that can continue: publickey,password
debug1: Next authentication method: password
dang@192.168.253.138's password:

Connecting to a Server using an SSH key

Once the key is configured on the server, we can then use it to connect.

If the key you want to use is in a known location (usually .ssh) it should be detected automatically and used.

For example using the key we generated above we can just connect with the command below. If you set a password for the key, you will be prompted the first time you connect3.

dang@DESKTOP-KJDVQ2J:~$ ssh dang@192.168.253.138
Last login: Fri Jan 22 17:33:10 2021 from 192.168.253.1
dang@ArchVM ~$

If the key is not in the .ssh directory (for example you find one in a CTF, and don't want to pollute your folders) we can tell SSH which key to use, with the -i flag

In the code below, we have found a key and stashed it in the /tmp directory. We then tell SSH to use that key to connect

dang@DESKTOP-KJDVQ2J:/tmp$ ls
id_ed25519  tmpgsa7fw07
dang@DESKTOP-KJDVQ2J:/tmp$ ssh -i id_ed25519 dang@192.168.253.138
Last login: Fri Jan 22 17:45:55 2021 from 192.168.253.1
dang@ArchVM ~$

TIP: Getting a better shell with SSH Keys

We may be able to use SSH keys to gain access to a system we don't have the password for. If we can write to a users authorized_keys file we might be able to append a key that we own, then use SSH to login to the box as the user.

Its a nice way of moving between a restricted shell, and a full function SSH shell. You sill may need to work out the password, but a decent terminal is worth it.

So our POC for this is:

  1. We have a low privilege shell on a server (from netcat or similar)
  2. We have write access a users authorized_keys file $ echo <key> >> authorized_keys
  3. We append our public key to the key file
  4. Then SSH into the system using our private key

Note

I tend to use this quite a lot on the challenges. It gives me a nice way of pushing you from one docker container (for a web exploit),
to another (with the root exploit) in a reasonably transparent way.

So, this weakness is not as common in the real world as you see in the module, but it is still worth thinking about.

SSH Server Settings

It may also be worth checking the server configuration file (/etc/ssh/sshd_config).

While it tends to ship with a set of sane default options, there are a few things you might want to look out for,

Port
We might not always listen on port 22.
AuthorizedKeysFile
Its not very common, but you can change the location of the authorized_keys files. If this has been set, it may be worth poking around that directory
Root Login

By default, root login over SSH is disabled. However there are a few possible settings

  • yes Allow login using both keys and passwords
  • prohibit-password Allow login with SSH keys
AllowUsers / DenyUsers
We can also stop other users accessing via SSH. Specifying the Allow Users will only allow usernames that match the regexp to SSH into the system. DenyUsers blocks users matching a regexp from SSH access.
Forwarding Commands

We can also control what can be forwarded over SSH. For example stopping tunnelling, or X11 Forwarding

  • X11Forwarding Stop X11 Forwarding (default no)
  • AllowAgentForwarding: Can we forward SSH agent commands
  • AllowTcpForwarding Allow TCP sockets to be forwarded (default YES)
  • AllowStreamLocalForwarding Allow UDP sockets to be forwarded (default YES)
  • GatewayPorts Modifies how ports are available in tunnels (default no)
  • PermitTunnel Do we allow SSH tunnelling

SSH Tunnelling

Another useful tool is SSH Tunnelling. This allows us to open ports that may not be accessible to the outside world. We can use tunnels for accessing a service that is usually only available locally, or punching through a firewall.

To explain SSH tunnels we will use the following example network:

  • The hacker in the public network
  • A server (in this case a database server) in a private network
  • A web server, that is publicly accessible on port 80.

Tunnel Network setup

A real-world example would be where we use a local tunnel to open a port that is usually not publicly available. For example, imagine a case where we have a MySQL server on a remote machine. The sysadmin (system administrator) has followed good practice and this server is only available locally (ie only on the SQL server box). However, at times (for example for maintenance or testing) it is desirable to be able to connect to the SQL server remotely. We now have two options; either reconfigure the server to allow connections (and try to remember to close it back again), or to use a SSH tunnel to allow a temporary connection.

To create our local tunnel, we map the service between the private and a publicly available port on the target machine, the tunnel then waits for a connection to the public port and forwards data to the private service.. I like to think of this as the tunnel equivalent of the bind shell.

The general format for a local SSH tunnel is

ssh -L <myPort>:localhost:<remotePort> <user>@<target>

For simplicity, let's imagine the sysadmin who wants to access the server is also inside the private network. What happens next is:

  • The sysadmin opens an SSH connection to the remote service
  • They use the SSH connection to create a tunnel linking port 3306 (standard for MySQL) on the database server, to port 4242 on their own machine.

The command typed on the sysadmin's machine looks like this:

sysdamin$ ssh -L 4242:localhost:3306 user@dbserver

Let's break that down

  • ssh -L asks SSH to create a local tunnel
  • 4242:localhost:3306 connects port 3306 on the target to port 4242 on the sysadmin's machine
  • user@dbserver provides valid credentials for the server we are connecting to.

We can then connect to the SQL service via their own machine by connecting to port 4242.

sysadin$ mysql -h 127.0.0.1:4242 -u root -p

The complex part of the concept is the 4242:localhost:3306 part. This is taken from the perspective of the tunnel end point, ie localhost actually means localhost on the database server.

Local Tunnelling

So what happens if the sysadmin is already using port 4242? We can ask the tunnel to connect to a different end point. In this case let's setup a tunnel to port 8080 (a popular HTTP alternative port) We modify the command to take the correct ports:

sysdamin$ ssh -L 8080:localhost:3306 user@dbserver

Now the MySQL server will be accessible on the sysadmin machine on port 8080.

Local tunnels through an intermediary

For this second example of SSH tunnelling, let's imagine the following:

  • The hacker has SSH access to the web server through the firewall
  • The web server is also inside the firewall, so has access to the database
  • The hacker's access to the database is blocked by the firewall

In this case we need to take a slightly different approach:

  • From the hacker's machine we open an SSH connection to the webserver
  • We ask the web server to create a tunnel from port 8080 on the hacker's machine to port 3306 on the web server:
ssh -L 8080:webserver.org:3306 testuser@192.168.227.134

The hacker can now connect to the SQL database on localhost port 8080.

Tunnelling though an intermediate

Reverse Tunnels

Reverse tunnels allow to forward connections in the same way. However, in this case the tunnel is started from inside the 'private' part of the network. I like to think of these like a reverse shell, where the connection is open and waiting for us to connect to it.

In this case, we need a publicly available machine that we have SSH access to. This could be the gateway web server that we used before, or any other machine (for example an amazon cloud server) that we can contact.

The syntax for reverse tunnels is similar to that for local tunnels, but with the -R flag, and the remote and local ports switched:

ssh -R <remote port>:<local address>:<local port>  username@publicserver

Let's look at an example here using the same network setup as we used in the local tunnel example:

  • We have managed to get access to the database server
  • We have SSH access to the web server

To forward port 3306 to the outside world via the web server we use the command:

ssh -R 3306:localhost:3306 user@webserver

Reverse Tunnelling

The SQL port is now forwarded and available via the web server. And we can connect to access the data.

Summary

In this article we have looked at using SSH. There was a lot of stuff here, so good work for getting to the end of it.

We covered some of the basics of using SSH, and how we can use public key based SSH to authenticate. We also took a brief look at interesting SSH server settings. Finally, we looked at SSH tunnels, and how we could use them to forward ports from a remote service.

Task

In the Lab materials there are some examples for you to try some of the SSH features. Playing with things like SSH tunnels and using keys for authentication.


  1. For a given value of passwordless, you will probably want to set a key phrase for your private key ;) 

  2. If not, why not. Save your fingers from typing passwords all the time. 

  3. Hopefully, the system will remember the passphrase for the duration of your HOST terminal session. If it doesn't you may need to configure ssh-agent to handle this for you 

  4. Emacs 

  5. There is also a Tramp mode for Docker containers, which is not only useful, but can save a layer or two installing any editors. 

Back to top