Glibc ghost(CVE-2015-0235)漏洞简要分析及检测

本文详细介绍了Glibc Ghost漏洞(CVE-2015-0235)的原理,包括如何通过gethostbyname*()函数触发本地或远程的缓冲区溢出。分析了漏洞修复的方法,并探讨了多种利用场景,如WordPress的xmlrpc pingback、iputils工具集中的clockdiff和tracepath,以及Exim邮件服务器。同时,提供了Snort检测规则以防止此类攻击。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.漏洞简介

代码审计公司Qualys的研究人员在glibc库中的__nss_hostname_digits_dots()函数中发现了一个缓冲区溢出的漏洞,这个bug可以经过gethostbyname*()函数被本地或者远程的触发。应用程序主要使用gethostbyname*()函数发起DNS请求,这个函数会将主机名称转换为ip地址。

2.POC验证

测试漏洞的POC来自http://www.openwall.com/lists/oss-security/2015/01/27/9的第4节,主要代码如下,原理是通过调用gethostbyname_r函数,关键参数为temp和name。拿64位系统举例,如果经过gethostbyname_r的调用刚好能够覆盖temp.buffer的1024个字节,那么temp.canary就不会变,则glibc库没有该漏洞,但是如果temp.canary的前8字节被覆盖为0则说明漏洞存在:

#define CANARY "in_the_coal_mine"

struct {
  char buffer[1024];
  char canary[sizeof(CANARY)];
} temp = { "buffer", CANARY };

int main(void) {
  ... ...
  size_t len = sizeof(temp.buffer) - 16*sizeof(unsigned char) - 2*sizeof(char *) - 1;
  char name[sizeof(temp.buffer)];
  memset(name, '0', len);
  name[len] = '\0';

  retval = gethostbyname_r(name, &resbuf, temp.buffer, sizeof(temp.buffer), &result, &herrno);

  if (strcmp(temp.canary, CANARY) != 0) {
    puts("vulnerable");
    exit(EXIT_SUCCESS);
  }
  ... ...
}

POC运行成功:

这里写图片描述

3.漏洞原理与修复分析

漏洞原理

有漏洞的函数是nss/digits_dots.c中的__nss_hostname_digits_dots函数,而这个函数只有在nss/getXXbyYY.c和nss/getXXbyYY_r.c中被用到,且仅当定义了HANDLE_DIGITS_DOTS宏时才会被调用,这个宏则是定义在inet目录中的gethstbynm系列文件中,因此仅当调用gethostbyname*系列函数时会被调用:

#ifdef HANDLE_DIGITS_DOTS
  switch (__nss_hostname_digits_dots (name, resbuf, &buffer, NULL,
                      buflen, result, &status, AF_VAL,
                      H_ERRNO_VAR_P))
    {
    case -1:
      return errno;
    case 1:
      goto done;
    }
#endif

下面来进入__nss_hostname_digits_dots函数分析:

int
__nss_hostname_digits_dots (const char *name, struct hostent *resbuf,
                char **buffer, size_t *buffer_size,
                size_t buflen, struct hostent **result,
                enum nss_status *status, int af, int *h_errnop)
{
  ... ...
  if (isdigit (name[0]) || isxdigit (name[0]) || name[0] == ':')
    {
      const char *cp;
      char *hostname;
      typedef unsigned char host_addr_t[16];
      host_addr_t *host_addr;
      typedef char *host_addr_list_t[2];
      host_addr_list_t *h_addr_ptrs;
      char **h_alias_ptr;
      size_t size_needed;
      int addr_size;

  ... ...
      size_needed = (sizeof (*host_addr)
             + sizeof (*h_addr_ptrs) + strlen (name) + 1);

      if (buffer_size == NULL)
        {
      if (buflen < size_needed)
        {
          if (h_errnop != NULL)
        *h_errnop = TRY_AGAIN;
          __set_errno (ERANGE);
          goto done;
        }
    }
      else if (buffer_size != NULL && *buffer_size < size_needed)
    {
      char *new_buf;
      *buffer_size = size_needed;
      new_buf = (char *) realloc (*buffer, *buffer_size);

      if (new_buf == NULL)
        {
          ... ...
          goto done;
        }
      *buffer = new_buf;
    }

      memset (*buffer, '\0', size_needed);

      host_addr = (host_addr_t *) *buffer;
      h_addr_ptrs = (host_addr_list_t *)
    ((char *) host_addr + sizeof (*host_addr));
      h_alias_ptr = (char **) ((char *) h_addr_ptrs + sizeof (*h_addr_ptrs));
      hostname = (char *) h_alias_ptr + sizeof (*h_alias_ptr);

      if (isdigit (name[0]))
    {
      for (cp = name;; ++cp)
        {
          if (*cp == '\0')
        {
          int ok;

          if (*--cp == '.')
            break;
          if (af == AF_INET)
            ok = __inet_aton (name, (struct in_addr *) host_addr);
          else
            {
              assert (af == AF_INET6);
              ok = inet_pton (af, name, host_addr) > 0;
            }
          if (! ok)
            {
              *h_errnop = HOST_NOT_FOUND;
              if (buffer_size)
            *result = NULL;
              goto done;
            }
        }
          ... ...
          resbuf->h_name = strcpy (hostname, name);
          h_alias_ptr[0] = NULL;
          resbuf->h_aliases = h_alias_ptr;
          (*h_addr_ptrs)[0] = (char *) host_addr;
     
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值