next up previous contents index
Next: The analy Analyzer Up: Analyzers and Events Previous: The login Analyzer   Contents   Index

Subsections


The portmapper Analyzer

The portmapper analyzer monitors one particularly important form of remote procedure call (RPC) [RFC1831,RFC1832] traffic: the portmapper service, used to map between RPC program (and version) numbers and the TCP or UDP port on which the service runs for a particular host. For example, rstatd is an RPC service that provides ``remote host status monitoring'' so that a set of hosts can be informed when any of them reboots. rstatd has been assigned a standard RPC program number of 100002. To find out the corresponding TCP or UDP port on a given host, a remote host would usually first contact the portmapper RPC service running on the host and request the port corresponding to program 100002.


Table: Types of calls to the RPC portmapper service.
Call Meaning
NULL A do-nothing call typically provided by all RPC services.
GETPORT Look up the port associated with a given RPC program.
SET Add a new port mapping (or replace an existing mapping) for an RPC program.
UNSET Remove a port mapping.
DUMP Retrieve all of the RPC program mappings.
CALLIT Both look up a program and then directly call it.


All in all, clients can make six different types of calls to the portmapper, as summarized in Table [*]. Attackers often use GETPORT and DUMP to see whether a host may be running an RPC service vulnerable to a known exploit.

The analyzer uses a capture filter of ``port 111'' (§ ), equivalent to ``tcp port 111 or udp port 111'' (since the portmapper service ordinarily accepts calls using either TCP or UDP, both on port 111). It checks the different types of portmapper calls against policies expressed using a number of different variables.

Note: An important point not to overlook is that an attacker does not have to first call the portmapper service in order to call an RPC program. They might instead happen to know the port on which the service runs a priori, since for example it may generally run on the same port for a particular operating system; or they might scan the host's different TCP or UDP ports directly looking for a reply from the service. Thus, while portmapper monitoring proves very useful in detecting attacks, it does not provide comprehensive monitoring of attempts to exploit RPC services.


portmapper variables

The standard script provides the following redefinable variables:

[rpc_programs : table[count] of string] Maps RPC program numbers to a string used to name the service. For example, the [100002] entry is mapped to "rstatd".

Default: a large list of RPC services.

[NFS_services : set of string] Lists the names of those RPC services that correspond to Network File System (NFS) [RFC1094,RFC1813] services. This variable is provided because it is convenient to express policies specific to accessing NFS file systems.

Default: the services mountd, nfs, pcnfsd, nlockmgr, rquotad, status.

Deficiency: Bro's notion of NFS is currently confined to just knowledge of the existence of these services. It does not analyze the particulars of different NFS operations.

[RPC_okay : set[addr, addr, string]] Indexed by the host providing a given service and then by the host accessing the service. If an entry is present, it means that the given access is allowed. For example, an entry of:

    [1.2.3.4, 5.6.7.8, "rstatd"]
means that host 5.6.7.8 is allowed to access the rstatd service on host 1.2.3.4.

Default: empty.

[RPC_okay_nets : set[net]] A set of networks allowed to make GETPORT requests without complaint. The notion behind providing this variable is that the listed networks are trusted. However, the trust doesn't extend beyond GETPORT to other portmapper requests, because GETPORT is the only portmapper operation used routinely by a set of hosts trusted by another set of hosts (but that don't belong to the same group, and hence are not issuing SET and UNSET calls).

Default: empty.

[RPC_okay_services : set[string]] A set of services for which GETPORT requests should not generate complaints. These might be services that are widely invoked and believed exploit-free, such as walld, though care should be taken with blithely assuming that a given service is indeed exploit-free.

Note that, like for RPC_okay_nets, the trust does not extend beyond GETPORT, because it should be the only portmapper operation routinely invoked.

Default: empty.

[NFS_world_servers : set[addr]] A set of hosts that provide public access to an NFS file system, and thus should not have any of their NFS traffic flagged as possibly sensitive. (The presumption here is that such public servers have been carefully secured against any remote NFS operations.) An example of such a server might be one providing read-only access to a public database.

Default: empty.

[RPC_dump_okay : set[addr, addr]] Indexed first by the host requesting a portmapper dump, and second by the host from which it's requesting the dump. If an entry is present, then the dump operation is not flagged.

Default: empty.

[any_RPC_okay : set[addr, string]] Pairs of hosts and services for which any GETPORT access to the given service is allowed.

sun-rpc.mcast.net Default:

    [NFS_world_servers, NFS_services],
    [sun-rpc.mcast.net, "ypserv"]
The first of these allows access to any NFS service of any of the NFS_world_servers, using Bro's cross-product initialization feature (§ ). The second allows ypserv requests to the multicast address reserved for RPC multicasts.7.2

[suppress_pm_log : table[addr, string] of bool] Do not generate real-time alerts for access by the given address for the given service. Note that unlike most Bro policy variables, this one is not const but is modified at run-time to add to it any host that invokes the walld RPC service, so that such access is only reported once for each host.

Default: empty, but dynamic as discussed above.


portmapper functions

The standard script provides the following externally accessible functions:

[rpc_prog(p: count): string ] Returns the name of the RPC program with the given number, if it's present in rpc_programs; otherwise returns the text "unknown-<p>".

[pm_check_getport(r: connection, prog: string): bool ] Checks a GETPORT request for the given program against the policy expressed by RPC_okay_services, any_RPC_okay, RPC_okay, and RPC_okay_nets, returning true if the request violates policy, false if it's allowed.

[pm_activity(r: connection, log_it: bool) ] A bookkeeping function invoked when there's been portmapper activity on the given connection.

The function records the connection via record_connection, unless it is a TCP connection (which will instead be recorded by connection_finished). If log_it is true then the function generates a real-time alert of the form:

rpc: <connection-id> <RPC-service> <r$addl>
For example:
    972616255.679799 rpc: 65.174.102.21/832 > 182.7.9.47/portmapper
	pm_getport: nfs -> 2049/udp
However, it does not generate the alert if either the client host and service are present in suppress_pm_log, or if it already generated an alert in the past for the same client, server and service (to prevent alert cascades).

[pm_request(r: connection, proc: string, addl: string, log_it: bool) ] Invoked when the given connection has made a portmapper request of some sort for the given RPC procedure proc. addl gives an annotation to add to the connection's addl field. If log_it is true, then connection should be logged; it will also be logged if the function determines that it is hot.

The function first invokes check_scan and check_hot (with a mode of CONN_ESTABLISHED), unless r is a TCP connection, in which case these checks have already been made by connection_established. The function then adds addl to the connection's addl field, though if the field's length already exceeds 80 bytes, then it just tacks on "..." (unless already present). This last is necessary because Bro will sometimes see zillions of successive portmapper requests that all use the same connection ID, and these will each add to addl until it becomes unwieldy in size. Deficiency: Clearly, the byte limit of 80 should be adjustable.

Finally, the function invokes check_hot with a mode of CONN_FINISHED, and pm_activity to finish up bookkeeping for the connection.

No return value.

[pm_attempt(r: connection, proc: string, status: count, addl: string, log_it: bool) ] Invoked when the given connection attempted to make a portmapper request of some sort, but the request failed or went unanswered. The arguments are the same as for pm_request, with the addition of status, which gives the RPC status code corresponding to why the attempt failed (see below).

The function first invokes check_scan and check_hot (with a mode of CONN_ATTEMPTED), unless r is a TCP connection, in which case these checks have already been made by connection_attempt.


Table: Types of RPC status codes.
Status description Meaning
"ok" The call succeeded.
"prog unavail" The call was for an RPC program that has not registered with the portmapper.
"mismatch" The call was for a version of the RPC program that has not registered with the portmapper.
"garbage args" The parameters in the call did not decode correctly.
"system err" A system error (such as out-of-memory) occurred when processing the call.
"timeout" No reply was received within 24 seconds of the request.
"auth error" The caller failed to authenticate to the server, or was not authorized to make the call.
"unknown" An unknown error occurred.


The function then adds addl to the connection's addl field, along with a text description of the RPC status code, as given in Table [*].

No return value.


portmapper event handlers

The standard script handles the following events:

[pm_request_null (r: connection)] Invoked upon a successful portmapper request for the ``null'' procedure. The script invokes pm_request with log_it=F.

[pm_request_set (r: connection, m: pm_mapping, success: bool)] Invoked upon a nominally successful portmapper request to set the portmapper binding m. The script invokes pm_request with log_it=T. success is true if the server honored the request, false otherwise; the script turns this into an annotation of "ok" or "failed".

The pm_mapping type (for m) has three fields, program: count, version: count and p: port, the port for the mapping of the given program and version. pm_mapping

[pm_request_unset (r: connection, m: pm_mapping, success: bool)] Invoked upon a nominally successful portmapper request to remove a portmapper binding. The script invokes pm_request with log_it=T. success is true if the server honored the request, false otherwise; the script turns this into an annotation of "ok" or "failed".

[pm_request_getport (r: connection, pr: pm_port_request, p: port)] Invoked upon a successful portmapper request to look up a portmapper binding. pr, of type pm_port_request, has three fields: program: count, version: count, and is_tcp: bool, this last indicating whether the caller is request the TCP or UDP port, if the given program/version has mappings for both. The script invokes pm_request with log_it set according to the return value of pm_check_getport and an annotation of the mapping.

[pm_request_dump (r: connection, m: pm_mappings)] Invoked upon a successful portmapper request to dump the portmapper bindings. The script invokes pm_request with log_it=T unless RPC_dump_okay indicates that the dump call is allowed. The script ignores m, which gives the mappings as a table[count] of pm_mapping, where the table index simply reflects the order in which the mappings were returned, starting with an index of 1. Deficiency: What the script should do, instead, is keep track of the mappings so that Bro can identify the service associated with connections for otherwise unknown ports.

[pm_request_callit (r: connection, pm_callit_request, p: port)] Invoked upon a successful portmapper request to look up and call an RPC procedure. The script invokes pm_request with log_it=T unless the combination of the caller and the program are in suppress_pm_log. Finally, if the program called is walld, then the script adds the caller to suppress_pm_log.

The pm_callit_request type has four fields: pm_callit_request program: count, version: count, proc: count, and arg_size: count. These reflect the procedure being looked up and called, and the size of the arguments being passed to it, respectively. Deficiency: Currently, the event engine does not do any analysis or refinement of the arguments passed to the procedure (such as making them available to the event handler) or the return value. p is the port value returned by the call.

[pm_attempt_null (r: connection, status: count)] Invoked upon a failed portmapper request for the ``null'' procedure. status gives the reason for the failure. The script invokes pm_attempt with log_it=T.

[pm_attempt_set (r: connection, status: count, m: pm_mapping)] Invoked upon a failed portmapper request to set the portmapper binding m. The script invokes pm_attempt with log_it=T.

[pm_attempt_unset (r: connection, status: count, m: pm_mapping)] Invoked upon a failed portmapper request to remove a portmapper binding. The script invokes pm_attempt with log_it=T.

[pm_attempt_getport (r: connection, status: count, pr: pm_port_request)] Invoked upon a failed portmapper request to look up a portmapper binding. pr, of type pm_port_request, has three fields: program: count, version: count, and is_tcp: bool, this last indicating whether the caller requested the TCP or UDP port. The script invokes pm_attempt with log_it set according to the return value of pm_check_getport.

[pm_attempt_dump (r: connection, status: count)] Invoked upon a failed portmapper request to dump the portmapper bindings. The script invokes pm_attempt with log_it=T unless RPC_dump_okay indicates that the dump call is allowed.

[pm_attempt_callit (r: connection, status: count, pm_callit_request)] Invoked upon a failed portmapper request to look up and call an RPC procedure. The script invokes pm_attempt with log_it=T unless the combination of the caller and the program are in suppress_pm_log. Finally, if the program called is walld, then the script adds the caller to suppress_pm_log.

[pm_bad_port (r: connection, bad_p: count)] Invoked when a portmapper request or response includes an invalid port number. Since ports are represented by unsigned 4-byte integers, they can stray outside the allowed range of 0-65535 by being $\ge$ 65536. The script invokes conn_weird_addl with a weird tag of "bad_pm_port".


next up previous contents index
Next: The analy Analyzer Up: Analyzers and Events Previous: The login Analyzer   Contents   Index
Vern Paxson 2004-03-21