1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-08 13:41:29 +00:00
Files
nmap/scripts/ftp-anon.nse

120 lines
3.0 KiB
Lua

description = [[
Checks if an FTP server allows anonymous logins.
]]
---
-- @output
--- Default behavior
-- PORT STATE SERVICE
-- 21/tcp open ftp
-- |_ftp-anon: Anonymous FTP login allowed (FTP code 230)
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 "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
--- Connects to the FTP server and checks if the server allows anonymous logins.
action = function(host, port)
local socket = nmap.new_socket()
local code, message
local err_catch = function()
socket:close()
end
local try = nmap.new_try(err_catch)
try(socket:connect(host.ip, port.number, port.protocol))
buffer = stdnse.make_buffer(socket, "\r?\n")
-- Read banner.
code, message = read_reply(buffer)
if code and code == 220 then
try(socket:send("USER anonymous\r\n"))
code, message = 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)
end
if code == 332 then
-- 332: Need account for login.
-- This is rarely seen but may come in response to a
-- 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)
if code == 331 then
-- 331: User name okay, need password.
try(socket:send("PASS IEUser@\r\n"))
code, message = read_reply(buffer)
end
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)
else
stdnse.print_debug(1, "ftp-anon: got code %d %q.", code, message)
end
end
end