# frozen_string_literal: true # # ronin-masscan - A Ruby library and CLI for working with masscan. # # Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com) # # ronin-masscan is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # ronin-masscan is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with ronin-masscan. If not, see . # require 'ronin/masscan/cli/command' require 'ronin/masscan/cli/filtering_options' require 'command_kit/colors' require 'command_kit/printing/indent' require 'masscan/output_file' module Ronin module Masscan class CLI module Commands # # Greps the scanned services from masscan scan file(s) for the given # pattern. # # ## Usage # # ronin-masscan grep [options] PATTERN MASSCAN_FILE [...] # # ## Options # # -P, --protocol tcp|udp Filters the targets by protocol # --ip IP Filters the targets by IP # --ip-range CIDR Filters the targets by IP range # -p, --ports {PORT | PORT1-PORT2},... # Filters targets by port number # --with-app-protocol APP_PROTOCOL[,...] # Filters targets with the app protocol # --with-payload STRING Filters targets containing the payload # --with-payload-regex /REGEX/ Filters targets with the matching payload # -h, --help Print help information # # ## Arguments # # PATTERN The pattern to search for # MASSCAN_FILE ... The masscan scan file(s) to parse # class Grep < Command usage '[options] PATTERN MASSCAN_FILE [...]' include CommandKit::Colors include CommandKit::Printing::Indent include FilteringOptions argument :pattern, required: true, desc: 'The pattern to search for' argument :masscan_file, required: true, repeats: true, desc: 'The masscan scan file(s) to parse' description 'Greps the scanned services from masscan scan file(s)' man_page 'ronin-masscan-grep.1' # # Runs the `ronin-masscan grep` command. # # @param [String] pattern # The pattern to search for. # # @param [Array] masscan_files # The nmap `.xml` files to parse. # def run(pattern,*masscan_files) masscan_files.each do |masscan_file| unless File.file?(masscan_file) print_error "no such file or directory: #{masscan_file}" next end output_file = begin ::Masscan::OutputFile.new(masscan_file) rescue ArgumentError => error print_error(error.message) exit(1) end records = grep_records(output_file,pattern) highlight_records(records,pattern) end end # # Greps the masscan output file for the pattern. # # @param [::Masscan::OutputFile] output_file # The masscan output file to search. # # @param [String] pattern # The pattern to search for. # def grep_records(output_file,pattern) records = filter_records(output_file) records.filter { |record| match_record(record,pattern) } end # # Determines if the masscan record includes the pattern. # # @param [::Masscan::Status, ::Masscan::Banner] record # The masscan record to search. # # @param [String] pattern # The pattern to search for. # # @return [Boolean] # Indicates whether the masscan record contains the pattern. # def match_record(record,pattern) case record when ::Masscan::Banner record.app_protocol.match(pattern) || record.payload.match(pattern) end end # # Prints the open ports for the IP. # # @param [Array<::Masscan::Status, ::Masscan::Banner>] records # The masscan records to print. # # @param [String] pattern # The pattern to highlight. # def highlight_records(records,pattern) records.group_by(&:ip).each do |ip,records_for_ip| puts "[ #{ip} ]" puts records_for_ip.group_by { |record| [record.port, record.protocol] }.each do |(port,protocol),records_for_port| indent do puts "#{port}/#{protocol}" indent do records_for_port.each do |record| highlight_record(record,pattern) end end end end puts end end # # Prints the masscan record with the pattern highlighted. # # @param [::Masscan:Status, ::Masscan::Banner] record # The masscan record to print. # # @param [String] pattern # The pattern to highlight. # def highlight_record(record,pattern) case record when ::Masscan::Banner highlight_banner_record(record,pattern) end end # # Prints the masscan banner record with the pattern highlighted. # # @param [::Masscan::Banner] banner # The masscan banner record to print. # # @param [String] pattern # The pattern to highlight. # def highlight_banner_record(banner,pattern) payload = highlight(banner.payload,pattern) app_protocol = highlight(banner.app_protocol,pattern) if payload.include?("\n") # multiline? puts app_protocol indent do payload.chomp.each_line(chomp: true) do |line| puts line end end else puts "#{app_protocol}\t#{payload}" end end # # Highlights the pattern in the text. # # @param [String] text # The text to modify. # # @param [String] pattern # The pattern to highlight. # # @return [String] # The modified text. # def highlight(text,pattern) text.to_s.gsub(pattern,colors.red(pattern)) end end end end end end