From c0c397b5bbf6fae4822a63fef2fd7c950b9028ea Mon Sep 17 00:00:00 2001 From: dmiller Date: Wed, 26 Jul 2017 21:29:32 +0000 Subject: [PATCH] Add STAT support to ftp-syst.nse --- CHANGELOG | 4 +- scripts/ftp-syst.nse | 129 +++++++++++++++++++++++++++++++++---------- 2 files changed, 103 insertions(+), 30 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index bd63d28dd..797fbc54a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,7 @@ # 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-syst sends SYST and STAT commands to FTP servers to get system + version and connection 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] diff --git a/scripts/ftp-syst.nse b/scripts/ftp-syst.nse index 11db45752..bf667494c 100644 --- a/scripts/ftp-syst.nse +++ b/scripts/ftp-syst.nse @@ -5,9 +5,11 @@ local string = require "string" local table = require "table" description = [[ -Sends an FTP SYST command and returns the result. +Sends FTP SYST and STAT commands and returns the result. -The canonical response of "UNIX Type: L8" is stripped or ignored, since it is meaningless. +The canonical SYST response of "UNIX Type: L8" is stripped or ignored, since it +is meaningless. Typical FTP response codes (215 for SYST and 211 for STAT) are +also hidden.response codes (215 for SYST and 211 for STAT) are also hidden. References: * https://cr.yp.to/ftp/syst.html @@ -19,10 +21,55 @@ categories = {"default", "discovery", "safe"} --- -- @output --- |_ftp-syst: UNIX MikroTik 6.39.2 +-- | ftp-syst: +-- | SYST: UNIX MikroTik 6.34.3 +-- | STAT: +-- | Enver Curri FTP server (MikroTik 6.34.3) status: +-- | Logged in as +-- | TYPE: ASCII; STRUcture: File; transfer MODE: Stream +-- | No data connection +-- |_End of status +-- +-- | ftp-syst: +-- | STAT: +-- | FTP server status: +-- | Connected to 192.0.2.13 +-- | Logged in as ftp +-- | TYPE: ASCII +-- | No session bandwidth limit +-- | Session timeout in seconds is 300 +-- | Control connection is plain text +-- | Data connections will be plain text +-- | At session startup, client count was 1 +-- | vsFTPd 2.0.5 - secure, fast, stable +-- |_End of status +-- +-- | ftp-syst: +-- | SYST: Version: Linux 2.6.26.8-rt16 +-- | STAT: +-- | HES_CPE FTP server status: +-- | ftpd (GNU inetutils) 1.4.1 +-- | Connected to 72.14.177.105 +-- | Waiting for user name +-- | TYPE: ASCII, FORM: Nonprint; STRUcture: File; transfer MODE: Stream +-- | No data connection +-- |_End of status +-- +-- @xmloutput +-- Version: Linux 3.10.73 +-- +-- FRITZ!Box7490 FTP server status: +-- Connected to 72.14.177.105 +-- Waiting for user name +-- TYPE: ASCII, FORM: Nonprint; STRUcture: File; transfer MODE: Stream +-- No data connection +-- End of status portrule = shortport.port_or_service({21,990}, {"ftp","ftps"}) +local function do_syst(socket, buffer) +end + action = function(host, port) local socket, code, message, buffer = ftp.connect(host, port) if not socket then @@ -34,38 +81,64 @@ action = function(host, port) return nil end + -- SYST 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 + local syst + repeat + 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) + break + end + if code == 215 then + local stripped = message:gsub("^UNIX Type: L8 *", "") + if stripped ~= "" then + syst = stripped + end + break + elseif code < 300 then + syst = ("%d %s"):format(code, message) + break + 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 + end + end + until not auth_done + + -- STAT + if not socket:send("STAT\r\n") then + if syst then + return {SYST=syst} 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 + + local out = stdnse.output_table() + out.SYST = syst + local code, stat = ftp.read_reply(buffer) + + if code then + if code == 211 then + out.STAT = "\n" .. stat + elseif code < 300 then + out.STAT = ("%d\n%s"):format(code, stat) end end ftp.close(socket) + if #out > 0 then + return out + end end