Nmap Development mailing list archives
Re: Nmap-NSE Release candidate 2
From: doug () hcsw org
Date: Wed, 9 Aug 2006 05:05:51 -0700
Hi Diman! On Mon, Aug 07, 2006 at 02:53:16PM +0200 or thereabouts, Diman Todorov wrote:
And remember, I always jump up and down with joy when I receive feedback, regardless if positive or negative ;)
I'm glad to hear that except I'm afraid you might be doing a lot of jumping
up and down as this is a very long piece of feedback. :)
I just downloaded your latest NSE distribution. It works well and I look forward
to having it in the main distribution. Here are some of my comments:
I don't know if there are any plans to add user-specifiable parameters
into NSE but I have a feeling that eventually we will run into this sort of need.
As an example, what do we do when somebody writes a cool NSE script that checks
an FTP server for a specific vulnerability but, oh no, the user needs to be able
to specify a user/password combination before the vulnerability can be tested?
Do we make the user edit the lua source file? Do we add some means to pass
parameters through Nmap into the lua script? Or do we simply consider these
cases beyond the scope of NSE?
A conceivable plan could be
nmap -sC --script=whatever.lua --script-params='user="desmond",passwd="molly"' target
which could bind 'user and 'passwd in the lua environments.
I noticed in many of your portrule tests you require the numerical port
of the service AND the service string to match. For example:
chargenTest.lua:portrule = function(host, port)
chargenTest.lua- if port.number == 19
chargenTest.lua- and port.service == "chargen"
chargenTest.lua- and port.protocol == "udp"
chargenTest.lua- then
daytimeTest.lua:portrule = function(host, port)
daytimeTest.lua- if port.number == 13
daytimeTest.lua- and port.service == "daytime"
daytimeTest.lua- and port.protocol == "udp"
daytimeTest.lua- then
echoTest.lua:portrule = function(host, port)
echoTest.lua- if port.number == 7
echoTest.lua- and port.service == "echo"
echoTest.lua- and port.protocol == "udp"
echoTest.lua- then
Since these are some of the simplest NSE scripts we can probably expect them
to be used as examples and skeletons for all the scripts to come. For instance,
Marek's excellent new ftpbounce.lua script:
ftpbounce.lua:portrule = function(host, port)
ftpbounce.lua- if port.number == 21
ftpbounce.lua- and port.service == "ftp"
ftpbounce.lua- and port.protocol == "tcp"
ftpbounce.lua- then
If I'm reading this code right, these scripts won't be run against
discovered services that version detection finds them on a non-standard
port. Although this probably isn't that big a deal for chargen and its ilk,
I can certainly envision somebody wanting to test FTP servers running
on unpredictable ports.
The other method used in your scripts, Diman, will probably be more effective
because it will be applied depending on the service detected. For instance:
showHTMLTitle.lua:portrule = function(host, port)
showHTMLTitle.lua- if
showHTMLTitle.lua- ( port.number == 80
showHTMLTitle.lua- or port.service == "http")
showHTMLTitle.lua- and port.protocol == "tcp"
This should run on any port that version detection discovered to speak HTTP.
Web-servers embedded in an incredibly diverse range of different devices listen on
wacky random ports all the time - and hundreds (thousands?) of them are identified by
version detection to be HTTP. This is a great symbiosis of version detection and NSE
and has huge future potential!
But, by the same token, if port.number is 80 and port.service is known to *not* be http,
do we really want to run this script? I think it might be better to always ignore
port numbers and only depend on the service name + transport protocol.
One of the biggest benefits of version detection is that it makes people realise
just how little security benefit they get from obfuscating port numbers and runnning
services on non-standard ports. In fact, this is where much of the version detection
effort has been focused: distinguishing and identifying different protocols. I think
NSE could take advantage of this information very nicely - especially in the HTTP
examples.
Accounting for just the service name should also add another layer of stability
and assurance to somebody running or writing lua scripts: version detection has
verified that, at least superficially, this port seems to speak the service we're
hoping it to speak.
Perhaps a useful abstraction that could be made is to have a shortcut
in portrule. For instance, what if portrule could be either a function
OR a string? First of all, thank lua's dynamic typing system. Then
consider being able to specify portrules like the following:
portrule = "http/tcp"
instead of the longer
portrule = function(host, port)
if port.service == "http"
and port.protocol == "tcp"
then
end
Naturally some portrules would still require functions for more elaborate
tests.
Until I looked at the lua source code, it wasn't clear to me that port 113
needed to be scanned and determined to be an auth service before the
showOwner.lua script would run. Perhaps some sort of documentation or warning
is appropriate? Also, in a similar vein to above, what if we (god knows why)
find an auth service on a port other than 113? Couldn't somehow the showOwner.lua
script try the scan with any auth services discovered? And could we make this
general so that this sort of behaviour is automatic (by having some sort
of connect() wrapper that accepts service names instead of ports, for example)?
I know lua provides a fairly complete regular expression library but I still
wonder if it would be worthwhile creating some sort of lua binding for
PPCRE. Nmap requires libppcre for version detection so we can always count on
it being linked. (Interestingly, Nmap will soon have 3 distinct, incompatible
regular expression libraries available - POSIX, PPCRE, and lua's)
I would personally rather use ppcre to create regular expressions for the
following reasons:
o I don't want to learn the syntax and idiosyncracies of yet another regular
expression library! I've already (more-or-less) committed to memory
POSIX regexs, Perl regexs, and cl-ppcre's s-expression regexs and I would
rather not try to stuff another in my poor brain!
o Although I suspect the performance difference for simple patterns is
pretty negligible, it's never bad to have an efficient regex library
especially when you can make use of back-patterns, non-greedy matching, etc.
o There might be further opportunities to integrate NSE with version detection
and other applications that use regexs to match network data. Believe me, the
perl regular expression format is as close to a standard as you can get.
One of the best things about designing a scripting language extension is that we
can hunt for and discover common scripting idioms and make them conveniently
available to the scripter. In lua, which has no macro capability, this means
adding functions to the default environment. I can see you've recognised this
and have added a number of useful functions for people to use. A good example is
the receive_lines() function documented so:
status, value = socket_object.receive_lines(n)
Tries to receive at least n lines from an open connection. On success the
returned value of status is true and the received data is stored in value.
If the connection attempt has failed, value contains a description of the
error condition stored as a string.
This is handy because a whole heap of protocols use the concept of "lines" to
divide data and functions like this one make it really easy to read and process
data line-by-line.
But why stop there?
Here is an example from showHTMLTitle.lua:
result = ""
while true do
status, s = socket:receive_lines(1)
if status == false then
break
end
result = result .. s
end
This seems to be a common idiom used in NSE scripts (it already appears verbatim
in several default NSE scripts). It says to receive as many lines as possible from
the target and store all results in a variable bound to 'result.
Why not have a function, say, get_contents() (a la Perl's LWP) so this can be
written like so:
status, result = socket:get_contents()
Furthermore, when we abstract properly we can start to make efficiency
improvements. In version detection, it isn't necessary to read all the data
from a socket in order to match successfuly. In fact, version detection would
likely take much, much longer if this were the case. In version detection,
the regular expressions are compared after every chunk of data received
so as soon as we have enough to match we finish up. This helps for timeout
purposes, large contents (like super long index.html files on httpds or
annoying FTP banners), laggy network connections, and so on.
If we take this to a further level of abstraction, we could make use of
this same tactic in NSE. Imagine a function
get_contents_until_match = function(regex)
local result
while not regex_match(regex, result)
Keep reading from the socket and concatenating the output onto result
until regex matches. Once it does, return the result and a good status.
If it doesn't, or we get some other error from the socket, return
a bad status.
end
end
so we can do things like so:
status, result = get_contents_until_match("^HTTP/1.0 \d\d\d .*\r\n")
if (status == true) then
Do something now that we know we have at least the first line of an
HTTP transaction.
else
Good thing we didn't try to parse this service!
end
Please excuse any errors in the above pseudo code - As you might be able
to tell I haven't implemented or tested them. :)
(BTW I really like the fact that lua has multiple return values! This is
always very convenient and is one thing that most languages lack. Yes,
it's features like multiple-value-bind that common lisp and lua programmers
take for granted but often find missing in other languages.)
It's great that you use the script's 'id values to interact with the C
code in output.cc:
char* formatScriptOutput(struct script_scan_result ssr) {
...
result += "| " + std::string(ssr.id) + ":" + sep;
But why stop there?
I can't seem to find any use of the 'description values in the C source or
any of the lua scripts. Does the description serve any purpose other than
being a comment in the source code?
Auto-documentation like this is a very powerful concept and could perhaps
be developed further. Consider quickly being able to see and choose exactly
which scripts will be applied against a discovered service. This would be
easy if most scripts used the "portrule can be string or function" suggestion
above. It isn't that big a deal right now but I can envision it becoming a
problem once we reach some critical threshold number of lua scripts in
the distribution!
In a program I started work on a while ago, which I call nuff, I make
extensive use of scheme macros and this sort of auto-documentation concept so
that a utility can automatically be well-documented. The parameters, their types
and acceptable values, whether this utility requires root privileges, etc etc,
can all be automatically determined and documented. I hope to someday muster
up the time and energy to complete this set of macros for publication.
Out of curiosity, I'm wondering why you chose NSE for some of your scripts
instead of version detection. For example, malware/kibuvDetection.lua seems
to accomplish the same thing that adding a match line or 2 to the probes file
would.
Anyways, these are just some random ideas. Take them for what they're worth.
Best,
Doug
_______________________________________________ Sent through the nmap-dev mailing list http://cgi.insecure.org/mailman/listinfo/nmap-dev
Current thread:
- Nmap-NSE Release candidate 2 Diman Todorov (Aug 07)
- Re: Nmap-NSE Release candidate 2 Fyodor (Aug 07)
- IPv6 Testing of Nmap-NSE? Fyodor (Aug 08)
- Re: Nmap-NSE Release candidate 2 doug (Aug 09)
- Re: Nmap-NSE Release candidate 2 Diman Todorov (Aug 09)
- Re: Nmap-NSE Release candidate 2 doug (Aug 12)
- Re: Nmap-NSE Release candidate 2 Fyodor (Aug 13)
- Re: Nmap-NSE Release candidate 2 Diman Todorov (Aug 09)
- <Possible follow-ups>
- RE: Nmap-NSE Release candidate 2 Mike C (sec) (Aug 10)
