[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Full-Disclosure] Apache - all versions vulnerability in OLD procesors.



Apache - all versions vulnerability in OLD procesors.

I.  Entry.

    Vulnerability in probably all versions of apache web server, 
default
install (as of version 1.3.29).

II. Vulnerability details.

        There are few scenarios, few calls leading to that 
bug. 
The first call is in mod_auth, mod_auth3 and mod_auth4. As 
follows:

"src/modules/standard/mod_auth.c"
and
"src/modules/standard/mod_aut3.c"
and
"src/modules/standard/mod_aut4.c"
static int authenticate_basic_user(request_rec *r)
{
...
...
    const char *sent_pw;
    char *real_pw;
...
...
    if ((res = ap_get_basic_auth_pw(r, &sent_pw)))
        return res;
...
...
    if (!(real_pw = get_pw(r, c->user, sec->auth_pwfile))) {
       ...
       ...
    }
...
...
    invalid_pw = ap_validate_password(sent_pw, real_pw);
...
...
}

request_rec structure is declared in "src/include/httpd.h".

Now look at ap_validate_password() function 
in "src/ap/ap_check.c":

API_EXPORT(char *) ap_validate_password(const char *passwd, 
const char *hash)
{
    char sample[120];
...
...
    /* Netscape / SHA1 ldap style strng
     */
    else if (strncmp(hash, AP_SHA1PW_ID, AP_SHA1PW_IDLEN) == 0) {

        ap_sha1_base64(passwd, strlen(passwd), sample);
    }
...
...
}

AP_SHA1PW_ID in "src/include/ap_sha1.h" is defined as:
...
...
#define AP_SHA1PW_ID "{SHA}"
...
...

Ok. So to the heart of problem. So for strncmp hash to be zero, 
above must be
passwd to ap_get_basic_auth_pw() function:

"src/main/http_pro.c"
API_EXPORT(int) ap_get_basic_auth_pw(request_rec *r, const char 
**pw)
{
...
...
}

The second argument, pw is evaluated inside ap_validate_password 
that is called
from inside get_pw():

"src/modules/standard/mod_auth.c"
static char *get_pw(request_rec *r, char *user, char 
*auth_pwfile)
{
...
...
}

Ok function ap_validate_password calls ap_sha1_base64(). Take a 
closer look:

"src/ap/ap_sha1.c"
API_EXPORT(void) ap_sha1_base64(const char *clear, int len, char 
*out)
{
...
...
    AP_SHA1_CTX context;
...
...
    ap_SHA1Init(&context);
    ap_SHA1Update(&context, clear, len);
...
...
}

AP_SHA1_CTX:

"src/ap/ap_sha1.c"
typedef struct {
    AP_LONG digest[5];             /* message digest */
    AP_LONG count_lo, count_hi;    /* 64-bit bit count */
    AP_LONG data[16];              /* SHA data buffer */
    int local;                     /* unprocessed amount in data 
*/
} AP_SHA1_CTX;

ok, now ap_SHA1Init():

"src/ap/ap_sha1.c"
API_EXPORT(void) ap_SHA1Init(AP_SHA1_CTX *sha_info)
{
    sha_info->digest[0] = 0x67452301L;
    sha_info->digest[1] = 0xefcdab89L;
    sha_info->digest[2] = 0x98badcfeL;
    sha_info->digest[3] = 0x10325476L;
    sha_info->digest[4] = 0xc3d2e1f0L;
    sha_info->count_lo = 0L;
    sha_info->count_hi = 0L;
    sha_info->local = 0;
}

"src/ap/ap_sha1.c"
API_EXPORT(void) ap_SHA1Update(AP_SHA1_CTX *sha_info, const char 
*buf,
                               unsigned int count)
{
...
...
    const AP_BYTE *buffer = (const AP_BYTE *) buf;
...
...
    while (count >= SHA_BLOCKSIZE) {
        ebcdic2ascii((AP_BYTE *)sha_info->data, buffer, 
SHA_BLOCKSIZE);
        buffer += SHA_BLOCKSIZE;
        count -= SHA_BLOCKSIZE;
        maybe_byte_reverse(sha_info->data, SHA_BLOCKSIZE);
        sha_transform(sha_info);
    }
    ebcdic2ascii((AP_BYTE *)sha_info->data, buffer, count);
...
...
}

Aha... good, while count is bigger or equal following constant:

"src/ap/ap_sha1.c"
...
...
#define SHA_BLOCKSIZE           64
...
...

Hm... ok, this get's evaluated further more in ebcdic2ascii() ?

"src/ap/ap_ebcdi.c"
API_EXPORT(void *)
ebcdic2ascii(void *dest, const void *srce, size_t count)
{
    unsigned char *udest = dest;
    const unsigned char *usrce = srce;

    while (count-- != 0) {
        *udest++ = os_toascii[*usrce++];
    }

    return dest;
}

Above function copies 64 bytes, structre AP_SHA1_CTX is an array 
of 16 elements.
Take a look at structure element declaration :

"src/include/ap_sha1.h"
typedef unsigned long AP_LONG;     /* a 32-bit quantity */

This is fine, assuming that we have 32 bits CPU, and sizeof
(unsigned long) equals 4. So 4*16=64.
There is no guarantee that on some archs unsigned long is going 
to stay 32 bit width. When it's
either longer or shorter (I am not sure if long can be 16 bits 
long, but possibly ANSI C standart
doesn't say anythin about it's length in bits). Ie. on 64bit 
platforms, depending on compiler
options, and compiler it self long can be either 64 (default) or 
32 bits. 
Take a look at that:

siusiak% uname -a; cat a.c; cc a.c; ./a.out
 OSF1 sirppi V4.0 1229 alpha
 int main(void) { 
        printf("%d\n",sizeof(unsigned long)); 
        return 0; 
        }
 8
siusiak%

When sizeof( unsigned long )!=4 it can lead to memory corruption 
in function ebcdic2ascii(),
which will either copy too much, copyied in this example 32 
bytes more than he should and
that situaction do this bug! To bypass this not popular 
vulnerability we should only
resolution is quite simple, SHA_BLOCKSIZE should be declared as 
sizeof(unsigned long)*16.
Possibly SHA_BLOCKSIZE must stay 64 bytes long, than obviously 
author should take care more
about single elements size.


III. Exploit.

        I no time to have a very close look on it, so no proof 
of concept is provided this time.
Thanks for attention for read this shit.

--
pi3 (pi3ki31ny) - pi3ki31ny@xxxxx (BIG thx for appelast and 
giejot)
http://www.pi3.int.pl

"MJINKS, swanky pijemy w czerwcu no nie? ;-)"


----------------------------------------------------
Światowy Dzień Książki z Klubem Świat Książki - sprawdź jakie
atrakcje przygotowaliśmy!
http://klik.wp.pl/?adr=http%3A%2F%2Fksiazki.wp.pl%2Fwiadomosci%2Fwiadomosc.html%3Fid%3D28588&sid=163


_______________________________________________
Full-Disclosure - We believe in it.
Charter: http://lists.netsys.com/full-disclosure-charter.html