From 950ded6b7e846a76ce2773074110e2f1042b739d Mon Sep 17 00:00:00 2001 From: Postmodern Date: Thu, 13 Jun 2024 20:55:42 -0700 Subject: [PATCH] Added the `ronin-nmap new` command (closes #5). --- README.md | 13 ++ data/templates/script.rb.erb | 56 ++++++++ gemspec.yml | 1 + lib/ronin/nmap/cli/commands/new.rb | 211 +++++++++++++++++++++++++++++ man/ronin-nmap-new.1.md | 70 ++++++++++ 5 files changed, 351 insertions(+) create mode 100644 data/templates/script.rb.erb create mode 100644 lib/ronin/nmap/cli/commands/new.rb create mode 100644 man/ronin-nmap-new.1.md diff --git a/README.md b/README.md index 6bca5ca..0f1e769 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Commands: grep help import + new print scan ``` @@ -95,6 +96,18 @@ Convert an nmap XML scan file to JSON: $ ronin-nmap convert scan.xml scan.json ``` +Generate a new nmap scanner Ruby script: + +```shell +$ ronin-nmap new scanner.rb --ports 22,80,443,8000-9000 --target example.com +``` + +Generate a new nmap XML parser script: + +```shell +$ ronin-nmap new parser.rb --parser --xml-file path/to/nmap.xml --printing +``` + ## Examples Performing an `nmap` scan and returning the parsed nmap XML data: diff --git a/data/templates/script.rb.erb b/data/templates/script.rb.erb new file mode 100644 index 0000000..3fa6a00 --- /dev/null +++ b/data/templates/script.rb.erb @@ -0,0 +1,56 @@ +#!/usr/bin/env ruby + +require 'ronin/nmap' + +<%- if @script_type == :parser -%> +<%- if @xml_file %> +xml = Ronin::Nmap.parse(<%= @xml_file.inspect %>) +<%- else -%> +xml = Ronin::Nmap.parse(ARGV[0]) +<%- end -%> +<%- else -%> +xml = Ronin::Nmap.scan( +<%- case @targets.length -%> +<%- when 0 -%> + ARGV[0], +<%- when 1 -%> + <%= @targets[0].inspect %>, +<%- else -%> + <%= @targets.inspect %>, +<%- end -%> +<%- if @syn_scan -%> + syn_scan: true, +<%- end -%> +<%- if @ports -%> + ports: <%= @ports.inspect %>, +<%- else -%> + # ports: [22, 80, 443, 8000..9000], +<%- end -%> +<%- if @xml_file -%> + xml_file: <%= @xml_file.inspect %> +<%- else -%> + # xml_file: "path/to/file.xml" +<%- end -%> +) +<%- end -%> +<% if @print -%> + +xml.each_host do |host| + puts "[ #{host.ip} ]" + + host.each_port do |port| + puts " #{port.number}/#{port.protocol}\t#{port.state}\t#{port.service}" + + port.scripts.each do |id,script| + puts " [ #{id} ]" + + script.output.each_line { |line| puts " #{line}" } + end + end +end +<%- end -%> +<%- if @import -%> + +Ronin::DB.connect +Ronin::Nmap::Importer.import(xml) +<%- end -%> diff --git a/gemspec.yml b/gemspec.yml index 9176c51..64c5f94 100644 --- a/gemspec.yml +++ b/gemspec.yml @@ -26,6 +26,7 @@ generated_files: - man/ronin-nmap-dump.1 - man/ronin-nmap-grep.1 - man/ronin-nmap-import.1 + - man/ronin-nmap-new.1 - man/ronin-nmap-print.1 - man/ronin-nmap-scan.1 diff --git a/lib/ronin/nmap/cli/commands/new.rb b/lib/ronin/nmap/cli/commands/new.rb new file mode 100644 index 0000000..61eabac --- /dev/null +++ b/lib/ronin/nmap/cli/commands/new.rb @@ -0,0 +1,211 @@ +# frozen_string_literal: true +# +# ronin-nmap - A Ruby library for automating nmap and importing nmap scans. +# +# Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com) +# +# ronin-nmap 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-nmap 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-nmap. If not, see . +# + +require 'ronin/nmap/cli/command' +require 'ronin/nmap/root' + +require 'ronin/core/cli/generator' + +module Ronin + module Nmap + class CLI + module Commands + # + # Generates a new nmap ruby script. + # + # ## Usage + # + # ronin-nmap new [options] FILE + # + # ## Options + # + # --parser Generate a nmap XML parser script + # --scanner Generate a nmap scanner script + # --printing Adds additional printing of the nmap scan data + # --import Also import the nmap XML scan data + # --xml-file XML_FILE Sets the XML file to write to or parse + # -p {PORT | [PORT1]-[PORT2]},... Sets the port range to scan + # --ports + # --target TARGET Sets the targets to scan (Defaults: ARGV[0]) + # -h, --help Print help information + # + # ## Arguments + # + # FILE The path to the new nmap ruby script. + # + # ## Examples + # + # ronin-nmap new scanner.rb --ports 22,80,443,8000-9000 --target example.com + # ronin-nmap new parser.rb --parser --xml-file path/to/nmap.xml --printing + # + class New < Command + + include Core::CLI::Generator + + template_dir File.join(ROOT,'data','templates') + + usage '[options] FILE' + + option :parser, desc: 'Generate a nmap XML parser script' do + @script_type = :parser + end + + option :scanner, desc: 'Generate a nmap scanner script' do + @script_type = :scanner + end + + option :printing, desc: 'Adds additional printing of the nmap scan data' do + @printing = true + end + + option :import, desc: 'Also import the nmap XML scan data' do + @import = true + end + + option :xml_file, value: { + type: String, + usage: 'XML_FILE' + }, + desc: 'Sets the XML file to write to or parse' do |file| + @xml_file = file + end + + option :ports, short: '-p', + value: { + type: String, + usage: '{PORT | [PORT1]-[PORT2]},...' + }, + desc: 'Sets the port range to scan' do |ports| + @ports = parse_port_range(ports) + rescue ArgumentError => error + raise(OptionParser::InvalidArgument,error.message) + end + + option :target, value: { + type: String, + usage: 'TARGET' + }, + desc: 'Sets the targets to scan (Defaults: ARGV[0])' do |target| + @targets << target + end + + argument :path, desc: 'The path to the new nmap ruby script' + + description 'Generates a new nmap ruby script' + + man_page 'ronin-nmap-new.1' + + examples [ + "scanner.rb --ports 22,80,443,8000-9000 --target example.com", + "parser.rb --parser --xml-file path/to/nmap.xml --printing" + ] + + # The script type. + # + # @return [:scan, :parse] + attr_reader :script_type + + # The optioanl XML file to write to or parse. + # + # @return [String, nil] + attr_reader :xml_file + + # The optional ports to scan. + # + # @return [Array, "-", nil] + attr_reader :ports + + # The targets to scan. + # + # @return [Array] + attr_reader :targets + + # + # Initializes the `ronin-nmap new` command. + # + # @param [Hash{Symbol => Object}] kwargs + # Additional keyword arguments for the command. + # + def initialize(**kwargs) + super(**kwargs) + + @script_type = :scan + @targets = [] + end + + # + # Runs the `ronin-nmap new` command. + # + # @param [String] file + # The path to the new nmap ruby script. + # + def run(file) + @directory = File.dirname(file) + + mkdir @directory unless @directory == '.' + + erb "script.rb.erb", file + chmod '+x', file + end + + # + # Parses a port range. + # + # @param [String] ports + # The port range to parse. + # + # @return [Array, "-"] + # The parsed port range. + # + # @raise [ArgumentError] + # An invalid port range was given. + # + def parse_port_range(ports) + case ports + when '-' then '-' + else + ports.split(',').map do |port| + case port + when /\A\d+-\d+\z/ + start, stop = port.split('-',2) + + (start.to_i..stop.to_i) + when /\A\d+-\z/ + start = port.chomp('-') + + (start.to_i..) + when /\A-\d+\z/ + stop = port[1..] + + (..stop.to_i) + when /\A\d+\z/ + port.to_i + else + raise(ArgumentError,"invalid port range: #{ports.inspect}") + end + end + end + end + + end + end + end + end +end diff --git a/man/ronin-nmap-new.1.md b/man/ronin-nmap-new.1.md new file mode 100644 index 0000000..99cda9e --- /dev/null +++ b/man/ronin-nmap-new.1.md @@ -0,0 +1,70 @@ +# ronin-nmap-new 1 "2023-03-01" Ronin Nmap "User Manuals" + +## NAME + +ronin-nmap-new - Generates a new nmap ruby script + +## SYNOPSIS + +`ronin-nmap new` [options] *FILE* + +## DESCRIPTION + +Generates a new nmap scanner or parser Ruby script that uses the `ronin-nmap` +library. + +## ARGUMENTS + +*FILE* +: The path to the new Ruby script to generate. + +## OPTIONS + +`--parser` +: Generates a new nmap XML parser Ruby script. + +`--scanner` +: Generates a new nmap scanner Ruby script. + +`--printing` +: Adds additional code to the Ruby script that prints the nmap XML scan data. + Is compatible with both `--parser` and `--scanner`. + +`--import` +: Adds additional code to the Ruby script that imports the nmap XML scan data. + Is compatible with both `--parser` and `--scanner`. + +`--xml-file` *XML_FILE* +: Parses or writes the scan results to the given XML File. + Is compatible with both `--parser` and `--scanner`. + +`-p`, `--port` {*PORT* \| \[*PORT1*\]-\[*PORT2*\]},... +: Specifies the ports to scan. Not compatible with the `--parser` option. + +`--target` *TARGET* +: Adds a target to scan. May be a host name, IP, IP CIDR range (ex: + `192.168.1.1/24`), or IP glob range (ex: `192.168.*.1-4`). + Not compatible with the `--parser` option. + +`-h`, `--help` +: Print help information + +## EXAMPLES + +Generates a new nmap scanner Ruby script that scans `example.com`, ports 22, 80, +443, and 8000 through 9000: + + $ ronin-nmap new scanner.rb --ports 22,80,443,8000-9000 --target example.com + +Generates a new nmap XML parser script that parses `path/to/nmap.xml` and prints +the scan information. + + $ ronin-nmap new parser.rb --parser --xml-file path/to/nmap.xml --printing + +## AUTHOR + +Postmodern + +## SEE ALSO + +[ronin-nmap-scan](ronin-nmap-scan.1.md), [ronin-nmap-print](ronin-nmap-print.1.md), [ronin-nmap-import](ronin-nmap-import.1.md)