From 86e7a63bf621b60c86bc49bd9e1a476734c2465a Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 16 Sep 2009 14:15:13 +0000 Subject: [PATCH] Added a script called http-malware-host.nse. Its future intention is to discover hosts that are serving malware (for example, that are compromised and have malicious code inserted). At the moment, it checks for one specific attack discussed here: http://blog.unmaskparasites.com/2009/09/11/dynamic-dns-and-botnet-of-zombie-web-servers/ --- CHANGELOG | 7 +++ scripts/http-malware-host.nse | 90 +++++++++++++++++++++++++++++++++++ scripts/script.db | 1 + 3 files changed, 98 insertions(+) create mode 100644 scripts/http-malware-host.nse diff --git a/CHANGELOG b/CHANGELOG index 4ce73e535..71adccb8d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,12 @@ # Nmap Changelog ($Id$); -*-text-*- +o Added a script called http-malware-host.nse. Its future intention + is to discover hosts that are serving malware (for example, that + are compromised and have malicious code inserted). At the moment, + it checks for one specific attack discussed here: + http://blog.unmaskparasites.com/2009/09/11/dynamic-dns-and-botnet-of-zombie-web-servers/ + [Ron] + o Added a check for a SMBv2 vulnerability (CVE-2009-3103) to smb-check-vulns. Due to its nature (it performs a DoS, then checks if the system is still online), the script isn't run by default diff --git a/scripts/http-malware-host.nse b/scripts/http-malware-host.nse new file mode 100644 index 000000000..8567eafbe --- /dev/null +++ b/scripts/http-malware-host.nse @@ -0,0 +1,90 @@ +description = [[ +Looks for signature of known server compromises. Currently, the only signature it looks for is +the one discussed here: + + +This is done by requesting the page /ts/in.cgi?open2 and looking for an errant 302 (it attempts +to detect srevers that always return 302). + +Thanks to Denis from the above link for finding this technique! +]] + +--- +--@output +-- Interesting ports on www.sopharma.bg (84.242.167.49): +-- PORT STATE SERVICE REASON +-- 80/tcp open http syn-ack +-- |_ http-infected: Host appears to be clean +-- 8080/tcp open http-proxy syn-ack +-- | http-malware-host: Host appears to be infected (/ts/in.cgi?open2 redirects to http://last-another-life.ru:8080/index.php) +-- |_ See: http://blog.unmaskparasites.com/2009/09/11/dynamic-dns-and-botnet-of-zombie-web-servers/ +-- + +author = "Ron Bowes " + +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" + +categories = {"malware"} + +require 'stdnse' +require 'http' +require 'stdnse' + +portrule = function(host, port) + local svc = { std = { ["http"] = 1, ["http-alt"] = 1, ["http-proxy"] = 1 }, + ssl = { ["https"] = 1, ["https-alt"] = 1 } } + if port.protocol ~= 'tcp' + or not ( svc.std[port.service] or svc.ssl[port.service] ) then + return false + end + -- Don't bother running on SSL ports if we don't have SSL. + if (svc.ssl[port.service] or port.version.service_tunnel == 'ssl') + and not nmap.have_ssl() then + return false + end + return true +end + +local function go(host, port) + -- Check what response we get for a 404 + local result, result_404, known_404 = http.identify_404(host, port) + if(result == false) then + return false, "Couldn't identify 404 message: " .. result_404 + end + + -- If the 404 result is a 302, we're going to have trouble + if(result_404 == 302) then + return false, "Unknown pages return a 302 response; unable to check" + end + + -- Perform a GET request on the file + result = http.get_url("http://" .. host.ip .. ":" .. port.number .. "/ts/in.cgi?open2") + if(result == nil) then + return false, "Couldn't perform GET request" + end + + if(result.status == 302) then + if(result.header.location) then + return true, string.format("Host appears to be infected (/ts/in.cgi?open2 redirects to %s)\nSee: http://blog.unmaskparasites.com/2009/09/11/dynamic-dns-and-botnet-of-zombie-web-servers/", result.header.location) + else + return true, string.format("Host appears to be infected (/ts/in.cgi?open2 return a redirect)\nSee: http://blog.unmaskparasites.com/2009/09/11/dynamic-dns-and-botnet-of-zombie-web-servers/") + end + end + + return true, nil +end + +action = function(host, port) + local status, result = go(host, port) + + if(status == false) then + if(nmap.debugging() > 0) then + return "ERROR: " .. result + else + return nil + end + end + + return result +end + diff --git a/scripts/script.db b/scripts/script.db index 7f12743c0..431d91875 100644 --- a/scripts/script.db +++ b/scripts/script.db @@ -19,6 +19,7 @@ Entry { filename = "http-enum.nse", categories = { "discovery", "intrusive", "vu Entry { filename = "http-favicon.nse", categories = { "default", "discovery", } } Entry { filename = "http-headers.nse", categories = { "discovery", } } Entry { filename = "http-iis-webdav-vuln.nse", categories = { "intrusive", "vuln", } } +Entry { filename = "http-malware-host.nse", categories = { "malware", } } Entry { filename = "http-open-proxy.nse", categories = { "default", "discovery", "external", "intrusive", } } Entry { filename = "http-passwd.nse", categories = { "intrusive", "vuln", } } Entry { filename = "http-trace.nse", categories = { "discovery", } }