summaryrefslogtreecommitdiffstats
path: root/scripts/http-cookie-flags.nse
blob: 84872fd4054ab749023cf461397524b6e67bca94 (plain)
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
local http = require "http"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"

description = [[
Examines cookies set by HTTP services.  Reports any session cookies set
without the httponly flag.  Reports any session cookies set over SSL without
the secure flag.  If http-enum.nse is also run, any interesting paths found
by it will be checked in addition to the root.
]]

---
-- @usage
-- nmap -p 443 --script http-cookie-flags <target>
--
-- @output
-- 443/tcp open  https
-- | http-cookie-flags:
-- |   /:
-- |     PHPSESSID:
-- |       secure flag not set and HTTPS in use
-- |   /admin/:
-- |     session_id:
-- |       secure flag not set and HTTPS in use
-- |       httponly flag not set
-- |   /mail/:
-- |     ASPSESSIONIDASDF:
-- |       httponly flag not set
-- |     ASP.NET_SessionId:
-- |_      secure flag not set and HTTPS in use
--
-- @args path Specific URL path to check for session cookie flags. Default: / and those found by http-enum.
-- @args cookie Specific cookie name to check flags on. Default: A variety of commonly used session cookie names and patterns.
--
-- @xmloutput
-- <table key="/">
-- <table key="PHPSESSID">
-- <elem>secure flag not set and HTTPS in use</elem>
-- </table>
-- </table>
-- <table key="/admin/">
-- <table key="session_id">
-- <elem>secure flag not set and HTTPS in use</elem>
-- <elem>httponly flag not set</elem>
-- </table>
-- </table>
-- <table key="/mail/">
-- <table key="ASPSESSIONIDASDF">
-- <elem>httponly flag not set</elem>
-- </table>
-- <table key="ASP.NET_SessionId">
-- <elem>secure flag not set and HTTPS in use</elem>
-- </table>
-- </table>
--
-- @see http-enum.nse
-- @see http-security-headers.nse

categories = { "default", "safe", "vuln" }
author = "Steve Benson"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
dependencies = {"http-enum"}

portrule = shortport.http

-- a list of patterns indicating cookies which are likely session cookies
local session_cookie_patterns = {
  '^PHPSESSID$',
  '^CFID$',
  '^CFTOKEN$',
  '^VOXSQSESS$',
  '^CAKEPHP$',
  '^FedAuth$',
  '^ASPXAUTH$',
  '^session$',
  '[Ss][Ee][Ss][Ss][Ii][Oo][Nn][^%a]*[Ii][Dd]'
}

-- check cookies set on a particular URL path. returns a table with problem
-- cookie names mapped to a table listing each problem found.
local check_path = function(is_session_cookie, host, port, path)
  stdnse.debug1("start check of %s", path)
  local path_issues = stdnse.output_table()
  local resp = http.get(host, port, path)
  if not resp.status then
    stdnse.debug1("Error retrieving %s: %s", path, resp["status-line"])
    return nil
  end

  if not resp.cookies then
    stdnse.debug2("No cookies on %s", path)
    return nil
  end

  for _,cookie in ipairs(resp.cookies) do
    stdnse.debug2('  cookie: %s', cookie.name)
    local issues = stdnse.output_table()
    if is_session_cookie(cookie.name) then
      stdnse.debug2('    IS a session cookie')
      if port.service=='https' and not cookie.secure then
        stdnse.debug2('    * no secure flag and https')
        issues[#issues+1] = 'secure flag not set and HTTPS in use'
      end
      if not cookie.httponly then
        stdnse.debug2('    * no httponly')
        issues[#issues+1] = 'httponly flag not set'
      end
    end

    if #issues>0 then
      path_issues[cookie.name] = issues
    end

  end

  stdnse.debug1("end check of %s : %d issues found", path, #path_issues)
  if #path_issues>0 then
    return path_issues
  else
    return nil
  end
end

action = function(host, port)
  local all_issues = stdnse.output_table()
  local specified_path = stdnse.get_script_args(SCRIPT_NAME..".path")
  local specified_cookie = stdnse.get_script_args(SCRIPT_NAME..".cookie")

  -- create a function, is_session_cookie, which accepts a cookie name and
  -- returns true if it is likely a session cookie, based on script-args
  local is_session_cookie
  if specified_cookie == nil then
    is_session_cookie = function(cookie_name)
      for _, pattern in ipairs(session_cookie_patterns) do
        if string.find(cookie_name, pattern) then
          return true
        end
      end
      return false
    end
  else
    is_session_cookie = function(cookie_name)
      return cookie_name==specified_cookie
    end
  end

  -- build a list of URL paths to check cookies for based on script-args and
  -- http-enum results.
  local paths_to_check = {}
  if specified_path == nil then
    stdnse.debug2('path script-arg is nil; checking / and anything from http-enum')
    paths_to_check[#paths_to_check+1] = '/'
    for _,path in ipairs( stdnse.registry_get({host.ip, 'www', port.number, 'all_pages'}) or {}) do
      paths_to_check[#paths_to_check+1] = path
    end
  else
    stdnse.verbose1('path script-arg is %s; checking only that path', specified_path)
    paths_to_check[#paths_to_check+1] = specified_path
  end

  -- check desired cookies on all desired paths
  for _,path in ipairs(paths_to_check) do
    all_issues[path] = check_path(is_session_cookie, host, port, path)
  end

  if #all_issues>0 then
    return all_issues
  else
    return nil
  end

end