local ftp = require "ftp" local shortport = require "shortport" local stdnse = require "stdnse" local string = require "string" local table = require "table" description = [[ Sends FTP SYST and STAT commands and returns the result. 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 ]] author = "Daniel Miller" license = "Same as Nmap--See https://nmap.org/book/man-legal.html" categories = {"default", "discovery", "safe"} --- -- @output -- | 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 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 -- SYST local auth_done = false 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 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