diff --git a/CHANGELOG b/CHANGELOG index c91f2e68c..bd63d28dd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,11 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] ftp-syst sends a SYST command to FTP servers to get system version + information. [Daniel Miller] + +o [NSE] FTP scripts like ftp-anon and ftp-brute now correctly handle + TLS-protected FTP services and use STARTTLS when necessary. [Daniel Miller] + o [NSE][GH#936] Function url.escape no longer encodes so-called "unreserved" characters, including hyphen, period, underscore, and tilde, as per RFC 3986. [nnposter] diff --git a/scripts/ftp-syst.nse b/scripts/ftp-syst.nse new file mode 100644 index 000000000..11db45752 --- /dev/null +++ b/scripts/ftp-syst.nse @@ -0,0 +1,71 @@ +local ftp = require "ftp" +local shortport = require "shortport" +local stdnse = require "stdnse" +local string = require "string" +local table = require "table" + +description = [[ +Sends an FTP SYST command and returns the result. + +The canonical response of "UNIX Type: L8" is stripped or ignored, since it is meaningless. + +References: +* https://cr.yp.to/ftp/syst.html +]] + +author = "Daniel Miller" +license = "Same as Nmap--See https://nmap.org/book/man-legal.html" +categories = {"default", "discovery", "safe"} + +--- +-- @output +-- |_ftp-syst: UNIX MikroTik 6.39.2 + +portrule = shortport.port_or_service({21,990}, {"ftp","ftps"}) + +action = function(host, port) + local socket, code, message, buffer = ftp.connect(host, port) + if not socket then + stdnse.debug1("Couldn't connect: %s", code or message) + return nil + end + if code and code ~= 220 then + stdnse.debug1("banner code %d %q.", code, message) + return nil + end + + local auth_done = false + ::TRY_AGAIN:: + if not socket:send("SYST\r\n") then + return nil + end + code, message = ftp.read_reply(buffer) + if not code then + stdnse.debug1("SYST error: %s", message) + return nil + end + if code == 215 then + local stripped = message:gsub("^UNIX Type: L8 *", "") + if stripped ~= "" then + return stripped + else + return nil + end + elseif code < 300 then + return ("%d %s"):format(code, message) + elseif not auth_done and -- we haven't tried logging in yet + ( code == 503 -- Command SYST not accepted during Connected + or code == 521 -- Not logged in - Secure authentication required + or (code % 100) // 10 == 3 -- x3x codes are auth-related + ) then + -- Try logging in + local status, code, message = ftp.auth(socket, buffer, "anonymous", "IEUser@") + if status then + auth_done = true + goto TRY_AGAIN + end + end + + ftp.close(socket) + +end diff --git a/scripts/script.db b/scripts/script.db index 3617dbec6..ba87ca378 100644 --- a/scripts/script.db +++ b/scripts/script.db @@ -127,6 +127,7 @@ Entry { filename = "ftp-bounce.nse", categories = { "default", "safe", } } Entry { filename = "ftp-brute.nse", categories = { "brute", "intrusive", } } Entry { filename = "ftp-libopie.nse", categories = { "intrusive", "vuln", } } Entry { filename = "ftp-proftpd-backdoor.nse", categories = { "exploit", "intrusive", "malware", "vuln", } } +Entry { filename = "ftp-syst.nse", categories = { "default", "discovery", "safe", } } Entry { filename = "ftp-vsftpd-backdoor.nse", categories = { "exploit", "intrusive", "malware", "vuln", } } Entry { filename = "ftp-vuln-cve2010-4221.nse", categories = { "intrusive", "vuln", } } Entry { filename = "ganglia-info.nse", categories = { "default", "discovery", "safe", } }