forked from rapid7/metasploit-framework
-
Notifications
You must be signed in to change notification settings - Fork 1
/
msfupdate
executable file
·208 lines (180 loc) · 6.74 KB
/
msfupdate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#!/usr/bin/env ruby
# -*- coding: binary -*-
#
# $Id$
#
# This keeps the framework up-to-date
#
# $Revision$
#
msfbase = __FILE__
while File.symlink?(msfbase)
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
end
@msfbase_dir = File.dirname(msfbase)
@args = ARGV.dup
Dir.chdir(@msfbase_dir)
$stderr.puts "[*]"
$stderr.puts "[*] Attempting to update the Metasploit Framework..."
$stderr.puts "[*]"
$stderr.puts ""
# Bail right away, no waiting around for consoles.
if not (Process.uid == 0 or File.stat(msfbase).owned?)
$stderr.puts "[-] ERROR: User running msfupdate does not own the Metasploit installation"
$stderr.puts "[-] Please run msfupdate as the same user who installed Metasploit."
exit 0x10
end
def is_apt
File.exists?(File.expand_path(File.join(@msfbase_dir, '.apt')))
end
# Are you an installer, or did you get here via a source checkout?
def is_installed
File.exists?(File.expand_path(File.join(@msfbase_dir, "..", "engine", "update.rb"))) && !is_apt
end
def is_git
File.directory?(File.join(@msfbase_dir, ".git"))
end
# Adding an upstream enables msfupdate to pull updates from
# Rapid7's metasploit-framework repo instead of the repo
# the user originally cloned or forked.
def add_git_upstream
$stdout.puts "[*] Attempting to add remote 'upstream' to your local git repository."
system("git", "remote", "add", "upstream", "git://github.com/rapid7/metasploit-framework.git")
$stdout.puts "[*] Added remote 'upstream' to your local git repository."
end
# This only exits if you actually pass a wait option, otherwise
# just returns nil. This is likely unexpected, revisit this.
def maybe_wait_and_exit(exit_code=0)
if @actually_wait
$stdout.puts ""
$stdout.puts "[*] Please hit enter to exit"
$stdout.puts ""
$stdin.readline
exit exit_code
end
end
def apt_upgrade_available(package)
require 'open3'
installed = nil
upgrade = nil
::Open3.popen3({'LANG'=>'en_US.UTF-8'}, "apt-cache", "policy", package) do |stdin, stdout, stderr|
stdout.each do |line|
installed = $1 if line =~ /Installed: ([\w\-+.:~]+)$/
upgrade = $1 if line =~ /Candidate: ([\w\-+.:~]+)$/
break if installed && upgrade
end
end
if installed && installed != upgrade
upgrade
else
nil
end
end
@args.each_with_index do |arg,i|
case arg
# Handle the old wait/nowait argument behavior
when "wait", "nowait"
@wait_index = i
@actually_wait = (arg == "wait")
when /--git-remote=([^\s]*)?/
@git_remote = $1
@git_remote_index = i
when /--git-branch=([^\s]*)?/
@git_branch = $1
@git_branch_index = i
end
end
@args[@wait_index] = nil if @wait_index
@args[@git_remote_index] = nil if @git_remote_index
@args[@git_branch_index] = nil if @git_branch_index
@args = @args.compact
####### Since we're Git, do it all that way #######
if is_git
$stdout.puts "[*] Checking for updates via git"
$stdout.puts "[*] Note: Updating from bleeding edge"
out = `git remote show upstream` # Actually need the output for this one.
add_git_upstream unless $?.success? and out =~ %r{(https|git|git@github\.com):(//github\.com/)?(rapid7/metasploit-framework\.git)}
remote = @git_remote || "upstream"
branch = @git_branch || "master"
# This will save local changes in a stash, but won't
# attempt to reapply them. If the user wants them back
# they can always git stash pop them, and that presumes
# they know what they're doing when they're editing local
# checkout, which presumes they're not using msfupdate
# to begin with.
#
# Note, this requires at least user.name and user.email
# to be configured in the global git config. Installers should
# take care that this is done. TODO: Enforce this in msfupdate
committed = system("git", "diff", "--quiet", "HEAD")
if committed.nil?
$stderr.puts "[-] ERROR: Failed to run git"
$stderr.puts ""
$stderr.puts "[-] If you used a binary installer, make sure you run the symlink in"
$stderr.puts "[-] /usr/local/bin instead of running this file directly (e.g.: ./msfupdate)"
$stderr.puts "[-] to ensure a proper environment."
maybe_wait_and_exit 1
elsif not committed
system("git", "stash")
$stdout.puts "[*] Stashed local changes to avoid merge conflicts."
$stdout.puts "[*] Run `git stash pop` to reapply local changes."
end
system("git", "reset", "HEAD", "--hard")
system("git", "checkout", branch)
system("git", "fetch", remote)
system("git", "merge", "#{remote}/#{branch}")
$stdout.puts "[*] Updating gems..."
require 'bundler'
Bundler.with_clean_env do
system("bundle", "install")
end
end
if is_installed
update_script = File.expand_path(File.join(@msfbase_dir, "..", "engine", "update.rb"))
product_key = File.expand_path(File.join(@msfbase_dir, "..", "engine", "license", "product.key"))
if File.exists? product_key
if File.readable? product_key
system("ruby", update_script)
else
$stdout.puts "[-] ERROR: Failed to update Metasploit installation"
$stdout.puts ""
$stdout.puts "[-] You must be able to read the product key for the"
$stdout.puts "[-] Metasploit installation in order to run msfupdate."
$stdout.puts "[-] Usually, this means you must be root (EUID 0)."
maybe_wait_and_exit 10
end
else
$stdout.puts "[-] ERROR: Failed to update Metasploit installation"
$stdout.puts ""
$stdout.puts "[-] In order to update your Metasploit installation,"
$stdout.puts "[-] you must first register it through the UI, here:"
$stderr.puts "[-] https://localhost:3790"
$stderr.puts "[-] (Note: Metasploit Community Edition is totally"
$stderr.puts "[-] free and takes just a few seconds to register!)"
maybe_wait_and_exit 11
end
end
if is_apt
# For more information, see here:
# https://community.rapid7.com/community/metasploit/blog/2013/01/17/metasploit-updates-and-msfupdate
$stdout.puts "[*] Checking for updates via the APT repository"
$stdout.puts "[*] Note: expect weekly(ish) updates using this method"
system("apt-get", "-qq", "update")
packages = []
packages << 'metasploit-framework' if framework_version = apt_upgrade_available('metasploit-framework')
packages << 'metasploit' if pro_version = apt_upgrade_available('metasploit')
if packages.empty?
$stdout.puts "[*] No updates available"
else
$stdout.puts "[*] Updating to version #{pro_version || framework_version}"
system("apt-get", "install", "--assume-yes", *packages)
if packages.include?('metasploit')
start_cmd = File.expand_path(File.join(@msfbase_dir, '..', '..', '..', 'scripts', 'start.sh'))
system(start_cmd) if ::File.executable_real? start_cmd
end
end
end
unless is_git || is_installed || is_apt
raise RuntimeError, "Cannot determine checkout type: `#{@msfbase_dir}'"
end
maybe_wait_and_exit(0)