[<prev] [next>] [day] [month] [year] [list]
Message-ID: <408a87996c2ec@wp.pl>
From: pi3ki31ny at wp.pl (Adam Zabrocki)
Subject: 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@...pl (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
Powered by blists - more mailing lists