Samba远程代码执行-分析(CVE-2017-7494)

经历了前一阵windows的EternalBlue之后,某天看见了360的 samba高危预警,这个号称linux端的EternalBlue(EternalRed),于是便研究了一波

概述(抄)

Samba是在Linux和UNIX系统上实现SMB协议的一个软件。2017年5月24日Samba发布了4.6.4版本,中间修复了一个严重的远程代码执行漏洞,漏洞编号CVE-2017-7494,漏洞影响了Samba 3.5.0 之后到4.6.4/4.5.10/4.4.14中间的所有版本。

原因分析

注:个人认为对于已经打过patch的代码,根据他的git diff可以很方便也很简单地定位到漏洞代码

首先从samba github上打的patch入手来看的话

github-patch

patch代码就只添加了一行过滤,

所以可以确定成因在于is_known_pipename函数中对pipename路径符号的过滤不全导致的。

那么继续向下分析其将pipename传入smb_probe_module

bool is_known_pipename(const char *pipename, struct ndr_syntax_id *syntax)
{
	//...
	status = smb_probe_module("rpc", pipename);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10, ("is_known_pipename: %s unknown\n", pipename));
		return false;
	}
	//...
}

smb_probe_module则将pipename传入do_smb_load_module

NTSTATUS smb_probe_module(const char *subsystem, const char *module)
{
	return do_smb_load_module(subsystem, module, true);
}

再继续深入do_smb_load_module,其在加载模块时对module_name进行了一次判断(模块路径第一个字符是否为/
若为false,则直接调用load_module函数加载模块,若成功加载将返回一个init函数指针,并执行它

static NTSTATUS do_smb_load_module(const char *subsystem,
				   const char *module_name, bool is_probe)
{
	//...
	if (subsystem && module_name[0] != '/') {
		full_path = talloc_asprintf(ctx,
					    "%s/%s.%s",
					    modules_path(ctx, subsystem),
					    module_name,
					    shlib_ext());
		if (!full_path) {
			TALLOC_FREE(ctx);
			return NT_STATUS_NO_MEMORY;
		}

		DEBUG(5, ("%s module '%s': Trying to load from %s\n",
			  is_probe ? "Probing": "Loading", module_name, full_path));
		init = load_module(full_path, is_probe, &handle);
	} else {
		init = load_module(module_name, is_probe, &handle);
	}
	//...
	if (!init) {
		TALLOC_FREE(ctx);
		return NT_STATUS_UNSUCCESSFUL;
	}
	
	DEBUG(2, ("Module '%s' loaded\n", module_name));

	status = init(NULL); //RCE POINT!!!
}

那么load_module究竟又做了什么呢?它返回的init函数指针又是什么?
它直接打开path,若成功加载则调用dlsym函数用于加载该模块中的samba_init_module

init_module_fn load_module(const char *path, bool is_probe, void **handle_out)
{
	//...
	
	handle = dlopen(path, RTLD_NOW);
	
	//...
	
	if (handle == NULL) {
		//...
	}
	
	init_fn = (init_module_fn)dlsym(handle, SAMBA_INIT_MODULE);
	
	//...
}

//...

#define SAMBA_INIT_MODULE "samba_init_module"

至此,整个漏洞成因也就清晰了,is_known_pipename函数由于路径符号过滤不严谨,导致攻击者可以通过上传恶意so文件,并且猜测路径导致任意代码执行。

攻击过程

条件:攻击者拥有上传文件权限

  • 首先攻击者上传含有恶意代码的so文件至samba服务器
  • 猜测绝对路径,通过构造以/开头的路径名(eg. /home/samba/attacker.so)发送到服务端,使其返回该文件FID
  • 请求该FID便可调用so文件中的samba_init_module,实现任意代码执行。

POC

在自己搭建的环境中,没利用成功,期末考后继续尝试

posted @ 2017-07-08 10:48  tr3e  阅读(932)  评论(0编辑  收藏  举报