-
Notifications
You must be signed in to change notification settings - Fork 14.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add the fuzzy-use plugin #19471
Add the fuzzy-use plugin #19471
Changes from all commits
32d242c
233cd61
a1a59cf
e812463
ae26319
caf79d8
3227e2e
ba65ecc
9b0af80
34ed383
669ea97
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
require 'socket' | ||
|
||
# this is the main routine that's executed in the grandchild process (msfconsole -> fzf -> this) | ||
if $PROGRAM_NAME == __FILE__ | ||
exit 64 unless ARGV.length == 2 | ||
|
||
UNIXSocket.open(ARGV[0]) do |sock| | ||
sock.write ARGV[1] + "\n" | ||
sock.flush | ||
|
||
puts sock.read | ||
end | ||
exit 0 | ||
end | ||
|
||
module Msf | ||
class Plugin::FuzzyUse < Msf::Plugin | ||
class ConsoleCommandDispatcher | ||
include Msf::Ui::Console::CommandDispatcher | ||
|
||
FZF_THEME = { | ||
'fg' => '-1', | ||
'fg+' => 'white:regular:bold', | ||
'bg' => '-1', | ||
'bg+' => '-1', | ||
'hl' => '-1', | ||
'hl+' => 'red:regular:bold', | ||
'info' => '-1', | ||
'marker' => '-1', | ||
'prompt' => '-1', | ||
'spinner' => '-1', | ||
'pointer' => 'blue:bold', | ||
'header' => '-1', | ||
'border' => '-1', | ||
'label' => '-1', | ||
'query' => '-1' | ||
}.freeze | ||
|
||
def initialize(driver) | ||
super | ||
|
||
@module_dispatcher = Msf::Ui::Console::CommandDispatcher::Modules.new(driver) | ||
end | ||
|
||
def name | ||
'FuzzyUse' | ||
end | ||
|
||
# | ||
# Returns the hash of commands supported by this dispatcher. | ||
# | ||
def commands | ||
{ | ||
'fzuse' => 'A fuzzy use command added by the FuzzyUse plugin' | ||
} | ||
end | ||
|
||
def pipe_server(socket_path) | ||
server = UNIXServer.new(socket_path) | ||
File.chmod(0600, socket_path) | ||
loop do | ||
client = server.accept | ||
begin | ||
unless (input_string = client.gets&.chomp).blank? | ||
if (mod = framework.modules.create(input_string)) | ||
client.puts(Serializer::ReadableText.dump_module(mod)) | ||
end | ||
end | ||
rescue StandardError | ||
end | ||
client.close | ||
end | ||
rescue EOFError | ||
ensure | ||
server.close if server | ||
File.delete(socket_path) if File.exist?(socket_path) | ||
end | ||
|
||
# | ||
# This method handles the fzuse command. | ||
# | ||
def cmd_fzuse(*args) | ||
selection = nil | ||
|
||
Dir.mktmpdir('msf-fzuse-') do |dir| | ||
socket_path = File.join(dir, "msf-fzuse.sock") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are your thoughts on using Ruby's tmp file API and creating the file in the
Which I think should allow us to drop the wrapper There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That doesn't work:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah that makes sense 👍 So I guess this would be enough to move the file into the
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it necessary to put it in the config directory? The file only lasts a couple of seconds while the user is picking their module and I wasn't aware there was a precedent of placing temp files in the config directory. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We've had PRs that put wordlists etc into places outside of |
||
server_thread = Thread.new { pipe_server(socket_path) } | ||
|
||
query = args.empty? ? '' : args.first | ||
ruby = RbConfig::CONFIG['bindir'] + '/' + RbConfig::CONFIG['ruby_install_name'] + RbConfig::CONFIG['EXEEXT'] | ||
|
||
color = "--color=#{FZF_THEME.map { |key, value| "#{key}:#{value}" }.join(',')}" | ||
Open3.popen3('fzf', '--select-1', '--query', query, '--pointer=->', color, '--preview', "'#{ruby}' '#{__FILE__}' '#{socket_path}' '{1}'", '--preview-label', "Module Information") do |stdin, stdout, stderr, wait_thr| | ||
framework.modules.module_types.each do |module_type| | ||
framework.modules.module_names(module_type).each do |module_name| | ||
stdin.puts "#{module_type}/#{module_name}" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a blocker; it'd be interesting to add actions here potentially |
||
end | ||
end | ||
stdin.close | ||
selection = stdout.read | ||
end | ||
|
||
server_thread.kill | ||
server_thread.join | ||
end | ||
|
||
return if selection.blank? | ||
|
||
selection.strip! | ||
@module_dispatcher.cmd_use(selection) | ||
end | ||
end | ||
|
||
def initialize(framework, opts) | ||
super | ||
|
||
unless defined?(UNIXSocket) | ||
# This isn't a requirement that can be fixed by installing something | ||
print_error("The FuzzyUse plugin has loaded but the Ruby environment does not support UNIX sockets.") | ||
return | ||
end | ||
|
||
missing_requirements = [] | ||
missing_requirements << 'fzf' unless Msf::Util::Helper.which('fzf') | ||
zeroSteiner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
unless missing_requirements.empty? | ||
print_error("The FuzzyUse plugin has loaded but the following requirements are missing: #{missing_requirements.join(', ')}") | ||
print_error("Please install the missing requirements, then reload the plugin by running: `unload fzuse` and `load fzuse`.") | ||
return | ||
end | ||
|
||
add_console_dispatcher(ConsoleCommandDispatcher) | ||
|
||
print_status('FuzzyUse plugin loaded.') | ||
end | ||
|
||
def cleanup | ||
remove_console_dispatcher('FuzzyUse') | ||
end | ||
|
||
def name | ||
'fuzzy_use' | ||
end | ||
|
||
def desc | ||
'A plugin offering a fuzzy use command' | ||
end | ||
|
||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a question; Why is it called
fzuse
instead offzfuse
👀There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm guessing the rationale is the
fzf
is considered an implementation detail, and in the futurefzuse
could technically be powered by anything? 🤔There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went with
fzuse
because it's fuzzy-user.fzf
is named as it is because it's fuzzy-finder.