CIFS: Common Insecurities Fail Scrutiny
=======================================
*Hobbit*, Avian Research, hobbit@avian.org, January 1997
Abstract
========
An analysis of TCP/IP NetBIOS file-sharing protocols is presented, and the
steps involved in making a client to server SMB connection described in some
detail. Emphasis is placed on protocol and administrative vulnerabilities at
various stages and fixes/workarounds for some of them, with the hope that the
reader will better understand attacks and defenses alike. Several examples
are presented, based upon using programs from the Unix Samba package to probe
a target IP network and survey it for potential problems.
Introduction
============
We will explore the Shared Message Block protocol and related issues, at the
network level and higher, in the interest of presenting useful knowledge about
Microsoft networking [loosely aka any of CIFS, NetBEUI/NetBIOS, Lan Manager
compatible] security issues. Microsoft systems and applications, based on NT
and various flavors of Windows, are forcibly entering homes and offices the
world over and all expecting to speak SMB-based filesharing protocols among
themselves as well as with products from other vendors. As the network
security community has come to expect from most commercial offerings, these
systems are distributed with poorly configured security settings which are
seldom changed or even reviewed by their new owners before being plugged into
the Internet. This leaves many of them vulnerable to trivial attacks, and
administrators who *do* try to address the security issues often miss or
misconfigure things, perhaps making their systems less obviously vulnerable
but nonetheless still vulnerable. A major factor in the difficulty is that
many security practitioners are venturing into new territory here, which
turns out to be riddled with unexpected and undocumented pitfalls. People
relatively new to the overall networking security field, including many of
those implementing and installing said operating systems, often lack the
experience gained from other OSes and environments and have no idea where to
look for potential problems.
No specific audience is targeted here, but administrators with a primarily
Unix and NFS background that are now being asked to also support Windows and
NT environments may benefit the most from this. A necessarily Unix-centric
viewpoint is taken, since that is where the author's main strengths are, but
more importantly because Unix-based source code for a protocol implementation
is freely available. Andy Tridgell's Samba package represents an amazing
amount of very solid and still-evolving work, and allows Unix systems to
interoperate with Microsoft and Lan Manager platforms to access files and
other resources over TCP/IP networks. The examples and discussion herein
refer to the "stable release" version 1.9.15 patchlevel 8 of Samba, with
some minimal modifications geared toward exploring the security aspects
of the protocol. While not the latest release, it suffices here, and the
documentation that comes with it is highly recommended reading. The evolving
Internet-draft for the Common Internet File System, or CIFS, is also a key
reference work that expands upon original or "core" SMB and explains most of
what the boys in Redmond hope will become a full Internet standard. Their own
implementations mostly adhere to the draft, and many other vendors already
support CIFS or some subset thereof. A few issues specific to NT necessarily
appear, but NT security itself is a whole different bucket of worms and is
mostly outside the scope of this text.
So far there seems to be very little hard information available about this,
although I am aware of at least one other ongoing related effort. Several
megabytes of NT-security archives, random whitepapers, RFCs, the CIFS spec,
the Samba stuff, a few MS knowledge-base articles, strings extracted from
binaries, and packet dumps have been dutifully waded through during the
information-gathering stages of this project, and there are *still* many
missing pieces. Some compatible platforms were unavailable for testing,
notably OS/2. While often tedious, at least the way has been generously
littered with occurrences of clapping hand to forehead and muttering "crikey,
what are they *thinking*?!" The intent is not to compete against other works
in progress, it is rather to aid them in moving forward.
This document may be freely copied and quoted in whole or part, provided that
proper attribution is included. Many of the ideas contained herein are not
new, although it is possible that one or two hitherto unknown problems or
methods have been independently discovered. The point is to collect the
information into one place and describe a stepwise procedure for evaluating
this type of network environment, in a way that those of us who have hitherto
mostly shunned any dealings with Microsoft and other PC network products can
readily understand.
Groundwork: What's out there?
=============================
Little needs to be said here. Given a target network or set of IP addresses,
well-known methods can be used for finding the target hosts -- the procedure
which at least one large contractor refers to as "network contour assessment."
DNS zone dumps in conjunction with tools such as "fping" can quickly locate
active machines. To specifically locate potential SMB servers, scanning for
TCP port 139 is a fairly safe bet. In the absence of packet filtering,
connection attempts there either open or get refused so it is unnecessary to
wait around for long timeouts. If machines respond to pinging or other
connectivity tests but TCP connections to 139 time out, then it is likely that
there is a packet filter in the way protecting against NetBIOS traffic. A
Unix parallel would be running something like "rpcinfo -p" against a set of
targets to find NFS servers, which may or may not be protected by a filter
blocking traffic to the portmapper at TCP/UDP 111.
We will therefore assume having collected a list of potential SMB servers, and
proceed to attack a single target therein. Note however that information
gleaned from neighboring machines may be useful, just as in the traditional
Unix-based environment. Remembering various information about a network as a
whole and plugging it back into specific host attacks is a classic approach
amply detailed in numerous papers.
Phase 0: Name determination
===========================
To establish an SMB session to a typical target, one must not only have its IP
address but also know its "computer name." This is an arbitrary name similar
to a DNS hostname assigned by an administrator, unique within an organization
or at least a given LAN, and in many installations the computername and DNS
name are the same for administrative convenience. Name resolution is by
definition a separate entity from SMB itself, and employs a variety of methods
including static files, DNS, WINS, and local-wire broadcasts. When a machine
is running NetBIOS over TCP/IP, or "NBT", it attaches its own little name
service to UDP port 137, which makes a continual effort to both locate and
disseminate as much info as it can about services on the local LAN. One of
its functions is periodically broadcasting its own set of names on to the
local wire, to notify immediate neighbors that it exists and offers services.
IP routers generally do not forward these broadcasts, so passive receivers
outside an immediate subnet will not learn these names or which IP hosts they
belong to. Fortunately there is usually an easy way to remotely determine the
name, known as a "node status query." The name service also replies to direct
queries about certain names associated with its own particular host, and if it
is running as a WINS server it can give out even more information.
There are two basic query types -- IP address, and node status. Status query
might be more properly called name query, since sending one should elicit an
answer containing all of a target's NetBIOS names. Both are remarkably
similar in structure to DNS queries, and are indeed a variant of the DNS
protocol itself. A NetBIOS address query is for resource record type 32 and a
status query is type 33; both of class IN or 1. With traditional NetBEUI over
non-IP transports such as with local-LAN IPX, computer names are normally
uppercase, 16 bytes long, and padded with spaces which are illegal characters
in the DNS spec for hostnames. To get around this in IP environments, NetBIOS
names are mangled into a rather bizarre format. The official spec for this is
in RFCs 1001 and 1002, but to quickly sum it up: Each ASCII character in a
name is split into 4-bit halves, and each half is added to ascii value 0x41
[uppercase "A"] to form a new byte. Each original character therefore becomes
two mangled characters in the range A-P, doubling the entire length to 32
bytes. Thus, the name "FEH" gets padded out with spaces and becomes
ascii string "FEH " -- is
hex 46 45 48 20 20 20 20 20 20 20 20 20 20 20 20 20 -- split into
hex 4 6 4 5 4 8 2 0 2 0 2 0 2 0 2 0 2 0 ...etc... -- add to "A" gives
hex 45 47 45 46 45 49 43 41 43 41 43 41 ...etc... -- which is
mangled string "EGEFEICACACACACACACACACACACACACA"
The
name_mangle() routine in Samba's util.c does this translation. The
characteristic "...CACACACA" string trailer makes NetBIOS names easily
recognizable when they show up in packet dumps and such. Of particular
interest is the
wildcard name "*", but padded with *nulls* instead of spaces.
This mangles to "CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA". Under most circumstances,
name-service listeners are required to reply to queries for this wildcard name
as well as for their own computernames. Therefore sending a status query
for this "*" name is very likely to produce a name reply as resource records
containing the target's NetBIOS names, which oddly enough come back in
*non-mangled* format. Multiple copies of some names usually show up, but they
are subtly different. In practice the 16th byte of a non-mangled name is a
type byte, which is a different animal from a DNS resource-record type! When
a NetBIOS machine comes up its "name registration" broadcasts contain multiple
instances of its own name and other strings, but with several different
*NetBIOS* name types that can indicate different services. Note herein that
mangled names are of length 32 or
0x20, address queries are RR type 32, and
several returned names have *type* 0x20. Therefore a lot of 0x20s show up in
these DNS-style packets and can make things rather confusing. There seem to
be many name types, not particularly well documented except maybe in knowledge
bases or resource kits, but the important ones are
0x00 base computernames and workgroups, also in "*" queries
0x01 master browser, in magic __MSBROWSE__ cookie
0x03 messaging/alerter service; name of logged-in user
0x20 resource-sharing "server service" name
0x1B domain master-browser name
0x1C domain controller name
0x1E domain/workgroup master browser election announcement ?
The mangling example above has 0x20 as its type byte, therefore building the
name variant used when connecting to fileservers. Server and workstation
machines alike can provide various different services, and are thus usually
aware of more than one name/type at once. In fact most of them return a group
of five or so in a status reply, including the base computer name and whatever
"
workgroup" the target is a member of. Name type 0 should be used with the
special "*" query which is null-padded anyway, or a response is unlikely. If
NO name of type 0x20 is present in the list, it is unlikely that the machine
in question has been configured to share any of its own resources and attempts
to connect sessions to it will likely fail. Name type 0x3 in the reply often
reveals the
username logged in at the machine's
console, and should be
collected as a potential username to try against this or neighboring targets.
The base name may also be the same as a username, since in typical small
office environments the machines are often associated with specific people.
The special name "^A^B__MSBROWSE__^B^A" [last char being
control-A, or type 1]
indicates a "
master browser" which is a machine that collects info about
neighboring machines -- in particular, their IP addresses. A master browser
is a fortunate find since we can likely get a "browse list" from that machine
[described later] and then possibly query that same target for all the other
names and addresses it claims to know about.
One can do "nbtstat -A {ip-addr}" from a Microsoft platform to direct "*"
queries to a specific IP-aware target and obtain its name list. In the
absence of a mapping in an LMHOSTS file or some other mechanism, a specific
machine can be found using "nbtstat -a \\NAME" if it is on the local wire. An
address query is sent to the broadcast address of the connected subnet, and if
a machine responds then a unicast status query is sent to it. For reasons
unfathomable Microsoft platforms usually send status replies FROM UDP 137 TO
UDP 137, regardless of the UDP source ports of query packets, so the querying
application must locally bind to 137 [requiring root on Unix boxes] to ensure
that replies can be received. Oddly enough, *address* replies are normally
returned to whatever source port the query was from! To handle this fine
example of the IP savvy out there in Redmond, a tiny patch is needed for the
"nmblookup" Samba program, which as it comes grabs a high port and is unlikely
to receive status replies. It will then work similarly to "nbtstat" when run
as root, sending the "*" query if given the "-S \*" argument [quoting "*" to
the shell], and also accepts a *unicast* target IP as the -B argument.
Nmblookup also has an interesting feature that allows setting the hex name
type in a query -- for example, a name of the form "TARGET#1C" forces the name
type to be 0x1C. A slightly more "raw" equivalent of the generic "*" query,
which sometimes elicits a response containing no names but a response
nonetheless, can be done using netcat to locally bind UDP port 137 and send a
query. Feed the following input bytes into "nc -v -u -w 3 -p 137 target 137"
and the output through "cat -v":
0x00 # . 1
0x03 # . 2 # xid
0x00 # . 3
0x00 # . 4 # flags
0x00 # . 5
0x01 # . 6 # qcnt
0x00 # . 7
0x00 # . 8 # rcnt
0x00 # . 9
0x00 # . 10 # nscnt
0x00 # . 11
0x00 # . 12 # acnt
0x20 # 13 # namelen
0x43 # C 14 # mangled "*" ...
0x4b # K 15
0x41 # A 16
0x41 # A 17
0x41 # A 18
0x41 # A 19
0x41 # A 20
0x41 # A 21
0x41 # A 22
0x41 # A 23
0x41 # A 24
0x41 # A 25
0x41 # A 26
0x41 # A 27
0x41 # A 28
0x41 # A 29
0x41 # A 30
0x41 # A 31
0x41 # A 32
0x41 # A 33
0x41 # A 34
0x41 # A 35
0x41 # A 36
0x41 # A 37
0x41 # A 38
0x41 # A 39
0x41 # A 40
0x41 # A 41
0x41 # A 42
0x41 # A 43
0x41 # A 44
0x41 # A 45 # [embedded type byte]
0x00 # . 46 # terminator
0x00 # . 47
0x21 # ! 48 # querytype NBTSTAT
0x00 # . 49
0x01 # . 50 # class IN
In rare cases, an additional "
scope ID" may be tacked on to mangled names in
the format "EGEFEICACACACACACACACACACACACACA.scope" just like in multipart DNS
names. A scope does not contain spaces, and therefore can and indeed is sent
unchanged in hostname queries. Scope names are further discussed later under
"defenses", since they can play a role therein.
Firing "*" queries at either selected hosts or the IP subnet's directed
broadcast is another way of probing around for active SMB hosts. Most routers
do not forward directed-subnet broadcast, but ones that do may get you all the
answers in one or two shots! In most cases, scanning for TCP port 139 and
following up with unicast UDP status queries is still likely to be faster and
more reliable, especially when a target for some reason won't respond to "*"
queries. This sometimes happens if the messaging or alerter service is shut
down on the target, which is one recommended security procedure in several
documents. If you suspect this case, try asking for "WORKGROUP", parts of the
target's DNS name, and other likely strings like variants on the name of the
organization or people within it. Status-querying explicitly for a machine's
name or workgroup using type 0 should also cause it to respond, and a lack of
any type 0x3 names in the list would confirm that messaging is disabled.
Whether due to packet filters or some other reason, getting *no* reply for
all this effort is still not a reason to give up -- it is UDP after all, and
further name guesses can be plugged in during the next phase.
Phase 1: The TCP session
========================
Next we open a TCP connection to port 139 on the target. There is no longer a
need for any special local ports, so smbclient can run as a normal Unix user.
The "called" target's computername of the appropriate type and the "caller"
client name are name-mangled and plugged into a Session Request block sent to
the server. The idea here is to sanity-check the name determination step and
ensure that one is conversing with the correct machine -- especially wise in
the inevitable cases of outdated LMHOSTS files or DNS data. If the target
server's name is right a "positive response" is sent back, and the connection
remains open. If the wrong server name is passed in, a "negative response" is
sent along with an error code, and the server end of the connection starts a
TCP shutdown by sending a FIN. Nothing further can be done with the failed
connection; a new one must be opened to try a different servername. The name
of the connecting client is largely irrelevant and can even be null, although
its name type is generally 0. However, the name the client supplies is the
name that gets logged during later phases such as user logins. The client
name may also affect behavior against NT machines which have such settable
parameters as which workstations a given user may log in from. It appears
that the source IP address is *completely* irrelevant to Microsoft-based
servers, which simply accept the given client name. This is a first hint
about how much functionality is left up to the client. A vague Unix parallel
might be faking the client hostname in mount requests to be something in the
target's export list, which usually worked against early NFS implementations.
This session request is only the first of many steps taken behind the scenes
by most client commands. From a command prompt on a Microsoft box one does
"net use \\TARGET\SHARENAME" to begin access to a filesystem, or "net view
\\TARGET" to see a target's list of available services. Samba's "smbclient"
accepts the same syntax, although the backslashes need to be isolated from
the shell by enclosing in quotes or specifying \\\\TARGET\\RESOURCE. It also
accepts "-L TARGET" to list the available resources, which in any case is
what we want to do first. Smbclient by default picks up the caller name
from the hostname of the Unix machine it is running on, but we can specify
"-n fakename" to set it to something arbitrary.
An error response is usually one of two: either the passed servername wasn't
correct, or the name was right but no service of the requested name type is
running. Smbclient translates these errors respectively as "called name not
present" or "not listening on called name." Usually if server-name/type 0x20
is unreachable, the target is not sharing its resources at all and there isn't
much more we can do with it. Sessions to server-name/type 0x3 may work to
reach the messaging service and is sometimes a way to check if we got at least
one name right, but short of sending annoying messages to the console user it
is not particularly useful. Smbclient has a "-M" argument to do message
sending. The spec provides for a "not listening for CALLING name" error,
implying a potential facility for access restriction by specific client, but
today's implementations don't seem to care.
If all UDP name queries above have failed, the same sorts of guessing at the
target's computername can be tried here, one per TCP connection. If the
connection is relayed via an intermediate machine such as a proxy, the client
must still supply the correct name of the target server. Microsoft clients
can be faked out with an appropriate LMHOSTS entry with the name of the final
destination but the IP address of the *relayer*. As long as the final target
sees its own name in the request, it doesn't matter how it got there. An
example fast way to script up different LMHOSTS names on the fly would be
having "#INCLUDE ramdisk-file-name" in the main LMHOSTS file, to avoid
repeatedly writing to the hard drive just to test a bunch of targets. The
CIFS spec mentions that the magic target name "*SMBSERVER" is supposed to be
some sort of wildcard, but it is optional and no current Microsoft platforms
seem to accept it to open sessions. Samba does, simply because by design it
accepts any old pair of names for sessions and more sensibly logs the client's
IP address if appropriately configured.
Using a relay host can foil backtracing efforts by someone who notices odd
network activity or log entries and goes to investigate. A suitable relayer
program can take just about any form, such a simple netcat script, a SOCKS
gateway, or even Microsoft's own "Catapult" proxy package. The relay would
presumably listen on TCP 139 and forward the connection, but with smbclient
the relay can listen on any other port and we can supply the "-p {portnum}"
argument to reach it. If a high-port relay is already behind a packet filter
that blocks TCP 139 but allows >1024, not only is the firewall bypassed but
the resulting server connection may look like a completely normal one from a
trusted inside host.
Some Linux distributions anticipate being used as Samba servers, and come
with an "nbsession" entry in inetd.conf but no server program to handle the
connection. These will listen on TCP 139 but immediately close, while
noting an appropriate error in the syslog.
(next page)
(this whitepaper written by hobbit and found on http://www.avian.org .. refer to paragraph five above [or if you're a finical little twit (just kidding of course) you may look at the original text on the aforementioned site] to verify the legality of this reproduction.)