mirror of
https://github.com/nmap/nmap.git
synced 2026-02-10 07:26:34 +00:00
o [NSE] Added support for forcing scripts to run agains certain ports by adding
a plus in front of the script name. [Martin Swende]
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
# Nmap Changelog ($Id$); -*-text-*-
|
||||
|
||||
o [NSE] Added support for forcing scripts to run agains certain ports by adding
|
||||
a plus in front of the script name. [Martin Swende]
|
||||
|
||||
o [NSE] Added the script broadcast-wake-on-lan that wakes systems from sleep
|
||||
by sending a Wake On Lan packet. [Patrik]
|
||||
|
||||
|
||||
104
nse_main.lua
104
nse_main.lua
@@ -350,7 +350,7 @@ do
|
||||
print_debug(1,
|
||||
"A thread for %s yielded unexpectedly in the file or %s function:\n%s\n",
|
||||
self.filename, rule, traceback(co));
|
||||
elseif s and rule_return then
|
||||
elseif s and (rule_return or self.forced_to_run) then
|
||||
local thread = {
|
||||
co = co,
|
||||
env = env,
|
||||
@@ -381,25 +381,46 @@ do
|
||||
local quiet_errors = {
|
||||
[REQUIRE_ERROR] = true,
|
||||
}
|
||||
|
||||
-- script = Script.new(filename)
|
||||
-- Creates a new Script Class for the script.
|
||||
-- Arguments:
|
||||
-- filename The filename (path) of the script to load.
|
||||
-- script_params The script selection parameters table.
|
||||
-- Possible key/value pairs:
|
||||
-- selection: A string to indicate the script selection type.
|
||||
-- "name": Selected by name or pattern.
|
||||
-- "category" Selected by category.
|
||||
-- "file path" Selected by file path.
|
||||
-- "directory" Selected by directory.
|
||||
-- verbosity: A boolean, if set to true the script will get a
|
||||
-- verbosity boost. Scripts selected by name or
|
||||
-- file paths must set this to true.
|
||||
-- forced: A boolean to indicate if the script will be
|
||||
-- forced to run regardless to its rule results.
|
||||
-- (e.g. "+script").
|
||||
-- Returns:
|
||||
-- script The script (class) created.
|
||||
function Script.new (filename, selected_by_name)
|
||||
function Script.new (filename, script_params)
|
||||
local script_params = script_params or {};
|
||||
assert(type(filename) == "string", "string expected");
|
||||
if not find(filename, "%.nse$") then
|
||||
log_error(
|
||||
"Warning: Loading '%s' -- the recommended file extension is '.nse'.",
|
||||
filename);
|
||||
end
|
||||
|
||||
local basename = match(filename, "([^/\\]+)$") or filename;
|
||||
if selected_by_name then
|
||||
print_debug(2, "Script %s was selected by name.", basename);
|
||||
end
|
||||
local short_basename = match(filename, "([^/\\]+)%.nse$") or
|
||||
match(filename, "([^/\\]+)%.[^.]*$") or filename;
|
||||
|
||||
if debugging() > 1 then
|
||||
print_debug(2, "Script %s was selected by %s%s.",
|
||||
basename,
|
||||
script_params.selection and
|
||||
script_params.selection or "(unknown)",
|
||||
script_params.forced and " and forced to run" or "");
|
||||
end
|
||||
local file_closure = assert(loadfile(filename));
|
||||
-- Give the closure its own environment, with global access
|
||||
local env = {
|
||||
@@ -470,7 +491,9 @@ do
|
||||
license = rawget(env, "license"),
|
||||
dependencies = rawget(env, "dependencies"),
|
||||
threads = {},
|
||||
selected_by_name = not not selected_by_name,
|
||||
-- Make sure that the following are boolean types.
|
||||
selected_by_name = not not script_params.verbosity,
|
||||
forced_to_run = not not script_params.forced,
|
||||
};
|
||||
return setmetatable(script, {__index = Script, __metatable = Script});
|
||||
end
|
||||
@@ -500,7 +523,8 @@ local function get_chosen_scripts (rules)
|
||||
"database appears to be corrupt or out of date;\n"..
|
||||
"\tplease update using: nmap --script-updatedb");
|
||||
|
||||
local chosen_scripts, entry_rules, used_rules, files_loaded = {}, {}, {}, {};
|
||||
local chosen_scripts, files_loaded = {}, {};
|
||||
local entry_rules, used_rules, forced_rules = {}, {}, {};
|
||||
|
||||
-- Tokens that are allowed in script rules (--script)
|
||||
local protected_lua_tokens = {
|
||||
@@ -508,6 +532,19 @@ local function get_chosen_scripts (rules)
|
||||
["or"] = true,
|
||||
["not"] = true,
|
||||
};
|
||||
|
||||
-- Was this category selection forced to run (e.g. "+script").
|
||||
-- Return:
|
||||
-- Boolean: True if it's forced otherwise false.
|
||||
-- String: The new cleaned string.
|
||||
local function is_forced_set (str)
|
||||
local substr, count = gsub(str, "^%+", "");
|
||||
if count > 0 then
|
||||
return true, substr;
|
||||
end
|
||||
return false, str;
|
||||
end
|
||||
|
||||
-- Globalize all names in str that are not protected_lua_tokens
|
||||
local function globalize (str)
|
||||
local lstr = lower(str);
|
||||
@@ -520,7 +557,10 @@ local function get_chosen_scripts (rules)
|
||||
|
||||
for i, rule in ipairs(rules) do
|
||||
rule = match(rule, "^%s*(.-)%s*$"); -- strip surrounding whitespace
|
||||
local original_rule = rule;
|
||||
local forced, rule = is_forced_set(rule);
|
||||
used_rules[rule] = false; -- has not been used yet
|
||||
forced_rules[rule] = forced;
|
||||
-- Globalize all `names`, all visible characters not ',', '(', ')', and ';'
|
||||
local globalized_rule =
|
||||
gsub(rule, "[\033-\039\042-\043\045-\058\060-\126]+", globalize);
|
||||
@@ -528,8 +568,9 @@ local function get_chosen_scripts (rules)
|
||||
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);
|
||||
error("Bad script rule:\n\t"..original_rule.." -> "..err);
|
||||
end
|
||||
-- These are used to reference and check all the rules later.
|
||||
entry_rules[globalized_rule] = {
|
||||
original_rule = rule,
|
||||
compiled_rule = compiled_rule,
|
||||
@@ -551,35 +592,52 @@ local function get_chosen_scripts (rules)
|
||||
r_categories[lower(category)] = true; -- Lowercase the entry
|
||||
end
|
||||
|
||||
-- Was this entry selected by name with the --script option? We record
|
||||
-- whether it was so that scripts so selected can get a verbosity boost.
|
||||
-- See nmap.verbosity.
|
||||
local selected_by_name = false;
|
||||
-- The script selection parameters table.
|
||||
local script_params = {};
|
||||
|
||||
-- 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
|
||||
if r_categories[lower(pattern)] then
|
||||
script_params.selection = "category";
|
||||
return true;
|
||||
end
|
||||
|
||||
-- Check filename with wildcards
|
||||
pattern = gsub(pattern, "%.nse$", ""); -- remove optional extension
|
||||
pattern = gsub(pattern, "[%^%$%(%)%%%.%[%]%+%-%?]", "%%%1"); -- esc magic
|
||||
pattern = gsub(pattern, "%*", ".*"); -- change to Lua wildcard
|
||||
pattern = "^"..pattern.."$"; -- anchor to beginning and end
|
||||
local found = not not find(escaped_basename, pattern);
|
||||
selected_by_name = selected_by_name or found;
|
||||
return found;
|
||||
if find(escaped_basename, pattern) then
|
||||
script_params.selection = "name";
|
||||
script_params.verbosity = true;
|
||||
return true;
|
||||
end
|
||||
|
||||
return false;
|
||||
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
|
||||
-- Clear and set the environment of the compiled script rule
|
||||
local compiled_rule = setfenv(rule_table.compiled_rule, env)
|
||||
local status, found = pcall(compiled_rule)
|
||||
if not status then
|
||||
error("Bad script rule:\n\t"..rule_table.original_rule..
|
||||
" -> script rule expression not supported.");
|
||||
end
|
||||
-- The script rule matches a category or a pattern
|
||||
if found then
|
||||
used_rules[rule_table.original_rule] = true;
|
||||
script_params.forced = not not forced_rules[rule_table.original_rule];
|
||||
local t, path = cnse.fetchscript(filename);
|
||||
if t == "file" then
|
||||
if not files_loaded[path] then
|
||||
chosen_scripts[#chosen_scripts+1] = Script.new(path, selected_by_name);
|
||||
local script = Script.new(path, script_params)
|
||||
chosen_scripts[#chosen_scripts+1] = script;
|
||||
files_loaded[path] = true;
|
||||
-- do not break so other rules can be marked as used
|
||||
end
|
||||
@@ -597,6 +655,8 @@ local function get_chosen_scripts (rules)
|
||||
-- Now load any scripts listed by name rather than by category.
|
||||
for rule, loaded in pairs(used_rules) do
|
||||
if not loaded then -- attempt to load the file/directory
|
||||
local script_params = {};
|
||||
script_params.forced = not not forced_rules[rule];
|
||||
local t, path = cnse.fetchscript(rule);
|
||||
if t == nil then -- perhaps omitted the extension?
|
||||
t, path = cnse.fetchscript(rule..".nse");
|
||||
@@ -604,14 +664,18 @@ local function get_chosen_scripts (rules)
|
||||
if t == nil then
|
||||
error("'"..rule.."' did not match a category, filename, or directory");
|
||||
elseif t == "file" and not files_loaded[path] then
|
||||
local script = Script.new(path, true);
|
||||
script_params.selection = "file path";
|
||||
script_params.verbosity = true;
|
||||
local script = Script.new(path, script_params);
|
||||
chosen_scripts[#chosen_scripts+1] = script;
|
||||
files_loaded[path] = true;
|
||||
elseif t == "directory" then
|
||||
for f in cnse.dir(path) do
|
||||
local file = path .."/".. f
|
||||
if find(f, "%.nse$") and not files_loaded[file] then
|
||||
chosen_scripts[#chosen_scripts+1] = Script.new(file);
|
||||
script_params.selection = "directory";
|
||||
local script = Script.new(path, script_params);
|
||||
chosen_scripts[#chosen_scripts+1] = script;
|
||||
files_loaded[file] = true;
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user