diff --git a/CHANGELOG b/CHANGELOG index b87b7fb2d..d0ee01818 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,7 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Created an ftp.lua library. [David] + o [NSE] Added gopher-ls.nse by Toni Ruotto, which lists the root of a Gopher server. diff --git a/nselib/ftp.lua b/nselib/ftp.lua new file mode 100644 index 000000000..d3b52a0a4 --- /dev/null +++ b/nselib/ftp.lua @@ -0,0 +1,52 @@ +--- +-- FTP functions. +-- +-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html + +module(... or "ftp", package.seeall) + +--- +-- Read an FTP reply and return the numeric code and the message. See RFC 959, +-- section 4.2. +-- @param buffer should have been created with +-- stdnse.make_buffer(socket, "\r?\n"). +-- @return numeric code or nil. +-- @return text reply or error message. +function read_reply(buffer) + local readline + local line, err + local code, message + local _, p, tmp + + line, err = buffer() + if not line then + return line, err + end + + -- Single-line response? + code, message = string.match(line, "^(%d%d%d) (.*)$") + if code then + return tonumber(code), message + end + + -- Multi-line response? + _, p, code, message = string.find(line, "^(%d%d%d)-(.*)$") + if p then + while true do + line, err = buffer() + if not line then + return line, err + end + tmp = string.match(line, "^%d%d%d (.*)$") + if tmp then + message = message .. "\n" .. tmp + break + end + message = message .. "\n" .. line + end + + return tonumber(code), message + end + + return nil, string.format("Unparseable response: %q", line) +end diff --git a/scripts/ftp-anon.nse b/scripts/ftp-anon.nse index 0d4f362f8..01d3f02c4 100644 --- a/scripts/ftp-anon.nse +++ b/scripts/ftp-anon.nse @@ -27,53 +27,11 @@ author = "Eddie Bell, Rob Nicholls, Ange Gutek, David Fifield" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"default", "auth", "safe"} +require "ftp" require "shortport" portrule = shortport.port_or_service(21, "ftp") --- Read an FTP reply and return the numeric code and the message. See RFC 959, --- section 4.2. The buffer argument should have been created with --- stdnse.make_buffer(socket, "\r?\n"). On error, returns nil and an error --- message. -local function read_reply(buffer) - local readline - local line, err - local code, message - local _, p, tmp - - line, err = buffer() - if not line then - return line, err - end - - -- Single-line response? - code, message = string.match(line, "^(%d%d%d) (.*)$") - if code then - return tonumber(code), message - end - - -- Multi-line response? - _, p, code, message = string.find(line, "^(%d%d%d)-(.*)$") - if p then - while true do - line, err = buffer() - if not line then - return line, err - end - tmp = string.match(line, "^%d%d%d (.*)$") - if tmp then - message = message .. "\n" .. tmp - break - end - message = message .. "\n" .. line - end - - return tonumber(code), message - end - - return nil, string.format("Unparseable response: %q", line) -end - -- --------------------- -- Directory listing function. -- We ask for a PASV connexion, catch the port returned by the server, send a @@ -90,7 +48,7 @@ local function list(socket, target, max_lines) if not status then return status, err end - code, message = read_reply(buffer) + code, message = ftp.read_reply(buffer) -- Compute the PASV port as given by the server -- The server should answer with something like @@ -160,14 +118,14 @@ action = function(host, port) buffer = stdnse.make_buffer(socket, "\r?\n") -- Read banner. - code, message = read_reply(buffer) + code, message = ftp.read_reply(buffer) if code and code == 220 then try(socket:send("USER anonymous\r\n")) - code, message = read_reply(buffer) + code, message = ftp.read_reply(buffer) if code == 331 then -- 331: User name okay, need password. try(socket:send("PASS IEUser@\r\n")) - code, message = read_reply(buffer) + code, message = ftp.read_reply(buffer) end if code == 332 then @@ -176,11 +134,11 @@ action = function(host, port) -- USER or PASS command. As we're doing this -- anonymously, send back a blank ACCT. try(socket:send("ACCT\r\n")) - code, message = read_reply(buffer) + code, message = ftp.read_reply(buffer) if code == 331 then -- 331: User name okay, need password. try(socket:send("PASS IEUser@\r\n")) - code, message = read_reply(buffer) + code, message = ftp.read_reply(buffer) end end end