1
0
mirror of https://github.com/nmap/nmap.git synced 2026-01-22 22:29:03 +00:00

Let ftp-anon.nse return a directory listing when anonymous login is

allowed, and add a ftp-anon.maxlist argument to control the listing.
This is adapted from a patch by Gutek.
This commit is contained in:
david
2010-08-27 19:21:34 +00:00
parent 8729f667fe
commit e0918fedc4
2 changed files with 115 additions and 9 deletions

View File

@@ -1,5 +1,8 @@
# Nmap Changelog ($Id$); -*-text-*-
o [NSE] Made the ftp-anon script return a directory listing when
anonymous login is allowed. [Gutek, David]
o [NSE] Added the nmap.resolve() function which takes a host name and
optionally an address family (such as "inet") and returns a table
containing all of its matching addresses. If no address family is

View File

@@ -1,13 +1,26 @@
description = [[
Checks if an FTP server allows anonymous logins.
If anonymous is allowed, gets a directory listing of the root directory
and highlights writeable files.
]]
---
-- @args ftp-anon.maxlist The maximum number of files to return in the
-- directory listing. By default it is 20, or unlimited if verbosity is
-- enabled. Use a negative number to disable the limit.
--
-- @output
--- Default behavior
-- PORT STATE SERVICE
-- 21/tcp open ftp
-- |_ftp-anon: Anonymous FTP login allowed (FTP code 230)
-- | ftp-anon: Anonymous FTP login allowed (FTP code 230)
-- | -rw-r--r-- 1 1170 924 31 Mar 28 2001 .banner
-- | d--x--x--x 2 root root 1024 Jan 14 2002 bin
-- | d--x--x--x 2 root root 1024 Aug 10 1999 etc
-- | drwxr-srwt 2 1170 924 2048 Jul 19 18:48 incoming [NSE: writeable]
-- | d--x--x--x 2 root root 1024 Jan 14 2002 lib
-- | drwxr-sr-x 2 1170 924 1024 Aug 5 2004 pub
-- |_Only 6 shown. Use --script-args ftp-anon.maxlist=-1 to see all.
author = "Eddie Bell, Rob Nicholls, Ange Gutek, David Fifield"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
@@ -60,6 +73,63 @@ local function read_reply(buffer)
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
-- LIST on the commands socket, connect to the data one and read the directory
-- list sent.
-- ---------------------
local function list(socket, target, max_lines)
local status, err
-- ask the server for a Passive Mode: it should give us a port to
-- listen to, where it will dump the directory listing
buffer = stdnse.make_buffer(socket, "\r?\n")
status, err = socket:send("PASV\r\n")
if not status then
return status, err
end
code, message = read_reply(buffer)
-- Compute the PASV port as given by the server
-- The server should answer with something like
-- 2xx Entering Passive Mode (a,b,c,d,hp,lp)
-- (-- IP--,PORT)
-- PORT is (hp x 256) + lp
local high, low = string.match(message, "%(%d+,%d+,%d+,%d+,(%d+),(%d+)%)")
if not high then
return nil, string.format("Can't parse PASV response: %q", message)
end
local pasv_port = high * 256 + low
-- Send the LIST command on the commands socket. "Fire and forget"; we
-- don't need to take care of the answer on this socket.
status, err = socket:send("LIST\r\n")
if not status then
return status, err
end
local list_socket = nmap.new_socket()
status, err = list_socket:connect(target, pasv_port, "tcp")
if not status then
return status, err
end
local listing = {}
while not max_lines or #listing < max_lines do
local status, data = list_socket:receive_buf("\r?\n", false)
if not status then
return status, data
end
if data == "" then
break
end
listing[#listing + 1] = data
end
return true, listing
end
--- Connects to the FTP server and checks if the server allows anonymous logins.
action = function(host, port)
@@ -69,6 +139,20 @@ action = function(host, port)
socket:close()
end
local max_list = nmap.registry.args[SCRIPT_NAME .. ".maxlist"]
if not max_list then
if nmap.verbosity() == 0 then
max_list = 20
else
max_list = nil
end
else
max_list = tonumber(max_list)
if max_list < 0 then
max_list = nil
end
end
local try = nmap.new_try(err_catch)
try(socket:connect(host, port))
@@ -100,20 +184,39 @@ action = function(host, port)
end
end
socket:close()
if code and code >= 200 and code < 300 then
-- We are primarily looking for 230: User logged in, proceed.
return "Anonymous FTP login allowed (FTP code " .. code .. ")"
elseif code == 421 then
-- 421: Service not available, closing control connection.
elseif code == 530 then
-- 530: Not logged in.
else
if not code then
stdnse.print_debug(1, "ftp-anon: got socket error %q.", message)
elseif code == 421 or code == 530 then
-- Don't log known error codes.
-- 421: Service not available, closing control connection.
-- 530: Not logged in.
else
stdnse.print_debug(1, "ftp-anon: got code %d %q.", code, message)
end
return nil
end
local result = {}
result[#result + 1] = "Anonymous FTP login allowed (FTP code " .. code .. ")"
if not max_list or max_list > 0 then
local listing = try(list(socket, host, max_list))
socket:close()
for _, item in ipairs(listing) do
-- Just a quick passive check on user rights.
if string.match(item, "^[d-].......w.") then
item = item .. " [NSE: writeable]"
end
result[#result + 1] = item
end
if max_list and #listing == max_list then
result[#result + 1] = string.format("Only %d shown. Use --script-args %s.maxlist=-1 to see all.", #listing, SCRIPT_NAME)
end
end
return table.concat(result, "\n")
end