标题: Metasploit中smb_ms17_010.rb面临139/TCP时的缺陷 创建: 2017-07-03 15:58 更新: 链接: https://scz.617.cn/windows/201707031558.txt C:\metasploit-framework\embedded\framework\modules\auxiliary\scanner\smb\smb_ms17_010.rb use auxiliary/scanner/smb/smb_ms17_010 set ShowProgress false set ConnectTimeout 5 set THREADS 16 set RHOSTS x.x.x.x run Metasploit扫描MS17-010的方案完全同Nmap,但"Session Setup AndX Request"中的 "Max Buffer"用0xffdf。看上去只有Nessus动用了Unicode。 smb_ms17_010.rb缺省只扫445/TCP。如果想扫139/TCP,必须: set SMBDirect false set RPORT 139 或者 unset SMBDirect set RPORT 139 有人给我科普139与445的区别,我就当听相声了。 切换端口好理解。稍微说一下SMBDirect设成false或unset它,就是多发一个NBT层的 Session request(0x81),如果不发这个报文直接Negotiate Protocol(0x72),会得 到Negative session response(0x83),Error code指示Unspecified error(0x8f), 其实就是说,你丫发的不是NBT层的Session request(0x81)。Metasploit在该问题上 不够"智能",应该在指定139时自动将SMBDirect调成false。单独提供SMBDirect参数, 除了理论上的可配置性外,没有任何实际意义,换句话说,你有多大机率碰上非139、 445的SMB通信?使用139时,MSF使用了固定的"*SMBSERVER",并没有尝试从137/UDP 取相应值。与此同时,从Vista开始139/TCP不认"*SMBSERVER",返回 Negative session response(0x83),Error code指示Called name not present(0x82)。 于是针对Vista及之后的Windows,smb_ms17_010.rb扫不了它们的139/TCP,换句话说, 会漏报。 Metasploit的SMB插件处理139时可能都有前述缺陷,即固定使用"*SMBSERVER",而不 尝试从137/UDP动态获取。如果只扫一个IP,可以手工设置SMBName成相应值,但在批 量扫描过程中没法这样干。 关于最近几个月被勒索软件高频使用的MS17-010,我想再次提醒运维人员注意的是, 该洞不只是445可用,139同样可用。利用139攻击时,只需要多发1至2个报文,视目 标系统而定。在管理性扫描中,务必同时检查两个端口。之前碰上过通过FW封掉445 应急(对付上级)的,结果被139搂中。 下面这个同时支持139/TCP的单扫工具不是广告,只是和运维人员结个善缘: 《采用Nessus扫描方案的Windows版MS17-010单扫工具》 https://scz.617.cn/windows/MS17-010-Nessus.exe https://scz.617.cn/windows/201706221521.txt 说到通过FW缓解,又有少见多怪的接不了地气的反革命意淫派以上帝视角发表评论了, 核心意思是,打个补丁就解决的事,搞这么复杂干什么。我想,这种幼稚需要时间去 治疗,不要放弃治疗啊,请好好活到时间够了的那一天。 Wireshark抓包很容易验证smb_ms17_010.rb扫描139/TCP时缺陷,但你可能更想从源 码中直接确认。 C:\metasploit-framework\embedded\framework\modules\auxiliary\scanner\smb\smb_ms17_010.rb 检查run_host(),扫描MS17-010的核心步骤是: do_smb_setup_tree connect simple.login simple.connect do_smb_ms17_010_probe make_smb_trans_ms17_010 显然SMB空会话是在simple.login、simple.connect阶段完成。simple不在smb_ms17_010.rb 里,那只能是从祖先类继承得来。smb_ms17_010.rb最开始有个: include Msf::Exploit::Remote::SMB::Client 对应 C:\metasploit-framework\embedded\framework\lib\msf\core\exploit\smb\client.rb 在其中看到 -------------------------------------------------------------------------- module Msf module Exploit::Remote::SMB module Client include Msf::Exploit::Remote::Tcp include Msf::Exploit::Remote::NTLM::Client SIMPLE = Rex::Proto::SMB::SimpleClient ... def connect(global=true) ... # Disable direct SMB when SMBDirect has not been set # and the destination port is configured as 139 direct = smb_direct if(datastore.default?('SMBDirect') and rport.to_i == 139) direct = false end c = SIMPLE.new(s, direct) ... self.simple = c if global c end -------------------------------------------------------------------------- connect()中面临139时对SMBDirect的处理其实是对的,但它这个逻辑要求SMBDirect 未被设置,而加载smb_ms17_010.rb后你会发现SMBDirect已被设成true。所以必须 unset SMBDirect,connect()才能自动根据139将direct设成false。 simple在上述client.rb中定义,回溯到 Rex::Proto::SMB::SimpleClient 对应 C:\metasploit-framework\embedded\framework\lib\rex\proto\smb\simpleclient.rb 在其中看到 -------------------------------------------------------------------------- module Rex module Proto module SMB class SimpleClient ... def initialize(socket, direct = false) self.socket = socket self.direct = direct self.client = Rex::Proto::SMB::Client.new(socket) self.shares = { } end ... def login(name = '', user = '', pass = '', domain = '', verify_signature = false, usentlmv2 = false, usentlm2_session = true, send_lm = true, use_lanman_key = false, send_ntlm = true, native_os = 'Windows 2000 2195', native_lm = 'Windows 2000 5.0', spnopt = {}) begin if (self.direct != true) self.client.session_request(name) end ... self.client.negotiate ... ok = self.client.session_setup(user, pass, domain) rescue ::Interrupt raise $! rescue ::Exception => e ... end return true end -------------------------------------------------------------------------- 回溯到 Rex::Proto::SMB::Client 对应 C:\metasploit-framework\embedded\framework\lib\rex\proto\smb\client.rb 在其中看到 -------------------------------------------------------------------------- module Rex module Proto module SMB class Client ... def session_request(name = '*SMBSERVER', do_recv = true) name ||= '*SMBSERVER' data = '' data << "\x20" + UTILS.nbname_encode(name) + "\x00" data << "\x20" + CONST::NETBIOS_REDIR + "\x00" pkt = CONST::NBRAW_PKT.make_struct pkt.v['Type'] = 0x81 pkt['Payload'].v['Payload'] = data # Most SMB implementations can't handle this being fragmented ret = self.smb_send(pkt.to_s, EVADE::EVASION_NONE) return ret if not do_recv res = self.smb_recv ack = CONST::NBRAW_PKT.make_struct ack.from_s(res) if (ack.v['Type'] != 130) raise XCEPT::NetbiosSessionFailed end return ack end --------------------------------------------------------------------------