Update fzuse to be self contained

This commit is contained in:
Spencer McIntyre
2024-09-17 15:16:53 -04:00
parent e812463d5f
commit ae26319e8f
2 changed files with 61 additions and 135 deletions
+61 -25
View File
@@ -1,3 +1,18 @@
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
###
#
@@ -39,37 +54,60 @@ module Msf
}
end
def start_pipe_server(socket_path)
def pipe_server(socket_path)
server = UNIXServer.new(socket_path)
File.chmod(0600, socket_path)
loop do
client = server.accept
unless (input_string = client.gets&.chomp).blank?
if (mod = framework.modules.create(input_string))
client.puts(Serializer::ReadableText.dump_module(mod))
end
end
client.close
end
rescue EOFError
ensure
server.close if server
File.delete(socket_path) if File.exist?(socket_path)
end
Thread.new do
pipe_server(socket_path)
end
end
#
# This method handles the fuzzy_use command.
#
def cmd_fzuse(*args)
previewer = File.join(Msf::Config.install_root, 'tools', 'modules', 'print.py')
metadata_path = Msf::Modules::Metadata::Cache.instance.get_user_store
module_types = framework.modules.module_types
query = args.empty? ? '' : args.first
selection = nil
# alternative preview:
# jq \'to_entries[] | select(.value.fullname == "{1}") | .value\' db/modules_metadata_base.json | bat --language=json --color=always
stdin, stdout, stderr, wait_thr = Open3.popen3('fzf', '--select-1', '--query', query, '--preview', "#{previewer} --metadata-path '#{metadata_path}' '{1}'", '--preview-label', "Module Information") do |stdin, stdout, stderr, wait_thr|
module_types
module_types.each do |module_type|
framework.modules.module_names(module_type).each do |module_name|
stdin.puts "#{module_type}/#{module_name}"
end
end
stdin.close
exit_status = wait_thr.value
selection = stdout.read
Dir.mktmpdir('msf-fzuse-') do |dir|
File.chmod(0700, dir)
socket_path = File.join(dir, "msf-fzuse.sock")
server_thread = start_pipe_server(socket_path)
query = args.empty? ? '' : args.first
ruby = RbConfig::CONFIG['bindir'] + '/' + RbConfig::CONFIG['ruby_install_name'] + RbConfig::CONFIG['EXEEXT']
Open3.popen3('fzf', '--select-1', '--query', query, '--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}"
end
end
stdin.close
selection = stdout.read
end
server_thread.kill
end
selection.strip!
return if selection.blank?
selection.strip!
@module_dispatcher.cmd_use(selection)
end
end
@@ -83,11 +121,9 @@ module Msf
#
def initialize(framework, opts)
super
missing_requirements = []
missing_requirements << 'fzf' unless Msf::Util::Helper.which('fzf')
missing_requirements << 'python' unless Msf::Util::Helper.which('python')
missing_requirements << 'python-rich' unless system("python -c 'import rich'", out: File::NULL, err: File::NULL)
unless missing_requirements.empty?
print_error("The FuzzyUse plugin has loaded but the following requirements are missing: #{missing_requirements.join(', ')}")
-110
View File
@@ -1,110 +0,0 @@
#!/usr/bin/env python
import argparse
import json
import pathlib
from rich import box
from rich.console import Console
from rich.panel import Panel
from rich.syntax import Syntax
from rich.table import Table
from rich.tree import Tree
__version__ = '1.0'
RANKS = {
600: 'Excellent',
500: 'Great',
400: 'Good',
300: 'Normal',
200: 'Average',
100: 'Low',
0: 'Manual'
}
framework_root = pathlib.Path(__file__).parent.parent.parent
default_metadata_path = (framework_root / 'db' / 'modules_metadata_base.json')
def get_notes(module_metadata):
tree = Tree('Notes', hide_root=True)
for key, values in module_metadata.get('notes', {}).items():
node = tree.add(f"[italic]{key}[/italic]")
for value in values:
node.add(value)
return tree
def get_description(module_metadata):
description = ''
paragraphs = module_metadata['description'].split('\n\n')
for paragraph in paragraphs:
for line in paragraph.split('\n'):
description += line.strip() + '\n'
description += '\n'
return description.strip()
def get_authors(module_metadata):
return get_bulleted_list(module_metadata['author'])
def get_targets(module_metadata):
return get_bulleted_list(module_metadata['targets'])
def get_references(module_metadata):
references = []
for reference in module_metadata.get('references', []):
if reference.startswith('URL-'):
reference = reference[4:]
references.append(reference)
return get_bulleted_list(references)
def get_bulleted_list(items):
formatted = ''
for item in items:
formatted += f"[bold]•[/bold] {item}\n"
return formatted.strip()
def main():
parser = argparse.ArgumentParser(description='fzuse helper', conflict_handler='resolve')
parser.add_argument('module_name', help='module name to display')
parser.add_argument('--metadata-path', default=default_metadata_path, type=pathlib.Path, help='the path to the module metadata')
parser.add_argument('-v', '--version', action='version', version='%(prog)s Version: ' + __version__)
arguments = parser.parse_args()
with arguments.metadata_path.open('r') as file_h:
all_metadata = json.load(file_h)
module_metadata = next((metadata for metadata in all_metadata.values() if metadata['fullname'] == arguments.module_name), None)
if not module_metadata:
return
table = Table(show_header=False, box=box.MINIMAL)
table.add_column(justify='right')
table.add_column()
table.add_row('[bold yellow]Name[/bold yellow]', module_metadata['name'])
table.add_row('[bold yellow]Module[/bold yellow]', module_metadata['fullname'])
table.add_row('[bold yellow]Platform[/bold yellow]', module_metadata['platform'])
table.add_row('[bold yellow]Arch[/bold yellow]', module_metadata['arch'])
table.add_row('[bold yellow]Rank[/bold yellow]', RANKS[module_metadata['rank']])
table.add_row('[bold yellow]Disclosed[/bold yellow]', module_metadata['disclosure_date'])
console = Console(color_system='256')
console.print(table)
panel_title = lambda v: f"[bold yellow]{v}[/bold yellow]"
console.print(Panel(get_authors(module_metadata), title=panel_title('Provided by'), title_align='left'))
console.print(Panel(get_notes(module_metadata), title=panel_title('Notes'), title_align='left'))
if module_metadata.get('targets'):
console.print(Panel(get_targets(module_metadata), title=panel_title('Targets'), title_align='left'))
console.print(Panel(get_description(module_metadata), title=panel_title('Description'), title_align='left'))
if module_metadata.get('references'):
console.print(Panel(get_references(module_metadata), title=panel_title('References'), title_align='left'))
if module_metadata.get('path', ''):
if pathlib.Path(module_metadata['path']).is_file():
module_path = pathlib.Path(module_metadata['path'])
elif pathlib.Path(framework_root / module_metadata['path'][1:]).is_file():
module_path = pathlib.Path(framework_root / module_metadata['path'][1:])
if module_path:
syntax = Syntax.from_path(module_path, background_color='default', line_numbers=True)
console.print(Panel(syntax, title=panel_title('Source code'), title_align='left'))
if __name__ == '__main__':
main()