1
0
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:
ron
2009-11-08 21:31:06 +00:00
parent d650503778
commit 7d67b08e66
22 changed files with 3875 additions and 565 deletions

View File

@@ -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", } }

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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
View 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