Samba远程崩溃或代码执行漏洞(CVE-2015-0240)简要分析

来源:互联网 发布:最牛象棋软件 编辑:程序博客网 时间:2024/06/05 16:06

1.搭建samba环境

注意:在Linux系统上使用源码编译来安装samba必须要将系统自带的全部关于samba的软件均删除,可用如下命令查看已安装的samba软件:

[bb@localhost bin]$ rpm -qa | grep sambasamba-common-3.6.23-14.el6_6.x86_64samba-winbind-3.6.23-14.el6_6.x86_64samba-winbind-clients-3.6.23-14.el6_6.x86_64samba-client-3.6.23-14.el6_6.x86_64

然后直接全部删除:

[bb@localhost bin]$ rpm -qa | grep samba | xargs sudo rpm -e --nodeps

删除完成之后从https://download.samba.org/pub/samba/stable/samba-3.6.23.tar.gz下载源码,解压后进入source3目录,先执行./configure进行配置,然后对Makefile文件稍作更改,在编译选项中加入-g以便gdb对其进行调试。随后就是make编译,make install安装,默认安装路径是/usr/local/samba,安装后目录如下:

[bb@localhost samba]$ lsbin  include  lib  private  sbin  share  swat  var

首先进入lib目录,将源码中example目录下的smb.conf.dufault文件复制到当前目录并改名为smb.conf;然后执行以下命令:

[bb@localhost bin]$ sudo vim /etc/ld.so.conf

在打开的文件中添加一行

/usr/local/samba/lib

退出并执行ldconfig更新动态链接库缓存。然后就是到/usr/local/samba/bin目录下执行:

[bb@localhost bin]$ sudo ./smbpasswd -a bbNew SMB password:Retype new SMB password:Added user bb.

然后需要关闭防火墙:

[bb@localhost ~]$ sudo service iptables stop[sudo] password for bb: iptables: Flushing firewall rules:                         [  OK  ]iptables: Setting chains to policy ACCEPT: filter          [  OK  ]iptables: Unloading modules:                               [  OK  ]

最后则是到sbin目录下启动smbd和nmbd程序,完成后可以看到进程如下:

[bb@localhost sbin]$ pgrep smbd63596360[bb@localhost sbin]$ pgrep nmbd6365

然后我在Windows上登陆便会提示输入用户名和密码,正确之后便能访问bb用户的目录文件:
这里写图片描述

2.源码分析过程

首先是在redhat的博客https://securityblog.redhat.com/2015/02/23/samba-vulnerability-cve-2015-0240/上看到了漏洞的代码是在_netr_ServerPasswordSet函数中,如下图所示:

这里写图片描述

其中第一方框处事creds的声明的地方,这里并未对其进行初始化,第二处将creds的地址作为参数,想必是对其进行初始化,第三处则是当第二处的函数netr_creds_server_step_check返回错误也就是验证creds失败则会将creds指向的内存释放,但是释放之前并未对creds指针进行有效性检查。那什么条件会使得netr_creds_server_step_check函数运行失败呢?

下面是netr_creds_server_step_check的代码:

这里写图片描述

显然schannel_check_creds_state函数用来初始化creds结构指针的,继续看这个函数:

这里写图片描述

带有方框的两部分都是受客户端的控制并可能导致验证失败的地方,第一处只要客户端的机器没有在服务器上验证通过过就会触发(可以是一个新的计算机名或是伪造一个计算机名),第二处只要随意伪造一个凭证即可触发。现在需要验证这两种情况的可能性。

3.验证POC

在github上https://gist.github.com/worawit/33cc5534cb555a0b710b看到了一个poc。

3.1

首先验证第一个原因,也就是未经过验证的计算机导致崩溃。运行samba服务端,使用gdb attach上去然后在_netr_ServerPasswordSet函数处下断点:

(gdb) cContinuing.Breakpoint 1, _netr_ServerPasswordSet (p=0x7f1ceeb92d10, r=0x7f1ceeb96520)    at rpc_server/netlogon/srv_netlog_nt.c:12051205    {

一直运行到根据主机名获取相应凭证的函数,可以看到这里的计算机名就是客户端的ip地址192.168.1.132:

(gdb) sschannel_fetch_session_key_tdb (tdb_sc=0x7fa334355f30, mem_ctx=0x7fa334355e40,    computer_name=0x7fa33436d890 "192.168.1.132", pcreds=0x7fff538e2ba8)    at ../libcli/auth/schannel_state_tdb.c:128

随后构造一个字符串:

(gdb) n144             keystr = talloc_asprintf(mem_ctx, "%s/%s",(gdb) p keystr$15 = 0x7fa334360650 "SECRETS/SCHANNEL/192.168.1.132"

然后根据这个字符串从tdb中获取相应的凭证:

(gdb) n151             value = tdb_fetch_bystring(tdb_sc->tdb, keystr);

继续跟踪可以看到其计算hash值的算法:

/* This is based on the hash algorithm from gdbm */unsigned int tdb_old_hash(TDB_DATA *key){    uint32_t value; /* Used to compute the hash value.  */    uint32_t   i;   /* Used to cycle through random values. */    /* Set the initial value from the key size. */    for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++)        value = (value + (key->dptr[i] << (i*5 % 24)));    return (1103515243 * value + 12345);}

查看运行返回的值已经是NULL了,随后返回失败的结果,指向creds的指针并没有初始化:

(gdb) n152             if (!value.dptr) {(gdb) p value$16 = {dptr = 0x0, dsize = <value optimized out>}

最后则是在释放指针的时候崩溃:

(gdb) n1224                    TALLOC_FREE(creds);(gdb) cContinuing.Program received signal SIGSEGV, Segmentation fault.0x00007fa331e572f0 in talloc_parent_chunk (ptr=<value optimized out>) at ../lib/talloc/talloc.c:407407             while (tc->prev) tc=tc->prev;

3.2

接下去是使用验证过的计算机但将其凭证修改为其他值,这就需要先验证通过,http://www.freebuf.com/vuls/59898.html和https://technet.microsoft.com/zh-tw/exchange/cc237127中都表明需要使用netlogon服务都必须先通过NetrServerAuthenticate验证。代码如下所示:

这里写图片描述

但是经过多次尝试和改变,服务器端一直都是密码错误,不知为何:

这里写图片描述

所以第二种方法就暂时无法验证。

4.修复方法

知道了原因修复起来就简单多了,直接在creds声明的时候初始化为NULL,然后在释放前判断其是否为NULL即可。

0 0
原创粉丝点击