mirror of
https://github.com/zeek/zeek.git
synced 2025-10-04 15:48:19 +00:00
Merge commit 'e8bff4779e
' into topic/policy-scripts-new
* commit 'e8bff4779e
':
Initial and incomplete http.bro
This commit is contained in:
commit
e259da6f8b
1 changed files with 285 additions and 0 deletions
285
policy/http.bro
Normal file
285
policy/http.bro
Normal file
|
@ -0,0 +1,285 @@
|
||||||
|
##! Yay, this is the new HTTP script
|
||||||
|
|
||||||
|
## Author: Seth Hall <seth@icir.org> - Inspired by the work many others.
|
||||||
|
|
||||||
|
@load functions
|
||||||
|
@load notice
|
||||||
|
@load software
|
||||||
|
|
||||||
|
module HTTP;
|
||||||
|
|
||||||
|
redef enum Log::ID += { HTTP };
|
||||||
|
redef enum Software::Type += {
|
||||||
|
WEB_SERVER,
|
||||||
|
WEB_BROWSER,
|
||||||
|
WEB_BROWSER_PLUGIN,
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
type LogTags: enum {
|
||||||
|
## Indicator of a URI based SQL injection attack.
|
||||||
|
URI_SQLI,
|
||||||
|
## Indicator of client body based SQL injection attack. This is
|
||||||
|
## typically the body content of a POST request.
|
||||||
|
POST_SQLI,
|
||||||
|
## Indicator of a cookie based SQL injection attack.
|
||||||
|
COOKIE_SQLI,
|
||||||
|
};
|
||||||
|
|
||||||
|
type Log: record {
|
||||||
|
ts: time;
|
||||||
|
id: conn_id;
|
||||||
|
method: string &default="";
|
||||||
|
host: string &default="";
|
||||||
|
uri: string &default="";
|
||||||
|
referrer: string &default="";
|
||||||
|
user_agent: string &default="";
|
||||||
|
request_body_size: count &default=0;
|
||||||
|
response_body_size: count &default=0;
|
||||||
|
status_code: count &default=0;
|
||||||
|
status_msg: string &default="";
|
||||||
|
username: string &default="";
|
||||||
|
password: string &default="";
|
||||||
|
## This is a set of indicators of various attributes discovered and
|
||||||
|
## related to a particular request/response pair.
|
||||||
|
tags: set[LogTags];
|
||||||
|
|
||||||
|
# TODO: I think I want this in a separate script. Not sure I like it here.
|
||||||
|
## All of the headers that may indicate if the request was proxied.
|
||||||
|
proxied: set[string];
|
||||||
|
|
||||||
|
# Do this in a separate script.
|
||||||
|
#post_vars: vector of string;
|
||||||
|
|
||||||
|
# Do these in a separate script.
|
||||||
|
#mime_type: string &default="";
|
||||||
|
#generate_md5: bool &default=F;
|
||||||
|
#md5: string &default="";
|
||||||
|
#file_name: string; ##maybe if the header's there?
|
||||||
|
};
|
||||||
|
|
||||||
|
type LogPoint: enum {
|
||||||
|
AFTER_REQUEST,
|
||||||
|
AFTER_REQUEST_BODY,
|
||||||
|
AFTER_REPLY,
|
||||||
|
AFTER_REPLY_BODY,
|
||||||
|
};
|
||||||
|
|
||||||
|
## Define the default point at which you'd like the logging to take place.
|
||||||
|
## If you wait until after the reply body, you can be assured that you will
|
||||||
|
## get the most data, but at the expense of a delayed log which could
|
||||||
|
## be substantial in the event of a large file download. It's typically
|
||||||
|
## not much of a problem. To mitigate, you may want to change this value
|
||||||
|
## to AFTER_REPLY which will cause the log action to take place after all
|
||||||
|
## of the response headers.
|
||||||
|
## This is settable per-session too by setting the $log_point value
|
||||||
|
## in a SessionInfo record.
|
||||||
|
const default_log_point = AFTER_REPLY_BODY &redef;
|
||||||
|
|
||||||
|
type SessionInfo: record {
|
||||||
|
log: Log;
|
||||||
|
#pending_requests: Request;
|
||||||
|
log_point: LogPoint &default=default_log_point;
|
||||||
|
};
|
||||||
|
|
||||||
|
## This regular expression is used to match URI based SQL injections
|
||||||
|
const match_sql_injection_uri =
|
||||||
|
# /[\?&][^[:blank:]\|]+?=[\-0-9%]+([[:blank:]]|\/\*.*?\*\/)*['"]?([[:blank:]]|\/\*.*?\*\/|\)?;)+([hH][aA][vV][iI][nN][gG]|[uU][nN][iI][oO][nN]|[eE][xX][eE][cC]|[sS][eE][lL][eE][cC][tT]|[dD][eE][lL][eE][tT][eE]|[dD][rR][oO][pP]|[dD][eE][cC][lL][aA][rR][eE]|[cC][rR][eE][aA][tT][eE]|[iI][nN][sS][eE][rR][tT])[^a-zA-Z&]/
|
||||||
|
/[\?&][^[:blank:]\|]+?=[\-0-9%]+([[:blank:]]|\/\*.*?\*\/)*['"]?([[:blank:]]|\/\*.*?\*\/|\)?;)+([oO][rR]|[aA][nN][dD])([[:blank:]]|\/\*.*?\*\/)+['"]?[^a-zA-Z&]+?=/
|
||||||
|
| /[\?&][^[:blank:]]+?=[\-0-9%]*([[:blank:]]|\/\*.*?\*\/)*['"]([[:blank:]]|\/\*.*?\*\/)*(\-|\+|\|\|)([[:blank:]]|\/\*.*?\*\/)*([0-9]|\(?[cC][oO][nN][vV][eE][rR][tT]|[cC][aA][sS][tT])/
|
||||||
|
| /[\?&][^[:blank:]\|]+?=([[:blank:]]|\/\*.*?\*\/)*['"]([[:blank:]]|\/\*.*?\*\/|;)*([oO][rR]|[aA][nN][dD]|[hH][aA][vV][iI][nN][gG]|[uU][nN][iI][oO][nN]|[eE][xX][eE][cC]|[sS][eE][lL][eE][cC][tT]|[dD][eE][lL][eE][tT][eE]|[dD][rR][oO][pP]|[dD][eE][cC][lL][aA][rR][eE]|[cC][rR][eE][aA][tT][eE]|[rR][eE][gG][eE][xX][pP]|[iI][nN][sS][eE][rR][tT]|\()[^a-zA-Z&]/
|
||||||
|
| /[\?&][^[:blank:]]+?=[^\.]*?([cC][hH][aA][rR]|[aA][sS][cC][iI][iI]|[sS][uU][bB][sS][tT][rR][iI][nN][gG]|[tT][rR][uU][nN][cC][aA][tT][eE]|[vV][eE][rR][sS][iI][oO][nN]|[lL][eE][nN][gG][tT][hH])\(/ &redef;
|
||||||
|
|
||||||
|
## The list of HTTP headers typically used to indicate a proxied request.
|
||||||
|
const proxy_headers: set[string] = {
|
||||||
|
"HTTP-FORWARDED",
|
||||||
|
"FORWARDED",
|
||||||
|
"HTTP-X-FORWARDED-FOR",
|
||||||
|
"X-FORWARDED-FOR",
|
||||||
|
"HTTP-X-FORWARDED-FROM",
|
||||||
|
"X-FORWARDED-FROM",
|
||||||
|
"HTTP-CLIENT-IP",
|
||||||
|
"CLIENT-IP",
|
||||||
|
"HTTP-FROM",
|
||||||
|
"FROM",
|
||||||
|
"HTTP-VIA",
|
||||||
|
"VIA",
|
||||||
|
"HTTP-XROXY-CONNECTION",
|
||||||
|
"XROXY-CONNECTION",
|
||||||
|
"HTTP-PROXY-CONNECTION",
|
||||||
|
"PROXY-CONNECTION",
|
||||||
|
} &redef;
|
||||||
|
|
||||||
|
## List of all active HTTP session indexed by conn_id.
|
||||||
|
global active_conns: table[conn_id] of SessionInfo &read_expire=5mins;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
event bro_init()
|
||||||
|
{
|
||||||
|
Log::create_stream("HTTP", "HTTP::Log");
|
||||||
|
Log::add_default_filter("HTTP");
|
||||||
|
}
|
||||||
|
|
||||||
|
# DPD configuration.
|
||||||
|
const ports = {
|
||||||
|
80/tcp, 81/tcp, 631/tcp, 1080/tcp, 3138/tcp,
|
||||||
|
8000/tcp, 8080/tcp, 8888/tcp,
|
||||||
|
};
|
||||||
|
redef dpd_config += {
|
||||||
|
[[ANALYZER_HTTP, ANALYZER_HTTP_BINPAC]] = [$ports = ports],
|
||||||
|
};
|
||||||
|
redef capture_filters += {
|
||||||
|
["http"] = "tcp and port (80 or 81 or 631 or 1080 or 3138 or 8000 or 8080 or 8888)"
|
||||||
|
};
|
||||||
|
|
||||||
|
function new_http_log(id: conn_id): Log
|
||||||
|
{
|
||||||
|
local tags: set[LogTags] = set();
|
||||||
|
local proxied: set[string] = set();
|
||||||
|
local log: Log = [$ts=network_time(), $id=id, $tags=tags, $proxied=proxied,
|
||||||
|
# TODO: some bug with record default initialization
|
||||||
|
$user_agent="", $request_body_size=0, $response_body_size=0, $status_code=0, $status_msg="", $username="", $password="", $referrer="", $host=""
|
||||||
|
];
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_http_session(id: conn_id): SessionInfo
|
||||||
|
{
|
||||||
|
if ( id in active_conns )
|
||||||
|
return active_conns[id];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
local info: SessionInfo;
|
||||||
|
info$log = new_http_log(id);
|
||||||
|
active_conns[id] = info;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event http_request(c: connection, method: string, original_URI: string,
|
||||||
|
unescaped_URI: string, version: string) &priority=1
|
||||||
|
{
|
||||||
|
#print fmt("REQUEST: %s %s", method, original_URI);
|
||||||
|
local sess = get_http_session(c$id);
|
||||||
|
|
||||||
|
sess$log$method = method;
|
||||||
|
sess$log$uri = unescaped_URI;
|
||||||
|
|
||||||
|
if ( match_sql_injection_uri in unescaped_URI )
|
||||||
|
add sess$log$tags[URI_SQLI];
|
||||||
|
}
|
||||||
|
|
||||||
|
event http_reply(c: connection, version: string, code: count, reason: string) &priority=1
|
||||||
|
{
|
||||||
|
#print fmt("REPLY: %s", code);
|
||||||
|
local sess = get_http_session(c$id);
|
||||||
|
|
||||||
|
sess$log$status_code = code;
|
||||||
|
sess$log$status_msg = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
event http_header(c: connection, is_orig: bool, name: string, value: string) &priority=1
|
||||||
|
{
|
||||||
|
local sess = get_http_session(c$id);
|
||||||
|
|
||||||
|
if ( is_orig ) # client headers
|
||||||
|
{
|
||||||
|
if ( name == "REFERER" )
|
||||||
|
sess$log$referrer = value;
|
||||||
|
|
||||||
|
else if ( name == "HOST" )
|
||||||
|
sess$log$host = value;
|
||||||
|
|
||||||
|
else if ( name == "CONTENT-LENGTH" )
|
||||||
|
sess$log$request_body_size = to_count(value);
|
||||||
|
|
||||||
|
else if ( name == "USER-AGENT" )
|
||||||
|
{
|
||||||
|
sess$log$user_agent = value;
|
||||||
|
#
|
||||||
|
#if ( ignored_user_agents in value )
|
||||||
|
# return;
|
||||||
|
#
|
||||||
|
#if ( /Java\// in value )
|
||||||
|
# {
|
||||||
|
# local java_tokens = split_n(value, /Java\//, F, 2);
|
||||||
|
# if ( |java_tokens| == 2 )
|
||||||
|
# {
|
||||||
|
# local java_string = fmt("Java/%s", java_tokens[2]);
|
||||||
|
# local java_ver = default_software_parsing(java_string);
|
||||||
|
# event software_version_found(c, c$id$orig_h,
|
||||||
|
# java_ver,
|
||||||
|
# WEB_BROWSER_PLUGIN);
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
#if ( addr_matches_hosts(c$id$orig_h, track_user_agents_for) &&
|
||||||
|
# value !in known_user_agents[c$id$orig_h] )
|
||||||
|
# {
|
||||||
|
# if ( c$id$orig_h !in known_user_agents )
|
||||||
|
# known_user_agents[c$id$orig_h] = set();
|
||||||
|
# add known_user_agents[c$id$orig_h][value];
|
||||||
|
# ci$new_user_agent = T;
|
||||||
|
# }
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ( name in proxy_headers )
|
||||||
|
add sess$log$proxied[fmt("%s -> %s", name, value)];
|
||||||
|
|
||||||
|
else if ( name == "AUTHORIZATION" )
|
||||||
|
{
|
||||||
|
if ( /^[bB][aA][sS][iI][cC] / in value )
|
||||||
|
{
|
||||||
|
local userpass = decode_base64(sub(value, /[bB][aA][sS][iI][cC][[:blank:]]/, ""));
|
||||||
|
local up = split(userpass, /:/);
|
||||||
|
if ( |up| >= 2 )
|
||||||
|
{
|
||||||
|
sess$log$username = up[1];
|
||||||
|
sess$log$password = up[2];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sess$log$username = "<problem-decoding>";
|
||||||
|
sess$log$password = userpass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else # server headers
|
||||||
|
{
|
||||||
|
if ( name == "SERVER" )
|
||||||
|
{
|
||||||
|
local si = Software::default_parse(value, c$id$resp_h, WEB_SERVER);
|
||||||
|
Software::found(c, si);
|
||||||
|
}
|
||||||
|
else if ( name == "CONTENT-LENGTH" )
|
||||||
|
sess$log$response_body_size = to_count(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event http_begin_entity(c: connection, is_orig: bool) &priority=1
|
||||||
|
{
|
||||||
|
local sess = get_http_session(c$id);
|
||||||
|
|
||||||
|
if ( is_orig )
|
||||||
|
if ( sess$log_point == AFTER_REQUEST )
|
||||||
|
Log::write("HTTP", sess$log);
|
||||||
|
else
|
||||||
|
if ( sess$log_point == AFTER_REQUEST )
|
||||||
|
Log::write("HTTP", sess$log);
|
||||||
|
}
|
||||||
|
|
||||||
|
event http_message_done(c: connection, is_orig: bool, stat: http_message_stat) &priority=1
|
||||||
|
{
|
||||||
|
local sess = get_http_session(c$id);
|
||||||
|
|
||||||
|
if ( is_orig )
|
||||||
|
if ( sess$log_point == AFTER_REQUEST_BODY )
|
||||||
|
Log::write("HTTP", sess$log);
|
||||||
|
else
|
||||||
|
if ( sess$log_point == AFTER_REPLY_BODY )
|
||||||
|
Log::write("HTTP", sess$log);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue