Guides

MUD Security with open-source tools

This guide provides a technical roadmap for securing a legacy MUD codebase and its hosting environment. It focuses on mitigating common vulnerabilities found in C-based engines like Diku, Circle, or ROM, including buffer overflows, weak credential storage, and network-level abuse.

6-10 hours6 steps
MUD Security with open-source tools hero illustration
1

Harden the Host Network and SSH Access

Before modifying the MUD code, secure the server. Most MUDs run on high-numbered ports (e.g., 4000, 1234). Use a firewall to restrict all traffic except the game port and a non-standard SSH port. Disable root login and password authentication in favor of SSH keys.

firewall_setup.sh
sudo ufw default deny incoming
sudo ufw allow 2222/tcp
sudo ufw allow 4000/tcp
sudo ufw enable

⚠ Common Pitfalls

  • Ensure you have verified your SSH key works on the new port before enabling the firewall to avoid lockout.
  • Some legacy MUD clients do not support non-standard ports if they are hardcoded to 23.
2

Replace Unsafe String Functions

Legacy C codebases are rife with 'strcpy', 'strcat', and 'sprintf', which are primary vectors for buffer overflow exploits. Replace these with their length-limited counterparts: 'strncpy', 'strncat', and 'snprintf'. Pay special attention to the 'MAX_STRING_LENGTH' constants used throughout the engine.

act.comm.c
// Bad: sprintf(buf, "Player %s has entered.", ch->name);
// Good:
snprintf(buf, sizeof(buf), "Player %s has entered.", ch->name);

⚠ Common Pitfalls

  • strncpy does not guarantee a null terminator if the source is longer than the limit; manual termination may be required.
  • Off-by-one errors when calculating remaining buffer size in complex string concatenations.
3

Implement BCrypt for Password Hashing

Most legacy MUDs use the standard 'crypt()' function with DES, which is trivial to crack. Integrate a library like 'libbcrypt' to upgrade player security. Modify the 'save_char_obj' and 'load_char_obj' functions to handle modern hashes.

nanny.c
#include <bcrypt.h>

char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE];
bcrypt_gensalt(12, salt);
bcrypt_hashpw(password, salt, hash);
// Store 'hash' in the player pfile.

⚠ Common Pitfalls

  • Existing players will have old hashes. You must implement a migration logic that checks against the old crypt() and re-hashes with BCrypt upon the first successful login.
4

Add Command Rate Limiting

Prevent players from flooding the server with commands (e.g., 'get all' in a room with thousands of items) which can lead to high CPU usage or crashes. Implement a 'wait state' or a 'pulse' counter on the descriptor level to reject input that exceeds a threshold.

comm.c
if (d->command_count > MAX_COMMANDS_PER_PULSE) {
    write_to_descriptor(d, "Slow down!\r\n");
    return;
}
d->command_count++;

⚠ Common Pitfalls

  • Setting limits too aggressively can break legitimate player scripts or aliases used for high-level gameplay.
  • Ensure the counter resets every game pulse.
5

Sanitize Input and Filter Control Codes

MUDs often process raw telnet sequences. Malicious players can send ANSI escape sequences or telnet control codes that can manipulate the terminal of other players or the server logs. Implement a filter in the input buffer to strip non-printable characters and unescaped tildes (often used for color codes).

utils.c
void sanitize_input(char *str) {
    char *p = str;
    while (*p) {
        if (*p < 32 && *p != '\r' && *p != '\n') *p = ' ';
        if (*p == '~') *p = '-'; // Prevent Pfile corruption in some engines
        p++;
    }
}

⚠ Common Pitfalls

  • Be careful not to strip legitimate color code delimiters if your game uses a custom color parser.
  • Telnet negotiation sequences (starting with 255/0xFF) must be handled by the telnet state machine, not just stripped.
6

Configure Automated External Backups

Security includes availability. Configure a cron job to rsync the 'lib' directory (containing player files and world data) to a remote server. Encrypt the backups if they contain sensitive player data like email addresses.

backup.sh
tar -czf /tmp/mud_backup.tar.gz ./lib/players ./lib/world
gpg -c /tmp/mud_backup.tar.gz
scp /tmp/mud_backup.tar.gz.gpg backup@remote-server:/backups/

⚠ Common Pitfalls

  • Running backups while the MUD is writing to pfiles can lead to corrupted archives. Use 'flock' or perform backups during low-activity periods.

What you built

Securing a MUD is an ongoing process of reducing attack surfaces. By hardening the OS, upgrading legacy C functions, and implementing modern authentication, you protect both the server's stability and your players' privacy. Regularly audit your 'pfile' directory for unusual growth and monitor logs for repeated failed login attempts.