In our internal engagement service line, we simulate an attacker on a corporate network, which is usually Windows-based. We use a variety of tools to gather information, but we were frustrated by reliability, performance and logging of tools dealing with scanning SMB shares, and so we wrote a small Impacket-based tool as a replacement.

The main use-case is as follows: you have credentials for a user account and a list of hosts, and you want to find out what SMB shares are accessible for that account on each host. You might be surprised at how many shares there are on a normal corporate network. For pentesters, scanning them lets us find shares that contain secrets that shouldn’t be exposed. Is there a Users share with everyone’s files? Does a workstation have its C:\\ drive accidentally exposed? Does someone in IT have a share with documentation and credentials for all admin accounts?

Scanning hosts with the SMB protocol can expose a bunch of other useful information too, like:

  • It’s an easy way to test local accounts. Found the password for a local Administrator account? Scan the whole network to find out if any other hosts have the same password. If you have the NTLM hash, you don’t even need the plaintext password.
  • It can be used to find admin access. Given a local or domain account, does it have any one-off admin access?
  • Establishing a SMB connection exposes the Windows version, the hostname, and various protocol versions and options.

smbls will establish a connection to each host in parallel, authenticate, retrieve the list of shares, and then dump all the information to a file. This can be done massively parallel because the bottleneck is I/O for each individual host, and this is exploited to make smbls very fast. The output file contains a lot of data, and was designed to be easy to parse and pull data out of. Data can be manually extracted using something like jq, or it can be fed into a more complex pipeline.

Here’s a made-up example that is very similar to ways we’ve used smbls on an internal (commands work with release 1.0.0):

Step 1

We put together a list of hosts that have port 445 for SMB open in targets.txt.

$ smbls -c /: -o unauth.json targets.txt
0/100 scanned 10.0.0.1 with _, error: auth
...
$ jq -r '.[] | select(.shares) | {ip: (.info.getRemoteHost), host: (.info.getServerDNSHostName), readshares: [.shares[] | select(.access != "") | {name: .name, type: .type, remark: .remark}]} | select(.readshares != [])' unauth.json
{
    "ip": "10.1.2.3",
    "host": "workstation123.example.com",
    "readshares": [
        {
            "name": "MyShare",
            "type": "DISKTREE",
            "remark": ""
        }
    ]
}

This revealed that there was a share called “MyShare” on “workstation123.example.com” that was accessible without authentication. Using other techniques, assume that we find credentials for a domain user account here.

Step 2

Now we scan with a domain account:

$ smbls -c example/jsmith:Password123 -O . targets.txt
0/100 scanned 10.0.0.1 with example_jsmith,
...
$ jq -r '.[]|select(.admin)|.info.getServerDNSHostName' example_jsmith.json
server456.example.com

This shows that we found a server where our account had admin access. With this access, we can use other tools and techniques to dump cached credentials and hashes.

Step 3

We can scan for other computers using the same local Administrator password by using a hash from server456.example.com:

$ smbls -c localhost/Administrator#aad3b435b51404eeaad3b435b51404ee:cb488e39380c4a9f3753b3d6fea7f550 -o admin_server456.json targets.txt
0/100 scanned 10.0.0.1 with localhost_Administrator,
...
$ jq -r '.[]|select(.admin)|.info.getServerDNSHostName' admin_server456.json
server456.example.com
server789.example.com
...

From this we’ve found a bunch of other servers that we also have admin access on. Maybe we can find domain admin creds on one of these. :)

You can grab a copy of the source code from GitHub (issues welcome) or install it with pip install smbls.