Skip to content

Commit

Permalink
Added the ronin-masscan grep command (closes #6).
Browse files Browse the repository at this point in the history
  • Loading branch information
postmodern committed Apr 11, 2024
1 parent 4e1bdc3 commit cd3b232
Show file tree
Hide file tree
Showing 5 changed files with 529 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Commands:
completion
convert
dump
grep
help
import
print
Expand Down
1 change: 1 addition & 0 deletions gemspec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ generated_files:
- man/ronin-masscan-completion.1
- man/ronin-masscan-convert.1
- man/ronin-masscan-dump.1
- man/ronin-masscan-grep.1
- man/ronin-masscan-import.1
- man/ronin-masscan-print.1
- man/ronin-masscan-scan.1
Expand Down
230 changes: 230 additions & 0 deletions lib/ronin/masscan/cli/commands/grep.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
# frozen_string_literal: true
#
# ronin-masscan - A Ruby library and CLI for working with masscan.
#
# Copyright (c) 2023-2024 Hal Brodigan ([email protected])
#
# 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 <https://www.gnu.org/licenses/>.
#

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<String>] masscan_files
# The nmap `.xml` files to parse.
#
def run(pattern,*masscan_files)
masscan_files.each do |masscan_file|
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
49 changes: 49 additions & 0 deletions man/ronin-masscan-grep.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# ronin-masscan-grep 1 "2023-03-01" Ronin Masscan "User Manuals"

## NAME

ronin-masscan-grep - Greps the scanned services from masscan scan file(s)

## SYNOPSIS

`ronin-masscan grep` [options] *PATTERN* *MASSCAN_FILE*

## DESCRIPTION

## ARGUMENTS

*MASSCAN_FILE*
: The masscan scan file to import.

## OPTIONS

`-P`, `--protocol` `tcp`|`udp`
: Filters the targets by the protocol of the open port.

`--ip` *IP*
: Filters the targets by a specific IP address.

`--ip-range` *CIDR*
: Filters the targets by a CIDR IP range (ex: `192.168.1.0/24`).

`-p`, `--ports` {*PORT* | *PORT1-PORT2*},...
: Filter `IP:PORT` or `HOST:PORT` pairs who's ports are in the gvien port list.
The port list is a comma separated list of port numbers (`443`) or port
ranges (`8000-9000`).

`--with-app-protocol` *APP_PROTOCOL*,...
: Filters targets by the app protocol names.

`--with-payload` *STRING*
: Filters targets by the payload substring.

`--with-payload-regex` `/`*REGEX*`/`
: Filters targets how's payload string matches the regular expression.

## AUTHOR

Postmodern <[email protected]>

## SEE ALSO

[ronin-masscan-print](ronin-masscan-print.1.md)
Loading

0 comments on commit cd3b232

Please sign in to comment.