summaryrefslogtreecommitdiffstats
path: root/scripts/ftp-bounce.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/ftp-bounce.nse')
-rw-r--r--scripts/ftp-bounce.nse113
1 files changed, 113 insertions, 0 deletions
diff --git a/scripts/ftp-bounce.nse b/scripts/ftp-bounce.nse
new file mode 100644
index 0000000..cb5476f
--- /dev/null
+++ b/scripts/ftp-bounce.nse
@@ -0,0 +1,113 @@
+local nmap = require "nmap"
+local shortport = require "shortport"
+local stdnse = require "stdnse"
+local string = require "string"
+local ftp = require "ftp"
+
+description=[[
+Checks to see if an FTP server allows port scanning using the FTP bounce method.
+]]
+author = "Marek Majkowski"
+license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
+
+---
+-- @args ftp-bounce.username Username to log in with. Default
+-- <code>anonymous</code>.
+-- @args ftp-bounce.password Password to log in with. Default
+-- <code>IEUser@</code>.
+-- @args ftp-bounce.checkhost Host to try connecting to with the PORT command.
+-- Default: scanme.nmap.org
+--
+-- @output
+-- PORT STATE SERVICE
+-- 21/tcp open ftp
+-- |_ftp-bounce: bounce working!
+--
+-- PORT STATE SERVICE
+-- 21/tcp open ftp
+-- |_ftp-bounce: server forbids bouncing to low ports <1025
+
+categories = {"default", "safe"}
+
+portrule = shortport.port_or_service({21, 990}, {"ftp", "ftps"})
+
+local function get_portfmt()
+ local arghost = stdnse.get_script_args(SCRIPT_NAME .. ".checkhost") or "scanme.nmap.org"
+ local reg = nmap.registry[SCRIPT_NAME] or {}
+ local addr = reg[arghost]
+ if not addr then
+ local status, addrs = nmap.resolve(arghost, "inet")
+ if not status or #addrs < 1 then
+ stdnse.verbose1("Couldn't resolve %s, scanning 10.0.0.1 instead.", arghost)
+ addr = "10.0.0.1"
+ else
+ addr = addrs[1]
+ end
+ reg[arghost] = addr
+ end
+ nmap.registry[SCRIPT_NAME] = reg
+ return string.format("PORT %s,%%s\r\n", (string.gsub(addr, "%.", ",")))
+end
+
+action = function(host, port)
+ local user = stdnse.get_script_args(SCRIPT_NAME .. ".username") or "anonymous"
+ local pass = stdnse.get_script_args(SCRIPT_NAME .. ".password") or "IEUser@"
+
+ -- BANNER
+ local socket, code, message, buffer = ftp.connect(host, port, {request_timeout=10000})
+ if not socket then
+ return nil
+ end
+ if code < 200 or code > 299 then
+ socket:close()
+ return nil
+ end
+
+ socket:set_timeout(5000)
+ -- USER
+ local status, code, message = ftp.auth(socket, buffer, user, pass)
+ if not status then
+ stdnse.debug1("Authentication rejected: %s %s", code or "socket", message)
+ ftp.close(socket)
+ return nil
+ end
+
+ -- PORT highport
+ local portfmt = get_portfmt()
+ -- This is actually port 256*80 + 80 = 20560
+ if not socket:send(string.format(portfmt, "80,80")) then
+ stdnse.debug1("Can't send PORT")
+ return nil
+ end
+ code, message = ftp.read_reply(buffer)
+ if not code then
+ stdnse.debug1("Error after PORT: %s", message)
+ return nil
+ end
+ if code < 200 or code > 299 then
+ stdnse.verbose1("PORT response: %d %s", code, message)
+ ftp.close(socket)
+ -- return "server forbids bouncing"
+ return nil
+ end
+
+ -- PORT lowport
+ if not socket:send(string.format(portfmt, "0,80")) then
+ stdnse.debug1("Can't send PORT")
+ return nil
+ end
+ code, message = ftp.read_reply(buffer)
+ if not code then
+ stdnse.debug1("Error after PORT: %s", message)
+ return nil
+ end
+ if code < 200 or code > 299 then
+ stdnse.verbose1("PORT (low port) response: %d %s", code, message)
+ ftp.close(socket)
+ return "server forbids bouncing to low ports <1025"
+ end
+
+ ftp.close(socket)
+ return "bounce working!"
+end
+