Nmap Development mailing list archives

Re: Bug in SMB when multiple scripts are connecting to same host


From: Chris Woodbury <woodbusy () gmail com>
Date: Mon, 28 Feb 2011 13:44:34 -0600

Ron-

Thanks for the response. Don't worry about the delay - 'better late than
never' is my motto ;). I hadn't thought of the lockout implications of
separate account lists; so, yes, you certainly wouldn't want to go that
route. With that in mind, I put some more thought into it, and it seems to
me that mutexes are the best approach.

I made a patch that adds mutexes to start_session_basic() and
start_session_extended(). My thinking was that the first script to get there
would be responsible for finding the right account (or exhausting the
possibilities), and that, once that was done, the other scripts could follow
along and already have that account waiting for their get_account() call. I
had to put in an "unlock" before each of the short-circuit returns; so, it's
not exactly pretty, but it gets the job done.

I also tested the change empirically using my testsmb script and rigorlessly
confirmed that the problem is fixed.

Let me know what you think
-chris

On Sun, Feb 27, 2011 at 7:13 PM, Ron <ron () skullsecurity net> wrote:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi Chris,

Sorry for my delayed response. I'm... well, at this point 20 days behind on
reading nmap-dev, but I'm doing my best to keep up!

I'm going to be that this vulnerability was introduced a few months ago,
when I decided to let SMB scripts run in parallel with each other - before
that, I had a mutex in the start/stop functions so no two SMB scripts could
ever go in parallel. I figured out how to run concurrent scripts in the
protocol, though, and fixed it. Apparently, I introduced this when I did
that.

I wish I could think of an easy way to fix that at the moment. Having every
script start from the first account is bad - that road leads to lockouts -
and mutexes (mutices?) could get messy since it's across a send/recv. It
*might* be possible to wrap a mutex around all the auth code (open at
negotiate, release at successful login or failure). Neither solution is very
pretty, though.

Anybody have any other ideas? I'm going to mark this as unread and go back
to it later.

(Do we have a proper bugtracker yet? ;) )

Ron

On Mon, 7 Feb 2011 19:28:18 -0600 Chris Woodbury <chris3e3 () gmail com>
wrote:
I've found what I think is a bug in the SMB library, which gets
triggered in the following situation:
1. Multiple scripts/script instances are running concurrently against
the same host via SMB and are in
smb.start_session_basic()/start_session_extended() at the same time.
2. The first entry in the SMB accounts list (nmap.registry[ IP ][
"smbaccounts" ] cannot log in.
3. There are no username overrides.

In this situation, script #1 gets the first account off the list and
tries to authenticate (unsuccessfully, as it will turn out). The
network I/O causes a switch to script #2, which also gets the first
account off the list and tries to authenticate (unsuccessfully as
well, of course). The scripts each get going again and both make it
down to the point where it is recognized that authentication has
failed (beginning on line 1216 or 1384) and smbauth.next_account() and
smbauth.get_account() are called to get the next account to try to
authenticate with. Here is the problem. Both scripts have realized
that their authentication attempts failed and call next_account(), but
next_account() and get_account() work on a list of accounts that is
shared for the host. Thus, script #1 gets the second account on the
list, but script #2 gets the *third* account on the list.

I have attached a script that should demonstrate this. If you make
another copy (e.g. testsmb2.nse) and run them both against a host that
allows anonymous logins (e.g. nmap -d2 -p 445 --script
testsmb,testsmb2 <host>), you should see one succeed and ther other
fail with "No accounts left to try." You may need to run in a couple
times to get this to happen.

Off the top of my head, it seems that this might be solved by making
the list of accounts (or the index) tied to the smbstate (and thus
specific to each script instance), or by using a mutex. I won't
pretend to understand SMB and the smb libraries like Ron does, so I
haven't tried to patch this myself.

Please let me know if I've missed or misunderstood anything, or if
anyone has questions.
-chris



Illustrative scenario: Four scripts are attempting to connect to a
share that is accessible with the anonymous login (i.e. null session).
No SMB credentials are given or discovered. The scripts aren't doing
anything fancy and are using the high-level SMB functions (e.g.
start_session_ex() ).
1. Script #1 starts first (the others are "started" but don't run
until Script #1 makes a network I/O call).
2. Script #1 calls smb.start_session_ex(), which calls smb.start(),
which calls smbauth.init_account(), which populates the account list
for the host with { guest, anonymous }.
3. Script #1's smb.start_session_ex() eventually calls
smb.start_session(), which in this example calls
smb.start_session_extended(). This calls smbauth.get_account(),
retrieving the guest account, which is then used in an authentication
attempt.
4. Script #1's authentication attempt is network I/O, so execution
switches to Script #2, which gets to the same point as Script #1, also
attempting to log in with the guest account.
5. The same for Script #3.
6. Execution switches back to Script #1, which sees that its login
attempt failed, so it calls smbauth.next_account() [smb.lua line
1386], which moves to the anonymous login. Script #1 then calls
smb.get_account() to get the new account to try, and loops back around
to make an authentication attempt with it.
7. Execution switches to Script #2, which does the same thing, except
that its call to next_account() exhausts the account list. Its call to
smb.get_account() gets an error saying there are "No accounts left to
try", which causes the original call to smb.start_session_ex() to
fail.
8. Execution switches to Script #1, which just authenticated with the
anonymous login, which was successful. Its call to
smb.start_session_ex() returns successfully.
9. Execution switches to Script #3, which does the same thing as
Script #2 in step 7. Its call to smb.start_session_ex() also fails
with "No accounts left to try".
10. Execution switches to Script #4, which has been waiting patiently.
Like the others, it starts with a call to smb.start_session_ex(), but
when it gets to the first call to smbauth.get_account() [line 1257],
there are no more accounts, and Script #4 also fails with "No accounts
left to try".

Thus, out of four scripts, only one of them was actually able to get
and use the valid credentials that were in the account list.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)

iEYEARECAAYFAk1q9rcACgkQ2t2zxlt4g/QT7QCfd24auwMBYhVylPleeHeTpP8Y
Z5MAnjFhfS5ZyWK0hZ0oTlSvGvjW79SG
=cF/r
-----END PGP SIGNATURE-----
_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://seclists.org/nmap-dev/

Attachment: smb_auth_mutex.patch
Description:

_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://seclists.org/nmap-dev/

Current thread: