From 853056904787cdd57cef73a3d75715842106c815 Mon Sep 17 00:00:00 2001 From: kris Date: Sun, 30 Mar 2008 20:33:33 +0000 Subject: [PATCH] Adding my Datafiles NSElib for parsing the nmap-* data files for scripts and also update rpcinfo.nse to use this library. Includes CHANGELOG and docs/scripting.xml updates --- CHANGELOG | 6 ++ docs/scripting.xml | 62 +++++++++++++++++++++ nselib/datafiles.lua | 128 +++++++++++++++++++++++++++++++++++++++++++ scripts/rpcinfo.nse | 42 +------------- 4 files changed, 199 insertions(+), 39 deletions(-) create mode 100644 nselib/datafiles.lua diff --git a/CHANGELOG b/CHANGELOG index 5f740f2fc..1c25b8301 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,6 +8,12 @@ o Reformat Nmap COPYING file (e.g. remove C comment markers, reduce when presented by the Windows executable (NSIS) installer. Thanks to Jah for the patch (which was modified slightly by Fyodor). +o Added NSE Datafiles library which reads and parses Nmap's nmap-* + data files for scripts. The functions (parse_protocols(), + parse_rpc() and parse_services()) return tables with numbers + (e.g. port numbers) indexing names (e.g. service names). The + rpcinfo.nse script was also updated to use this library. [Kris] + o Changed the NSE function nmap.set_port_state() so that it checks to see if the requested port is already in the requested state. This prevents "Duplicate port" messages during the script scan and the diff --git a/docs/scripting.xml b/docs/scripting.xml index 89e8da2a0..8055b6aca 100644 --- a/docs/scripting.xml +++ b/docs/scripting.xml @@ -1399,6 +1399,68 @@ if(s) code_to_be_done_on_match end + + + Data File Parsing Functions + + The datafiles module provides functions for reading and parsing + Nmap's data files (e.g. nmap-protocol, nmap-rpc, + etc.). These functions' return values are setup for use with exception handling via + nmap.new_try(). + + + + + + parse_protocols + + + This function reads and parses Nmap's nmap-protocols + file. bool is a boolen value indicating success. + If bool is true, then the second returned + value is a table with protocol numbers indexing the protocol + names. If bool is false, an error message + is returned as the second value instead of the table. + + + + + + + parse_rpc + + + This function reads and parses Nmap's nmap-rpc + file. bool is a boolen value indicating success. + If bool is true, then the second returned + value is a table with RPC numbers indexing the RPC names. If + bool is false, an error message is returned + as the second value instead of the table. + + + + + + + parse_services + + + This function reads and parses Nmap's nmap-services + file. bool is a boolen value indicating success. + If bool is true, then the second returned + value is a table containing two other tables: + tcp{} and udp{}. + tcp{} contains services indexed by TCP port + numbers. udp{} is the same, but for UDP. + You can pass "tcp" or "udp" as an argument to + parse_services() to only get the corresponding + table. If bool is false, an error message is + returned as the second value instead of the table. + + + + + Various Utility Functions diff --git a/nselib/datafiles.lua b/nselib/datafiles.lua new file mode 100644 index 000000000..b45d0d59d --- /dev/null +++ b/nselib/datafiles.lua @@ -0,0 +1,128 @@ +-- Kris Katterjohn 03/2008 + +module(..., package.seeall) + +require 'stdnse' + +-- These tables are filled by the following fill* functions +local protocols_table = {} +local rpc_table = {} +local services_table = {tcp={}, udp={}} + +-- Fills protocols or rpc table with values read from the nmap-* files +local filltable = function(filename, table) + if #table ~= 0 then + return true + end + + local path = nmap.fetchfile(filename) + + if path == nil then + return false + end + + local file = io.open(path, "r") + + -- Loops through file line-by-line + while true do + local l = file:read() + + if not l then + break + end + + l = l:gsub("%s*#.*", "") + + if l:len() ~= 0 then + local m = l:gsub("^([%a%d_-]+)%s+(%d+).*", "%2=%1") + + if m:match("=") then + local t = stdnse.strsplit("=", m) + table[tonumber(t[1])] = t[2] + end + end + end + + file:close() + + return true +end + +-- Fills services_table{} with values read from nmap-services +local fillservices = function() + if #services_table["tcp"] ~= 0 or + #services_table["udp"] ~= 0 then + return true + end + + local path = nmap.fetchfile("nmap-services") + + if path == nil then + return false + end + + local file = io.open(path, "r") + + -- Loops through nmap-services line-by-line + while true do + local l = file:read() + + if not l then + break + end + + l = l:gsub("%s*#.*", "") + + if l:len() ~= 0 then + local m = l:gsub("^([%a%d_-]+)%s+([%a%d/]+).*", "%2=%1") + + if m:match("=") and m:match("/") then + local t = stdnse.strsplit("=", m) + local s = stdnse.strsplit("/", t[1]) + + if s[2] ~= "tcp" and s[2] ~= "udp" then + services_table = {tcp={}, udp={}} + return false + end + + services_table[s[2]][tonumber(s[1])] = t[2] + end + end + end + + file:close() + + return true +end + +parse_protocols = function() + if not filltable("nmap-protocols", protocols_table) then + return false, "Error parsing nmap-protocols" + end + + return true, protocols_table +end + +parse_rpc = function() + if not filltable("nmap-rpc", rpc_table) then + return false, "Error parsing nmap-rpc" + end + + return true, rpc_table +end + +parse_services = function(protocol) + if protocol and protocol ~= "tcp" and protocol ~= "udp" then + return false, "Bad protocol for nmap-services: use tcp or udp" + end + + if not fillservices() then + return false, "Error parsing nmap-services" + end + + if protocol then + return true, services_table[protocol] + end + return true, services_table +end + diff --git a/scripts/rpcinfo.nse b/scripts/rpcinfo.nse index ecb8bc465..d1375c506 100644 --- a/scripts/rpcinfo.nse +++ b/scripts/rpcinfo.nse @@ -7,44 +7,7 @@ categories = {"safe","discovery"} require "shortport" require "packet" -require "stdnse" - -local rpc_numbers = {} - --- Fills rpc_numbers with values read from RPC file - Kris Katterjohn -local fillrpc = function() - local path = nmap.fetchfile("nmap-rpc") - - if path == nil then - return false, "Can't read from RPC file!" - end - - local file = io.open(path, "r") - - -- Loops through RPC file line-by-line - while true do - local l = file:read() - - if not l then - break - end - - l = l:gsub("%s*#.*", "") - - if l:len() ~= 0 then - local m = l:gsub("^([%a%d_]+)%s+(%d+).*", "%2=%1") - - if m:match("=") then - local t = stdnse.strsplit("=", m) - rpc_numbers[tonumber(t[1])] = t[2] - end - end - end - - file:close() - - return true -end +require "datafiles" portrule = shortport.port_or_service(111, "rpcbind") @@ -53,10 +16,11 @@ action = function(host, port) local transaction_id = "nmap" local socket = nmap.new_socket() local result = " \n" + local rpc_numbers catch = function() socket:close() end try = nmap.new_try( catch ) - try( fillrpc() ) + rpc_numbers = try(datafiles.parse_rpc()) local request = string.char(0x80,0,0,40) -- fragment header request = request .. transaction_id -- transaction id