Previous Page
Next Page

7.2. The SSHv1 Protocol

Like SSL, SSH is a transport-layer protocol and uses TCP to carry its packets. This has the usual advantages of providing an underlying reliable transport, freeing SSH from having to worry about retransmissions, packet ordering, and flow control. Unlike SSL, SSH does not require that either the local or remote application be SSH-aware. The situation is more analogous to an stunnel environment, such as that in Figure 6.24. That is, SSH provides a secure tunnel through which local and remote applications may communicate.

The most common case is shown in Figure 7.1: A local user is communicating with a remote shell. In this case, the ssh client is providing the local user with a terminal interface, but this is merely a convenience. This use of ssh is as a secure replacement for rsh and is virtually identical from the user's perspective.

Figure 7.1. SSH as a Remote Shell


Because the use of ssh as a replacement for rsh and telnet is so common, we first examine the SSH protocol from the point of view of the remote shell application. Later, we consider other applications and capabilities of the SSH protocols.

Let's start with a simple interactive session and watch the protocol in action as SSH connects; authenticates the server, client, and user; transfers user data securely; and finally disconnects. In order to see the unencrypted packets, we specify null encryption (-c none). As we see, ssh warns us that there will be no encryption and that the password will be passed in the clear, just as it is for, say, telnet.

Although the SSH protocol recommends that null encryption should be available for debugging purposes and although OpenSSH does provide support for it, there is no way to request it from the command line. We are using a patched version that recognizes the -c none option. Our patched server is listening on port 2022 instead of the normal port 22; that is why we specify -p 2022 on the call to ssh.

The -1 and -4 specify the version 1 protocol and IPv4, respectively.

$ ./ssh -1 -4 -c none -p 2022 guest@localhost
WARNING: Encryption is disabled! Password will be transmitted
in clear text.
guest@localhost's password:
Last login: Sat May 15 14:55:16 2004 from localhost
Have a lot of fun...
guest@linuxlt:~> ls
Documents public_html
guest@linuxlt:~> exit
logout
Connection to localhost closed.

After we supply our password, sshd starts a shell for us, and we list the home directory of user guest. Finally, we exit from the shell, and the connection is torn down.

Before studying the protocol messages for this session in detail, we must examine the SSHv1 binary protocol packet. Figure 7.2 shows the format of these packets.

Figure 7.2. The SSHv1 Binary Packet


As with the SSL packets from Chapter 6, the SSH packet does not necessarily align its data on word boundaries, so we display them as we did for SSL.

The length field is the size of the packet, not including the length field itself or the variable-length random padding field that follows it. The padding field is intended to make known text attacks more difficult. Its size is chosen to make the size of the encrypted part of the packet a multiple of 8 bytes.

This is presumably because all the block ciphers originally supported by SSHv1 have a 64-bit block size. The OpenSSH version 1 protocol supports only DES, 3DES, and Blowfish, all of which use a 64-bit block size.

Following the padding is a 1-byte type field that identifies the type of message that the packet contains. The type values are shown in Figure 7.3.

Figure 7.3. SSHv1 Message Types

No.

MessageName

Message

0

SSH_MSG_NONE

never sent

1

SSH_MSG_DISCONNECT

causes immediate connection teardown

2

SSH_SMSG_PUBLIC_KEY

server's public key

3

SSH_CMSG_SESSION_KEY

client choice of cipher and session key

4

SSH_CMSG_USER

userlogon name

5

SSH_CMSG_AUTH_RHOSTS

request for user rhosts type authentication

6

SSH_CMG_AUTH_RSA

request for user RSA authentication

7

SSH_SMSG_AUTH_RSA_CHALLENGE

server challenge for RSA authentication

8

SSH_CMSG_AUTH_RESPONSE

client response to RSA challenge

9

SSH_CMSG_AUTH_PASSWORD

request for password authentication

10

SSH_CMSG_REQUEST_PTY

request for a server pseudoterminal

11

SSH_CMSG_WINDOW_SIZE

client's window size

12

SSH_CMSG_EXEC_SHELL

request to start a user shell

13

SSH_CMSG_EXEC_CMD

request to run a command

14

SSH_SMSG_SUCCESS

server accepts last request

15

SSH_SMSG_FAILURE

server does not accept last request

16

SSH_CMSG_STDIN_DATA

client input data for shell/command

17

SSH_SMSG_STDOUT_DATA

output data from shell/command

18

SSH_SMSG_STDERR_DATA

STDERR output from shell/command

19

SSH_CMSG_EOF

client is finished sending data

20

SSH_SMSG_EXITSTATUS

exit status from shell/command

21

SSH_MSG_CHANNEL_OPEN_CONFIRMATION

indicates channel opened

22

SSH_MSG_CHANNEL_OPEN_FAILURE

indicates channel could not be opened

23

SSH_MSG_CHANNEL_DATA

data transmitted over channel

24

SSH_MSG_CHANNEL_CLOSE

sender is closing channel

25

SSH_MSG_CHANNEL_CLOSE_CONFIRMATION

sender acknowledges channel close

26

 

obsolete

27

SSH_SMSG_X11_OPEN

client is connected to proxy X-server

28

SSH_CMSG_PORT_FORWARD_REQUEST

client requests server port be forwarded

29

SSH_MSG_PORT_OPEN

connection made on forwarded port

30

SSH_CMSG_AGENT_REQUEST_FORWARDING

requests authentication agent forwarding

31

SSH_SMSG_AGENT_OPEN

requests channel to authentication agent

32

SSH_MSG_IGNORE

no op

33

SSH_CMSG_EXIT_CONFIRMATION

response to SSH_SMSG_EXITSTATUS

34

SSH_CMSG_X11_REQUEST_FORWARDING

requests a proxy X-server

35

SSH_CMSG_AUTH_RHOSTS_RSA

requests rhosts/RSA authentication

36

SSH_MSG_DEBUG

debugging information for peer

37

SSH_CMG_REQUEST_COMPRESSION

client requests compression


The type field is followed by the message data. The CRC field, which serves as a MAC, ends the packet. When encryption is enabled, everything except the length field is encrypted.

The protocol allows for optional compression of the data. This can be useful when SSH is used in low-bandwidth situations such as dial-up lines. If the client and server negotiate compression, only the type and data fields are compressed.

Many of these messages either carry no argumentsthey consist of only the length, padding, type, and CRC fieldsor have a single argument consisting of a string, integer, or extended integer. In these cases, we won't bother showing the message layout but will merely indicate what type of argument, if any, the message carries.

Server Authentication

The server authentication phase of the session, as shown in Figure 7.4, begins with the exchange of identification strings. When the SSH client, ssh, connects to the SSH server, sshd, the server sends an identification string indicating which of the protocols it supports and, perhaps, additional program version information. For example, if we connect to the SSH server with netcat, the server responds with its identification string:

$ nc linux 22
SSH-1.99-OpenSSH_3.5p1

Figure 7.4. SSHv1 Server Authentication


The SSH-1.99 is a special version number that tells the client that the server supports protocol versions 1 and 2. The -OpenSSH_3.5p1 is meant for human consumption and specifies the version of the OpenSSH SSH server. The client will respond with its own identification string, so that the peers will know which protocol to use.

After the server receives the client's identification string, the peers switch to the binary protocol, and the server sends the SSH_SMSG_PUBLIC_KEY message shown in Figure 7.5.

Figure 7.5. SSH_SMSG_PUBLIC_KEY Message


The cookie is 8 random bytes that are intended to make IP spoofing more difficult. The client must return these bytes to the server unchanged.

The host key is a permanent RSA public key that the client uses to verify the identity of the server. Unlike with SSL, this key is not signed by a third party. Rather, the client is expected to have a database of known host keys. In practice, this database is built by accepting the key as valid the first time a user connects to a host. Thereafter, the host must present the known key to the client for the session to proceed. SSH also supports a key-fingerprinting mechanism that allows a user to manually verify a site's key with the site's system administrator. There is also a proposal to make these fingerprints available through DNS by means of SSHFP (SSH key fingerprint) records.

The server also sends a second key, the server key. This key is regenerated periodically, once every hour by default, to help improve security. As we shall see, the client uses both of these keys to form its response to the server.

Finally, the message includes three 32-bit bit masks. The protocol flags bit mask is intended for protocol extension. The supported ciphers mask indicates which ciphers the server can use. The client will choose one of these for the session's cipher. The supported authentications mask indicates which user authentications the server supports. Again, the client will try one or more of these methods to authenticate the user.

Both sides use the information in this message to calculate a session ID by taking the MD5 hash of the concatenation of the moduli of the server and host keys and the cookie. As we'll see, the session ID is used in generating the session key and thus ensures that both the client and server contribute to the session key.

From Figure 7.4, we see that the client responds to the SSH_SMSG_PUBLIC_KEY message with an SSH_CMSG_SESSION_KEY message, shown in Figure 7.6.

Figure 7.6. SSH_CMSG_SESSION_KEY Message


The cipher type field contains the number of the cipher that the client has chosen for the session. The cookie and protocol flags fields are just as they were for the SSH_SMSG_PUBLIC_KEY message. In particular, the cookie must be returned to the server exactly as it was received by the client.

The encrypted session key field is a random 32-byte value chosen by the client. The session ID, calculated from the values in the SSH_SMSG_PUBLIC_KEY message, is exclusive-ORed into the first 16 bytes of the random value. This result is then encrypted twice: first by the smaller (usually the server) RSA key and then by the other (usually host) key.

Although this operation seems complicated, it accomplishes three separate tasks. First, by exclusive-ORing the session ID into the random value used for the session key, the client ensures that both the server and the client contribute to the final session key. Then, by encrypting with the host key, the client verifies the identity of the server, because the server must have the corresponding private key in order to recover the session key. Finally, by encrypting with the server key, the client ensures perfect forward secrecy by using the periodically changing server key.

Both sides now begin encrypting their packets. The server completes the server authentication phase by sending an SSH_SMSG_SUCCESS message. At this point, the peers have established a secure channel, and the server has authenticated itself to the client.

User Authentication

The next step is for the user to authenticate himself to the server. This can be done in several ways. SSH allows, but discourages, the insecure rhosts trusted-host model. Because it is easily spoofed, this model should never be used when security is important. SSH also supports a variation of the rhosts model in which the identity of the client machine is verified with an RSA key. This is an improvement but still relies on the client to certify the identity of the user. Once again, this method should not be used when security or user identity is a concern. The rhosts and rhosts/RSA methods are discussed in detail in [Barrett and Silverman 2002], so we will not belabor them further here.

A third authentication method is to use Kerberos. With this method the user obtains a "ticket" from the Kerberos server and sends it to the SSH server as authentication. Although Kerberos is a complicated system and requires a separate server, it may make sense when there is a large user base, especially if Kerberos is already in place. See [Garman 2003] for more information on Kerberos.

Next, there is a class of methods known as password authentication. In the simplest of these, which we'll examine shortly, the user merely supplies a password, which the server checks against its password file. Recall that after server authentication, the peers have established a secure channel, so this password is not passed in the clear as it is in, say, the telnet protocol.

The other password methods are variations of a one-time-password scheme. One example is the popular RSA SecurID system, which is described at RSA's Web site (<http://www.rsasecurity.com/node.asp?id=1156>). With SecurID, the user carries a hardware device, such as a key fob, that generates a pseudorandom number every 60 seconds. During authentication, the user enters the pseudorandom number and a private PIN. If the correct values are entered, the user is authenticated, and the session is started.

Another one-time-password scheme is the S/Key system [Haller 1994, Haller 1995], a challenge/response system. With S/Key, the user is prompted with a challenge and responds with a set of short words. This response can be obtained either programmaticallyfrom a PDA applet, sayor from a preprinted list. As with SecureID, passwords are not reused, thereby increasing security. The S/Key system is supported by OpenSSH.

Each of these password methods has its own protocol, but they all depend on the SSH secure channel for their security. The enhanced methods, such as SecureID and S/Key, derive their increased security from the fact that they use one-time passwords and are thus resistant to local passive attacks, such as keyboard loggers and other attempts to capture the password before it enters the encrypted channel; even if a password is captured, it is useless because it's not reused.

Figure 7.7 shows the protocol for a simple password-based user authentication.

Figure 7.7. User Authentication with a Password


First, the client sends the server the user's name as a string in the SSH_CMSG_USER message. If no further authentication is requiredrhosts authentication is being used, for examplethe server will respond with an SSH_SMSG_SUCCESS message, and authentication will be completed. If further credentials, such as a password, are required, the server will respond with an SSH_SMSG_FAILURE message, indicating that the user name alone is not sufficient. SSH_SMSG_SUCCESS and SSH_SMSG_FAILURE are both simple messages with no arguments.

At this point, the client will try various authentication methods until it finds one acceptable to the server. In our example session, the client first tries a standard password authentication by sending the server an SSH_CMSG_AUTH_PASSWORD message containing the user's password as a string. In our case, this is acceptable, and the server returns an SSH_SMSG_SUCCESS message indicating that the authentication is complete.

Because SSHv1 normally encrypts everything in its messages except the length, there is not much sense in dwelling on packet captures as a debugging aid. Nevertheless, it is instructive to see what the messages look like, to aid us in understanding the protocol. Therefore, let's briefly examine the messages from Figure 7.7 in detail.

We begin with the SSH_CMSG_USER message from the client. As we see from bytes 58 in line 1.4, the message is 14 bytes long. From line 1, we see that the TCP segment is 20 bytes long, so there are 20 - 14 - 4 = 2 bytes of padding. The padding is easy to spot because OpenSSH pads with zero bytes.

Next, we see the message type (0x04) in byte 11 of line 1.4. From Figure 7.3, we see that this is indeed the SSH_CMSG_USER message. Following the message type is the user's logon name, guest in this case. In SSH, string data is preceded by 4 bytes of length followed by the string data. There is no trailing NULL byte. Finally, the last 4 bytes are the CRC, which serves as a MAC.

In line 2, the server responds with its SSH_SMSG_FAILURE message. The message type (0x0f) is on line 2.4. Line 3 is the TCP ACK for the segment in line 2. This occurs because of the time it takes to type in the password.

1   14:56:52.381285 127.0.0.1.32802 > 127.0.0.1.2022: P 184:204(20)
     ack 316 win 3276 7 <nop,nop,timestamp 468782 468782> (DF)
1.1    4500 0048 0daa 4000 4006 2f04 7f00 0001    E..H..@.@./.....
1.2    7f00 0001 8022 07e6 2b99 26b8 2b24 4d08    ....."..+.&.+$M.
1.3    8018 7fff d4cb 0000 0101 080a 0007 272e    ..............'.
1.4    0007 272e 0000 000e 0000 0400 0000 0567    ..'............g
1.5    7565 7374 33b3 5ce1                        uest3..
2   14:56:52.381867 127.0.0.1.2022 > 127.0.0.1.32802: P 316:328(12)
     ack 204 win 3276 7 <nop,nop,timestamp 468782 468782> (DF)
2.1    4500 0040 0dab 4000 4006 2f0b 7f00 0001    E..@..@.@./.....
2.2    7f00 0001 07e6 8022 2b24 4d08 2b99 26cc    ......."+$M.+.&.
2.3    8018 7fff a93e 0000 0101 080a 0007 272e    .....>........'.
2.4    0007 272e 0000 0005 0000 000f 90bf 1d91    ..'.............
3   14:56:52.413821 127.0.0.1.32802 > 127.0.0.1.2022:  . ack 328
      win 32767 <nop,nop,timestamp 468786 468782> (DF)

Next, we see the SSH_CMSG_AUTH_PASSWORD message (type 0x09) carrying the password of knockknock. Because this password is acceptable to the server, it responds with a SSH_SMSG_SUCCESS message in line 5. As we see in line 5.4, the SSH_SMG_SUCCESS message carries no data other than its message type (0x0e).

4   14:56:59.534978 127.0.0.1.32802 > 127.0.0.1.2022: P 204:256(52)
           ack 328 win 3276 7 <nop,nop,timestamp 469498 468782> (DF)
4.1    4500 0068 0dba 4000 4006 2ed4 7f00 0001     E..h..@.@.......
4.2    7f00 0001 8022 07e6 2b99 26cc 2b24 4d14     ....."..+.&.+$M.
4.3    8018 7fff ecd7 0000 0101 080a 0007 29fa     ..............).
4.4    0007 272e 0000 0029 0000 0000 0000 0009     ..'....)........
4.5    0000 0020 6b6e 6f63 6b6b 6e6f 636b 0000     ....knockknock..
4.6    0000 0000 0000 0000 0000 0000 0000 0000     ................
4.7    0000 0000 7ba8 d3b8                         ....{...
5   14:56:59.536726 127.0.0.1.2022 > 127.0.0.1.32802: P 328:340(12)
            ack 256 win 3276 7 <nop,nop,timestamp 469498 469498> (DF)
5.1    4500 0040 0dbc 4000 4006 2efa 7f00 0001     E..@..@.@.......
5.2    7f00 0001 07e6 8022 2b24 4d14 2b99 2700     ......."+$M.+.'.
5.3    8018 7fff 3cf8 0000 0101 080a 0007 29fa     ....<.........).
5.4    0007 29fa 0000 0005 0000 000e e7b8 2d07     ..)...........-.

The final type of user authentication, RSA authentication, uses RSA keys as certificates. The public key is placed in the user's home directory on the server, usually in the .ssh subdirectory. Similarly, the private key is kept in the user's home directory on the client. If the client has an RSA key available, it will query the server as to whether it has the corresponding public key. It does this by sending the key's modulus in an SSH_CMSG_AUTH_RSA message. The entire RSA authentication process is shown in Figure 7.8.

Figure 7.8. SSHv1 RSA Authentication


The exchange starts with the client sending the usual SSH_CMSG_USER message with the user's logon name. The server responds with the SSH_SMSG_FAILURE message, indicating that further authentication is required. So far, this is the same as for password authentication, but with RSA authentication instead of the client sending the SSH_CMSG_AUTH_PASSWORD message, it sends an SSH_CMSG_AUTH_RSA message containing the modulus of the RSA key as an extended integer. The server checks in the user's home directory for a public key with the same modulus. If it finds one, the server uses it to encrypt a random 256-bit challenge, which it sends to the client in an SSH_SMSG_AUTH_RSA_CHALLENGE message. The challenge is carried as an extended integer. The client decrypts the challenge with its private key, combines it with the session ID, and takes an MD5 hash of the result. The hash is sent to the server in an SSH_CMSG_AUTH_RESPONSE message as 16 bytes of untyped data. The server takes its own MD5 hash of the challenge and session ID and compares it with the one it received from the client. If they match, the server sends the client an SSH_SMSG_SUCCESS message, indicating that the authentication succeeded. If the results don't match, the server will respond with an SSH_SMSG_FAILURE message, informing the client that it should try another form of user authentication.

Once user authentication is completed, SSH is ready to begin a user application. By default, this means that SSH will start a user shell on the server and transport the shell's standard input, output, and error over the encrypted channel. We show this for the case of our sample session in Figure 7.9.

Figure 7.9. Starting a User Shell


First, the client requests the server to start a pseudo-tty session for it with the SSH_CMSG_REQUEST_PTY message. This message contains the terminal type and characteristics of the user's session, as shown in Figure 7.10. After the server acknowledges the request with an SSH_SMSG_SUCCESS message, the client requests the server to start a shell and connect it to the pseudo-tty with an SSH_CMSG_EXEC_SHELL message. The client and server then exchange data with the SSH_CMSG_STDIN_DATA and SSH_SMSG_STDOUT_DATA messages. The SSH_CMSG_EXEC_SHELL message contains no arguments. The arguments for the SSH_CMSG_STDIN_DATA and SSH_SMSG_STDOUT_DATA messages are carried as string data.

Figure 7.10. The SSH_CMSG_REQUEST_PTY Message


When we typed exit, the shell sent a final "logout" message and terminated. That caused SSH to tear down the encrypted channel, as shown in Figure 7.11.

Figure 7.11. Channel Teardown


First, the server sent an SSH_SMSG_EXITSTATUS message containing the exit status of the shell. If the shell had been terminated by a signal, the server would have sent an SSH_MSG_DISCONNECT message containing a human-readable string indicating the cause of the termination. The client confirms the shell termination by responding with a SSH_CMSG_EXIT_CONFIRMATION message, which contains no arguments. These two messages are the last messages sent by the server and the client, respectively.

X11 Forwarding

Both versions of SSH can forward an X11 connection from an X-client on the SSH server to an X-server on the SSH client. This works by having the SSH server listen for connections to the X-server from localto the serverX-clients, in effect establishing an X11-server proxy. The SSH server then forwards the X-protocol messages from the X-client over an encrypted channel to the SSH client, which in turn forwards them to the real X-server on the SSH client side. Similarly, X-protocol messages from the X-server on the client are transferred over the encrypted channel to the X-client on the SSH server. In Figure 7.12, an X-client on the server host connects to the X-server proxy and has its X-protocol messages delivered over the SSH encrypted channel to the X-client proxy on the client host. The X-client proxy in turn connects to the real X-server and forwards messages from the X-client to it. Although both the X-client and X-server believe themselves to be directly connected to their peer and that their peer is on the same machine, the actual connection follows the shaded path shown in Figure 7.12.

Figure 7.12. A Forwarded X11 Connection


If the SSH client wishes to enable X11 forwarding, it sends the SSH server an SSH_CMSG_X11_REQUEST_FORWARDING message after completing the user authentication phase. This message will cause the server to arrange for local X-clients to connect to a socket on which it is listening.

The server does this by pointing the environment's DISPLAY variable at a socket on which it listens for connections. For example, if we ssh into linuxlt from bsd, specifying X11 forwarding, and echo the DISPLAY variable, we get

echo $DISPLAY
localhost:10.0

This corresponds to listening on the loopback interface at port 6010. If we echo the DISPLAY variable from the console of linuxlt, we get

echo $DISPLAY
:0

which corresponds to listening on all interfaces at port 6000. We can verify this by running netstat -naAinet on linuxlt. The relevant entries, after some reformatting, are as expected:

Proto   Local Address    Foreign Address   State
tcp     0.0.0.0:6000     0.0.0.0:*         LISTEN
tcp     127.0.0.1:6010   0.0.0.0:*         LISTEN

The SSH_CMSG_X11_REQUEST_FORWARDING message is shown in Figure 7.13.

Figure 7.13. The SSH_CMSG_X11_REQUEST_FORWARDING Message


The authentication protocol and authentication data fields contain data that the proxy X-server can use to authenticate X-clients on the SSH server. See [Barrett and Silverman 2002] for details on X11 authentication and authentication spoofing. The screen number field specifies the screen number to use. This field is present only if the SSH_PROTOFLAG_SCREEN_NUMBER bit is set in the protocol flags field of the client's SSH_CMSG_SESSION_KEY message.

When an X-client connects to the proxy X-server, the SSH server sends an SSH_SMSG_X11_OPEN message to the SSH client. This message contains a 32-bit integer field specifying the channel number that it will use for the connection and an optionalif both sides set the SSH_PROTOFLAG_HOST_IN_FWD_OPEN bit in their protocol flag fields during key exchangestring containing a description of the host opening the X11 connection.

The client responds by trying to connect to the real X11 server and sending an SSH_MSG_CHANNEL_OPEN_CONFIRMATION if the connection to the X-server succeeded or an SSH_MSG_CHANNEL_OPEN_FAILURE message if the connection failed.

The SSH_MSG_CHANNEL_OPEN_CONFIRMATION message, shown in Figure 7.14, contains two 32-bit integers. The remote channel field is the peer's identifier for the channel over which the X-protocol will be sent. It is the value contained in the SSH_SMSG_X11_OPEN message. The local channel field is the SSH client's identifier for the secure channel.

Figure 7.14. The SSH_MSG_CHANNEL_OPEN_CONFIRMATION Message


This is reminiscent of L2TP, in which each of the peers had its own identifier for a data channel.

The SSH_MSG_CHANNEL_OPEN_FAILURE message contains a single 32-bit integer that is the peer's identifier for the channel. In the context of X11 forwarding, this message means that the SSH client was unable to connect to the local X-server.

Once the client sends the SSH_MSG_CHANNEL_OPEN_CONFIRMATION, a secure, bidirectional data channel is available to the peers. As noted previously, the SSH server will use it to forward X-protocol messages from a local X-client, and the SSH client will use it to forward X-protocol messages from the X-server back to the X-client on the SSH server. The peers use the SSH_MSG_CHANNEL_DATA message, shown in Figure 7.15, to carry the channel data. The remote channel field is a 32-bit integer containing the peer's identifier for the channel. The data is carried as a string in the data field.

Figure 7.15. The SSH_MSG_CHANNEL_DATA Message


Either side of the X11 connection may close it by sending the SSH_MSG_CHANNEL_CLOSE message, which contains the peer's value for the channel number as an integer. For example, the SSH server will send this message when the X-client disconnects from the X-proxy server. The other side will let any queued data drain from the connection and will then send an SSH_MSG_CHANNEL_CLOSE_CONFIRMATION message to complete the channel teardown. Figure 7.16 shows the SSH protocol exchange in a typical forwarded X11 session.

Figure 7.16. Forwarded X11 Connection Flow


Port Forwarding

In the previous subsection, we examined SSH X11 forwarding. Both SSH versions also have a more general connection forwarding capability called port forwarding. This capability is much like that provided by stunnel. The most general case is shown in Figure 7.17: host A wants to connect securely to port R on host B. Instead of directly connectingpossibly across the Internet or other insecure pathto host B, host A connects to port L on the SSH client host instead. SSH then forwards this connection through a secure channel to the SSH server that connects to port R on host B. Thereafter, data from host A travels to the SSH client on the local connection, across the SSH tunnel, and finally across the local connection that the SSH server made to host B. Host B is unaware that forwarding is going on and merely listens for connections on port R as usual. Host A connects to the SSH client, which is listening on port L, instead of to host B.

Figure 7.17. SSH Local Port Forwarding


For security reasons, the default behavior of the SSH client is to listen for local connections only from applications on the same machine. A special flag to ssh is required to get the behavior shown in Figure 7.17. Often the remote application will be on the same machine as the SSH server, but the protocol makes no assumptions about this.

It is also possible to have remote applications connect back to the SSH client host or to a host reachable by the SSH client, as shown in Figure 7.18.

Figure 7.18. SSH Remote Port Forwarding


In this case, host B wants to connect to host A on port L. When the SSH client requests remote port forwarding, the SSH server listens for connections on the specified port: say, port R. When an application connects to port R, the connection is forwarded through the SSH tunnel to the SSH client, which then connects to port L on the target host: host A in Figure 7.18.

As we mentioned in Chapter 5 during our discussion of L2TP/IPsec, it is important to understand the end-to-end security of the tunnels that we set up. For example, if the client application and server application that we port forward between are on the same machines as the SSH client and server, the entire path between application and corresponding server is protected. If, on the other hand, we have a situation such as that in Figure 7.17 or Figure 7.18, parts of the path are unprotected. This may or may not be a problem. If, for instance, host A of Figure 7.17 is on the same trusted network as the SSH client machine, the fact that the connection between host A and the SSH client is not secured may be okay. The same configuration may not be okay if the connection between host A and the SSH client machine is exposed to untrusted parties. Thus, it is important to understand both the threat model and the security of each leg of a proposed VPN.

Let's look at the SSH protocol messages involved in forwarding a local port. We assume that the user has requested SSH to forward port L on the SSH client to port R on a remote server reachable by the SSH server and that the server is the first to disconnect, initiating the channel teardown. Figure 7.19 shows the SSH message flow.

Figure 7.19. Port Forwarding Protocol Exchange


The process begins when an application connects to port L on the SSH client host. The SSH client responds to the connection by sending the SSH server an SSH_MSG_PORT_OPEN message indicating that a connection has been made to a port for which the user requested forwarding and asking the server to connect to the specified host and port. The SSH_MSG_PORT_OPEN message is shown in Figure 7.20. The local channel field is the client's identification number for the channel. The SSH server will use this channel number when sending data to or about this connection to the client.

Figure 7.20. The SSH_MSG_PORT_OPEN Message


The host name is the name or address of the host that the SSH server should connect to. Similarly, the port field is the port to which it should connect.

After connecting to the specified host, the SSH server sends an SSH_MSG_CHANNEL_OPEN_CONFIRMATION message. If it can't connect to the remote host, the SSH server will send an SSH_MSG_CHANNEL_OPEN_FAILURE message instead. Assuming that the connection succeeds, the two (non-SSH) peers can start exchanging data, which is carried in SSH_MSG_CHANNEL_DATA messages, just as in X11 forwarding.

If the remote host disconnects from the SSH server, as shown in Figure 7.19, the SSH server will send an SSH_MSG_CHANNEL_CLOSE message, and the SSH client will respond with an SSH_MSG_CHANNEL_CLOSE_CONFIRMATION message after shutting down its end of the connection. If the local host disconnects first, the actions are the same except that the SSH client sends the SSH_MSG_CHANNEL_CLOSE, and the SSH server responds.

The protocol exchange for remote port forwarding is the same except that the SSH client must first tell the SSH server to start listening on a portport R, sayfor a connection from the remote host. This is accomplished by having the SSH client send an SSH_CMSG_PORT_FORWARD_REQUEST message, shown in Figure 7.21, right after user authentication. The server port field is the port on which the server is to listenport R in our example. The host name and port fields are the name or address and port to which the connection should be forwarded. These are the values that will be sent in the host name and port fields of the SSH_MSG_PORT_OPEN message when the remote host connects.

Figure 7.21. The SSH_CMSG_PORT_FORWARD_REQUEST Message


Running Remote Programs Through an SSH Tunnel

As mentioned previously, the most common use of SSH is to run a shell on a remote host. We can, however, ask SSH to start any program we please on the remote host and send its STDIN, STDOUT, and STDERR through the SSH tunnel.

Suppose, for example, that we want to run our echoit program on bsd securely from linuxlt:

$ ssh -t bsd echoit
Hello, world!
Hello, world!
^D                                    send an EOF to echoit
Connection to bsd closed.

The -t tells SSH to start a pseudo-tty for us. Because we are dealing with the remote program interactively through a terminal session, we need the pseudo-tty to handle terminal control characters and similar events for us.

Note that we don't get the usual login banner or shell prompt. That's because the SSH server executes our program directly, rather than starting a shell and letting us execute it from the shell.

Actually, even here the shell is involved. The SSH server executes the command by calling the shell with the -c switch. That is, the SSH server runs our command as

/usr/local/bin/bash -c echoit

where the /usr/local/bin/bash was obtained from the SHELL environment variable on bsd.

Also note that when we send echoit an EOF, its termination causes the SSH connection to be torn down just as if we had logged out of a shell connection.

From a protocol standpoint, running a remote program instead of a shell uses the same exchange of protocol messages that we saw in Figure 7.9, except that the SSH_CMSG_EXEC_SHELL message is replaced by an SSH_CMSG_EXEC_CMD message that contains the command to be run as a string.

The Secure Copy Program (SCP)

Using SSH to execute a trivial program, such as echoit, isn't very exciting, of course, and it doesn't begin to illustrate the power of the capability. When Tatu Ylönen wrote SSH, he intended it to replace the rcp routine, which copies files between local and remote hosts, as well as rsh and the other r-commands. To this end, he wrote a separate program, scp, to replace rcp.

The scp program is really just a patched version of rcp that sends it data through an SSH tunnel.

To understand how this works, let's suppose that we have two UNIX hosts: a local host, LH, and a remote host, RH. An instance of the SSH server, sshd, is listening for connections on the remote host, but otherwise no programs relevant to our discussion are running. On the local host, a user wishing to transfer the file main.c to the remote host invokes scp as

scp main.c RH:

To transfer the file securely, scp needs two things:

  1. A secure tunnel between the local and remote hosts to send the data through

  2. Another copy of scp running on the remote host to accept and store the file

The scp program can arrange for both of these things by spawning, via fork and exec, an instance of the SSH client as a child process and requesting it to execute a remote instance of scp. With OpenSSH, scp would invoke ssh as

ssh -x -oForwardAgent no -oClearAllForwardings yes RH scp -t main.c

The flags to ssh disable X11, port, and agent forwarding because this invocation of SSH will be used only to transfer the file.

See [Barrett and Silverman 2002] for information about agent forwarding.

Notice that ssh is asked to execute the following command on the remote host:

scp -t main.c

The -t flag tells the remote scp instance that it will be receiving a file, which it should store as main.c.

The flags to scp vary, depending on whether a single file is being transferred to or from the remote host or whether one or more files are being transferred to a directory on the remote host.

The four processes involved are shown in Figure 7.22. The dashed boxes represent child processes. The arrows between the scp and SSH instances represent pipes that connect the parent process to the STDIN, STDOUT, and STDERR of the child process.

Figure 7.22. An scp Connection


At this point, the two scp instances are connected through the SSH tunnel, and they perform the normal rcp protocol exchange to transfer the file. The important point here is that SSH has no special support for file transfers; it merely executes a remote application in the normal manner. Other distributed applications can be implemented in the same way.

As an example of how easy it is to implement secure distributed applications in this way, let's imagine that we have our mail delivered to a central serverRH, saythat for security reasons is reachable only by SSH. We want to know when new email has arrived without having to ssh onto the server to check. We can do this with a set of python programs: rbiff and rbiffd. We start rbiff on our local workstation and it periodically invokes rbiffd on the server to check for mail.

Figure 7.23 shows the rbiff program.

Figure 7.23. The rbiff Application


23

These lines import two modules that provide an interface to the system commands and time routines. We need these modules for the popen and sleep functions.

45

The notify function is called to provide notification when new mail arrives. For simplicity, we show it as just printing "New Mail," but an actual application would pop up a dialog box or change an icon.

6

This is the event loop of the application. Every 10 minutes (see line 13), this loop checks for mail on the server.

7

This is the only part of the two programs that is aware of or deals with SSH. We open a pipe to an instance of ssh that executes rbiffd on the server RH.

813

We read from the pipe, looking for a line that begins with "New." If we find one, we call notify to alert the user. When we get an EOF from the pipe, we close the pipe and sleep for 10 minutes.

 

The rbiffd (Figure 7.24) program has no knowledge of SSH. When invoked, the program merely checks the mailbox and writes its finding to STDOUT. We could run it directly from the command line without any change at all.


Figure 7.24. The rbiffd Application


23

We import the os module so that we can get the location of the user's mailbox from the environment.

47

We call the newmail function, not shown, to check whether new mail is in the user's mailbox. If so, "New Mail" is output to STDOUT. Otherwise, "No New Mail" is output.

 

We've omitted the newmail function to avoid getting bogged down in the minutia of checking for new mail, but the process involves merely checking the times that the mailbox was last written to and accessed.


Security Issues with SSHv1

As with any other program, attackers and security researchers have discovered bugs, such as potential buffer overflows, in the various implementations of SSHv1 that lead to exploits. These bugs were, of course, fixed as they were discovered, so they aren't a concern to us except, perhaps, as a cautionary tale on how difficult it is to implement softwareespecially security softwarein a robust and secure manner. On the other hand, we do need to be concerned with bugs in the protocol itself. These bugs are more difficult to deal with because fixing them requires fixing the protocol rather than merely patching a particular implementation.

One example of such a vulnerability is the session key recovery exploit based on Bleichenbacher's attack on RSA messages encoded with PKCS #1 v1.5 [Bleichenbacher 1998]. This exploit is a result of problems within the SSHv1 protocol, not of any particular implementation of the protocol. The basic idea is that the attacker captures the SSH_CMSG_SESSION_KEY message and then uses a combination of a timing attack and Bleichenbacher's attack to decode the SSH_CMSG_SESSION_KEY message and recover the session key. Once the session key is known, of course, the entire (captured) message stream can be read with ease.

Bleichenbacher's attack is an adaptive procedure that sends the SSH server carefully chosen ciphertext messages and observes the behavior of the server. From these observations, the attacker is able to repeatedly narrow the interval that the session key lies in. The attack, which is described in some detail in [Waissbein and Friedman 2001], takes about 220 + 219 messages to the server. Because the server key expires every hour, the attacker must make about 400 connections per second to the server.

Many servers can't support that rate of connection, and others, such as OpenSSH, limit the number of concurrent unauthenticated connections, so not all servers are vulnerable. Note, however, that this invulnerability is serendipitous, not the result of these implementations being better coded than those that are vulnerable. The problem here is that the SSHv1 protocol causes SSHv1 servers to leak information and thus act as "oracles" for the Bleichenbacher attack.

Fortunately, vulnerable servers can be patched to prevent this exploit by regenerating the server key when they detect the attack. The longer-term solution was to change the PKCS #1 encoding scheme to make it resistant to this type of attack. See [Bleichenbacher, Kalisky, and Staddon 1998] for a description of Bleichenbacher's attack and the changes made to PKCS #1 to prevent it.

Another, more serious, problem with the SSHv1 protocol was not so easily fixed and resulted in the abandonment of SSHv1 and the development of SSHv2. Recall that SSHv1 uses a CRC instead of a MAC. Although quite good at detecting random errors introduced into a file, CRCs make weak MACs. A MAC based on a cryptographic hash, such as MD5 or SHA-1, acts as a pseudorandom function.

These functions aren't truly random, of course, because they are deterministic. The point is that the output appears not to depend on the input in any predictable way. In particular, pseudorandomness requires that given part of the output of the function but not the input, it is infeasible to determine the rest of the output.

Put another way, it's extremely difficult to construct a message m having a particular hash. CRCs, on the other hand, have relatively simple mathematical properties that make it easier to construct a message m with a given CRC value.

An exploit based on the weakness of CRC-32 as a MAC allows the insertion of arbitrary data into an SSHv1 message stream. That is, the attacker is able to insert text into the encrypted message stream, which then decrypts to text of the attacker's choice. This means, among other things, that an attacker can execute arbitrary commands on the SSH server or can display arbitrary data on the client's screen. The details of the attack are given in [Futoransky, Kargieman, and Pacetti 1998].

This exploit was much more difficult to fix than the Bleichenbacher attack. SSHv1 servers were patched with the so-called CRC-32 compensation attack detector, but this merely made the attack more difficult, not impossible. At this time, most security experts recommend against using SSHv1 in any environment where active attacks are a serious concern.


Previous Page
Next Page