mirror of
https://github.com/nmap/nmap.git
synced 2025-12-14 11:49:01 +00:00
Merged in my changes from nmap-smb. The primary changes are:
* Updated the way authentication works on smb -- it's significantly cleaner now * smb-enum-shares.nse gives significantly better output now (it checks if shares are writable) * Added a script that checks if smbv2 is enabled on a server * Added smb-psexec, a script for executing commands on a remote Windows server. I also included some default scripts, a compiled .exe to run everything, and a ton of documentation (in the form of NSEDoc) * Added 'override' parameters to some of the functions in smb.lua, which lets the programmer override any field in an outgoing SMB packet without modifying smb.lua. * Lots of random code cleanups in the smb-* scripts/libraries
This commit is contained in:
@@ -40,17 +40,19 @@ Entry { filename = "robots.txt.nse", categories = { "default", "discovery", "saf
|
||||
Entry { filename = "rpcinfo.nse", categories = { "default", "discovery", "safe", } }
|
||||
Entry { filename = "skypev2-version.nse", categories = { "version", } }
|
||||
Entry { filename = "smb-brute.nse", categories = { "auth", "intrusive", } }
|
||||
Entry { filename = "smb-check-vulns.nse", categories = { "dos", "exploit", "intrusive", } }
|
||||
Entry { filename = "smb-check-vulns.nse", categories = { "dos", "exploit", "intrusive", "vuln", } }
|
||||
Entry { filename = "smb-enum-domains.nse", categories = { "discovery", "intrusive", } }
|
||||
Entry { filename = "smb-enum-processes.nse", categories = { "discovery", "intrusive", } }
|
||||
Entry { filename = "smb-enum-sessions.nse", categories = { "discovery", "intrusive", } }
|
||||
Entry { filename = "smb-enum-shares.nse", categories = { "discovery", "intrusive", } }
|
||||
Entry { filename = "smb-enum-users.nse", categories = { "discovery", "intrusive", } }
|
||||
Entry { filename = "smb-os-discovery.nse", categories = { "default", "discovery", "safe", } }
|
||||
Entry { filename = "smb-psexec.nse", categories = { "intrusive", } }
|
||||
Entry { filename = "smb-pwdump.nse", categories = { "intrusive", } }
|
||||
Entry { filename = "smb-security-mode.nse", categories = { "discovery", "safe", } }
|
||||
Entry { filename = "smb-server-stats.nse", categories = { "discovery", "intrusive", } }
|
||||
Entry { filename = "smb-system-info.nse", categories = { "discovery", "intrusive", } }
|
||||
Entry { filename = "smbv2-enabled.nse", categories = { "default", "safe", } }
|
||||
Entry { filename = "smtp-commands.nse", categories = { "default", "discovery", "safe", } }
|
||||
Entry { filename = "smtp-open-relay.nse", categories = { "demo", } }
|
||||
Entry { filename = "smtp-strangeport.nse", categories = { "malware", "safe", } }
|
||||
|
||||
@@ -311,19 +311,18 @@ end
|
||||
--@return Result, an integer value from the <code>results</code> constants.
|
||||
local function check_login(hostinfo, username, password, logintype)
|
||||
local result
|
||||
local domain
|
||||
local domain = ""
|
||||
local smbstate = hostinfo['smbstate']
|
||||
if(logintype == nil) then
|
||||
logintype = get_type(hostinfo)
|
||||
end
|
||||
--io.write(string.format("Trying %s:%s\n", username, password))
|
||||
|
||||
-- Determine if we have a password hash or a password
|
||||
if(#password == 32 or #password == 64 or #password == 65) then
|
||||
--io.write("Hash\n")
|
||||
-- It's a hash (note: we always use NTLM hashes)
|
||||
status, err = smb.start_session(smbstate, username, domain, nil, password, "ntlm", false, true)
|
||||
status, err = smb.start_session(smbstate, smb.get_overrides(username, domain, nil, password, "ntlm"), false)
|
||||
else
|
||||
status, err = smb.start_session(smbstate, username, domain, password, nil, logintype, false, false)
|
||||
status, err = smb.start_session(smbstate, smb.get_overrides(username, domain, password, nil, logintype), false)
|
||||
end
|
||||
|
||||
if(status == true) then
|
||||
@@ -850,7 +849,14 @@ function found_account(hostinfo, username, password, result)
|
||||
return false, err
|
||||
end
|
||||
|
||||
smb.add_account(hostinfo['host'], username, password)
|
||||
-- Check if we have an 'admin' account
|
||||
-- Try getting information about "IPC$". This determines whether or not the user is administrator
|
||||
-- since only admins can get share info. Note that on Vista and up, unless UAC is disabled, all
|
||||
-- accounts are non-admin.
|
||||
local is_admin = smb.is_admin(hostinfo['host'], username, '', password, nil, nil)
|
||||
|
||||
-- Add the account
|
||||
smb.add_account(hostinfo['host'], username, '', password, nil, nil, is_admin)
|
||||
|
||||
-- If we haven't retrieved the real user list yet, do so
|
||||
if(hostinfo['have_user_list'] == false) then
|
||||
|
||||
@@ -28,36 +28,37 @@ for shares that require a user account.
|
||||
-- sudo nmap -sU -sS --script smb-enum-shares.nse -p U:137,T:139 <host>
|
||||
--
|
||||
--@output
|
||||
-- Standard:
|
||||
-- | smb-enum-shares:
|
||||
-- | Anonymous shares: IPC$
|
||||
-- |_ Restricted shares: F$, ADMIN$, C$
|
||||
--
|
||||
-- Verbose:
|
||||
-- Host script results:
|
||||
-- | smb-enum-shares:
|
||||
-- | Anonymous shares:
|
||||
-- | IPC$
|
||||
-- | |_ Type: STYPE_IPC_HIDDEN
|
||||
-- | |_ Comment: Remote IPC
|
||||
-- | |_ Users: 1, Max: <unlimited>
|
||||
-- | |_ Path:
|
||||
-- | test
|
||||
-- | |_ Type: STYPE_DISKTREE
|
||||
-- | |_ Comment: This is a test share, with a maximum of 7 users
|
||||
-- | |_ Users: 0, Max: 7
|
||||
-- | |_ Path: C:\Documents and Settings\Ron\Desktop\test
|
||||
-- | Restricted shares:
|
||||
-- | ADMIN$
|
||||
-- | |_ Type: STYPE_DISKTREE_HIDDEN
|
||||
-- | |_ Comment: Remote Admin
|
||||
-- | |_ Users: 0, Max: <unlimited>
|
||||
-- | |_ Path: C:\WINNT
|
||||
-- | C$
|
||||
-- | |_ Type: STYPE_DISKTREE_HIDDEN
|
||||
-- | |_ Comment: Default share
|
||||
-- | |_ Users: 0, Max: <unlimited>
|
||||
-- |_ |_ Path: C:\
|
||||
-- | smb-enum-shares:
|
||||
-- | ADMIN$
|
||||
-- | |_ Type: STYPE_DISKTREE_HIDDEN
|
||||
-- | |_ Comment: Remote Admin
|
||||
-- | |_ Users: 0, Max: <unlimited>
|
||||
-- | |_ Path: C:\WINNT
|
||||
-- | |_ Anonymous access: <none>
|
||||
-- | |_ Current user ('test') access: READ/WRITE
|
||||
-- | C$
|
||||
-- | |_ Type: STYPE_DISKTREE_HIDDEN
|
||||
-- | |_ Comment: Default share
|
||||
-- | |_ Users: 0, Max: <unlimited>
|
||||
-- | |_ Path: C:\
|
||||
-- | |_ Anonymous access: <none>
|
||||
-- | |_ Current user ('test') access: READ
|
||||
-- | IPC$
|
||||
-- | |_ Type: STYPE_IPC_HIDDEN
|
||||
-- | |_ Comment: Remote IPC
|
||||
-- | |_ Users: 1, Max: <unlimited>
|
||||
-- | |_ Path:
|
||||
-- | |_ Anonymous access: READ <not a file share>
|
||||
-- | |_ Current user ('test') access: READ <not a file share>
|
||||
-- | test
|
||||
-- | |_ Type: STYPE_DISKTREE
|
||||
-- | |_ Comment: This is a test share, with a maximum of 7 users
|
||||
-- | |_ Users: 0, Max: 7
|
||||
-- | |_ Path: C:\Documents and Settings\Ron\Desktop\test
|
||||
-- | |_ Anonymous access: <none>
|
||||
-- |_ |_ Current user ('test') access: READ/WRITE
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
author = "Ron Bowes"
|
||||
@@ -73,194 +74,107 @@ hostrule = function(host)
|
||||
return smb.get_port(host) ~= nil
|
||||
end
|
||||
|
||||
---Attempts to connect to a list of shares as the anonymous user, returning which ones
|
||||
-- it has and doesn't have access to.
|
||||
--
|
||||
--@param host The host object.
|
||||
--@param shares An array of shares to check.
|
||||
--@return List of shares we're allowed to access.
|
||||
--@return List of shares that exist but are denied to us.
|
||||
function check_shares(host, shares)
|
||||
local smbstate
|
||||
local i
|
||||
local allowed_shares = {}
|
||||
local denied_shares = {}
|
||||
local function go(host)
|
||||
local status, shares, extra
|
||||
local response = " \n"
|
||||
|
||||
-- Begin the SMB session
|
||||
status, smbstate = smb.start(host)
|
||||
-- Get the list of shares
|
||||
status, shares, extra = smb.share_get_list(host)
|
||||
if(status == false) then
|
||||
return false, smbstate
|
||||
return false, string.format("Couldn't enumerate shares: %s", shares)
|
||||
end
|
||||
|
||||
-- Negotiate the protocol
|
||||
status, err = smb.negotiate_protocol(smbstate)
|
||||
if(status == false) then
|
||||
smb.stop(smbstate)
|
||||
return false, err
|
||||
-- Find out who the current user is
|
||||
local result, username, domain = smb.get_account(host)
|
||||
if(result == false) then
|
||||
username = "<unknown>"
|
||||
domain = ""
|
||||
end
|
||||
|
||||
-- Start up a null session
|
||||
status, err = smb.start_session(smbstate, "", "", "", "", "LM")
|
||||
if(status == false) then
|
||||
smb.stop(smbstate)
|
||||
return false, err
|
||||
if(extra ~= nil) then
|
||||
response = response .. extra .. "\n"
|
||||
end
|
||||
|
||||
-- Check for hosts that accept any share by generating a totally random name (we don't use a set
|
||||
-- name because then hosts could potentially fool us. Perhaps I'm in a paranoid mood today)
|
||||
local set = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
|
||||
local share = ""
|
||||
math.randomseed(os.time())
|
||||
for i = 1, 16, 1 do
|
||||
local random = math.random(#set)
|
||||
share = share .. string.sub(set, random, random)
|
||||
end
|
||||
|
||||
share = string.format("%s", share)
|
||||
stdnse.print_debug(2, "EnumShares: Trying a random share to see if server responds properly: %s", share)
|
||||
status, err = smb.tree_connect(smbstate, share)
|
||||
if(status == false) then
|
||||
if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then
|
||||
return false, "Server doesn't return proper value for non-existent shares (returns ACCESS_DENIED)"
|
||||
end
|
||||
else
|
||||
-- If we were actually able to connect to this share, then there's probably a serious issue
|
||||
smb.tree_disconnect(smbstate)
|
||||
return false, "Server doesn't return proper value for non-existent shares (accepts the connection)"
|
||||
end
|
||||
|
||||
-- Connect to the shares
|
||||
stdnse.print_debug(2, "EnumShares: Testing %d shares", #shares)
|
||||
for i = 1, #shares, 1 do
|
||||
local share = shares[i]
|
||||
|
||||
-- Change the share to the '\\ip\share' format
|
||||
local share = string.format("%s", shares[i])
|
||||
-- Start generating a human-readable string
|
||||
response = response .. share['name'] .. "\n"
|
||||
|
||||
if(type(share['details']) ~= 'table') then
|
||||
response = response .. string.format("|_ Couldn't get details for share: %s\n", share['details'])
|
||||
else
|
||||
local details = share['details']
|
||||
|
||||
-- Try connecting to the tree
|
||||
stdnse.print_debug(3, "EnumShares: Testing share %s", share)
|
||||
status, err = smb.tree_connect(smbstate, share)
|
||||
-- If it fails, checkwhy
|
||||
if(status == false) then
|
||||
-- If the result was ACCESS_DENIED, record it
|
||||
if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then
|
||||
stdnse.print_debug(3, "EnumShares: Access was denied")
|
||||
denied_shares[#denied_shares + 1] = shares[i]
|
||||
response = response .. string.format("|_ Type: %s\n", details['sharetype'])
|
||||
response = response .. string.format("|_ Comment: %s\n", details['comment'])
|
||||
response = response .. string.format("|_ Users: %s, Max: %s\n", details['current_users'], details['max_users'])
|
||||
response = response .. string.format("|_ Path: %s\n", details['path'])
|
||||
end
|
||||
|
||||
|
||||
-- A share of 'NT_STATUS_OBJECT_NAME_NOT_FOUND' indicates this isn't a fileshare
|
||||
if(share['user_can_write'] == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then
|
||||
-- Print details for a non-file share
|
||||
if(share['anonymous_can_read']) then
|
||||
response = response .. "|_ Anonymous access: READ <not a file share>\n"
|
||||
else
|
||||
-- If we're here, an error that we weren't prepared for came up.
|
||||
-- smb.stop(smbstate)
|
||||
-- return false, string.format("Error while checking shares: %s", err)
|
||||
response = response .. "|_ Anonymous access: <none> <not a file share>\n"
|
||||
end
|
||||
|
||||
-- Don't bother printing this if we're already anonymous
|
||||
if(username ~= '') then
|
||||
if(share['user_can_read']) then
|
||||
response = response .. "|_ Current user ('" .. username .. "') access: READ <not a file share>\n"
|
||||
else
|
||||
response = response .. "|_ Current user ('" .. username .. "') access: <none> <not a file share>\n"
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Add it to allowed shares
|
||||
stdnse.print_debug(3, "EnumShares: Access was granted")
|
||||
allowed_shares[#allowed_shares + 1] = shares[i]
|
||||
smb.tree_disconnect(smbstate)
|
||||
-- Print details for a file share
|
||||
if(share['anonymous_can_read'] and share['anonymous_can_write']) then
|
||||
response = response .. "|_ Anonymous access: READ/WRITE\n"
|
||||
elseif(share['anonymous_can_read'] and not(share['anonymous_can_write'])) then
|
||||
response = response .. "|_ Anonymous access: READ\n"
|
||||
elseif(not(share['anonymous_can_read']) and share['anonymous_can_write']) then
|
||||
response = response .. "|_ Anonymous access: WRITE\n"
|
||||
else
|
||||
response = response .. "|_ Anonymous access: <none>\n"
|
||||
end
|
||||
|
||||
|
||||
|
||||
if(username ~= '') then
|
||||
if(share['user_can_read'] and share['user_can_write']) then
|
||||
response = response .. "|_ Current user ('" .. username .. "') access: READ/WRITE\n"
|
||||
elseif(share['user_can_read'] and not(share['user_can_write'])) then
|
||||
response = response .. "|_ Current user ('" .. username .. "') access: READ\n"
|
||||
elseif(not(share['user_can_read']) and share['user_can_write']) then
|
||||
response = response .. "|_ Current user ('" .. username .. "') access: WRITE\n"
|
||||
else
|
||||
response = response .. "|_ Current user ('" .. username .. "') access: <none>\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Log off the user
|
||||
smb.stop(smbstate)
|
||||
|
||||
return true, allowed_shares, denied_shares
|
||||
return true, response
|
||||
end
|
||||
|
||||
|
||||
action = function(host)
|
||||
local status, result
|
||||
|
||||
local enum_result
|
||||
local result, shared
|
||||
local response = " \n"
|
||||
local shares = {}
|
||||
local allowed, denied
|
||||
|
||||
-- Try and do this the good way, make a MSRPC call to get the shares
|
||||
enum_result, shares = msrpc.enum_shares(host)
|
||||
|
||||
-- If that failed, try doing it with brute force. This almost certainly won't find everything, but it's the
|
||||
-- best we can do.
|
||||
if(enum_result == false) then
|
||||
if(nmap.debugging() > 0) then
|
||||
response = response .. string.format("ERROR: Couldn't enum all shares, checking for common ones (%s)\n", shares)
|
||||
end
|
||||
|
||||
-- Take some common share names I've seen
|
||||
shares = {"IPC$", "ADMIN$", "TEST", "TEST$", "HOME", "HOME$", "PORN", "PR0N", "PUBLIC", "PRINT", "PRINT$", "GROUPS", "USERS", "MEDIA", "SOFTWARE", "XSERVE", "NETLOGON", "INFO", "PROGRAMS", "FILES", "WWW", "STMP", "TMP", "DATA", "BACKUP", "DOCS", "HD", "WEBSERVER", "WEB DOCUMENTS", "SHARED"}
|
||||
|
||||
-- Try every alphabetic share, with and without a trailing '$'
|
||||
for i = string.byte("A", 1), string.byte("Z", 1), 1 do
|
||||
shares[#shares + 1] = string.char(i)
|
||||
shares[#shares + 1] = string.char(i) .. "$"
|
||||
end
|
||||
end
|
||||
|
||||
-- Break them into anonymous/authenticated shares
|
||||
status, allowed, denied = check_shares(host, shares)
|
||||
status, result = go(host)
|
||||
|
||||
if(status == false) then
|
||||
if(enum_result == false) then
|
||||
-- At this point, we have nothing
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. allowed
|
||||
else
|
||||
return nil
|
||||
end
|
||||
else
|
||||
-- If we're here, we have a valid list of shares, but couldn't check them
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. allowed .. "\nShares found: " .. stdnse.strjoin(", ", shares)
|
||||
else
|
||||
return stdnse.strjoin(", ", shares)
|
||||
end
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. result
|
||||
end
|
||||
end
|
||||
|
||||
if(result == false or nmap.verbosity() == 0) then
|
||||
return response .. string.format("Anonymous shares: %s\nRestricted shares: %s\n", stdnse.strjoin(", ", allowed), stdnse.strjoin(", ", denied))
|
||||
else
|
||||
response = response .. string.format("Anonymous shares:\n")
|
||||
for i = 1, #allowed, 1 do
|
||||
local status, info = msrpc.get_share_info(host, allowed[i])
|
||||
|
||||
response = response .. string.format(" %s\n", allowed[i])
|
||||
|
||||
if(status == false) then
|
||||
stdnse.print_debug(2, "ERROR: Couldn't get information for share %s: %s", allowed[i], info)
|
||||
else
|
||||
info = info['info']
|
||||
|
||||
if(info['max_users'] == 0xFFFFFFFF) then
|
||||
info['max_users'] = "<unlimited>"
|
||||
end
|
||||
|
||||
response = response .. string.format(" |_ Type: %s\n", msrpc.srvsvc_ShareType_tostr(info['sharetype']))
|
||||
response = response .. string.format(" |_ Comment: %s\n", info['comment'])
|
||||
response = response .. string.format(" |_ Users: %s, Max: %s\n", info['current_users'], info['max_users'])
|
||||
response = response .. string.format(" |_ Path: %s\n", info['path'])
|
||||
end
|
||||
end
|
||||
|
||||
response = response .. string.format("Restricted shares:\n")
|
||||
for i = 1, #denied, 1 do
|
||||
local status, info = msrpc.get_share_info(host, denied[i])
|
||||
|
||||
response = response .. string.format(" %s\n", denied[i])
|
||||
|
||||
if(status == false) then
|
||||
stdnse.print_debug(2, "ERROR: Couldn't get information for share %s: %s", denied[i], info)
|
||||
else
|
||||
info = info['info']
|
||||
if(info['max_users'] == 0xFFFFFFFF) then
|
||||
info['max_users'] = "<unlimited>"
|
||||
end
|
||||
|
||||
response = response .. string.format(" |_ Type: %s\n", msrpc.srvsvc_ShareType_tostr(info['sharetype']))
|
||||
response = response .. string.format(" |_ Comment: %s\n", info['comment'])
|
||||
response = response .. string.format(" |_ Users: %s, Max: %s\n", info['current_users'], info['max_users'])
|
||||
response = response .. string.format(" |_ Path: %s\n", info['path'])
|
||||
end
|
||||
end
|
||||
|
||||
return response
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
1360
scripts/smb-psexec.nse
Normal file
1360
scripts/smb-psexec.nse
Normal file
File diff suppressed because it is too large
Load Diff
@@ -89,8 +89,8 @@ require 'msrpc'
|
||||
require 'smb'
|
||||
require 'stdnse'
|
||||
|
||||
local SERVICE = "nmap-pwdump"
|
||||
local PIPE = "nmap-pipe"
|
||||
local SERVICE = "nmap-pwdump-"
|
||||
local PIPE = "nmap-pipe-"
|
||||
|
||||
local FILE1 = "nselib/data/lsremora.dll"
|
||||
local FILENAME1 = "lsremora.dll"
|
||||
@@ -105,31 +105,34 @@ end
|
||||
|
||||
---Stop/delete the service and delete the service file. This can be used alone to clean up the
|
||||
-- pwdump stuff, if this crashes.
|
||||
function cleanup(host)
|
||||
--
|
||||
--@param host The host object.
|
||||
--@param share The share to clean up on.
|
||||
--@param path The local path to the share.
|
||||
--@param service_name The name to use for the service.
|
||||
function cleanup(host, share, path, service_name)
|
||||
local status, err
|
||||
|
||||
stdnse.print_debug(1, "Entering cleanup() -- errors here can generally be ignored")
|
||||
stdnse.print_debug(1, "Entering cleanup('%s', '%s', '%s') -- errors here can generally be ignored", share, path, service_name)
|
||||
-- Try stopping the service
|
||||
status, err = msrpc.service_stop(host, SERVICE)
|
||||
status, err = msrpc.service_stop(host, SERVICE .. service_name)
|
||||
if(status == false) then
|
||||
stdnse.print_debug(1, "Couldn't stop service: %s", err)
|
||||
end
|
||||
|
||||
-- os.exit()
|
||||
|
||||
-- Try deleting the service
|
||||
status, err = msrpc.service_delete(host, SERVICE)
|
||||
status, err = msrpc.service_delete(host, SERVICE .. service_name)
|
||||
if(status == false) then
|
||||
stdnse.print_debug(1, "Couldn't delete service: %s", err)
|
||||
end
|
||||
|
||||
-- Delete the files
|
||||
status, err = smb.file_delete(host, "C$", "\\" .. FILENAME1)
|
||||
status, err = smb.file_delete(host, share, "\\" .. FILENAME1)
|
||||
if(status == false) then
|
||||
stdnse.print_debug(1, "Couldn't delete %s: %s", FILENAME1, err)
|
||||
end
|
||||
|
||||
status, err = smb.file_delete(host, "C$", "\\" .. FILENAME2)
|
||||
status, err = smb.file_delete(host, share, "\\" .. FILENAME2)
|
||||
if(status == false) then
|
||||
stdnse.print_debug(1, "Couldn't delete %s: %s", FILENAME2, err)
|
||||
end
|
||||
@@ -140,16 +143,16 @@ function cleanup(host)
|
||||
end
|
||||
|
||||
|
||||
function upload_files(host)
|
||||
function upload_files(host, share)
|
||||
local status, err
|
||||
|
||||
status, err = smb.file_upload(host, FILE1, "C$", "\\" .. FILENAME1)
|
||||
status, err = smb.file_upload(host, FILE1, share, "\\" .. FILENAME1)
|
||||
if(status == false) then
|
||||
cleanup(host)
|
||||
return false, string.format("Couldn't upload %s: %s\n", FILE1, err)
|
||||
end
|
||||
|
||||
status, err = smb.file_upload(host, FILE2, "C$", "\\" .. FILENAME2)
|
||||
status, err = smb.file_upload(host, FILE2, share, "\\" .. FILENAME2)
|
||||
if(status == false) then
|
||||
cleanup(host)
|
||||
return false, string.format("Couldn't upload %s: %s\n", FILE2, err)
|
||||
@@ -183,18 +186,19 @@ function read_and_decrypt(host, key, pipe)
|
||||
break
|
||||
end
|
||||
|
||||
stdnse.print_debug(1, "WaitForNamedPipe() failed: %s (this may be normal behaviour)", wait_result)
|
||||
j = j + 1
|
||||
-- TODO: Wait 50ms, if there's a time when we get an actual sleep()-style function.
|
||||
-- Wait 50ms, if there's a time when we get an actual sleep()-style function.
|
||||
stdnse.sleep(.05)
|
||||
until status == true
|
||||
|
||||
if(j == 100) then
|
||||
if(j == 10) then
|
||||
smbstop(smbstate)
|
||||
return false, "WaitForNamedPipe() failed, service may not have been created properly."
|
||||
end
|
||||
|
||||
-- Get a handle to the pipe
|
||||
status, create_result = smb.create_file(smbstate, "\\" .. pipe)
|
||||
local overrides = {}
|
||||
status, create_result = smb.create_file(smbstate, "\\" .. pipe, overrides)
|
||||
if(status == false) then
|
||||
smb.stop(smbstate)
|
||||
return false, create_result
|
||||
@@ -267,8 +271,28 @@ function go(host)
|
||||
local key = ""
|
||||
local i
|
||||
|
||||
local result
|
||||
local service_name
|
||||
local share, path
|
||||
|
||||
result, service_name = smb.get_uniqueish_name(host)
|
||||
if(result == false) then
|
||||
return false, string.format("Error generating service name: %s", service_name)
|
||||
end
|
||||
stdnse.print_debug("pwdump: Generated static service name: %s", service_name)
|
||||
|
||||
-- Try and find a share to use.
|
||||
result, share, path = smb.share_find_writable(host)
|
||||
if(result == false) then
|
||||
return false, string.format("Couldn't find a writable share: %s", share)
|
||||
end
|
||||
if(path == nil) then
|
||||
return false, string.format("Couldn't find path to writable share (we probably don't have admin access): '%s'", share)
|
||||
end
|
||||
stdnse.print_debug(1, "pwdump: Found usable share %s (%s)", share, path)
|
||||
|
||||
-- Start by cleaning up, just in case.
|
||||
cleanup(host)
|
||||
cleanup(host, share, path, service_name)
|
||||
|
||||
-- It seems that, in my tests, if a key contains either a null byte or a negative byte (>= 0x80), errors
|
||||
-- happen. So, at the cost of generating a weaker key (keeping in mind that it's already sent over the
|
||||
@@ -280,42 +304,42 @@ function go(host)
|
||||
end
|
||||
|
||||
-- Upload the files
|
||||
status, err = upload_files(host)
|
||||
status, err = upload_files(host, share)
|
||||
if(status == false) then
|
||||
stdnse.print_debug(1, "Couldn't upload the files: %s", err)
|
||||
cleanup(host)
|
||||
cleanup(host, share, path, service_name)
|
||||
return false, string.format("Couldn't upload the files: %s", err)
|
||||
end
|
||||
|
||||
-- Create the service
|
||||
status, err = msrpc.service_create(host, SERVICE, "c:\\servpw.exe")
|
||||
status, err = msrpc.service_create(host, SERVICE .. service_name, path .. "\\servpw.exe")
|
||||
if(status == false) then
|
||||
stdnse.print_debug(1, "Couldn't create the service: %s", err)
|
||||
cleanup(host)
|
||||
cleanup(host, share, path, service_name)
|
||||
|
||||
return false, string.format("Couldn't create the service on the remote machine: %s", err)
|
||||
end
|
||||
|
||||
-- Start the service
|
||||
status, err = msrpc.service_start(host, SERVICE, {PIPE, key, tostring(string.char(16)), tostring(string.char(0)), "servpw.exe"})
|
||||
status, err = msrpc.service_start(host, SERVICE .. service_name, {PIPE .. service_name, key, tostring(string.char(16)), tostring(string.char(0)), "servpw.exe"})
|
||||
if(status == false) then
|
||||
stdnse.print_debug(1, "Couldn't start the service: %s", err)
|
||||
cleanup(host)
|
||||
cleanup(host, share, path, service_name)
|
||||
|
||||
return false, string.format("Couldn't start the service on the remote machine: %s", err)
|
||||
end
|
||||
|
||||
-- Read the data
|
||||
status, results = read_and_decrypt(host, key, PIPE)
|
||||
status, results = read_and_decrypt(host, key, PIPE .. service_name)
|
||||
if(status == false) then
|
||||
stdnse.print_debug(1, "Error reading data from remote service")
|
||||
cleanup(host)
|
||||
cleanup(host, share, path, service_name)
|
||||
|
||||
return false, string.format("Failed to read password data from the remote service: %s", err)
|
||||
end
|
||||
|
||||
-- Clean up what we did
|
||||
cleanup(host)
|
||||
cleanup(host, share, path, service_name)
|
||||
|
||||
return true, results
|
||||
end
|
||||
|
||||
@@ -27,7 +27,12 @@ author = "Ron Bowes"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"discovery", "safe"}
|
||||
|
||||
-- Set the runlevel to above 1 to ensure this runs after the bulk of the scripts. That lets us more effectively
|
||||
-- find out which account we've been using.
|
||||
runlevel = 1.01
|
||||
|
||||
require 'smb'
|
||||
require 'stdnse'
|
||||
|
||||
-- Check whether or not this script should be run.
|
||||
hostrule = function(host)
|
||||
@@ -39,6 +44,7 @@ action = function(host)
|
||||
|
||||
local state
|
||||
local status, err
|
||||
local overrides = {}
|
||||
|
||||
status, state = smb.start(host)
|
||||
if(status == false) then
|
||||
@@ -49,7 +55,7 @@ action = function(host)
|
||||
end
|
||||
end
|
||||
|
||||
status, err = smb.negotiate_protocol(state)
|
||||
status, err = smb.negotiate_protocol(state, overrides)
|
||||
|
||||
if(status == false) then
|
||||
smb.stop(state)
|
||||
@@ -63,6 +69,11 @@ action = function(host)
|
||||
local security_mode = state['security_mode']
|
||||
|
||||
local response = ""
|
||||
|
||||
local result, username, domain = smb.get_account(host)
|
||||
if(result ~= false) then
|
||||
response = string.format("Account that was used for smb scripts: %s\%s\n", domain, stdnse.string_or_blank(username, '<blank>'))
|
||||
end
|
||||
|
||||
-- User-level authentication or share-level authentication
|
||||
if(bit.band(security_mode, 1) == 1) then
|
||||
|
||||
66
scripts/smbv2-enabled.nse
Normal file
66
scripts/smbv2-enabled.nse
Normal file
@@ -0,0 +1,66 @@
|
||||
description = [[
|
||||
Check whether or not a server is running the SMBv2 protocol.
|
||||
]]
|
||||
---
|
||||
--@usage
|
||||
-- nmap --script smbv2-enabled.nse -p445 <host>
|
||||
-- sudo nmap -sU -sS --script smbv2-enabled.nse -p U:137,T:139 <host>
|
||||
--
|
||||
--@output
|
||||
-- Host script results:
|
||||
-- |_ smb-v2-enabled: Server supports SMBv2 protocol
|
||||
--
|
||||
-- Host script results:
|
||||
-- |_ smb-v2-enabled: Server doesn't support SMBv2 protocol
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
author = "Ron Bowes"
|
||||
copyright = "Ron Bowes"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"default", "safe"}
|
||||
|
||||
require 'msrpc'
|
||||
require 'smb'
|
||||
require 'stdnse'
|
||||
|
||||
hostrule = function(host)
|
||||
return smb.get_port(host) ~= nil
|
||||
end
|
||||
|
||||
local function go(host)
|
||||
local status, smbstate, result
|
||||
local dialects = { "NT LM 0.12", "SMB 2.002", "SMB 2.???" }
|
||||
local overrides = {dialects=dialects}
|
||||
|
||||
status, smbstate = smb.start(host)
|
||||
if(not(status)) then
|
||||
return false, "Couldn't start SMB session: " .. smb
|
||||
end
|
||||
|
||||
status, result = smb.negotiate_protocol(smbstate, overrides)
|
||||
if(not(status)) then
|
||||
if(string.find(result, "SMBv2")) then
|
||||
return true, "Server supports SMBv2 protocol"
|
||||
end
|
||||
return false, "Couldn't negotiate protocol: " .. result
|
||||
end
|
||||
|
||||
return true, "Server doesn't support SMBv2 protocol"
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
local status, result = go(host)
|
||||
|
||||
if(not(status)) then
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. result
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user