经历了前一阵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入手来看的话
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
在自己搭建的环境中,没利用成功,期末考后继续尝试