Metasploit中smb_ms17_010.rb面临139/TCP时的问题

作者: saya 分类: 渗透测试 发布时间: 2019-01-14 15:21

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搂中。

说到通过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
————————————————————————–

转载 青衣十三楼

发表评论

电子邮件地址不会被公开。 必填项已用*标注

标签云