mirror of
https://github.com/nmap/nmap.git
synced 2025-12-09 22:21:29 +00:00
Structured script output.
Scripts may now return a key–value table, or such a table in addition to a string. The table will be automatically formatted for normal output and will appear as a hierarchy of elements in XML output. Some history and discussion of this development can be found at https://secwiki.org/w/Nmap/Structured_Script_Output. This is a merge of r29484:29569 from /nmap-exp/david/xml-output.
This commit is contained in:
@@ -162,6 +162,10 @@ void Target::Recycle() {
|
|||||||
|
|
||||||
Target::~Target() {
|
Target::~Target() {
|
||||||
FreeInternal();
|
FreeInternal();
|
||||||
|
while (!scriptResults.empty()) {
|
||||||
|
scriptResults.front().clear();
|
||||||
|
scriptResults.pop_front();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Target::FreeInternal() {
|
void Target::FreeInternal() {
|
||||||
|
|||||||
@@ -230,12 +230,22 @@
|
|||||||
|
|
||||||
<!ELEMENT cpe (#PCDATA)>
|
<!ELEMENT cpe (#PCDATA)>
|
||||||
|
|
||||||
<!ELEMENT script EMPTY >
|
<!ELEMENT script (table|elem)* >
|
||||||
<!ATTLIST script
|
<!ATTLIST script
|
||||||
id CDATA #REQUIRED
|
id CDATA #REQUIRED
|
||||||
output CDATA #REQUIRED
|
output CDATA #REQUIRED
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<!ELEMENT table (table|elem)* >
|
||||||
|
<!ATTLIST table
|
||||||
|
key CDATA #IMPLIED
|
||||||
|
>
|
||||||
|
|
||||||
|
<!ELEMENT elem (#PCDATA)>
|
||||||
|
<!ATTLIST elem
|
||||||
|
key CDATA #IMPLIED
|
||||||
|
>
|
||||||
|
|
||||||
<!ELEMENT os ( portused* , osmatch*, osfingerprint* ) >
|
<!ELEMENT os ( portused* , osmatch*, osfingerprint* ) >
|
||||||
|
|
||||||
<!ELEMENT portused EMPTY >
|
<!ELEMENT portused EMPTY >
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ Additionally, you can use:
|
|||||||
-- | sample-script:
|
-- | sample-script:
|
||||||
-- | This is some output
|
-- | This is some output
|
||||||
-- |_ Some more output
|
-- |_ Some more output
|
||||||
|
-- @xmloutput
|
||||||
|
-- <elem>This is some output</elem>
|
||||||
|
-- <table>
|
||||||
|
-- <elem>Some more output</elem>
|
||||||
|
-- </table>
|
||||||
--
|
--
|
||||||
-- @args sample-script.arg1 Here, we document each argument, how it's used, and
|
-- @args sample-script.arg1 Here, we document each argument, how it's used, and
|
||||||
-- necessary, the default value.
|
-- necessary, the default value.
|
||||||
@@ -171,23 +176,45 @@ action = function( host, port )
|
|||||||
target.add('192.168.1.1')
|
target.add('192.168.1.1')
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If your response is more complicated, you can build a table, potentially
|
-- Construct a table representing what the script has to report.
|
||||||
-- with subtables, and pass it to stdnse.format_output(). Each table can have
|
local output_tab = stdnse.output_table()
|
||||||
-- a list of output values, numerically, which will be displayed in order.
|
output_tab.name1 = 'value1'
|
||||||
-- Additionally, they can have the 'name' key, which will be displayed at the
|
output_tab.name2 = 'value2'
|
||||||
-- top, and the 'warning' key, which will only be displayed if debugging is
|
output_tab.subtable = { 'sub1', 'sub2', 'sub3' }
|
||||||
-- enabled. For more information and examples, see the documentation for
|
|
||||||
-- stdnse.format_output().
|
-- Returning this table will produce output like this:
|
||||||
|
-- | sample-script:
|
||||||
|
-- | name1: value1
|
||||||
|
-- | name2: value2
|
||||||
|
-- | subtable:
|
||||||
|
-- | sub1
|
||||||
|
-- | sub2
|
||||||
|
-- |_ sub3
|
||||||
|
--
|
||||||
|
-- If you need more control over output formatting, you can return a string in
|
||||||
|
-- addition to the table. stdnse.format_output() is a formatting function used
|
||||||
|
-- to make string output. Each table can have a list of output values,
|
||||||
|
-- numerically, which will be displayed in order. Additionally, they can have
|
||||||
|
-- the 'name' key, which will be displayed at the top, and the 'warning' key,
|
||||||
|
-- which will only be displayed if debugging is enabled. For more information
|
||||||
|
-- and examples, see the documentation for stdnse.format_output().
|
||||||
--
|
--
|
||||||
-- The following will display:
|
-- The following will display:
|
||||||
-- | sample-script:
|
-- | sample-script:
|
||||||
-- | value1
|
-- | Name 1: value1
|
||||||
-- | value2
|
-- | Name 2: value2
|
||||||
-- | This is a subtable
|
-- | This is a subtable
|
||||||
-- | subtable1
|
-- | sub1
|
||||||
-- |_ subtable2
|
-- | sub2
|
||||||
local response = {'value1', 'value2', {name="This is a subtable", 'subtable1', 'subtable2'}}
|
-- |_ sub3
|
||||||
return stdnse.format_output(true, response)
|
|
||||||
|
output_str = stdnse.format_output(true, {
|
||||||
|
'Name 1: ' .. 'value1',
|
||||||
|
'Name 2: ' .. 'value2',
|
||||||
|
{ name='This is a subtable', 'sub1', 'sub2', 'sub3' }
|
||||||
|
})
|
||||||
|
|
||||||
|
return output_tab, output_str
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1302,9 +1302,18 @@ NSE: Script Scanning completed.
|
|||||||
The action is the heart of an NSE script. It contains all of the
|
The action is the heart of an NSE script. It contains all of the
|
||||||
instructions to be executed when the script's prerule, portrule, hostrule or postrule
|
instructions to be executed when the script's prerule, portrule, hostrule or postrule
|
||||||
triggers. It is a Lua function which accepts the same arguments as the
|
triggers. It is a Lua function which accepts the same arguments as the
|
||||||
rule and can return either <literal>nil</literal> or a string. If a string is returned by a service script, the string and script's filename are printed in the Nmap port table output. A string returned by a host script is printed below the port table. No output is produced if the
|
rule. The return value of the action value may be a table of
|
||||||
script returns <literal>nil</literal>. For an example of an NSE
|
name–value pairs, a string, or <code>nil</code>. For an example of
|
||||||
action refer to <xref linkend="nse-tutorial-action"/>.
|
an NSE action refer to <xref linkend="nse-tutorial-action"/>.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
If the output of the action is a table, it is automatically formatted in
|
||||||
|
a structured fashion for inclusion in the normal (<option>-oN</option>)
|
||||||
|
and XML (<option>-oX</option>) output formats. If a string, the text is
|
||||||
|
displayed directly in normal output, and written as an XML attribute in
|
||||||
|
XML output, No output is produced if the script returns
|
||||||
|
<literal>nil</literal>. See <xref linkend="nse-structured-output"/> for
|
||||||
|
details of how different return values are handled.
|
||||||
</para>
|
</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
@@ -2166,6 +2175,140 @@ socket:close()
|
|||||||
</sect3>
|
</sect3>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="nse-structured-output">
|
||||||
|
<title>Structured and Unstructured Output</title>
|
||||||
|
<indexterm>structured script output</indexterm>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
NSE scripts should usually return a table representing their
|
||||||
|
output, one that is nicely organized and has thoughtfully chosen
|
||||||
|
keys. Such a table will be automatically formatted for screen
|
||||||
|
output and will be stored as nested elements in XML output.
|
||||||
|
Having XML output broken down logically into keys and values
|
||||||
|
makes it easier for other tools to make use of script output.
|
||||||
|
It is possible for a script to return only a string, but doing
|
||||||
|
so is deprecated. In the past, scripts could only return a
|
||||||
|
string, and their output was simply copied to the XML as a blob
|
||||||
|
of text–this is now known as <quote>unstructured
|
||||||
|
output</quote>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Suppose a script called <filename>user-list</filename> returns a
|
||||||
|
table as shown in this code sample. The following paragraphs
|
||||||
|
show how it appears in normal and XML output.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
local output = stdnse.output_table()
|
||||||
|
output.hostname = "slimer"
|
||||||
|
output.users = {}
|
||||||
|
output.users[#output.users + 1] = "root"
|
||||||
|
output.users[#output.users + 1] = "foo"
|
||||||
|
output.users[#output.users + 1] = "bar"
|
||||||
|
return output
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
A Lua table is converted to a string for normal output. The way
|
||||||
|
this works is: each nested table gets a new level of
|
||||||
|
indentation. Table entries with string keys are preceded by the
|
||||||
|
key and a colon; entries with integer keys simply appear in
|
||||||
|
order.
|
||||||
|
Unlike normal Lua tables, which are unordered, a table that
|
||||||
|
comes from <code>stdnse.output_table</code> will keep its keys in
|
||||||
|
the order they were inserted.
|
||||||
|
<xref linkend="nse-normal-structured-output"/> shows how the
|
||||||
|
example table appears in normal output.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<example id="nse-normal-structured-output">
|
||||||
|
<title>Automatic formatting of NSE structured output</title>
|
||||||
|
<screen>
|
||||||
|
PORT STATE SERVICE
|
||||||
|
1123/tcp open unknown
|
||||||
|
| user-list:
|
||||||
|
| hostname: slimer
|
||||||
|
| users:
|
||||||
|
| root
|
||||||
|
| foo
|
||||||
|
|_ bar
|
||||||
|
</screen>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The XML representation of a Lua table is constructed as follows.
|
||||||
|
Nested table become <code>table</code> elements. Entries of
|
||||||
|
tables that are not themselves tables become <code>elem</code>
|
||||||
|
elements. Entries (whether <code>table</code> or
|
||||||
|
<code>elem</code>) with string keys get a <code>key</code>
|
||||||
|
attribute (e.g.
|
||||||
|
<code><elem key="username">foo</elem></code>);
|
||||||
|
entries with integer keys have no <code>key</code> element and
|
||||||
|
their key is implicit in the order in which they appear.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
In addition to the above, whatever normal output the script
|
||||||
|
produces (even if automatically generated) is copied to the
|
||||||
|
<code>output</code> attribute of the <code>script</code>
|
||||||
|
element. Newlines and other special characters will be encoded
|
||||||
|
as XML character entities, for example <code>&#xa;</code>.
|
||||||
|
<xref linkend="nse-xml-structured-output"/> shows how the example
|
||||||
|
table appears in XML.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<example id="nse-xml-structured-output">
|
||||||
|
<title>NSE structured output in XML</title>
|
||||||
|
<screen><![CDATA[<script id="t" output="
hostname: slimer
users: 
 root
 foo
 bar">
|
||||||
|
<elem key="hostname">slimer</elem>
|
||||||
|
<table key="users">
|
||||||
|
<elem>root</elem>
|
||||||
|
<elem>foo</elem>
|
||||||
|
<elem>bar</elem>
|
||||||
|
</table>
|
||||||
|
</script>
|
||||||
|
]]></screen>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Some scripts need more control their normal output. This is the
|
||||||
|
case, for example, with scripts that need to display complex
|
||||||
|
tables. For complete control over the output, these scripts may
|
||||||
|
do either of these things:
|
||||||
|
<simplelist>
|
||||||
|
<member>return a string as second return value, or</member>
|
||||||
|
<member>set the <code>__tostring</code> metamethod on the
|
||||||
|
returned table.</member>
|
||||||
|
</simplelist>
|
||||||
|
The resulting string will be used in normal output, and the
|
||||||
|
table will be used in XML as usual. The formatted string may
|
||||||
|
contain newline characters to appear as multiple lines.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
If the above code example were modified in this way to return a
|
||||||
|
formatted string,
|
||||||
|
<programlisting>
|
||||||
|
local output = stdnse.output_table()
|
||||||
|
output.hostname = "slimer"
|
||||||
|
output.users = {}
|
||||||
|
output.users[#output.users + 1] = "root"
|
||||||
|
output.users[#output.users + 1] = "foo"
|
||||||
|
output.users[#output.users + 1] = "bar"
|
||||||
|
local output_str = string.format("hostname: %s\n", output.hostname)
|
||||||
|
output_str = output_str .. "\n" .. stdnse.strjoin(", ", output.users)
|
||||||
|
return output, output_str
|
||||||
|
</programlisting>
|
||||||
|
then the normal output would appear as follows:
|
||||||
|
<screen>
|
||||||
|
PORT STATE SERVICE
|
||||||
|
1123/tcp open unknown
|
||||||
|
| user-list:
|
||||||
|
| hostname: slimer
|
||||||
|
|_ users: root, foo, bar
|
||||||
|
</screen>
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
<sect2 id="nse-exceptions">
|
<sect2 id="nse-exceptions">
|
||||||
<title>Exception Handling</title>
|
<title>Exception Handling</title>
|
||||||
<indexterm><primary>exceptions in NSE</primary></indexterm>
|
<indexterm><primary>exceptions in NSE</primary></indexterm>
|
||||||
|
|||||||
10
nmap.cc
10
nmap.cc
@@ -1758,7 +1758,10 @@ int nmap_main(int argc, char *argv[]) {
|
|||||||
script_scan_results = get_script_scan_results_obj();
|
script_scan_results = get_script_scan_results_obj();
|
||||||
script_scan(Targets, SCRIPT_PRE_SCAN);
|
script_scan(Targets, SCRIPT_PRE_SCAN);
|
||||||
printscriptresults(script_scan_results, SCRIPT_PRE_SCAN);
|
printscriptresults(script_scan_results, SCRIPT_PRE_SCAN);
|
||||||
script_scan_results->clear();
|
while (!script_scan_results->empty()) {
|
||||||
|
script_scan_results->front().clear();
|
||||||
|
script_scan_results->pop_front();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -2055,7 +2058,10 @@ int nmap_main(int argc, char *argv[]) {
|
|||||||
if (o.script) {
|
if (o.script) {
|
||||||
script_scan(Targets, SCRIPT_POST_SCAN);
|
script_scan(Targets, SCRIPT_POST_SCAN);
|
||||||
printscriptresults(script_scan_results, SCRIPT_POST_SCAN);
|
printscriptresults(script_scan_results, SCRIPT_POST_SCAN);
|
||||||
script_scan_results->clear();
|
while (!script_scan_results->empty()) {
|
||||||
|
script_scan_results->front().clear();
|
||||||
|
script_scan_results->pop_front();
|
||||||
|
}
|
||||||
delete new_targets;
|
delete new_targets;
|
||||||
new_targets = NULL;
|
new_targets = NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
144
nse_main.cc
144
nse_main.cc
@@ -36,6 +36,9 @@
|
|||||||
#define NSE_SELECTED_BY_NAME "NSE_SELECTED_BY_NAME"
|
#define NSE_SELECTED_BY_NAME "NSE_SELECTED_BY_NAME"
|
||||||
#define NSE_CURRENT_HOSTS "NSE_CURRENT_HOSTS"
|
#define NSE_CURRENT_HOSTS "NSE_CURRENT_HOSTS"
|
||||||
|
|
||||||
|
#define NSE_FORMAT_TABLE "NSE_FORMAT_TABLE"
|
||||||
|
#define NSE_FORMAT_XML "NSE_FORMAT_XML"
|
||||||
|
|
||||||
#ifndef MAXPATHLEN
|
#ifndef MAXPATHLEN
|
||||||
# define MAXPATHLEN 2048
|
# define MAXPATHLEN 2048
|
||||||
#endif
|
#endif
|
||||||
@@ -113,7 +116,9 @@ static int script_set_output (lua_State *L)
|
|||||||
{
|
{
|
||||||
ScriptResult sr;
|
ScriptResult sr;
|
||||||
sr.set_id(luaL_checkstring(L, 1));
|
sr.set_id(luaL_checkstring(L, 1));
|
||||||
sr.set_output(luaL_checkstring(L, 2));
|
sr.set_output_tab(L, 2);
|
||||||
|
if (!lua_isnil(L, 3))
|
||||||
|
sr.set_output_str(luaL_checkstring(L, 3));
|
||||||
script_scan_results.push_back(sr);
|
script_scan_results.push_back(sr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -123,7 +128,9 @@ static int host_set_output (lua_State *L)
|
|||||||
ScriptResult sr;
|
ScriptResult sr;
|
||||||
Target *target = nseU_gettarget(L, 1);
|
Target *target = nseU_gettarget(L, 1);
|
||||||
sr.set_id(luaL_checkstring(L, 2));
|
sr.set_id(luaL_checkstring(L, 2));
|
||||||
sr.set_output(luaL_checkstring(L, 3));
|
sr.set_output_tab(L, 3);
|
||||||
|
if (!lua_isnil(L, 4))
|
||||||
|
sr.set_output_str(luaL_checkstring(L, 4));
|
||||||
target->scriptResults.push_back(sr);
|
target->scriptResults.push_back(sr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -136,7 +143,9 @@ static int port_set_output (lua_State *L)
|
|||||||
Target *target = nseU_gettarget(L, 1);
|
Target *target = nseU_gettarget(L, 1);
|
||||||
p = nseU_getport(L, target, &port, 2);
|
p = nseU_getport(L, target, &port, 2);
|
||||||
sr.set_id(luaL_checkstring(L, 3));
|
sr.set_id(luaL_checkstring(L, 3));
|
||||||
sr.set_output(luaL_checkstring(L, 4));
|
sr.set_output_tab(L, 4);
|
||||||
|
if (!lua_isnil(L, 5))
|
||||||
|
sr.set_output_str(luaL_checkstring(L, 5));
|
||||||
target->ports.addScriptResult(p->portno, p->proto, sr);
|
target->ports.addScriptResult(p->portno, p->proto, sr);
|
||||||
target->ports.numscriptresults++;
|
target->ports.numscriptresults++;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -236,6 +245,19 @@ static int l_xml_newline(lua_State *L)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_protect_xml(lua_State *L)
|
||||||
|
{
|
||||||
|
const char *text;
|
||||||
|
size_t len;
|
||||||
|
std::string output;
|
||||||
|
|
||||||
|
text = luaL_checklstring(L, 1, &len);
|
||||||
|
output = protect_xml(std::string(text, len));
|
||||||
|
lua_pushlstring(L, output.c_str(), output.size());
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int nse_fetch (lua_State *L, int (*fetch)(char *, size_t, const char *))
|
static int nse_fetch (lua_State *L, int (*fetch)(char *, size_t, const char *))
|
||||||
{
|
{
|
||||||
char path[MAXPATHLEN];
|
char path[MAXPATHLEN];
|
||||||
@@ -340,6 +362,7 @@ static void open_cnse (lua_State *L)
|
|||||||
{"xml_end_tag", l_xml_end_tag},
|
{"xml_end_tag", l_xml_end_tag},
|
||||||
{"xml_write_escaped", l_xml_write_escaped},
|
{"xml_write_escaped", l_xml_write_escaped},
|
||||||
{"xml_newline", l_xml_newline},
|
{"xml_newline", l_xml_newline},
|
||||||
|
{"protect_xml", l_protect_xml},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -356,14 +379,76 @@ static void open_cnse (lua_State *L)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptResult::set_output (const char *out)
|
/* Global persistent Lua state used by the engine. */
|
||||||
|
static lua_State *L_NSE = NULL;
|
||||||
|
|
||||||
|
void ScriptResult::clear (void)
|
||||||
{
|
{
|
||||||
output = std::string(out);
|
if (o.debugging > 3)
|
||||||
|
log_write(LOG_STDOUT, "ScriptResult::clear %d id %s\n", output_ref, get_id());
|
||||||
|
luaL_unref(L_NSE, LUA_REGISTRYINDEX, output_ref);
|
||||||
|
output_ref = LUA_NOREF;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *ScriptResult::get_output (void) const
|
void ScriptResult::set_output_tab (lua_State *L, int pos)
|
||||||
{
|
{
|
||||||
return output.c_str();
|
clear();
|
||||||
|
lua_pushvalue(L, pos);
|
||||||
|
output_ref = luaL_ref(L_NSE, LUA_REGISTRYINDEX);
|
||||||
|
if (o.debugging > 3)
|
||||||
|
log_write(LOG_STDOUT, "ScriptResult::set_output_tab %d id %s\n", output_ref, get_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptResult::set_output_str (const char *out)
|
||||||
|
{
|
||||||
|
output_str = std::string(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string format_obj(lua_State *L, int pos)
|
||||||
|
{
|
||||||
|
std::string output;
|
||||||
|
|
||||||
|
pos = lua_absindex(L, pos);
|
||||||
|
|
||||||
|
/* Look up the FORMAT_TABLE function from nse_main.lua and call it. */
|
||||||
|
lua_getfield(L, LUA_REGISTRYINDEX, NSE_FORMAT_TABLE);
|
||||||
|
if (lua_isnil(L, -1)) {
|
||||||
|
log_write(LOG_STDOUT, "%s: Cannot find function _R[\"%s\"] that should be in nse_main.lua\n",
|
||||||
|
SCRIPT_ENGINE, NSE_FORMAT_TABLE);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pushvalue(L, pos);
|
||||||
|
if (lua_pcall(L, 1, 1, 0) != 0) {
|
||||||
|
if (o.debugging)
|
||||||
|
log_write(LOG_STDOUT, "%s: Error in FORMAT_TABLE: %s\n", SCRIPT_ENGINE, lua_tostring(L, -1));
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
output = std::string(lua_tostring(L, -1));
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ScriptResult::get_output_str (void) const
|
||||||
|
{
|
||||||
|
std::string output;
|
||||||
|
|
||||||
|
/* Explicit string output? */
|
||||||
|
if (!output_str.empty())
|
||||||
|
return output_str;
|
||||||
|
|
||||||
|
/* Auto-formatted table output? */
|
||||||
|
lua_rawgeti(L_NSE, LUA_REGISTRYINDEX, output_ref);
|
||||||
|
if (!lua_isnil(L_NSE, -1))
|
||||||
|
output = format_obj(L_NSE, -1);
|
||||||
|
|
||||||
|
lua_pop(L_NSE, 1);
|
||||||
|
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptResult::set_id (const char *ident)
|
void ScriptResult::set_id (const char *ident)
|
||||||
@@ -381,12 +466,50 @@ ScriptResults *get_script_scan_results_obj (void)
|
|||||||
return &script_scan_results;
|
return &script_scan_results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void format_xml(lua_State *L, int pos)
|
||||||
|
{
|
||||||
|
pos = lua_absindex(L, pos);
|
||||||
|
|
||||||
|
/* Look up the FORMAT_XML function from nse_main.lua and call it. */
|
||||||
|
lua_getfield(L, LUA_REGISTRYINDEX, NSE_FORMAT_XML);
|
||||||
|
if (lua_isnil(L, -1)) {
|
||||||
|
log_write(LOG_STDOUT, "%s: Cannot find function _R[\"%s\"] that should be in nse_main.lua\n",
|
||||||
|
SCRIPT_ENGINE, NSE_FORMAT_XML);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pushvalue(L, pos);
|
||||||
|
if (lua_pcall(L, 1, 1, 0) != 0) {
|
||||||
|
if (o.debugging)
|
||||||
|
log_write(LOG_STDOUT, "%s: Error in FORMAT_XML: %s\n", SCRIPT_ENGINE, lua_tostring(L, -1));
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ScriptResult::write_xml() const
|
void ScriptResult::write_xml() const
|
||||||
{
|
{
|
||||||
|
std::string output_str;
|
||||||
|
|
||||||
xml_open_start_tag("script");
|
xml_open_start_tag("script");
|
||||||
xml_attribute("id", "%s", get_id());
|
xml_attribute("id", "%s", get_id());
|
||||||
xml_attribute("output", "%s", get_output());
|
|
||||||
xml_close_empty_tag();
|
output_str = get_output_str();
|
||||||
|
if (!output_str.empty())
|
||||||
|
xml_attribute("output", "%s", protect_xml(output_str).c_str());
|
||||||
|
|
||||||
|
/* Any table output? */
|
||||||
|
lua_rawgeti(L_NSE, LUA_REGISTRYINDEX, output_ref);
|
||||||
|
if (!lua_isnil(L_NSE, -1)) {
|
||||||
|
xml_close_start_tag();
|
||||||
|
format_xml(L_NSE, -1);
|
||||||
|
xml_end_tag();
|
||||||
|
} else {
|
||||||
|
xml_close_empty_tag();
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pop(L_NSE, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* int panic (lua_State *L)
|
/* int panic (lua_State *L)
|
||||||
@@ -625,9 +748,6 @@ void nse_gettarget (lua_State *L, int index)
|
|||||||
lua_replace(L, -2);
|
lua_replace(L, -2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Global persistent Lua state used by the engine. */
|
|
||||||
static lua_State *L_NSE = NULL;
|
|
||||||
|
|
||||||
void open_nse (void)
|
void open_nse (void)
|
||||||
{
|
{
|
||||||
if (L_NSE == NULL)
|
if (L_NSE == NULL)
|
||||||
|
|||||||
15
nse_main.h
15
nse_main.h
@@ -19,11 +19,20 @@ extern "C" {
|
|||||||
class ScriptResult
|
class ScriptResult
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::string output;
|
|
||||||
std::string id;
|
std::string id;
|
||||||
|
/* Structured output table, an integer ref in L_NSE[LUA_REGISTRYINDEX]. */
|
||||||
|
int output_ref;
|
||||||
|
/* Unstructured output string, for scripts that do not return a structured
|
||||||
|
table, or return a string in addition to a table. */
|
||||||
|
std::string output_str;
|
||||||
public:
|
public:
|
||||||
void set_output (const char *);
|
ScriptResult() {
|
||||||
const char *get_output (void) const;
|
output_ref = LUA_NOREF;
|
||||||
|
}
|
||||||
|
void clear (void);
|
||||||
|
void set_output_tab (lua_State *, int);
|
||||||
|
void set_output_str (const char *);
|
||||||
|
std::string get_output_str (void) const;
|
||||||
void set_id (const char *);
|
void set_id (const char *);
|
||||||
const char *get_id (void) const;
|
const char *get_id (void) const;
|
||||||
void write_xml() const;
|
void write_xml() const;
|
||||||
|
|||||||
111
nse_main.lua
111
nse_main.lua
@@ -43,6 +43,8 @@ local BASE = "NSE_BASE";
|
|||||||
local WAITING_TO_RUNNING = "NSE_WAITING_TO_RUNNING";
|
local WAITING_TO_RUNNING = "NSE_WAITING_TO_RUNNING";
|
||||||
local DESTRUCTOR = "NSE_DESTRUCTOR";
|
local DESTRUCTOR = "NSE_DESTRUCTOR";
|
||||||
local SELECTED_BY_NAME = "NSE_SELECTED_BY_NAME";
|
local SELECTED_BY_NAME = "NSE_SELECTED_BY_NAME";
|
||||||
|
local FORMAT_TABLE = "NSE_FORMAT_TABLE";
|
||||||
|
local FORMAT_XML = "NSE_FORMAT_XML";
|
||||||
|
|
||||||
-- This is a limit on the number of script instance threads running at once. It
|
-- This is a limit on the number of script instance threads running at once. It
|
||||||
-- exists only to limit memory use when there are many open ports. It doesn't
|
-- exists only to limit memory use when there are many open ports. It doesn't
|
||||||
@@ -283,14 +285,25 @@ do
|
|||||||
print_debug(1, fmt, ...);
|
print_debug(1, fmt, ...);
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Sets scripts output. Variable result is a string.
|
-- Sets script output. r1 and r2 are the (as many as two) return values.
|
||||||
function Thread:set_output(result)
|
function Thread:set_output(r1, r2)
|
||||||
|
-- Structure table and unstructured string outputs.
|
||||||
|
local tab, str
|
||||||
|
|
||||||
|
if r2 then
|
||||||
|
tab, str = r1, r2;
|
||||||
|
elseif type(r1) == "string" then
|
||||||
|
tab, str = nil, r1;
|
||||||
|
else
|
||||||
|
tab, str = r1, nil;
|
||||||
|
end
|
||||||
|
|
||||||
if self.type == "prerule" or self.type == "postrule" then
|
if self.type == "prerule" or self.type == "postrule" then
|
||||||
cnse.script_set_output(self.id, result);
|
cnse.script_set_output(self.id, tab, str);
|
||||||
elseif self.type == "hostrule" then
|
elseif self.type == "hostrule" then
|
||||||
cnse.host_set_output(self.host, self.id, result);
|
cnse.host_set_output(self.host, self.id, tab, str);
|
||||||
elseif self.type == "portrule" then
|
elseif self.type == "portrule" then
|
||||||
cnse.port_set_output(self.host, self.port, self.id, result);
|
cnse.port_set_output(self.host, self.port, self.id, tab, str);
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -902,18 +915,19 @@ local function run (threads_iter, hosts)
|
|||||||
current, running[co] = thread, nil;
|
current, running[co] = thread, nil;
|
||||||
thread:start_time_out_clock();
|
thread:start_time_out_clock();
|
||||||
|
|
||||||
local s, result = resume(co, unpack(thread.args, 1, thread.args.n));
|
-- Threads may have zero, one, or two return values.
|
||||||
|
local s, r1, r2 = resume(co, unpack(thread.args, 1, thread.args.n));
|
||||||
if not s then -- script error...
|
if not s then -- script error...
|
||||||
all[co], num_threads = nil, num_threads-1;
|
all[co], num_threads = nil, num_threads-1;
|
||||||
if debugging() > 0 then
|
if debugging() > 0 then
|
||||||
thread:d("%THREAD_AGAINST threw an error!\n%s\n",
|
thread:d("%THREAD_AGAINST threw an error!\n%s\n",
|
||||||
traceback(co, tostring(result)));
|
traceback(co, tostring(r1)));
|
||||||
else
|
else
|
||||||
thread:set_output("ERROR: Script execution failed (use -d to debug)");
|
thread:set_output("ERROR: Script execution failed (use -d to debug)");
|
||||||
end
|
end
|
||||||
thread:close(timeouts, result);
|
thread:close(timeouts, r1);
|
||||||
elseif status(co) == "suspended" then
|
elseif status(co) == "suspended" then
|
||||||
if result == NSE_YIELD_VALUE then
|
if r1 == NSE_YIELD_VALUE then
|
||||||
waiting[co] = thread;
|
waiting[co] = thread;
|
||||||
else
|
else
|
||||||
all[co], num_threads = nil, num_threads-1;
|
all[co], num_threads = nil, num_threads-1;
|
||||||
@@ -922,15 +936,7 @@ local function run (threads_iter, hosts)
|
|||||||
end
|
end
|
||||||
elseif status(co) == "dead" then
|
elseif status(co) == "dead" then
|
||||||
all[co], num_threads = nil, num_threads-1;
|
all[co], num_threads = nil, num_threads-1;
|
||||||
if type(result) == "string" then
|
thread:set_output(r1, r2);
|
||||||
-- Escape any character outside the range 32-126 except for tab,
|
|
||||||
-- carriage return, and line feed. This makes the string safe for
|
|
||||||
-- screen display as well as XML (see section 2.2 of the XML spec).
|
|
||||||
result = gsub(result, "[^\t\r\n\032-\126]", function(a)
|
|
||||||
return format("\\x%02X", byte(a));
|
|
||||||
end);
|
|
||||||
thread:set_output(result);
|
|
||||||
end
|
|
||||||
thread:d("Finished %THREAD_AGAINST.");
|
thread:d("Finished %THREAD_AGAINST.");
|
||||||
thread:close(timeouts);
|
thread:close(timeouts);
|
||||||
end
|
end
|
||||||
@@ -949,6 +955,75 @@ local function run (threads_iter, hosts)
|
|||||||
progress "endTask";
|
progress "endTask";
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- This function does the automatic formatting of Lua objects into strings, for
|
||||||
|
-- normal output and for the XML @output attribute. Each nested table is
|
||||||
|
-- indented by two spaces. Tables having a __tostring metamethod are converted
|
||||||
|
-- using tostring. Otherwise, integer keys are listed first and only their
|
||||||
|
-- value is shown; then string keys are shown prefixed by the key and a colon.
|
||||||
|
-- Any other kinds of keys. Anything that is not a table is converted to a
|
||||||
|
-- string with tostring.
|
||||||
|
local function format_table(obj, indent)
|
||||||
|
indent = indent or " ";
|
||||||
|
if type(obj) == "table" then
|
||||||
|
local mt = getmetatable(obj)
|
||||||
|
if mt and mt["__tostring"] then
|
||||||
|
-- Table obeys tostring, so use that.
|
||||||
|
return tostring(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
local lines = {};
|
||||||
|
-- Do integer keys.
|
||||||
|
for _, v in ipairs(obj) do
|
||||||
|
lines[#lines + 1] = indent .. format_table(v, indent .. " ");
|
||||||
|
end
|
||||||
|
-- Do string keys.
|
||||||
|
for k, v in pairs(obj) do
|
||||||
|
if type(k) == "string" then
|
||||||
|
lines[#lines + 1] = indent .. k .. ": " .. format_table(v, indent .. " ");
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return "\n" .. concat(lines, "\n");
|
||||||
|
else
|
||||||
|
return tostring(obj);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
_R[FORMAT_TABLE] = format_table
|
||||||
|
|
||||||
|
local format_xml
|
||||||
|
local function format_xml_elem(obj, key)
|
||||||
|
if key then
|
||||||
|
key = cnse.protect_xml(tostring(key));
|
||||||
|
end
|
||||||
|
if type(obj) == "table" then
|
||||||
|
cnse.xml_start_tag("table", {key=key});
|
||||||
|
cnse.xml_newline();
|
||||||
|
else
|
||||||
|
cnse.xml_start_tag("elem", {key=key});
|
||||||
|
end
|
||||||
|
format_xml(obj);
|
||||||
|
cnse.xml_end_tag();
|
||||||
|
cnse.xml_newline();
|
||||||
|
end
|
||||||
|
|
||||||
|
-- This function writes an XML representation of a Lua object to the XML stream.
|
||||||
|
function format_xml(obj, key)
|
||||||
|
if type(obj) == "table" then
|
||||||
|
-- Do integer keys.
|
||||||
|
for _, v in ipairs(obj) do
|
||||||
|
format_xml_elem(v);
|
||||||
|
end
|
||||||
|
-- Do string keys.
|
||||||
|
for k, v in pairs(obj) do
|
||||||
|
if type(k) == "string" then
|
||||||
|
format_xml_elem(v, k);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
cnse.xml_write_escaped(cnse.protect_xml(tostring(obj)));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
_R[FORMAT_XML] = format_xml
|
||||||
|
|
||||||
-- Format NSEDoc markup (e.g., including bullet lists and <code> sections) into
|
-- Format NSEDoc markup (e.g., including bullet lists and <code> sections) into
|
||||||
-- a display string at the given indentation level. Currently this only indents
|
-- a display string at the given indentation level. Currently this only indents
|
||||||
-- the string and doesn't interpret any other markup.
|
-- the string and doesn't interpret any other markup.
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
-- @name stdnse
|
-- @name stdnse
|
||||||
|
|
||||||
local _G = require "_G"
|
local _G = require "_G"
|
||||||
|
local coroutine = require "coroutine"
|
||||||
local math = require "math"
|
local math = require "math"
|
||||||
local nmap = require "nmap"
|
local nmap = require "nmap"
|
||||||
local os = require "os"
|
local os = require "os"
|
||||||
@@ -17,6 +18,7 @@ local error = error;
|
|||||||
local getmetatable = getmetatable;
|
local getmetatable = getmetatable;
|
||||||
local ipairs = ipairs
|
local ipairs = ipairs
|
||||||
local pairs = pairs
|
local pairs = pairs
|
||||||
|
local rawset = rawset
|
||||||
local require = require;
|
local require = require;
|
||||||
local select = select
|
local select = select
|
||||||
local setmetatable = setmetatable;
|
local setmetatable = setmetatable;
|
||||||
@@ -1032,4 +1034,50 @@ function seeall (env)
|
|||||||
setmetatable(env, m);
|
setmetatable(env, m);
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Return a table that keeps elements in order of insertion.
|
||||||
|
--
|
||||||
|
-- The pairs function, called on a table returned by this function, will yield
|
||||||
|
-- elements in the order they were inserted. This function is meant to be used
|
||||||
|
-- to construct output tables returned by scripts.
|
||||||
|
--
|
||||||
|
-- Reinserting a key that is already in the table does not change its position
|
||||||
|
-- in the order. However, removing a key by assigning to <code>nil</code> and
|
||||||
|
-- then doing another assignment will move the key to the end of the order.
|
||||||
|
--
|
||||||
|
-- @return An ordered table.
|
||||||
|
function output_table ()
|
||||||
|
local t = {}
|
||||||
|
local reverse = {}
|
||||||
|
local order = {}
|
||||||
|
local function iterator ()
|
||||||
|
for i, key in ipairs(order) do
|
||||||
|
coroutine.yield(key, t[key])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local mt = {
|
||||||
|
__newindex = function (_, k, v)
|
||||||
|
if reverse[k] then
|
||||||
|
rawset(t, k, v)
|
||||||
|
if v == nil then
|
||||||
|
table.remove(order, reverse[k])
|
||||||
|
reverse[k] = nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if v ~= nil then
|
||||||
|
table.insert(order, k)
|
||||||
|
reverse[k] = #order
|
||||||
|
end
|
||||||
|
rawset(t, k, v)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
__index = function (_, k)
|
||||||
|
return t[k]
|
||||||
|
end,
|
||||||
|
__pairs = function (_)
|
||||||
|
return coroutine.wrap(iterator)
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
return setmetatable({}, mt)
|
||||||
|
end
|
||||||
|
|
||||||
return _ENV;
|
return _ENV;
|
||||||
|
|||||||
54
output.cc
54
output.cc
@@ -425,16 +425,46 @@ int print_iflist(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NOLUA
|
#ifndef NOLUA
|
||||||
|
/* Escape control characters to make a string safe to display on a terminal. */
|
||||||
|
static std::string escape_for_screen(const std::string s) {
|
||||||
|
std::string r;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < s.size(); i++) {
|
||||||
|
char buf[5];
|
||||||
|
unsigned char c = s[i];
|
||||||
|
if (c == '\t' || c == '\r' || c == '\n' || (0x20 <= c && c <= 0x7e)) {
|
||||||
|
r += c;
|
||||||
|
} else {
|
||||||
|
Snprintf(buf, sizeof(buf), "\\x%02X", c);
|
||||||
|
r += buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do something to protect characters that can't appear in XML. This is not a
|
||||||
|
reversible transform, more a last-ditch effort to write readable XML with
|
||||||
|
characters that shouldn't be part of regular output anyway. The escaping that
|
||||||
|
xml_write_escaped is not enough; some characters are not allowed to appear in
|
||||||
|
XML, not even escaped. */
|
||||||
|
std::string protect_xml(const std::string s) {
|
||||||
|
/* escape_for_screen is good enough. */
|
||||||
|
return escape_for_screen(s);
|
||||||
|
}
|
||||||
|
|
||||||
static char *formatScriptOutput(ScriptResult sr) {
|
static char *formatScriptOutput(ScriptResult sr) {
|
||||||
std::vector<std::string> lines;
|
std::vector<std::string> lines;
|
||||||
|
|
||||||
const char *c_output;
|
std::string c_output;
|
||||||
const char *p, *q;
|
const char *p, *q;
|
||||||
std::string result;
|
std::string result;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
c_output = sr.get_output();
|
c_output = escape_for_screen(sr.get_output_str());
|
||||||
p = c_output;
|
if (c_output.empty())
|
||||||
|
return NULL;
|
||||||
|
p = c_output.c_str();
|
||||||
|
|
||||||
while (*p != '\0') {
|
while (*p != '\0') {
|
||||||
q = strchr(p, '\n');
|
q = strchr(p, '\n');
|
||||||
@@ -798,8 +828,10 @@ void printportoutput(Target *currenths, PortList *plist) {
|
|||||||
ssr_iter->write_xml();
|
ssr_iter->write_xml();
|
||||||
|
|
||||||
char *script_output = formatScriptOutput((*ssr_iter));
|
char *script_output = formatScriptOutput((*ssr_iter));
|
||||||
Tbl->addItem(rowno, 0, true, true, script_output);
|
if (script_output != NULL) {
|
||||||
free(script_output);
|
Tbl->addItem(rowno, 0, true, true, script_output);
|
||||||
|
free(script_output);
|
||||||
|
}
|
||||||
rowno++;
|
rowno++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2198,8 +2230,10 @@ void printscriptresults(ScriptResults *scriptResults, stype scantype) {
|
|||||||
iter->write_xml();
|
iter->write_xml();
|
||||||
|
|
||||||
script_output = formatScriptOutput((*iter));
|
script_output = formatScriptOutput((*iter));
|
||||||
log_write(LOG_PLAIN, "%s\n", script_output);
|
if (script_output != NULL) {
|
||||||
free(script_output);
|
log_write(LOG_PLAIN, "%s\n", script_output);
|
||||||
|
free(script_output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
xml_end_tag();
|
xml_end_tag();
|
||||||
}
|
}
|
||||||
@@ -2220,8 +2254,10 @@ void printhostscriptresults(Target *currenths) {
|
|||||||
iter->write_xml();
|
iter->write_xml();
|
||||||
|
|
||||||
script_output = formatScriptOutput((*iter));
|
script_output = formatScriptOutput((*iter));
|
||||||
log_write(LOG_PLAIN, "%s\n", script_output);
|
if (script_output != NULL) {
|
||||||
free(script_output);
|
log_write(LOG_PLAIN, "%s\n", script_output);
|
||||||
|
free(script_output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
xml_end_tag();
|
xml_end_tag();
|
||||||
}
|
}
|
||||||
|
|||||||
2
output.h
2
output.h
@@ -220,6 +220,8 @@ void printosscanoutput(Target *currenths);
|
|||||||
void printserviceinfooutput(Target *currenths);
|
void printserviceinfooutput(Target *currenths);
|
||||||
|
|
||||||
#ifndef NOLUA
|
#ifndef NOLUA
|
||||||
|
std::string protect_xml(const std::string s);
|
||||||
|
|
||||||
/* Use this function to report NSE_PRE_SCAN and NSE_POST_SCAN results */
|
/* Use this function to report NSE_PRE_SCAN and NSE_POST_SCAN results */
|
||||||
void printscriptresults(ScriptResults *scriptResults, stype scantype);
|
void printscriptresults(ScriptResults *scriptResults, stype scantype);
|
||||||
|
|
||||||
|
|||||||
11
portlist.cc
11
portlist.cc
@@ -147,6 +147,14 @@ void Port::freeService(bool del_service) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Port::freeScriptResults(void)
|
||||||
|
{
|
||||||
|
while (!scriptResults.empty()) {
|
||||||
|
scriptResults.front().clear();
|
||||||
|
scriptResults.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Fills in namebuf (as long as there is space in buflen) with the
|
/* Fills in namebuf (as long as there is space in buflen) with the
|
||||||
Name nmap normal output will use to describe the port. This takes
|
Name nmap normal output will use to describe the port. This takes
|
||||||
into account to confidence level, any SSL tunneling, etc. Truncates
|
into account to confidence level, any SSL tunneling, etc. Truncates
|
||||||
@@ -508,6 +516,9 @@ PortList::~PortList() {
|
|||||||
for(i=0; i < port_list_count[proto]; i++) { // free every Port
|
for(i=0; i < port_list_count[proto]; i++) { // free every Port
|
||||||
if(port_list[proto][i]) {
|
if(port_list[proto][i]) {
|
||||||
port_list[proto][i]->freeService(true);
|
port_list[proto][i]->freeService(true);
|
||||||
|
#ifndef NOLUA
|
||||||
|
port_list[proto][i]->freeScriptResults();
|
||||||
|
#endif
|
||||||
delete port_list[proto][i];
|
delete port_list[proto][i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ class Port {
|
|||||||
public:
|
public:
|
||||||
Port();
|
Port();
|
||||||
void freeService(bool del_service);
|
void freeService(bool del_service);
|
||||||
|
void freeScriptResults(void);
|
||||||
void getNmapServiceName(char *namebuf, int buflen, const char *rpcinfo) const;
|
void getNmapServiceName(char *namebuf, int buflen, const char *rpcinfo) const;
|
||||||
|
|
||||||
u16 portno;
|
u16 portno;
|
||||||
|
|||||||
@@ -13,9 +13,13 @@ sent, so the difference includes at least the duration of one RTT.
|
|||||||
---
|
---
|
||||||
-- @output
|
-- @output
|
||||||
-- 80/tcp open http
|
-- 80/tcp open http
|
||||||
-- |_ http-date: Thu, 23 Jul 2009 23:15:57 GMT; -6s from local time.
|
-- |_http-date: Thu, 02 Aug 2012 22:11:03 GMT; 0s from local time.
|
||||||
-- 80/tcp open http
|
-- 80/tcp open http
|
||||||
-- |_ http-date: Wed, 17 Jan 2007 09:29:10 GMT; -2y187d13h46m53s from local time.
|
-- |_http-date: Thu, 02 Aug 2012 22:07:12 GMT; -3m51s from local time.
|
||||||
|
--
|
||||||
|
-- @xmloutput
|
||||||
|
-- <elem key="date">2012-08-02T23:07:12Z</elem>
|
||||||
|
-- <elem key="delta">-231</elem>
|
||||||
|
|
||||||
author = "David Fifield"
|
author = "David Fifield"
|
||||||
|
|
||||||
@@ -39,9 +43,13 @@ action = function(host, port)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Should account for estimated RTT too.
|
local output_tab = stdnse.output_table()
|
||||||
local diff = stdnse.format_difftime(response_date, request_date)
|
-- ISO 8601 date and time.
|
||||||
|
output_tab.date = os.date("%Y-%m-%dT%H:%M:%SZ", os.time(response_date))
|
||||||
|
output_tab.delta = os.difftime(os.time(response_date), os.time(request_date))
|
||||||
|
|
||||||
return string.format("%s; %s from local time.",
|
local output_str = string.format("%s; %s from local time.",
|
||||||
response.header["date"], diff)
|
response.header["date"], stdnse.format_difftime(response_date, request_date))
|
||||||
|
|
||||||
|
return output_tab, output_str
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ original target.
|
|||||||
-- PORT STATE SERVICE
|
-- PORT STATE SERVICE
|
||||||
-- 80/tcp open http
|
-- 80/tcp open http
|
||||||
-- |_http-title: Go ahead and ScanMe!
|
-- |_http-title: Go ahead and ScanMe!
|
||||||
|
--
|
||||||
|
-- @xmloutput
|
||||||
|
-- <elem key="title">Go ahead and ScanMe!</elem>
|
||||||
|
-- @xmloutput
|
||||||
|
-- <elem key="title">Wikipedia, the free encyclopedia</elem>
|
||||||
|
-- <elem key="redirect_url">http://en.wikipedia.org/wiki/Main_Page</elem>
|
||||||
|
|
||||||
author = "Diman Todorov"
|
author = "Diman Todorov"
|
||||||
|
|
||||||
@@ -41,7 +47,7 @@ action = function(host, port)
|
|||||||
if resp.location then
|
if resp.location then
|
||||||
redirect_url = resp.location[#resp.location]
|
redirect_url = resp.location[#resp.location]
|
||||||
if resp.status and tostring( resp.status ):match( "30%d" ) then
|
if resp.status and tostring( resp.status ):match( "30%d" ) then
|
||||||
return ("Did not follow redirect to %s"):format( redirect_url )
|
return {redirect_url = redirect_url}, ("Did not follow redirect to %s"):format( redirect_url )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -64,10 +70,14 @@ action = function(host, port)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local output_tab = stdnse.output_table()
|
||||||
|
output_tab.title = title
|
||||||
|
output_tab.redirect_url = redirect_url
|
||||||
|
|
||||||
local output_str = display_title
|
local output_str = display_title
|
||||||
if redirect_url then
|
if redirect_url then
|
||||||
output_str = output_str .. "\n" .. ("Requested resource was %s"):format( redirect_url )
|
output_str = output_str .. "\n" .. ("Requested resource was %s"):format( redirect_url )
|
||||||
end
|
end
|
||||||
|
|
||||||
return output_str
|
return output_tab, output_str
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,24 +13,26 @@ organizationName, stateOrProvinceName, and countryName of the subject.
|
|||||||
|
|
||||||
<code>
|
<code>
|
||||||
443/tcp open https
|
443/tcp open https
|
||||||
| ssl-cert: Subject: commonName=www.paypal.com/organizationName=PayPal, Inc.\
|
| ssl-cert: Subject: commonName=www.paypal.com/organizationName=PayPal, Inc.\
|
||||||
/stateOrProvinceName=California/countryName=US
|
/stateOrProvinceName=California/countryName=US
|
||||||
| Not valid before: 2009-05-28 00:00:00
|
| Not valid before: 2011-03-23 00:00:00
|
||||||
|_ Not valid after: 2010-05-01 23:59:59
|
|_Not valid after: 2013-04-01 23:59:59
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
With <code>-v</code> it adds the issuer name and fingerprints.
|
With <code>-v</code> it adds the issuer name and fingerprints.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
443/tcp open https
|
443/tcp open https
|
||||||
| ssl-cert: Subject: commonName=www.paypal.com/organizationName=PayPal, Inc.\
|
| ssl-cert: Subject: commonName=www.paypal.com/organizationName=PayPal, Inc.\
|
||||||
/stateOrProvinceName=California/countryName=US
|
/stateOrProvinceName=California/countryName=US
|
||||||
| Issuer: commonName=VeriSign Class 3 Extended Validation SSL CA\
|
| Issuer: commonName=VeriSign Class 3 Extended Validation SSL CA\
|
||||||
/organizationName=VeriSign, Inc./countryName=US
|
/organizationName=VeriSign, Inc./countryName=US
|
||||||
| Not valid before: 2009-05-28 00:00:00
|
| Public Key type: rsa
|
||||||
| Not valid after: 2010-05-01 23:59:59
|
| Public Key bits: 2048
|
||||||
| MD5: c5b8 7ddd ccc7 537f 8861 b476 078d e8fd
|
| Not valid before: 2011-03-23 00:00:00
|
||||||
|_ SHA-1: dc5a cb8b 9eb9 b5de 7117 c536 8c15 0e75 ba88 702e
|
| Not valid after: 2013-04-01 23:59:59
|
||||||
|
| MD5: bf47 ceca d861 efa7 7d14 88ad 4a73 cb5b
|
||||||
|
|_SHA-1: d846 5221 467a 0d15 3df0 9f2e af6d 4390 0213 9a68
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
With <code>-vv</code> it adds the PEM-encoded contents of the entire
|
With <code>-vv</code> it adds the PEM-encoded contents of the entire
|
||||||
@@ -38,32 +40,73 @@ certificate.
|
|||||||
|
|
||||||
<code>
|
<code>
|
||||||
443/tcp open https
|
443/tcp open https
|
||||||
| ssl-cert: Subject: commonName=www.paypal.com/organizationName=PayPal, Inc.\
|
| ssl-cert: Subject: commonName=www.paypal.com/organizationName=PayPal, Inc.\
|
||||||
/stateOrProvinceName=California/countryName=US/serialNumber=3014267\
|
/stateOrProvinceName=California/countryName=US/1.3.6.1.4.1.311.60.2.1.2=Delaware\
|
||||||
/1.3.6.1.4.1.311.60.2.1.3=US/streetAddress=2211 N 1st St\
|
/postalCode=95131-2021/localityName=San Jose/serialNumber=3014267\
|
||||||
/1.3.6.1.4.1.311.60.2.1.2=Delaware/postalCode=95131-2021\
|
/streetAddress=2211 N 1st St/1.3.6.1.4.1.311.60.2.1.3=US\
|
||||||
/localityName=San Jose/organizationalUnitName=Information Systems\
|
/organizationalUnitName=PayPal Production/businessCategory=Private Organization
|
||||||
/2.5.4.15=V1.0, Clause 5.(b)
|
| Issuer: commonName=VeriSign Class 3 Extended Validation SSL CA\
|
||||||
| Issuer: commonName=VeriSign Class 3 Extended Validation SSL CA\
|
|
||||||
/organizationName=VeriSign, Inc./countryName=US\
|
/organizationName=VeriSign, Inc./countryName=US\
|
||||||
/organizationalUnitName=Terms of use at https://www.verisign.com/rpa (c)06
|
/organizationalUnitName=Terms of use at https://www.verisign.com/rpa (c)06
|
||||||
| Not valid before: 2009-05-28 00:00:00
|
| Public Key type: rsa
|
||||||
| Not valid after: 2010-05-01 23:59:59
|
| Public Key bits: 2048
|
||||||
| MD5: c5b8 7ddd ccc7 537f 8861 b476 078d e8fd
|
| Not valid before: 2011-03-23 00:00:00
|
||||||
| SHA-1: dc5a cb8b 9eb9 b5de 7117 c536 8c15 0e75 ba88 702e
|
| Not valid after: 2013-04-01 23:59:59
|
||||||
| -----BEGIN CERTIFICATE-----
|
| MD5: bf47 ceca d861 efa7 7d14 88ad 4a73 cb5b
|
||||||
| MIIFxzCCBK+gAwIBAgIQX02QuADDB7CVjZdooVge+zANBgkqhkiG9w0BAQUFADCB
|
| SHA-1: d846 5221 467a 0d15 3df0 9f2e af6d 4390 0213 9a68
|
||||||
|
| -----BEGIN CERTIFICATE-----
|
||||||
|
| MIIGSzCCBTOgAwIBAgIQLjOHT2/i1B7T//819qTJGDANBgkqhkiG9w0BAQUFADCB
|
||||||
...
|
...
|
||||||
|
| 9YDR12XLZeQjO1uiunCsJkDIf9/5Mqpu57pw8v1QNA==
|
||||||
|
|_-----END CERTIFICATE-----
|
||||||
</code>
|
</code>
|
||||||
]]
|
]]
|
||||||
|
|
||||||
---
|
---
|
||||||
-- @output
|
-- @output
|
||||||
-- 443/tcp open https
|
-- 443/tcp open https
|
||||||
-- | ssl-cert: Subject: commonName=www.paypal.com/organizationName=PayPal, Inc.\
|
-- | ssl-cert: Subject: commonName=www.paypal.com/organizationName=PayPal, Inc.\
|
||||||
-- /stateOrProvinceName=California/countryName=US
|
-- /stateOrProvinceName=California/countryName=US
|
||||||
-- | Not valid before: 2009-05-28 00:00:00
|
-- | Not valid before: 2011-03-23 00:00:00
|
||||||
-- |_ Not valid after: 2010-05-01 23:59:59
|
-- |_Not valid after: 2013-04-01 23:59:59
|
||||||
|
--
|
||||||
|
-- @xmloutput
|
||||||
|
-- <table key="subject">
|
||||||
|
-- <elem key="1.3.6.1.4.1.311.60.2.1.2">Delaware</elem>
|
||||||
|
-- <elem key="1.3.6.1.4.1.311.60.2.1.3">US</elem>
|
||||||
|
-- <elem key="postalCode">95131-2021</elem>
|
||||||
|
-- <elem key="localityName">San Jose</elem>
|
||||||
|
-- <elem key="serialNumber">3014267</elem>
|
||||||
|
-- <elem key="countryName">US</elem>
|
||||||
|
-- <elem key="stateOrProvinceName">California</elem>
|
||||||
|
-- <elem key="streetAddress">2211 N 1st St</elem>
|
||||||
|
-- <elem key="organizationalUnitName">PayPal Production</elem>
|
||||||
|
-- <elem key="commonName">www.paypal.com</elem>
|
||||||
|
-- <elem key="organizationName">PayPal, Inc.</elem>
|
||||||
|
-- <elem key="businessCategory">Private Organization</elem>
|
||||||
|
-- </table>
|
||||||
|
-- <table key="issuer">
|
||||||
|
-- <elem key="organizationalUnitName">Terms of use at https://www.verisign.com/rpa (c)06</elem>
|
||||||
|
-- <elem key="organizationName">VeriSign, Inc.</elem>
|
||||||
|
-- <elem key="commonName">VeriSign Class 3 Extended Validation SSL CA</elem>
|
||||||
|
-- <elem key="countryName">US</elem>
|
||||||
|
-- </table>
|
||||||
|
-- <table key="pubkey">
|
||||||
|
-- <elem key="type">rsa</elem>
|
||||||
|
-- <elem key="bits">2048</elem>
|
||||||
|
-- </table>
|
||||||
|
-- <table key="validity">
|
||||||
|
-- <elem key="notBefore">2011-03-23T00:00:00Z</elem>
|
||||||
|
-- <elem key="notAfter">2013-04-01T23:59:59Z</elem>
|
||||||
|
-- </table>
|
||||||
|
-- <elem key="md5">bf47cecad861efa77d1488ad4a73cb5b</elem>
|
||||||
|
-- <elem key="sha1">d8465221467a0d153df09f2eaf6d439002139a68</elem>
|
||||||
|
-- <elem key="pem">-----BEGIN CERTIFICATE-----
|
||||||
|
-- MIIGSzCCBTOgAwIBAgIQLjOHT2/i1B7T//819qTJGDANBgkqhkiG9w0BAQUFADCB
|
||||||
|
-- ...
|
||||||
|
-- 9YDR12XLZeQjO1uiunCsJkDIf9/5Mqpu57pw8v1QNA==
|
||||||
|
-- -----END CERTIFICATE-----
|
||||||
|
-- </elem>
|
||||||
|
|
||||||
author = "David Fifield"
|
author = "David Fifield"
|
||||||
|
|
||||||
@@ -129,7 +172,37 @@ function stringify_name(name)
|
|||||||
return stdnse.strjoin("/", fields)
|
return stdnse.strjoin("/", fields)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function parseCertificate(cert)
|
local function name_to_table(name)
|
||||||
|
local output = {}
|
||||||
|
for k, v in pairs(name) do
|
||||||
|
if type(k) == "table" then
|
||||||
|
k = stdnse.strjoin(".", k)
|
||||||
|
end
|
||||||
|
output[k] = v
|
||||||
|
end
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
local function format_time(t)
|
||||||
|
return os.date("%Y-%m-%dT%H:%M:%SZ", os.time(t))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function output_tab(cert)
|
||||||
|
local o = stdnse.output_table()
|
||||||
|
o.subject = name_to_table(cert.subject)
|
||||||
|
o.issuer = name_to_table(cert.issuer)
|
||||||
|
o.pubkey = cert.pubkey
|
||||||
|
o.validity = {}
|
||||||
|
for k, v in pairs(cert.validity) do
|
||||||
|
o.validity[k] = format_time(v)
|
||||||
|
end
|
||||||
|
o.md5 = stdnse.tohex(cert:digest("md5"))
|
||||||
|
o.sha1 = stdnse.tohex(cert:digest("sha1"))
|
||||||
|
o.pem = cert.pem
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
local function output_str(cert)
|
||||||
local lines = {}
|
local lines = {}
|
||||||
|
|
||||||
lines[#lines + 1] = "Subject: " .. stringify_name(cert.subject)
|
lines[#lines + 1] = "Subject: " .. stringify_name(cert.subject)
|
||||||
@@ -156,7 +229,7 @@ local function parseCertificate(cert)
|
|||||||
if nmap.verbosity() > 1 then
|
if nmap.verbosity() > 1 then
|
||||||
lines[#lines + 1] = cert.pem
|
lines[#lines + 1] = cert.pem
|
||||||
end
|
end
|
||||||
return lines
|
return stdnse.strjoin("\n", lines)
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
@@ -164,10 +237,8 @@ action = function(host, port)
|
|||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local lines = parseCertificate(cert)
|
|
||||||
|
|
||||||
return stdnse.strjoin("\n", lines)
|
return output_tab(cert), output_str(cert)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user