Check\ Your\ Tools

There’s a one-liner password spray script that a lot of folks use to see if anyone on a domain is using a bad password like LetMeIn! or Winter2015. It reads a list of users from a file, a list of passwords (or just one password, if you’ve got a healthy streak of paranoia about account lockouts) from another, and uses them to try to connect to the IPC share on a domain controller.

Like most one-liners, it’s easy to ruin the whole thing while substituting in the customer-specific parts. So, it’s good to test it on your own user account before you run it against the whole domain. If it finds your password (which you’ve given it) then you’ve got the syntax right, and it’ll find the others, too.

On a recent engagement, it didn’t work out that way. The sanity check failed to find the test account’s password, even though it was in the file to try. What followed was a lesson in paying attention, figuring out why things worked the way they did, and some manual reading.

The first steps in troubleshooting are to describe the problem clearly, then narrow a test case down to the smallest number of variables.

Problem: Test Fails with Valid Credentials

 The whole command looks like this:

@FOR /F %p in (password.txt) DO @FOR /F %n in (users.txt) DO @net use \\dc1\IPC$ %p /user:CORP\%n 1>NUL 2>&1 && echo [*] %n:%p && @net use /delete \\dc1\IPC$ > NUL

There’s a lot going on there, and – like most one-liners – it’s not obvious where a failure might happen. To make that a bit more readable:

 @FOR /F %p in (password.txt) DO

 @FOR /F %n in (users.txt) DO

   @net use \\dc1\IPC$ %p /user:CORP\%n 1>NUL 2>&1

   && echo [*] %n:%p

   && @net use /delete \\dc1\IPC$ > NUL

That is:

For each line in the file password.txt, set the %p variable.

For each %p variable, set the %n variable from the next line in the users.txt file.

Try to connect to the IPC$ share on a domain controller, and ignore the output.

If the connection succeeded, write a string to stdout with the username and password that worked.

And then if that worked, disconnect.

Now that the whole thing is clear, it’s time to narrow things down. Start at the beginning: try reading one line from a test file, and writing it to stdout. This will make sure we’re reading the files correctly. Maybe it’s a problem with line ending sequences or character encoding, or maybe I’m not remembering how to pass a filename on the command line. Could be anything.

Um…

OK. That was quick. The problem is that I’m only getting the first word on the line, not the whole line. I thought when you gave it a file, it would read it line by line. Time to Read The Fine Manual.

Wait, what?

“…breaking it up into individual lines of text, and then parsing each line into zero or more tokens…” That was unexpected.

Tab and space are the default delimiters, but I can specify something else, so I need something that’s not in my password. This’ll work:

Yes, that works.

But can I be sure that I’ll never want to try a password with a plus in it? How about the null character?

Nope.

How about the null string?

Yay!

Now that I know it was spaces causing the problem, I know what the next problem will be: I have to quote anything with a space in it for use on the command line. This is why lazy hackers like me still try to avoid spaces in filenames. Saves all that tedious quoting. So – do I need to quote that %p when it’s used as output in the script? Let’s find out:

Quotes, Please.

It worked when I quoted it, and failed when I didn’t. So we’ll be quoting from here on out.

So, the new, more resilient password spray one-liner now looks like this:

@FOR /F “delims=” %p in (password.txt) DO @FOR /F “delims=” %n in (users.txt) DO @net use \\dc1\IPC$ “%p” /user:CORP\%n 1>NUL 2>&1 && echo [*] %n:%p && @net use /delete \\dc1\IPC$ > NUL

I should really keep that someplace where I can find it.