The analyzer is generic in the sense that it applies to more than one protocol. Currently, Bro instantiates a login analyzer for both Telnet [RFC854] and Rlogin [RFC1282] traffic. In principle, it could do the same for other protocols such as SSH [YKSRL00] or perhaps X11 [RFC1013], if one could write the corresponding elements of the event engine to decrypt the SSH session (naturally, this would require access to the encryption keys) or extract authentication information and keystrokes from the X11 event stream. Note: The analyzer does an exceedingly limited form of SSH analysis; see hot_ssh_orig_ports.
For Telnet, the event engine knows how to remove in-band Telnet option
sequences [RFC855]
from the text stream, and does not deliver these to
the event handlers, except for a few options
that the engine
analyzes in detail (such as attempts to negotiate authentication).
Unfortunately, the Telnet protocol does not include any explicit
marking of username or password information (unlike the FTP protocol,
as discussed in §
Analyzing Rlogin is nominally easier than analyzing Telnet because Rlogin has a simpler in-band option scheme, and because the Rlogin protocol explicitly indicates the username in the initial connection dialog. However, this last is not actually a help to the analyzer, because for most Rlogin servers, if the initial username fails authentication (for example, is not present in the .rhosts file local to the server), then the server falls back on the same authentication dialog as with Telnet (prompting for username and then password, or perhaps just for a password to go with the transmitted username). Consequently, the event engine employs the same set of heuristics as for Telnet.
Each connection processed by the analyzer is in a distinct state:
user attempting to authenticate, user has successfully authenticated,
analyzer is skipping any further processing, or the analyzer is
confused (§
The analyzer uses a capture filter of ``tcp port 23 or tcp port 513''
(§ <
username>
.
So, for example, if user ``smith'' successfully authenticates,
then the connection's addl field will have
"smith" appended to it:
931803523.006848 254.377 telnet 324 8891 1.2.3.4 5.6.7.8 SF L "smith"while if ``smith'' failed to authenticate, the report will look like:
931803523.006848 254.377 telnet 324 8891 1.2.3.4 5.6.7.8 SF L fail/smithand if they first tried as ``smith'' and failed, and then succeeded as ``jones'', the record would look like:
931803523.006848 254.377 telnet 324 8891 1.2.3.4 5.6.7.8 SF L fail/smith "jones"
Note: The event engine's heuristics can sometimes get out of synch such that it interprets a password as a username; in addition, users sometimes type their password when they should instead enter their username. Consequently, the connection logs sometimes include passwords in the annotations, and so should be treated as very sensitive information (e.g., not readable by any user other than the one running Bro).
|
Bro attempts to detect such confusion. If it does, then it generates a login_confused event, after which the event engine will no longer attempt to follow the authentication dialog. In particular, it will not generate subsequent login_failure or login_success events. The login_confused event includes a string describing the type of confusion, using one of the values given in Table 7.19.1.
The standard script defines a large number of variables for refining the analysis policy:
The analyzer searches for these patterns both in the raw text typed by the user and the same lines after applying editing using the edit function twice: once with interpreting BS (ctrl-H) as delete-one-character, and once with DEL as the edit character. If any of these matches, then the analyzer considers the pattern to have matched.
eggdrop Default: a pattern matching occurrences of the strings ``rewt'', ``eggdrop'', ``loadmodule'', or ``/bin/eject''. The first of these is a popular username attackers use for root backdoor accounts. The second reflects that one prevalent class of attackers are devotees of Internet Relay Chat (IRC), who frequently upon breaking into an account install the IRC eggdrop utility.
[edited_input_trouble : pattern] is the same as input_trouble except the analyzer only checks the edited user input against the pattern, not the raw input (see above).
This variable is provided so you can specify patterns that can occur innocuously as typos; whenever the user corrects the typo before terminating the line, the pattern won't match, because it won't be present in the edited version of the line. In addition, for matches to these patterns, the analyzer delays reporting the match until it sees the next line of output from the server. It then includes both the line that triggered the match and the corresponding response from the server, which makes it easy for a human inspecting the logs to tell if the occurrence of the pattern was in fact innocuous.
Here's an example of an innocuous report:
936723303.760483 1.2.3.4/21550 > 5.6.7.8/telnet input "cd ..." yielded output "ksh: ...: not found."It was flagged because the user's input included ``...'', a name commonly used by attackers to surreptitiously hide a directory containing their tools and the like. However, we see from the Telnet server's response that this was not actual access to such a directory, but merely a typing mistake.
On the other hand:
937528764.579039 1.2.3.4/3834 > 5.6.7.8/telnet input "cd ..." yielded output "maroon# ftp sunspot.sunspot.noao.edu "shows a problem--the lines returned by the server was a root prompt (``maroon
#
''), to which the user issued a command to
access a remote FTP server.
Deficiency: The analyzer should decouple the notion of waiting to receive the server's reply from the notion of matching only the edited form of the line; there might be raw inputs for which it is useful to see the server's response, and edited inputs for which the server's response is unimportant in terms of knowing that the input spells trouble.
Default: the pattern
/[ \t]*cd[ \t]+((['"]?\.\.\.)|(["'](\.[^"']*)[ \t]))/which looks for a ``cd'' command to either a directory beginning with ``...'' (optionally quoted by the user) or a directory name beginning with ``.'' that is quoted and includes an embedded blank or tab.
[output_trouble : pattern] lists patterns that the analyzer should flag if they occur in the output sent by the login server back to the user.
PATH_UTMP smashdu.c Default: the pattern
/^-r.s.*root.*\/bin\/(sh|csh|tcsh)/ | /Jumping to address/ | /smashdu\.c/ | /PATH_UTMP/ | /Log started at =/ | /www\.anticode\.com/ | /smurf\.c by TFreak/ | /Trojaning in progress/ | /Super Linux Xploit/The first of these triggers any time the user inspects with the ls utility an executable whose pathname ends in /bin/ followed by one of the popular command shells, and the ls output shows that the command shell has been altered to be setuid to root. The remainder match either the output generated by some popular exploit tools (for example, ``Jumping to address'', present in many buffer overflow exploit tools), exploit tool names (``smashdu.c''), text found within the tool source code (``smurf.c by TFreak''), or URLs accessed (say via the lynx or fetch utilities) to retrieve attack software (``www.anticode.com'').
[backdoor_prompts : pattern] lists patterns that the analyzer should flag if they are seen as the first line sent by the server to the user, because they often correspond with backdoors that offer a remote user immediate command shell access without having to first authenticate.
Default: the pattern ``/^[!-~]*( ?)[#%$] /
'', which matches
a line that begins with a series of printable, non-blank characters and
ends with a likely prompt character, with a blank just after
the prompt character and perhaps before it.
[non_backdoor_prompts : pattern] lists patterns that if a possible backdoor prompt also matches, then the analyzer should not consider the server output as indicating a backdoor prompt. Used to limit false positives for backdoor_prompts.
Default: the pattern ``/^ *#.*#/
'', which catches lines with
more than one occurrence of a #
. Some servers generate such
lines as part of their welcome banner.
[hot_terminal_types : pattern] lists ``magic'' terminal types sometimes used by attackers to access backdoors. Both Telnet and Rlogin have mechanisms for negotiating a terminal type (name; e.g., ``xterm''); these backdoors trigger and skip authentication if the name has a particular value.
VT666 Default: the name ``VT666'', one of the trigger terminal types we've observed in practice.
[hot_telnet_orig_ports : set[port]] Some Telnet backdoors trigger if the ephemeral port used by the client side of the connection happens to be a particular value. This variable is used to list the port values whose use should be considered as possibly indicating a backdoor. Note: Clearly, this mechanism can generate false positives when the client by chance happens to choose one of the listed ports.
Default: 53982/tcp, one of the trigger ports we have observed in practice.
Deficiency: There should be a corresponding variable for Rlogin backdoors triggered by a similar mechanism.
[hot_ssh_orig_ports : set[port]] Similar to hot_telnet_orig_ports, only for SSH.
Default: 31337/tcp, a trigger port that we've observed in practice.
[skip_authentication : set[string]] A set of strings that, if present in the server's initial output (i.e., its welcome banner), indicates the analyzer should not attempt to analyze the session for an authentication dialog. This is used for servers that provide public access and don't bother authenticating the user.
Default: the string "WELCOME TO THE BERKELEY PUBLIC LIBRARY", which corresponds to a frequently accessed public server in the Berkeley area. (Obviously, we include this default as an example, and not because it will be appropriate for most Bro users! But it does little harm to include it.)
Deficiency: It would be more natural if this variable and a number of others listed below were of type pattern rather than set[string]. They are actually converted internally by the event engine into regular expressions.
[direct_login_prompts : set[string]] A set of strings that if seen during the authentication dialog mean that the user will be logged in as soon as they answer the prompt.
Default: "TERMINAL?", a prompt used by some terminal servers.
[login_prompts : set[string]] A set of strings corresponding to login username prompts during an authentication dialog.
Default: the strings
Login: login: Name: Username: User: Member Nameand the default contents of direct_login_prompts.
[login_failure_msgs : set[string]] A set of strings that if seen in text sent by the server during the authentication dialog correspond to a failed login attempt.
Default: the strings
invalid Invalid incorrect Incorrect failure Failure, User authorization failure, Login failed, INVALID Sorry, Sorry.
[login_non_failure_msgs : set[string]] A set of strings similar to login_failure_msgs that if present mean that the server text does not actually correspond to an authentication failure (i.e., if login_failure_msgs also matches, it's a false positive).
Default: the strings
Failures failures failure since last successful login failures since last successful login
[router_prompts : set[string]] A set of strings corresponding to prompts returned by the local routers when a user successfully authenticates to the router. For the purpose of this variable, see the next variable.
Default: empty.
[login_success_msgs : set[string]] A set of strings that if seen in text sent by the server during the authentication dialog correspond to a successful authentication attempt.
Default: the strings
Last login Last successful login Last successful login checking for disk quotas unsuccessful login attempts failure since last successful login failures since last successful loginand the default contents of the router_prompts variable.
Deficiency: Since by default router_prompts is empty, this last inclusion does nothing. In particular, if you redefine router_prompts then login_success_msgs will not pick up the change; you will need to redefine it to (again) include router_prompts, using: redef login_success_msgs += router_prompts. This is clearly a misfeature of Bro and will be fixed one fine day.
[login_timeouts : set[string]] A set of strings that if seen in text sent by the server during the authentication dialog correspond to the server having timed out the authentication attempt.
Default: the strings
timeout timed out Timeout Timed out Error reading command input(This last is returned by the VMS operating system.)
[non_ASCII_hosts : set[addr]] A set of addresses corresponding to hosts whose login servers do not (primarily) use 7-bit ASCII. The analyzer will not attempt to analyze authentication dialogs to such hosts, and will not complain about huge lines generated by either the sender or receiver (per excessive_line).
Default: empty.
[skip_logins_to : set[addr]] A set of addresses corresponding to hosts for which the analyzer should not attempt to analyze authentication dialogs.
Default: the (empty) contents of non_ASCII_hosts.
[always_hot_login_ids : set[string]] A set of usernames
that the analyzer should always flag as sensitive, even if they're seen in
a session for which the analyzer is confused (§
Default: the value of always_hot_ids defined by the hot analyzer.
[hot_login_ids : set[string]] A set of usernames
that the analyzer should flag as sensitive, unless it sees them
in a session for which the analyzer is confused (§
Default: the value of hot_ids defined by the hot-ids analyzer.
[rlogin_id_okay_if_no_password_exposed : set[string]] A set of username exceptions to hot_login_ids which the analyzer should not flag as sensitive if the user authenticated without exposing a password (so, for example, via .rhosts).
Default: the username "root".
The standard login script provides the following functions for external use:
[hot_login(c: connection, msg: string, tag: string) ]
Marks the given connection as hot, logs the given message, and
demultiplexes (§
[is_hot_id(id: string, successful: bool, confused: bool): bool ]
Returns true if the username id should be considered sensitive,
given that the user either did or did not successfully authenticate,
and that the analyze was or was not in a confused state
(§
[is_forbidden_id(id: string): bool ] Returns true if the username id is present in forbidden_ids or forbidden_id_patterns.
[edit_and_check_line(c: connection, line: string, successful: bool): check_info ]
Tests whether the given line of text seen on connection c includes
a sensitive username, after first applying BS and DEL
keystroke editing (§
The return value is a check_info record, which contains four check_info fields:
"then the different editing interpretations are "rob<
DEL><
BS><
BS>ot
"
ro<
BS><
BS>ot
"
and "root"
, so the return value will be:
"rob<
DEL><
BS><
BS>ot
,ro<
BS><
BS>ot
,root"
Deficiency: Ideally, these values would be returned in a list of some form, so that they can be accessed separately and unambiguously. The current form is really suitable only for display to a person, and even that can be quite confusing if line happens to contain commas already. Or, perhaps an algorithm of ``simply pick the shortest'' would find the correct editing every time anyway.
[edit_and_check_user(c: connection, user: string, successful: bool, fmt_s: string): bool ]
Tests whether the given username used for authentication on connection c
is sensitive, after first applying BS and DEL
keystroke editing (§
fmt_s is a fmt format specifying how the username information should be included in the connection's addl field. It takes two string parameters, the current value of the field and the expanded version of the username as described in expanded_line.
If edit_and_check_line indicates that the username is sensitive,
then edit_and_check_user records the connection into its own
demultiplexing files (§
Returns true if the connection is now considered ``hot,'' either due to having a sensitive username, or because it was hot upon entry to the function.
[edit_and_check_password(c: connection, password: string): bool ] Checks the given password to see whether it contains a sensitive username. If so, then marks the connection as hot and logs the sensitive password. No return value.
Note: The purpose of this function is to catch instances in which the event engine becomes out of synch with the authentication dialog and mistakes what is, in fact, a username being entered, for a password being entered. Such confusion can come about either due to a failure of the event engine's heuristics, or due to deliberate manipulation of the event engine by an attacker.
The standard login script handles the following events:
The analyzer first generates an account_tried
event to facilitate detection of password guessing, and then checks for
a sensitive username or password. If the username was not sensitive
and the password is empty, then no further analysis is applied, since
clearly the attempt was half-hearted and aborted. Otherwise, the
analyzer annotates the connection's addl
field with fail/<
username>
to mark the
authentication failure, and also checks the client_user to
see if it is sensitive. If we then find that the connection is
hot, the analyzer logs a message to that effect.
[login_success (c: connection, user: string, client_user: string, password: string, line: string)] Invoked when the event engine has seen a successful attempt to authenticate. The parameters are the same as for login_failure.
The analyzer invokes check_hot with mode APPL_ESTABLISHED
since the application session has now been established. It generates
an account_tried
event to facilitate detection of password guessing, and then checks for
a sensitive username or password. The event engine uses the special
password "<
none>
" to indicate that no password
was exposed, and this mitigates the sensitivity of logins using particular
usernames per rlogin_id_okay_if_no_password_exposed.
The analyzer annotates the connection's addl
field with "<
username>
" to mark the
successful authentication. Finally, if we then find that the connection
is hot, the analyzer logs a message to that effect.
[login_input_line (c: connection, line: string)]
Invoked for every line of text sent by the client side of the login
session to the server side. The analyzer matches the text against
input_trouble and edited_input_trouble and invokes
hot_login with a tag of "trb" if it sees a match,
which will log an alert concerning the connection. However, this
invocation is only done while the connection's hot
field count is , to avoid cascaded alerts when an attacker gets
really busy and steps on a lot of sensitive patterns.
[login_output_line (c: connection, line: string)] Invoked for every line of text sent by the server side of the login session to the client side. The analyzer checks backdoor_prompts and any pending input alerts that were waiting on the server output, per edited_input_trouble. These last are then logged unless the output matched the pattern:
/No such file or directory/Deficiency: Clearly, this pattern should not be hardwired but instead specified by a redefinable variable.
Finally, if the line is not too long and the text matches
output_trouble and the connection's hot
field count is (to avoid cascaded alerts), the analyzer
invokes hot_login with a tag of "trb".
Deficiency: ``Too long'' is hardwired to be a length
bytes.
It, too, should be specifiable via a redefinable variable. Note: We might
wonder if not checking overly long lines presents an evasion threat: the
attacker can bury their access to a sensitive string in an excessive line
and thus avoid detection. While this is true, it doesn't appear to cost
much. First, some of the sensitive patterns are generated in server output
that will be hard to manipulate into being overly long. Second, if the
attacker is trying to avoid detection, there are easier ways, such as
passing their output through a filter that alters it a good deal.
[login_confused (c: connection, msg: string, line: string)]
Invoked when the event engine's heuristics have concluded that they
have become confused and can no longer correctly track the authentication
dialog (§
Once declaring that it's confused, the event engine will no longer attempt to follow the authentication dialog. In particular, it will not generate subsequent login_failure or login_success events.
Upon this event, the standard
login script invokes check_hot with
mode APPL_ESTABLISHED since it could well be that the application
session is now established (it can't know for sure, of course, because
the event engine has given up). It annotates the connection's
addl field with
confused/<
line>
to mark the confused state,
and then logs to the weird file the particulars of the
connection and the type of confusion (msg). Deficiency: This should
be done by generating a weird-related event instead.
Finally, the analyzer invokes set_record_packets to specify that all of the packets associated with this connection should be recorded to the trace file. Note: For the current login analyzer, this call is not needed--it records every packet of every login session anyway, because the generally philosophy is that Bro should record whatever it analyzes, so that the analysis may be repeated or examined in detail. Since the current analyzer looks at every input and output line via login_input and login_output, it records all of the packets of every such analyzed session. There is commented-out text in login_success to be used if login_input and login_output are not being used; it turns off recording of a session's packets after the user has successfully logged in (assuming the connection is not considered hot).
[login_confused_text (c: connection, line: string)]
Invoked for every line the user types after the event engine has
entered the confused state. If the connection is not already
considered hot, then the analyzer checks for the presence of sensitive
usernames in the line using edit_and_check_line, and, if
present, annotates the connection's addl field
with confused/<
line>
, logs that the connection
has become hot, and invokes set_record_packets to record
to the trace file all of the packets associated with the connection.
[login_terminal (c: connection, terminal: string)] Invoked when the client transmits a terminal type to the server. The mechanism by which the client transmits the type depends on the underlying protocol (Rlogin or Telnet).
The handler checks the terminal type against hot_terminal_types and if it finds a match invokes hot_login with a tag of "trb".
[excessive_line (c: connection)] Invoked when the event engine observes a very long line sent by either the client or the server. Such long lines are seen as potential attempts by an attacker to evade the login analyzer; or, possibly, as a Login session carrying an unusual application. Note: One example we have observed occurs when a high-bandwidth binary payload protocol such as Napster is sent over the Telnet or Rlogin well-known port in an attempt to either evade detection or tunnel through a firewall.
This event is actually generic to any TCP connection carrying an application that uses the ``Network Virtual Terminal'' (NVT) abstraction, which presently comprises Telnet and FTP. But the only handler defined in the demonstration Bro policy is for Telnet, hence we discuss it here. For this reason, the handler first invokes is_login_conn to check whether the connection is in fact a login session. If so, then if the connection is not hot, and if the analyzer finds the server listed in non_ASCII_hosts, then it presumes the long line is due to use of a non-ASCII character set; the analyzer invokes set_login_state and set_record_packets to avoid further analysis or recording of the connection.
Otherwise, if the connection is still in the authentication dialog, then the handler generates a login_confused event with a confusion-type of "excessive_line", and changes the connection's state to confused.
Deficiency: The event engine is currently hardwired to consider a
line of bytes as ``excessive''; clearly this should be
user-redefinable.
[inconsistent_option (c: connection)] NVT options are specified by the client and server stating which options they are willing to support vs. which not, and then instructing one another which in fact they should or should not use for the current connection. If the event engine sees a peer violate either what the other peer has instructed it to do, or what it itself offered in terms of options in the past, then the engine generates an inconsistent_option event.
The handler for this event simply records an entry about it to the weird file. Deficiency: The event handler invocation does not include enough information to determine what option was inconsistently specified; in addition, it would be convenient to integrate the handling of problems like this within the general ``weird'' framework.
Note: As for excessive_line above, this event is actually a generic one applicable to any NVT-based protocol. It is handled here because the problem most often crops up for Telnet sessions. Note: Also, the handler does not check to see whether the connection is a login session (as it does for excessive_line); it serves as the handler for any NVT session with an excessive line.
Note: Finally, note that this event can be generated if the session contains a stream of binary data. One way this can occur is when the session is encrypted but Bro fails to recognize this fact.
[bad_option (c: connection)] If an NVT option is either ill-formed (e.g., a bad length field) or unrecognized, then the analyzer generates this event.
The processing of this event (recording information to the weird file) and the various notes and deficiencies associated with it are the same as those for inconsistent_option above.
[bad_option_termination (c: connection)] If an NVT option fails to be terminated correctly (for example, a character is seen within the option that is disallowed for use in the option), then the analyzer generates this event.
The processing of this event (recording information to the weird file) and the various notes and deficiencies associated with it are the same as those for inconsistent_option above.
[authentication_accepted (name: string, c: connection)] The NVT framework includes options for negotiating authentication. When such an option is sent from client to server and the server replies that it accepts the authentication, then the event engine generates this event.
The handler annotates the connection's addl field
with auth/<
name>
, unless that annotation is
already present.
[authentication_rejected (name: string, c: connection)] The same as authentication_accepted, except invoked when the server replies that it rejects the attempted authentication.
The handler annotates the connection's addl field
with auth-failed/<
name>
.
[authentication_skipped (c: connection)] Invoked when the event engine sees a line in the authentication dialog that matches skip_authentication.
The handler annotates the connection's addl field with ``(skipped)'' to mark that authentication was skipped, and then invokes skip_further_processing and (unless the connection is hot) set_record_packets to skip any further analysis of the connection, and to stop recording its packets to the trace file.
The handler first checks (via is_login_conn) whether this is a Telnet or Rlogin connection. If so, it generates an authentication_skipped event if the server's address occurs in skip_logins_to, and also (for Telnet) checks whether the client's port occurs in hot_telnet_orig_ports, invoking hot_login with the tag "orig" if it does.
For SSH connections, it likewise checks the client's port, but in hot_ssh_orig_ports, marking the connection as hot and logging a real-time alert if it is.
[activating_encryption (c: connection)] The NVT framework includes options for negotiating encryption. When such a series of options is successfully negotiated, the event engine generates this event. Note: The negotiation sequence is complex and can fail at a number of points. The event engine does not attempt to generate events for each possible failure, but instead only looks for the option sent after a successful negotiation sequence.
The handler annotates the connection's addl field with ``(encrypted)'' to mark that authentication was encrypted. Note: The event engine itself marks the connection as requiring no further processing. This is done by the event engine rather than the handler because the event engine cannot do its job (regardless of the policy the handler might desire) in the face of encryption.