PDA

Bekijk Volledige Versie : Apache - all versions vulnerability in OLD procesors.



Adam Zabrocki
24/04/04, 23:25
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@wp.pl (BIG thx for appelast and giejot)
http://www.pi3.int.pl

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

Chris Adams
26/04/04, 20:55
Once upon a time, Adam Zabrocki <pi3ki31ny@wp.pl> said:
> 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).

In ANSI C the basic types are not defined in terms of bits, however
there is a minimum magnitude required. An unsigned long is supposed to
hold at least 4294967295, which is 2^32-1. On most normal platforms,
that means an unsigned long must be at least 32 bits.

--
Chris Adams <cmadams@hiwaay.net>
Systems and Network Administrator - HiWAAY Internet Services
I don't speak for anybody but myself - that's enough trouble.

Peter J. Holzer
26/04/04, 21:15
--fdj2RfSjLxBAspz7
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

On 2004-04-24 15:53:03 -0000, Adam Zabrocki wrote:
> Apache - all versions vulnerability in OLD procesors.

Hmm, 64 bit processors are old?

[...]

> Aha... good, while count is bigger or equal following constant:
>=20
> "src/ap/ap_sha1.c"
> ...
> ...
> #define SHA_BLOCKSIZE 64
> ...
> ...
>=20
> Hm... ok, this get's evaluated further more in ebcdic2ascii() ?
>=20
> "src/ap/ap_ebcdi.c"
> API_EXPORT(void *)
> ebcdic2ascii(void *dest, const void *srce, size_t count)
> {
> unsigned char *udest =3D dest;
> const unsigned char *usrce =3D srce;
>=20
> while (count-- !=3D 0) {
> *udest++ =3D os_toascii[*usrce++];
> }
>=20
> return dest;
> }
>=20
> Above function copies 64 bytes, structre AP_SHA1_CTX is an array of 16 el=
ements.
> Take a look at structure element declaration :
>=20
> "src/include/ap_sha1.h"
> typedef unsigned long AP_LONG; /* a 32-bit quantity */
>=20
> This is fine, assuming that we have 32 bits CPU, and sizeof(unsigned long=
) equals 4. So 4*16=3D64.
> There is no guarantee that on some archs unsigned long is going to stay 3=
2 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).

How about looking it up? The C standard defines long as having at least
32 bits (and int and short as having at least 16 bits, and char as
having at least 8 bits, just for completeness).

> Ie. on 64bit platforms, depending on compiler
> options, and compiler it self long can be either 64 (default) or 32 bits.=
=20

Correct.

> When sizeof( unsigned long )!=3D4 it can lead to memory corruption in fun=
ction ebcdic2ascii(),
> which will either copy too much, copyied in this example 32 bytes more th=
an he should and
> that situaction do this bug!

No. It will still copy 64 (SHA_BLOCKSIZE) bytes, but the buffer will now
be 16*8 =3D 128 bytes long. So half of the buffer will be wasted, but no
overflow will occur.=20

hp

--=20
_ | Peter J. Holzer | Shooting the users in the foot is bad.=20
|_|_) | Sysadmin WSR / LUGA | Giving them a gun isn't.
| | | hjp@wsr.ac.at | -- Gordon Schumacher,
__/ | http://www.hjp.at/ | mozilla bug #84128

--fdj2RfSjLxBAspz7
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.7 (GNU/Linux)

iQDQAwUBQIzAOFLjemazOuKpAQEbPwXUCNUJBs/ZvAs0fw9od2x2SCqexoutolNU
l9gIGuwHMJsu+u6MznzkoXIeIt1lIlX83bt4t/UldrFAEYpFUn7yN/e5IaeB1BC/
wQcQeS8i+3n5i53+Jnn69qCuNrOOtcoiBVGsqa71fpepjCMMV+ c6n+dtfV1s22XZ
MbI/8LuoMBhUqlycQz9wYnOKRiN2j8EJsZmsE+571NZ3ct/OOA+KYYbWt+6smHw3
KabG5oPoRvBMwPjr/o3c/6aLDw==
=fvq/
-----END PGP SIGNATURE-----

--fdj2RfSjLxBAspz7--

Chris Adams
26/04/04, 21:45
Once upon a time, Adam Zabrocki <pi3ki31ny@wp.pl> said:
> 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"

Apache HTTPD 1.3.29 doesn't include a mod_aut3.c or mod_aut4.c, so this
is not a "default install".

> 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":

You mean src/ap/ap_checkpass.c?

<snip>

> 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);
> ...
> ...
> }

This whole section only gets called when CHARSET_EBCDIC is defined,
which is only on a few mainframe platforms, so this is not even active
on the vast majority of platforms.

> 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.

See my other message; an unsigned long will always be at least 32 bits
(or a storage size large enough to hold a 32 bit unsigned number, which
I believe on all Apache 1.3 supported platforms will be a binary 32 bit
or 64 bit allocation).

> 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.

I don't see a potential for a problem, because the ->data field is
always at least 64 bytes long, but only the first 64 bytes are used (and
since they are byte-addressed always, there is no uninitialized data
read).

Also, IIRC, the C compilers on the EBCDIC platforms uses a 32 bit
unsigned long, so there would be no problem anyway.
--
Chris Adams <cmadams@hiwaay.net>
Systems and Network Administrator - HiWAAY Internet Services
I don't speak for anybody but myself - that's enough trouble.

Adam Zabrocki
27/04/04, 22:15
Hello

In OLD procesors (i mean like x286 and some version x386) when the sizeof (unsigned long) == sizeof (unsgined int) there is bug becouse it's 2 bytes and... 2*16=32 but i don't have in this time box with that procesors for research this situactions and
this is only theory that bug becouse i don't know whether apache we can run on this OLD procesors. Second question is what about new and not standard procesor like 64 bits or alpha, risc and etc. (in this time it's no in standard uses in box). Some calls
for functions to copied bytes is given argument like sizeof (sha_info->data) and that write 8*16-64=64 bytes more and not control what can start some conflict in farther run that server.

Best regards Adam Zabrocki (pi3 - pi3ki31ny).

Peter Pentchev
28/04/04, 17:45
--Ns7jmDPpOpCD+GE/
Content-Type: text/plain; charset=windows-1251
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

On Mon, Apr 26, 2004 at 08:12:26PM -0000, Adam Zabrocki wrote:
>=20
> Hello
>=20
> In OLD procesors (i mean like x286 and some version x386) when the
> sizeof (unsigned long) =3D=3D sizeof (unsgined int) there is bug becouse =
it's
> 2 bytes and... 2*16=3D32

Eh?

Yes, you are talking about 16-bit processors, where the word size was 16
bits (two bytes), and sizeof(int) =3D=3D 2. However, I can't remember any C
compilers for Intel x86 processors where sizeof(long) !=3D 4. Yes, 'int' w=
as
a 16-bit type, but IIRC, 'long' has always been a 32-bit type (a so-called
DWORD back then) - just as the C standard requires, as others mentioned.

> but i don't have in this time box with that
> procesors for research this situactions and this is only theory that bug
> becouse i don't know whether apache we can run on this OLD procesors.

It's not just a matter of processors. You have to have OS support for
sockets and stuff. Yes, this can be done under Windows in 16-bit mode or
MS-DOS with the appropriate socket library, like WATTCP -
http://google.com/search?q=3DApache+WATTCP

Still, see above for the reasons this will NOT be a problem even then.
sizeof(long) >=3D 4, and that's that.

G'luck,
Peter

--=20
Peter Pentchev roam@ringlet.net roam@sbnd.net roam@FreeBSD.org
PGP key: http://people.FreeBSD.org/~roam/roam.key.asc
Key fingerprint FDBA FD79 C26F 3C51 C95E DF9E ED18 B68D 1619 4553
Thit sentence is not self-referential because "thit" is not a word.

--Ns7jmDPpOpCD+GE/
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (FreeBSD)

iD8DBQFAj1077Ri2jRYZRVMRAnI8AJwJPqNqL/YYngIu1MDasZxBVzDA6QCfR1Af
XhMG9xccP+uxfn9H6ITmq7w=
=CAb7
-----END PGP SIGNATURE-----

--Ns7jmDPpOpCD+GE/--