Samba是利用SMB协议实现文件共享的一款著名开源工具套件。日前 Samba 曝出一个严重安全漏洞,该漏洞出现在 smbd 文件服务端,漏洞编号为CNNVD-201502-363,可以允许攻击者远程远程执行恶意代码。
作为一款老牌系统工具,Samba的使用率非常高,更是作为很多*BSD 和苹果 OS X操作系统的组件存在,因此影响面非常大。Samba 支持的操作系统包括 Windows 95/98/NT,OS/2 和Linux。
以下的代码分析、调试和复现过程是在 samba-3.6.9 debuginfo 版中进行,系统版本为 CentOS 6.4。
在请求 Samba Netlogon 服务的时候,大致的交互流程如下图:
Samba客户端如果想访问服务器上的资源,首先需要一个授权验证的过程。客户端会通过NetrServerAuthenticate 请求先到 Samba 服务器进行认证,服务器在收到请求后,会到域控服务器获取客户端的用户信息,并进行一系列的认证操作。
在认证通过后,服务器会用客户端、服务器、以及客户端密码 hash 等信息,生成一个会话凭证(credential),并保存到本地文件中。默认情况下该文件的存储路径为/var/lib/samba/private/schannel_store.tdb。
生成文件时所用的 hash 索引用到了 Samba 客户端的计算机名,这个计算机名,是在 NetrServerAuthenticate请求中由客户端提供的。
后续再调用 NetrServerPasswordSet请求时,服务器会先取出之前存储的凭证,并将之和请求中客户端提供的凭证做校验,在校验成功后才进行设置密码的操作。漏洞恰恰就出现在 NetrServerPasswordSet请求从本地文件里获取凭证的过程中。
首先来看 _netr_ServerPasswordSet函数,代码部分如下:
可以看到,creds指针在声明的时候,没有赋初值,而且该指针是个局部变量,如果初始化时处理不当就会导致野指针。后续在函数 netr_creds_server_step_check 内部对 creds 进行初始化。如果初始化失败就会在 TALLOC_FREE(creds) 的地方释放这个指针,并返回错误码。如果初始化成功,后续就会正常使用这个指针。
因此,只有在 netr_creds_server_step_check 函数初始化失败,即 status 返回非零错误码后,才有可能造成 creds 未被赋值,进而在 TALLOC_FREE(creds) 的地方释放野指针。
接下来看 netr_creds_server_step_check 函数内部如何实现,如下图:
传入的是 creds 的二重指针,是为了给 creds 初始化,前面有个分支,读取了 smb.conf 文件中的配置信息。creds 指针又被传入了 schannel_check_creds_state 函数中。所以进一步跟进schannel_check_creds_state 函数,如下图:
由上面的代码中可以看到,这个函数中是最终给 creds 指针赋值的位置,而且赋值位置在函数的尾部,只要前面产生错误,就会直接跳转到 done 标签的部分,造成 creds 指针未被赋值。接下来我们就分别分析以下,可能会导致走到 done 分支函数的功能。
open_schannel_session_store 函数的功能是打开服务器端本地保存凭证的文件(默认路径为 /var/lib/samba/private/schannel_store.tdb,可在 smb.conf 文件中配置)。只有在文件打开失败的情况下,该函数才会返回失败,无法由远程触发,而且通常这个操作也不会失败。
schannel_fetch_session_key_tdb 函数的功能,是用 NetrServerPasswordSet 请求中提供的 computer name 字段作为索引的一部分,从 schannel_store.tdb 文件中保存的 hash 结构中获取当前发送请求的客户端凭证。这里对于攻击者来说是比较容易让服务端返回错误的地方。
像前面 Netlogon 中提及的,如果客户端在发送 NetrServerPasswordSet 请求前没有发送 NetrServerAuthenticate 请求,服务端本地就不会保存对应这个客户端的凭证记录。此时就会造成这里返回失败,或者换个角度来说,如果 NetrServerPasswordSet 请求所提供的计算机名,服务器没见过或为空,这个函数就会返回错误。
最后是netlogon_creds_server_step_check函数。该函数的功能实际上就是检查NetrServerPasswordSet 请求中所提供的凭证,是否跟服务器端保存的凭证一致,如果不一致,也会返回错误。这里猜测也是攻击者比较好控制的点,只需在构造 NetrServerPasswordSet 请求时填充非法凭证即可。
所以最终本应由 schannel_check_creds_state 函数初始化的 creds 指针,由于提前错误返回,使得该指针没有被赋值。再加上这个指针在声明时也没有被赋值,最终导致了野指针。并在 TALLOC_FREE(creds) 时,使用该野指针。
结合上面分析的内容,总结上述可能会导致初始化失败的位置,触发条件如下:
1)发送NetrServerPasswordSet请求的客户端之前没有在服务端进行过认证(没有发送过 NetrServerAuthenticate 请求);
2) 客户端发送的 NetrServerPasswordSet 请求的 computer name 字段值为空;
3) 客户端发送的 NetrServerPasswordSet 请求中的凭证为无效凭证;
还没有评论,来说两句吧...