http://www.skullsecurity.org/blog/?p=379
Hopefully you all read last week's post. It's a good introduction on what you need to know about smb-psexec.nse before using it, but I realize it's a little dry in terms of things you can do. I'm hoping to change that this week, though, as I'll be going over a bunch of sample configurations.
For what it's worth, this information is lifted, though heavily modified, from the NSEDoc for the script.
The configuration file for smb-psexec.nse is stored in the nselib/data/psexec directory. Depending on which operating system you're on, and how you install Nmap, it might be in one of the following places:
Note that up to and including Nmap 5.10BETA1, this folder will be missing on Windows. You'll need to download the Linux install and extract the 'psexec' folder.
The configuration file is mostly a module list. Each module is a program to run, and it's defined by a Lua table. It has, for example, the executable to run and the arguments to pass to it. There are all kinds of options, but it's probably easiest just to look at an example (note: all examples can be found in examples.lua, which is included with Nmap).
To test this stuff out, copy one of the built-in configuration files (such as example.lua), edit it, and run it with:
nmap -p139,445 -d --script=smb-psexec --script-args=smbuser=<username>,smbpass=<password>,config=<yourfilename> <target>
For example:
nmap -p139,445 -d --script=smb-psexec --script-args=smbuser=ron,smbpass=nor,config=experimental 10.0.0.123
mod = {} mod.upload = false mod.name = "Example 1: Membership of 'administrators'" mod.program = "net.exe" mod.args = "localgroup administrators" table.insert(modules, mod)
Let's take a closer look at the fields.
mod.upload is false, indicating that the program is already present on the remote system. Since the program is 'net.exe', it's installed by default on Windows and, obviously, doesn't have to be uploaded.
mod.name is simply used as part of the output.
mod.program and mod.args obviously define which program to run and the arguments to run it with. In this case, we're running "net localgroup administrators", which displays the list of administrators on the remote system. The output for this script is this:
| Example 1: Membership of 'administrators' | | Alias name administrators | | Comment Administrators have complete and unrestricted access to the computer/domain | | | | Members | | | | ------------------------------------------------------------------------------- | | Administrator | | ron | | test | | The command completed successfully. | | | |_
That works, but it's pretty ugly. If you're scanning a lot of systems, you're going to end up with a lot of empty lines and other junk that you just don't need.
But, there's a solution! We can use some other fields to clean up the output, including:
A couple things worth noting here. This cleanup is done on the client, after the data has been returned from the server. So, if you're worried about a password field crossing the wire, or your output is thousands of lines, this isn't going to speed anything up. Additionally, the patterns are Lua patterns.
So let's apply a few of these fields to our previous output:
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)
Now we're using mod.remove to remove some of the crap, as well as mod.noblank to remove the blank lines.
We can see that the output is now much cleaner:
| Example 2: Membership of 'administrators', cleaned | | Administrator | | ron | |_test
For our next command, we're going to run ipconfig.exe, which outputs a significant amount of information. Let's say that all we want is the IP address and MAC address. Let's look at how to do it.
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)
Go ahead and type ipconfig /all to see what it looks like; I'll wait right here.
This time, we use the mod.find to list the lines we want. Obviously, we're looking for three patterns: "IP Address", "Physical Address", and "Ethernet adapter". Then, we use mod.replace to replace ". " with nothing, "-" with ":", and "Physical Address" with "MAC Address" (arguably unnecessary). Here's the final output:
| Example 3: IP Address and MAC Address | | Ethernet adapter Local Area Connection: | | MAC Address: 00:0C:29:12:E6:DB | |_ IP Address: 192.168.1.21
Next topic: variables!
Variables can be used in any field in the configuration file, such as find/replace, arguments, program name, etc. There are two types of variables: built-in and user-supplied.
Built-in variables are set by the script. There are tons of them available, ranging in usefulness. Here are a bunch of them (I tried to put the more useful ones at the top):
User-supplied variables are provided on the commandline (in the --script-args argument) by the user when he or she runs the program. For example, to set the $test variable to 123, the user would pass --script-args=123. The required variables are controlled by the mod.req_args field in the configuration file, so to make $test a required field, you'd add mod.req_args to "test".
Here is a module that pings the local ip address, $lhost, which is a built-in variable:
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)
And the output:
| Example 4: Can the host ping our address? | | Pinging 192.168.1.100 with 32 bytes of data: | | Reply from 192.168.1.100: bytes=32 time<1ms TTL=64 | | Reply from 192.168.1.100: bytes=32 time<1ms TTL=64 | | Reply from 192.168.1.100: bytes=32 time<1ms TTL=64 | |_Reply from 192.168.1.100: bytes=32 time<1ms TTL=64
And this module pings an arbitrary address that the user is expected to give, $host:
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)
And the output:
$ ./nmap -n -d -p445 --script=smb-psexec --script-args=smbuser=test,smbpass=test,config=examples,host=1.2.3.4 192.168.1.21
| Example 5: Can the host ping 1.2.3.4? | | Pinging 1.2.3.4 with 32 bytes of data: | | Request timed out. | | Request timed out. | | Request timed out. | |_Request timed out.
For the final example, we'll use the 'upload' setting to upload the "fgdump.exe", run it, download its output file, and clean up its logfile. You'll have to put fgdump.exe in the same folder as the script for this to work:
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)
The -l argument for fgdump (in mod.args) supplies the name of the logfile. That file is listed in the mod.tempfiles field. What, exactly, does mod.tempfiles do? It simply gives Nmap a list of files to delete after the program runs. It's good for deleting logfiles other other artifacts your programs leave.
mod.url is displayed to the user in an error message if mod.program isn't found in Nmap's data directory. And finally, mod.outfile is the file that is downloaded from the system, since fgdump.exe doesn't print to stdout (pwdump6, for example, doesn't require mod.outfile).
The following is a list of all possible fields in the 'mod' variable:
As you can see, there are a ton of options. Check out my default scripts for more ideas/examples!