A brief digression about SMB
============================
So far none of this has involved any actual Shared Message Block protocol.
The CIFS spec contains a detailed rundown on SMB packet formats. While SMB
can run over various transports including IP, here we only discuss its usual
interaction via TCP 139. A 4-byte block length is sent down the TCP stream
followed by the block itself, so the transport handlers then know how much to
read from or write to the network. SMB is thus independent of how IP-level
packets split up the stream -- it doesn't care, it just keeps reading a
connected socket until it satisfies the length's worth or times out. SMB
blocks can be up to 65536 bytes long *excluding* the length integer, but in
practice the blocks are usually smaller. SMB also trusts the TCP reliable
transport layer to segregate different client sessions. In an alternate mode
that uses UDP 138 the data blocks look almost the same, except that 12 bytes
of unused "filler" are used under UDP to pass various session and sequencing
context info. Many SMB request types support what is called the "AndX"
mechanism, which provides a way to send several requests at once. Fields in
these specify how to locate any subsequent SMB requests that were "batched"
into this block. See the spec for more information.
The Samba code builds SMB blocks into buffers using a bunch of hairy macros
with names like "SSVAL" to move short and long integers around and convert
byte-ordering. [For a fun time, try unsnarling "byteorder.h".] Since Samba
builds these internal buffers to include the 4-byte block length at offset 0,
any other offsets described here are relative to that. After the block length
comes the SMB header itself, starting at offset 4 in our reference frame with
0xFF, 'S', 'M', 'B'. A one-byte command code and several fixed-length fields
follow, ending the SMB header proper. The command code indicates the type of
SMB being requested or responded to. The request / response descriptions in
CIFS exclude the header, and only detail what follows. After the header is a
length byte and a variable-length bunch of two-byte "parameter words", and
finally any associated buffers which can contain values, strings, file data,
or whatever. A rough chart of this is given in Appendix C. The parameter
words begin at offset 37 and are where most of the work gets done; in Samba
they are called "smb_vwvN" where N is a number starting with 0. The buffers
start at a variable offset depending on how many parameter words preceded;
Samba has a routine called smb_buf() to dig through and find it. It should be
noted that while the leading length bytes are in network order, all values
inside the SMB blocks must be in "Intel" or little-endian order! In general
both the block structure *and* what gets placed into it is all rather complex
and confusing, and if it's any reassurance, the comments in earlier versions
of Samba hint that much of it started as total guesswork and verbatim copying
of block sections from packet dumps of sessions between MS boxes. As more
SMB-savvy contributors came into the Samba development picture, these blind
but somehow functional shots in the dark became better explained and recoded.
When working with NT we often encounter something called "unicode", a somewhat
warped international character encoding standard. Strings are encoded into
sequences of two-byte words, wasting twice the storage space required. This
causes the string "ABC" to appear as "41 00 42 00 43 00" in hex dumps, and
pops up in registry entries, SMB packets, and many other places. The lengths
of unicode strings are usually stored elsewhere, such as but not always in
an SMB parameter word, and there is sometimes confusion about precisely how
long any string is. For example, is unicode "ABC" of size 3 or size 6? If we
include and count a terminating null as required when sending passwords, is it
then of size 4, 7, or perhaps even 8? To make matters even worse the strings
must in theory be word-aligned in memory, and to force this to be true a
leading null is supposed to be *inserted* ahead of the first character. The
latest Samba server version contains a small fix for a common case where NT
clients cannot quite decide consistently about the length of a null password
string, and may send it as either 1 or 0.
One important part of the header we need to be aware of is two words beginning
at offset 9 -- the response class and error codes, called smb_rcls and
smb_err. These describe protocol errors in some detail, and there is a fairly
large translation table of the most common errors near the end of client.c.
The two error classes we usually ever see in practice are DOS and SERVER.
There are several different possible class/error response combinations to
describe any one kind of problem, such as failure to authenticate a user, and
which pairs get sent back depends upon what type of platform the target is.
The patch kit below includes a small routine called interpret_error() that
boils an assortment of common errors down into a couple of standard return
codes. This helps us distinguish between fatal errors, nonfatal errors and
password problems, which figures significantly in a later phase of the attack.
Some of the information here is not documented in CIFS, but can be found by
doing "net helpmsg {smb_err #}" under NT, which seems to have a very complete
set of error message texts available.
Phase 2: Dialect negotiation
============================
Assuming an open TCP connection and successful session request, SMB request
blocks may now be sent. The next step is for client and server to agree on
the "dialect" of SMB protocol they can support. Over time, SMB has evolved
from earliest "Microsoft networks" core protocol, through two types of Lan
Manager and up to the current variant that NT uses. Each new dialect adds a
couple of features, to support things like new authentication protocols and
long filenames. The client sends a list of dialects it supports as [get
this!] a bunch of null-terminated ASCII strings, including entries like
PC NETWORK PROGRAM 1.0
MICROSOFT NETWORKS 1.03
LANMAN1.0
LM1.2X002
LANMAN2.1
NT LM 0.12
which the server string-compares against dialects it recognizes and picks the
"highest" common protocol level. There is a big comment in Samba's server.c
just before reply_negprot() describing what most server platforms do with
this. A response is built and sent back to the client, containing several
important items: a numeric index into the dialect list to indicate which to
use, some security-relevant flags, and an optional 8-byte "
encryption key" to
use for
authentication. This "key" is a random challenge nonce that the
server generates and temporarily remembers. A confusingly named "session
key" is also sent, which is just some sort of unique but mostly unimportant
identifier and *not* the same as the cryptkey.
Most SMB servers support backward dialect compatibility, and even if we
support the latest NT we can always lie and exclude some of the later dialects
from the list we send. Sessions between two NT machines involve more complex
security protocols, so for our attack purposes it is definitely worth our
while to convince a server that we are a dumb old client that can't handle
the fancier stuff. Microsoft clients can't do this but smbclient can, with a
settable max_protocol variable, and we should therefore plug "-m LANMAN2" into
our command line to force the server to dumb itself down somewhat. Smbclient
also parses dialects as strings here, not numeric levels.
The security mode flags appear in smb_vwv1, and we need to pay attention to
the low two bits thereof. Smbclient tells us what this "sec mode" is at debug
level 3. The earlier NetBIOS implementations optionally required a simple
password to connect to a shared filesystem, and had no real concept of *who*
was connecting as long as the correct password was supplied. Everyone using
such a fileshare must know the single password for it, which is considered
fairly lame from a security standpoint. This is called "share-level
security", and is used by Windows for Workgroups, Samba if appropriately
configured, and maybe some other Lan Manager platforms. Later dialects have a
concept of individual user login, and indicate this "user-level security" by
setting the LSB of the security flags. The next higher bit in the flags
indicates whether the client should use "password encryption" or not. Thus if
smbclient reports "sec mode 3" as it does when connecting to most NT servers,
both of these bits are set. Sometimes we see a reference to "server-level
security", but this simply means that authentication data is forwarded to a
Domain Controller machine for validation and does not affect the mode bits.
Dialect negotiation must occur on a connection before other SMB types may be
sent. If dialect negotiation fails for some reason, the server sends a FIN
along with the response and the TCP connection must be closed and reopened.
One way to observe this is to try negotiating the dialect either twice or not
at all on a given connection. If a server is running in user-level security
and a protocol is negotiated that does not support user login at all, the
server will generally set the user-level bit anyway and wind up refusing to
allow most other SMB transactions on that connection until successful user
authentication is performed. This happens during the next phase.
Phase 3: SMB session setup
==========================
A server running in user-level security generally requires this step before
allowing access to shared resources. This phase can be skipped entirely
against share-level servers, or used anyway to pass additional info about
buffer sizes and client capabilities. Normally here is where usernames and
passwords get plugged in and the "attack" really begins. The official CIFS
name for this phase is SessionSetupAndX, implying once again that additional
SMB requests can and often are batched into this block. Note carefully that
despite the unfortunately confusing name, this "session setup" is a very
DIFFERENT animal from the RFC1001/1002-style TCP "session setup" done in
phase 0! In general the different TCP sessions distinguish between client
*machines*, while a "UID" determined during this SMB setup phase distinguishes
an individual *user* on a given client. This implies that all SMB traffic
between a given client and server pair may pass over a single TCP connection
regardless of originating user, although this is not required behavior by any
means since servers can support several concurrent TCP connections. It also
implies that multiple SMB setup requests can be sent across the single
connection instance, which is perhaps the key thing that throws it wide open
to various attacks.
The contents of this block and the server response vary somewhat depending
on the agreed dialect level and security flags. The most relevant items in
the request are a username and either a plaintext password or a hash derived
from it. Other items include maximum buffer sizes, various other client
information such as its domain and running OS [all of which can be faked up],
and perhaps further SMB commands via the AndX mechanism.
Microsoft boxes collect a username and password through one or another "logon"
dialog. Under WFWG, the simplest command-line way to change them on the fly
is "net logon {user}" which sets them up for a subsequent "net use". NT
requires a login to use the client workstation and saves the username and
password from that as default credentials for subsequent filesharing, but
these can be overridden in its "net" command line with optional /USER and
password arguments. Smbclient accepts "-U username", and asks for a password
that it will plug in at the appropriate time. The unmodified version accepts
a password on the command line as an optional argument after the sharename, or
by using the format "-U user%passwd". In many cases the password must be in
all UPPERCASE, but some servers may accept or even require mixed-case even in
LANMAN-only dialect -- this is a bit of a crap shoot, so try it both ways.
The Samba server has hooks to try a couple of different permutations in an
effort to authenticate oddball clients, with appropriate warnings about
reduced keyspace.
Under user-level security a successful login means we are basically "in" as
either the target user or a guest. The SMB response contains some strings
containing the server's OS and version, and an important SMB header field
called the UID. This is not quite the same thing as a Unix UID, although for
convenience Samba does use the Unix UID of the authenticating user here.
Microsoft servers construct an internal set of user credentials and rights and
assign the UID as a token that refers to it. The UID is in theory unique only
within the context of the enclosing TCP connection -- if multiple SMB sessions
are active across one TCP connection the UID distinguishes the separate users
there, and in theory different users on different TCP sockets could wind up
being assigned the same UID. There is also a process-ID or PID header field
that the *client* initiates, but that seems to hold little relevance except
for some file-locking calls. Again, regardless of server platform the UID is
merely a reference token and while playing games with UID/GID values may be
effective against NFS servers, trying it here it only produces "invalid UID"
SMB errors or is simply ignored by the server. The server can optionally set
a flag in the setup response that indicates that a given session is a "guest"
login. Samba does this and NT does not, but the setting of this bit seems
irrelevant to the rights a given active UID has on the server anyway.
There are several possible error responses here, which our interpret_error
routine turns into something we can recognize to mean whether to continue the
attack or give up. An unknown username and/or password in most cases comes
back as "access denied" unless unknown/null users get mapped to GUEST. There
are some errors that imply that the supplied credentials were right but there
is some other problem, such as "account disabled" or "cannot log in from the
network." In such cases further attempts with a given username will probably
be unproductive, but remember that here the TCP connection remains open
regardless of the return status, allowing ample opportunity for retries with
any other username and password. Protocol errors or transient server problems
can also occur, some of which may imply that a new TCP session is needed.
Two important usernames to try right off against Microsoft platforms are
ADMINISTRATOR and GUEST, since these usually exist out of the box and all too
often have null passwords. If the ADMINISTRATOR login has been renamed to
"something obscure" as recommended in several texts, its new name may show up
somewhere on the target network as a type 0x3 anyway. As mentioned before,
any other base computernames and type 0x3 messaging names collected from the
target network are all potential usernames. A machine running the Microsoft
web server may have an account of the form IUSR_{basename} that got quietly
created during setup, and it is said that the SQL server pulls similar stunts.
A null username or one that is unknown to the server is often accepted as a
guest login that allows some limited amount of poking around -- often enough
access to at least read files from the server if not write to them. Any hint
at an account used for disk backups in an NT environment should be pursued,
since such an account probably has "backup" privileges to read the entire
filesystem including the normally inaccessible SAM security database. If the
server is running Samba itself, a null username and password may grant guest
access. Try some Unix accounts that have known or null passwords -- Samba by
default disallows logins by accounts with null passwords, but for any
allowable ones does not check for a valid user shell like other daemons do.
Try likely null ones anyway since some sites may be configured to allow them.
An exception to the null-password rule is Samba's default "pcguest" account in
smb.conf, which many sites remap to "nobody" or something rather than create a
new /etc/passwd entry.
If the client supports password encryption, it uses the user's password as
input to one or both of two possible encryption algorithms referred to as the
LANMAN method and the NT method. These algorithms are described in CIFS in
excruciating detail, and reviewed in Appendix A here. By deliberately dumbing
down our negotiated protocol level we can eliminate the need for the NT-style
field even if connecting to an NT-dialect server. For backward compatibility
NT accepts the LANMAN password format, which completely obviates the increased
security supposedly given by long case-sensitive passwords. It is important
to understand that it is the CLIENT that chooses whether or not to use
password encryption, and the server's "use encryption" security mode bit is
just a gentle suggestion. If a server cannot authenticate via a 24-byte
crypto response it is supposed to use whatever is given AS PLAINTEXT. This is
another major weakness in the protocol spec, since a compliant server cannot
enforce use of encryption! We therefore don't even need "libdes" or the Samba
crypto support for our attack kit, we can just send plaintext passwords.
Furthermore, since at this point we can send multiple SetupAndX exchanges
REGARDLESS of whether they succeed or fail, the opportunity for brute-force
guessing is obvious. Most stock client apps are not useful as brute-forcing
engines since they exit after one or two failed authentications, but our patch
kit modifies smbclient's send_login() routine to keep trying until it either
succeeds or runs out of passwords to try.
While this phase is ripe for brute-force attacks, it is also where servers
might start logging things. Entries wind up, relative to their respective
system-root directories, in "audit.log" under Windows, "config\secevent.evt"
under the NT system directory, and "var/log.smb" on a Samba server. Microsoft
platforms [particularly NT] open their log files in an exclusive way that
prevents other processes from directly reading or modifying them, and Samba's
logfiles can be protected against normal users. Unfortunately the default
setup for what *gets* logged is weak or nonexistent. Windows seems only to
log full filesharing connection attempts, which do not happen at this phase,
and the logging is controlled via simple SYSTEM.INI lines. NT out of the box
logs NOTHING -- one must configure the NT "system policy" to *do* the logging
for both failed and successful user logins, and only the name given by the
connecting client is saved -- NOT its IP address. Other Microsoft platforms
have the same problem. Unless someone actively runs "netstat -a" during the
attack or provides some third-party enhanced logging facility, no useful
backtracing information will be saved. The Samba server by default only logs
successful filesharing connections. This pretty much lets an attacker guess
at Unix user passwords all day and never be noticed, similar to what vanilla
rexec allows. Setting up more meaningful logging gets rather involved and is
covered later under "defenses." In all cases, recall also that any TCP
connections can be run through an intermediate relay which will cause the
relay's IP address to be observed instead of the real source of an attack.
NT servers exhibit several quirks worth mentioning, most of which reveal that
the design of the authentication backend is at best naive. A cleartext
unicode NT password can be sent in smb_vwv8 but if the alignment is screwed up
or the length given as uneven, the returned error is "parameter incorrect" and
the event log entry is just "unexpected error." If a properly formed NT
password is given under NT LM dialect, encrypted or otherwise, any LANMAN
style one in smb_vwv7 is apparently ignored. Upon valid authentication, other
error codes returned can mean things like "account disabled", "network access
denied", "cannot log in from this workstation", as well as several others that
arguably give out too much information that could help guide an attack. Users
can be configured such that they can only log in from certain named clients,
but not only can the client send an arbitrary caller name, it turns out that
using either a null name or even a single space handily bypasses this silly
restriction and allows the login anyway.
NT has the capability to "lock out" accounts after some number of failed login
attempts. While there is no specific error to indicate this, it is quite easy
to remotely determine [at least against NT 4.0 with non-permanent user lockout
policy] when a temporary account lockout happens. Any failed login usually
causes the server to delay for 2 or 3 seconds before sending the SMB "access
denied" error, to slow down brute-force attacks. Attempts on a valid username
will elicit these delayed responses until the lockout threshold is reached,
and then suddenly there is NO delay anymore and subsequent guesses on the
same username are denied immediately! If account lockout is enabled, the
default threshold is between 5 and 10 tries and the lockout time is 30
minutes. Therefore in most cases it doesn't take very long to make the
lockout perceptibly happen.
If attempts on one known-to-exist username triggers login-failure lockout but
another one does not, chances are that the second one is the administrator
account. Conversely, if attempts on ADMINISTRATOR trigger lockout, it is
probably a decoy and the real one has been renamed. Lockout does not apply
to the administrative account, with the ostensible idea being prevention of
*total* denial of service attacks. This leaves ADMINISTRATOR or the
equivalent accountname open to unlimited guessing. Even the access-denied
delay can be effectively bypassed. The delay is imposed per TCP connection,
so by opening up 10 connections and pounding in different sets of passwords
an attacker gets a tenfold increase in brute-force speed. Such an attack
probably occupies significant server CPU time since not only does the event
logging go crazy, but each plaintext guess must be re-hashed on the *server*
side for comparison against the stored OWF. A workaround sometimes suggested
to combat this is an obscure registry setting that causes the whole server to
shut down when the event log fills, but that just allows an even worse denial
of service.
(prev page - top - next page)