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