COMMAND
ssh
SYSTEMS AFFECTED
ssh-1.2.30
PROBLEM
Matt Power found following. If you use the ssh-1.2.30 ssh client
on Solaris 8, a password typed for password authentication can
often be partially recovered from the client process memory long
afterwards, using pcat (from the TCT distribution). As far as
known, the problem is that ssh reads the password using fgets,
and the program happens to not subsequently overwrite the relevant
memory locations used by that stdio buffer. (It overwrites the
main password buffer, but not the stdio buffer.)
Here are the results so far: on Solaris 8, if this password data
is recoverable at all, the pcat output file will apparently always
have at least the first four characters of the password. (It's
easy to determine which four characters come from the password).
Knowing four characters will sometimes help a lot with manual
guessing of the full password, and provide a significant
"workfactor reduction" if you're able to launch an exhaustive
search attack.
On Red Hat Linux 6.2 i386, in many tries Matt never seen any
obvious part of a login password in the ssh process memory. This
does not, however, mean that Linux keeps you safe from someone
who gains root access and wants to find out what login or
encryption passwords you typed in the past. They can instead
sometimes recover passwords from the portions of /dev/mem
corresponding to kernel tty->read_buf's long after the password
was typed (there might be a similar level of tty-buffer risks
with the kernel on Solaris or other systems).
SOLUTION
Here's a workaround that appears to address the issue for Solaris
8:
*** ssh-1.2.30/readpass.c.old Wed Jul 5 11:26:50 2000
--- ssh-1.2.30/readpass.c Thu Aug 3 15:46:11 2000
***************
*** 94,96 ****
{
! char buf[1024], *cp;
unsigned char quoted_prompt[512];
--- 94,96 ----
{
! char buf[1024], *cp, *fs;
unsigned char quoted_prompt[512];
***************
*** 223,225 ****
/* Read the passphrase from the terminal. */
! if (fgets(buf, sizeof(buf), f) == NULL)
{
--- 223,228 ----
/* Read the passphrase from the terminal. */
! buf[0] = '\0';
! fs = fgets(buf, sizeof(buf), f);
! memset(f->_base, 0, strlen(buf));
! if (fs == NULL)
{
Note that this is not portable. If you wanted to use a similar
approach on other systems, you might need to use f->_IO_read_base
rather than f->_base. Also, you could instead modify the code so
that read(2) is used for password input, avoiding stdio
completely.
For the record, OpenSSH does not have this problem since it does
not use stdio for reading such data. Instead it uses a routine
which calls read() per-character, so that proper semantics result
from hitting ^C and EOF on the wire. The code we use is similar
to that found in getpass(3).