1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-08 13:41:29 +00:00

[NSE] Added Boolean Operators for --script. You may now use a boolean operator

("and", "or", or "not") combined with categories, filenames, and filenames with
a '*', wildcard, to match many files.  This change requires the script database
to be recompiled. Please see the thread at
http://seclists.org/nmap-dev/2009/q2/0100.html for more information.
This commit is contained in:
batrick
2009-04-30 05:49:47 +00:00
parent 6b1421db69
commit 98e51ec007
3 changed files with 88 additions and 47 deletions

View File

@@ -1,5 +1,12 @@
# Nmap Changelog ($Id$); -*-text-*- # Nmap Changelog ($Id$); -*-text-*-
o [NSE] Added Boolean Operators for --script. You may now use a boolean
operator ("and", "or", or "not") combined with categories, filenames,
and filenames with a '*', wildcard, to match many files. This change
requires the script database to be recompiled. Please see the thread at
http://seclists.org/nmap-dev/2009/q2/0100.html for more
information. [Patrick]
o [Ncat] In SSL mode, Ncat now always uses secure connections, meaning o [Ncat] In SSL mode, Ncat now always uses secure connections, meaning
that it uses only good ciphers and doesn't use SSLv2. Certificates that it uses only good ciphers and doesn't use SSLv2. Certificates
can optionally be verified with the --ssl-verify and --ssl-trustfile can optionally be verified with the --ssl-verify and --ssl-trustfile

View File

@@ -335,10 +335,11 @@ int script_updatedb (void)
" ' categories field is not a table')\n" " ' categories field is not a table')\n"
" local basename = assert(match(script, '[/\\\\]?([^/\\\\]-%.nse)$'))\n" " local basename = assert(match(script, '[/\\\\]?([^/\\\\]-%.nse)$'))\n"
" table.sort(categories)\n" " table.sort(categories)\n"
" db:write('Entry { filename = \"', basename, '\", categories = {')\n"
" for j, category in ipairs(categories) do\n" " for j, category in ipairs(categories) do\n"
" db:write('Entry { category = \"', lower(category)," " db:write(' \"', lower(category), '\",')\n"
" '\", filename = \"', basename, '\" }\\n')\n"
" end\n" " end\n"
" db:write(' } }\\n')\n"
"end\n" "end\n"
"db:close()\n"; "db:close()\n";
int status = SCRIPT_ENGINE_SUCCESS; int status = SCRIPT_ENGINE_SUCCESS;

View File

@@ -249,25 +249,14 @@ do
end end
-- check_rules(rules) -- check_rules(rules)
-- Ensures reserved rules are not explicitly specified.
-- Adds the "default" category if no rules were specified. -- Adds the "default" category if no rules were specified.
-- Adds reserved rules that were internally specified (--sV for "version"). -- Adds other implicitly specified rules (e.g. "version")
-- --
-- Arguments: -- Arguments:
-- rules The array of rules to check. -- rules The array of rules to check.
local function check_rules (rules) local function check_rules (rules)
local reserved = { if cnse.default and #rules == 0 then rules[1] = "default" end
version = not not cnse.scriptversion, if cnse.scriptversion then rules[#rules+1] = "version" end
};
for i, rule in ipairs(rules) do
if reserved[lower(rule)] ~= nil then
error("explicitly specifying rule '"..rule.."' is prohibited");
end
end
if cnse.default and #rules == 0 then rules[1] = "default"; end
for rule, option in pairs(reserved) do
if option then rules[#rules+1] = rule; end
end
end end
-- chosen_scripts = get_chosen_scripts(rules) -- chosen_scripts = get_chosen_scripts(rules)
@@ -290,42 +279,86 @@ local function get_chosen_scripts (rules)
"database appears to be corrupt or out of date;\n".. "database appears to be corrupt or out of date;\n"..
"\tplease update using: nmap --script-updatedb"); "\tplease update using: nmap --script-updatedb");
local chosen_scripts, entry_rules, files_loaded = {}, {}, {}; local chosen_scripts, entry_rules, used_rules, files_loaded = {}, {}, {}, {};
-- Initialize entry_rules with the list of rules provided by the user. -- Tokens that are allowed in script rules (--script)
-- Each element of entry_rules may refer to another canonical element. local protected_lua_tokens = {
-- Here the lower-case rule points to the potentially mixed-case rule ["and"] = true,
-- provided by the user. ["or"] = true,
for i, rule in ipairs(rules) do ["not"] = true,
entry_rules[lower(rule)] = rule; };
entry_rules[rule] = false; -- Globalize all names in str that are not protected_lua_tokens
end local function globalize (str)
local lstr = lower(str);
-- Start by loading scripts by category. This function is run on each if protected_lua_tokens[lstr] then
-- Entry in script.db. return lstr;
local function entry (script_entry)
local category, filename = script_entry.category, script_entry.filename;
assert(type(category) == "string" and type(filename) == "string");
-- Don't load a file more than once.
if files_loaded[filename] then return end
-- Do we have a rule for this category (or an "all" rule)?
if entry_rules[category] ~= nil or
entry_rules.all ~= nil and category ~= "version" then
local index = entry_rules[category] ~= nil and category or "all";
local mark = entry_rules[index];
-- mark may point to the actual mixed case category passed via command
-- line
if type(mark) == "boolean" then
entry_rules[index] = true;
else else
entry_rules[mark] = true; return 'm("'..str..'")';
end end
end
-- Escape a magic character by prepending the '%' escape character
local function escape_magic (str)
return "%"..str;
end
for i, rule in ipairs(rules) do
rule = match(rule, "^%s*(.-)%s*$"); -- strip surrounding whitespace
used_rules[rule] = false; -- has not been used yet
-- Globalize all `names`, all visible characters not ',', '(', ')', and ';'
local globalized_rule =
gsub(rule, "[\033-\039\042-\043\045-\058\060-\126]+", globalize);
-- Precompile the globalized rule
local compiled_rule, err = loadstring("return "..globalized_rule, "rule");
if not compiled_rule then
err = err:match("rule\"]:%d+:(.+)$"); -- remove (luaL_)where in code
error("Bad script rule:\n\t"..rule.." -> "..err);
end
entry_rules[globalized_rule] = {
original_rule = rule,
compiled_rule = compiled_rule,
};
end
-- Checks if a given script, script_entry, should be loaded. A script_entry
-- should be in the form: { filename = "name.nse", categories = { ... } }
local function entry (script_entry)
local categories, filename = script_entry.categories, script_entry.filename;
assert(type(categories) == "table" and type(filename) == "string",
"script database appears corrupt, try `nmap --script-updatedb`");
local escaped_basename = match(filename, "([^/\\]-)%.nse$") or
match(filename, "([^/\\]-)$");
local r_categories = {all = true}; -- A reverse table of categories
for i, category in ipairs(categories) do
assert(type(category) == "string", "bad entry in script database");
r_categories[lower(category)] = true; -- Lowercase the entry
end
-- A matching function for each script rule.
-- If the pattern directly matches a category (e.g. "all"), then
-- we return true. Otherwise we test if it is a filename or if
-- the script_entry.filename matches the pattern.
local function m (pattern)
-- Check categories
if r_categories[lower(pattern)] then return true end
-- Check filename with wildcards
pattern = gsub(pattern, "%.nse$", ""); -- remove optional extension
pattern = gsub(pattern, "[%^%$%(%)%%%.%[%]%+%-%?]", escape_magic);
pattern = gsub(pattern, "%*", ".*"); -- change to Lua wildcard
pattern = "^"..pattern.."$"; -- anchor to beginning and end
return not not find(escaped_basename, pattern);
end
local env = {m = m};
for globalized_rule, rule_table in pairs(entry_rules) do
if setfenv(rule_table.compiled_rule, env)() then -- run the compiled rule
local t, path = cnse.fetchfile_absolute(filename); local t, path = cnse.fetchfile_absolute(filename);
assert(t == "file", filename.." is not a file!"); assert(t == "file", filename.." is not a file!");
if not files_loaded[path] then
chosen_scripts[#chosen_scripts+1] = Script.new(path); chosen_scripts[#chosen_scripts+1] = Script.new(path);
files_loaded[filename] = true; used_rules[rule_table.original_rule] = true;
files_loaded[path] = true;
end
end
end end
end end
@@ -333,14 +366,14 @@ local function get_chosen_scripts (rules)
db_closure(); -- Load the scripts db_closure(); -- Load the scripts
-- Now load any scripts listed by name rather than by category. -- Now load any scripts listed by name rather than by category.
for rule, loaded in pairs(entry_rules) do for rule, loaded in pairs(used_rules) do
if not loaded then -- attempt to load the file/directory if not loaded then -- attempt to load the file/directory
local t, path = cnse.fetchfile_absolute(rule); local t, path = cnse.fetchfile_absolute(rule);
if t == nil then -- perhaps omitted the extension? if t == nil then -- perhaps omitted the extension?
t, path = cnse.fetchfile_absolute(rule..".nse"); t, path = cnse.fetchfile_absolute(rule..".nse");
end end
if t == nil then if t == nil then
error("No such category, filename or directory: '"..rule.."'"); error("'"..rule.."' did not match a category, filename, or directory");
elseif t == "file" and not files_loaded[path] then elseif t == "file" and not files_loaded[path] then
chosen_scripts[#chosen_scripts+1] = Script.new(path); chosen_scripts[#chosen_scripts+1] = Script.new(path);
files_loaded[path] = true; files_loaded[path] = true;