glic2.17漏洞分析

2015年02月02日

漏洞影响范围

漏洞发现官方Qualys研究人员称影响范围是glibc2.2--glibc2.17之间的各个版本。 实际上在glibc2.18已经修正了这个错误,时间是2013年。但gnu官方并没有将此列为漏洞处理,可能觉得这个漏洞非常难以利用吧。 需要研究源代码的可以到gnu官网下载相应版本的源代码。

调用过程研究

漏洞的发生位置是int __nss_hostname_digits_dots(...)函数。这个函数实现在nss/digits_dots.c(glibc2.17)。

35. int
36. __nss_hostname_digits_dots (const char *name, struct hostent *resbuf,
37. 			    char **buffer, size_t *buffer_size,
38. 			    size_t buflen, struct hostent **result,
39. 			    enum nss_status *status, int af, int *h_errnop)

对这个函数的调用发生在gethostbynamegethostbyname_r(前一个函数的reenter版,支持多线程)函数中。这两个函数在glibc的源代码中寻找起来比较坑爹,不过又学到了超级大牛们是如何写代码的(充分利用宏)。

函数gethostbyname实现在nss/getXXbyYY.c文件中。实际上这个文件如同文件名一样,定义了一个通用的方法,XX和YY根据不同的宏定义,可以实现不同的接口名称。gethostbyname只是其中的一个接口名称。

50. /* To make the real sources a bit prettier.  */
51. #define REENTRANT_NAME APPEND_R (FUNCTION_NAME)
52. #define APPEND_R(name) APPEND_R1 (name)
53. #define APPEND_R1(name) name##_r
54. #define INTERNAL(name) INTERNAL1 (name)
56. #define INTERNAL1(name) __##name
...
86. LOOKUP_TYPE *
87. FUNCTION_NAME (ADD_PARAMS)
88. {
...

接口定义在inet/gethstbynm.c文件中。

#define LOOKUP_TYPE	struct hostent
#define FUNCTION_NAME	gethostbyname
#define DATABASE_NAME	hosts
#define ADD_PARAMS	const char *name
#define ADD_VARIABLES	name
#define BUFLEN		1024
#define NEED_H_ERRNO	1

#define HANDLE_DIGITS_DOTS	1

#include <nss/getXXbyYY.c>

函数gethostbyname_r就更坑爹了。其实现在nss/getXXbyYY_r.c文件中。同上面一样,这个文件也是定义了一个通用的方法,通过一系列宏,来确定接口名称,只是更复杂一些。

69. #define REENTRANT_NAME APPEND_R (FUNCTION_NAME)
70. #ifdef FUNCTION2_NAME
71. # define REENTRANT2_NAME APPEND_R (FUNCTION2_NAME)
72. #else
73. # define REENTRANT2_NAME NULL
74. #endif
75. #define APPEND_R(name) APPEND_R1 (name)
76. #define APPEND_R1(name) name##_r
77. #define INTERNAL(name) INTERNAL1 (name)
78. #define INTERNAL1(name) __##name
...
146. int
147. INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer,
148. 			   size_t buflen, LOOKUP_TYPE **result H_ERRNO_PARM
149. 			   EXTRA_PARAMS)
150. {
...
319. #ifdef NO_COMPAT_NEEDED
320. strong_alias (INTERNAL (REENTRANT_NAME), REENTRANT_NAME);

可见里面使用带参数的宏,在定义的name后面加上_r,前面加上__。这样如果#define FUNCTION_NAME gethostbyname的话,经过转换,接口定义就变成了__gethostbyname_r。怎么没有gethostbyname_r?实际上,这个接口名称是在Ln 320定义的一个函数的别名。

此函数的接口定义在inet/gethstbynm_r.c文件中。

#define LOOKUP_TYPE	struct hostent
#define FUNCTION_NAME	gethostbyname
#define DATABASE_NAME	hosts
#define ADD_PARAMS	const char *name
#define ADD_VARIABLES	name
...
#include "../nss/getXXbyYY_r.c"