zeek/policy/http-identified-files.bro

115 lines
3.4 KiB
Text

# $Id:$
#
# Analyze HTTP entities for sensitive types (e.g., executables).
#
# Contributed by Seth Hall.
@load http-reply
module HTTP;
const http_identified_log = open_log_file("http-id");
export {
# Base the libmagic analysis on this many bytes. Currently,
# we will in fact use fewer (basically, just what's in the
# first data packet).
const magic_content_limit = 1024 &redef;
# These MIME types are logged and generate a Notice. The patterns
# need to match the entire description as returned by libMagic.
# For example, for plain text it can return
# "text/plain charset=us-ascii", so you might want to use
# /text\/plain.*/.
const watched_mime_types =
/application\/x-dosexec/ # Windows and DOS executables
| /application\/x-executable/ # *NIX executable binary
&redef;
const watched_descriptions = /PHP script text/ &redef;
# URLs included here are not logged and notices are not generated.
# Take care when defining patterns to not be overly broad.
const ignored_urls =
/^http:\/\/www\.download\.windowsupdate\.com\// &redef;
redef enum Notice += {
# Generated when we see a MIME type we flagged for watching.
HTTP_WatchedMIMEType,
# Generated when the file extension doesn't match
# the file contents.
HTTP_IncorrectFileType,
};
# Create patterns that *should* be in the URLs for specific MIME types.
# Notices are generated if the pattern doesn't match.
const mime_types_extensions = {
["application/x-dosexec"] = /\.([eE][xX][eE]|[dD][lL][lL])/,
} &redef;
}
event http_entity_data(c: connection, is_orig: bool, length: count, data: string)
{
if ( is_orig )
# For now we only inspect server responses.
return;
local s = lookup_http_request_stream(c);
local msg = get_http_message(s, is_orig);
@ifndef ( content_truncation_limit )
# This is only done if http-body.bro is not loaded.
msg$data_length = msg$data_length + length;
@endif
# For the time being, we'll just use the data from the first packet.
# Don't continue until we have enough data.
# if ( msg$data_length < magic_content_limit )
# return;
# Right now, only try this for the first chunk of data
if ( msg$data_length > length )
return;
local abstract = sub_bytes(data, 1, magic_content_limit);
local magic_mime = identify_data(abstract, T);
local magic_descr = identify_data(abstract, F);
if ( (magic_mime == watched_mime_types ||
watched_descriptions in magic_descr) &&
s$first_pending_request in s$requests )
{
local r = s$requests[s$first_pending_request];
local host = (s$next_request$host=="") ?
fmt("%s", c$id$resp_h) : s$next_request$host;
event file_transferred(c, abstract, magic_descr, magic_mime);
local url = fmt("http://%s%s", host, r$URI);
if ( ignored_urls in url )
return;
local file_type = "";
if ( magic_mime == watched_mime_types )
file_type = magic_mime;
else
file_type = magic_descr;
local message = fmt("%s %s %s %s",
id_string(c$id), file_type, r$method, url);
NOTICE([$note=HTTP_WatchedMIMEType, $msg=message, $conn=c,
$method=r$method, $URL=url]);
print http_identified_log, fmt("%.06f %s %s",
network_time(), s$id, message);
if ( (magic_mime in mime_types_extensions &&
mime_types_extensions[magic_mime] !in url) ||
(magic_descr in mime_types_extensions &&
mime_types_extensions[magic_descr] !in url) )
NOTICE([$note=HTTP_IncorrectFileType, $msg=message,
$conn=c, $method=r$method, $URL=url]);
}
}