├── COPYING ├── README.markdown ├── awk ├── tls_cn_contains_nul.awk ├── tls_subject_field.awk └── tls_wildcard_mismatch.awk └── bro ├── bodies.bro ├── duqu.bro └── meta.bro /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Matthias Vallentin 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | * Neither the name of Matthias Vallentin 18 | nor the names of the contributors may be used to endorse or 19 | promote products derived from this software without specific prior 20 | written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 25 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 26 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 27 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 29 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 31 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 32 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | This repository is a mixed bag of Bro scripts that are too specific to be 2 | included in the official 3 | [Bro scripts repository](http://git.bro-ids.org/bro-scripts.git). 4 | The scripts are of expirimental nature and might have a few edges, so you are 5 | welcome to ping me for feedback and clarifications. 6 | 7 | The repository contains Bro scripts in the directory `bro` and awk scripts for 8 | post-processing the logs in `awk`. 9 | 10 | Please see the file `COPYING` for the licence details. 11 | 12 | Documentation 13 | ============= 14 | 15 | bodies.bro 16 | ---------- 17 | This script reassembles HTTP bodies and raises an event with the complete 18 | contents. Concretely, it reassembles the current request and/or response body 19 | via the `http_entity_{begin,data,end}` events and raises the new event 20 | `http_body` which has the following signature: 21 | 22 | http_body_complete: event(c: connection); 23 | 24 | Upon handling `http_body_complete`, you can be sure that `c$http$body` contains 25 | the full string of the HTTP response unless the body exceeds 26 | `HTTP::max_body_size` bytes, in which case the body is chopped off at that 27 | size. 28 | 29 | Aside from `c$http$body`, this script adds a second field to `c$http` named 30 | `reassembl_body` which determines whether the current connection should 31 | reassemble the body. For example, if you observe some suspicious header value, 32 | you could set `c$http$reassembl_body = T` and hand the `http_body_complete` 33 | event. Note that this flag is *per connection* and not per HTTP message, which 34 | means you would need to turn it off after handling `http_body_complete` if you 35 | wanted body reassembly at the HTTP message level. 36 | 37 | Because the keeping track of all HTTP bodies would likely exceed the amount of 38 | available memory, we need to focus of a subset of HTTP message bodies. The 39 | script offers the following variables in the HTTP namespace in addition to 40 | `c$http$reassembl_body`: 41 | 42 | ## Flag that indicates whether to hook request bodies. 43 | const hook_request_bodies = F &redef; 44 | 45 | ## Flag that indicates whether to hook reply bodies. 46 | const hook_reply_bodies = T &redef; 47 | 48 | ## The pattern applies 49 | const hook_host_pattern = /.*/ &redef; 50 | 51 | ## Do not buffer more than this amount of bytes per HTTP message. 52 | const max_body_size = 50000000; 53 | 54 | Requires Bro 2.x 55 | -------------------------------------------------------------------------------- /awk/tls_cn_contains_nul.awk: -------------------------------------------------------------------------------- 1 | # Check whether the CN field of a TLS certificate subject contains a NUL byte. 2 | function tls_cn_contains_nul(subject) 3 | { 4 | return tls_extract_field("CN", subject) ~ /\\x00/; 5 | } 6 | -------------------------------------------------------------------------------- /awk/tls_subject_field.awk: -------------------------------------------------------------------------------- 1 | # Extract the value of TLS certificate subject field. 2 | # Returns the extracted value or the empty string if the field does not exist. 3 | function extract_field(field, subject) 4 | { 5 | cn = ""; 6 | split(subject, s, /,/); 7 | for (i in s) 8 | { 9 | split(s[i], kv, /=/); 10 | if (kv[1] == field) 11 | { 12 | cn = kv[2]; 13 | break; 14 | } 15 | } 16 | 17 | return cn; 18 | } 19 | -------------------------------------------------------------------------------- /awk/tls_wildcard_mismatch.awk: -------------------------------------------------------------------------------- 1 | # Check whether a TLS server name (SNI) matches the subject CN field advertised 2 | # in the certificate. 3 | # Returns true on mismatch and false otherwise. 4 | function tls_wildcard_mismatch(server_name, subject) 5 | { 6 | if (server_name == "-") 7 | return 0; 8 | 9 | cn = tls_subject_field("CN", subject) 10 | if (cn == "") 11 | return 0; 12 | 13 | wildcard = index(cn, "*"); 14 | if (wildcard > 0) 15 | { 16 | suffix = substr(cn, wildcard + 2, length(cn) - wildcard - 1); 17 | if (index(server_name, suffix) > 0) 18 | return 0; 19 | } 20 | else if (server_name == cn) 21 | return 0; 22 | 23 | return 1; 24 | } 25 | -------------------------------------------------------------------------------- /bro/bodies.bro: -------------------------------------------------------------------------------- 1 | ##! This script reassembles full HTTP bodies and raises an event with the 2 | ##! complete contents. 3 | 4 | module HTTP; 5 | 6 | export { 7 | redef record Info += { 8 | body: string &optional; 9 | reassemble_body: bool &default=F; 10 | }; 11 | 12 | ## Flag that indicates whether to hook request bodies. 13 | const hook_request_bodies = F &redef; 14 | 15 | ## Flag that indicates whether to hook reply bodies. 16 | const hook_reply_bodies = T &redef; 17 | 18 | ## The pattern applies 19 | const hook_host_pattern = /.*/ &redef; 20 | 21 | ## Do not buffer more than this amount of bytes per HTTP message. 22 | const max_body_size = 50000000; 23 | } 24 | 25 | ## Users write a handler for this event to process the current HTTP body. 26 | event http_body_complete(c: connection) &priority=-5 27 | { 28 | delete c$http$body; 29 | } 30 | 31 | event http_begin_entity(c: connection, is_orig: bool) 32 | { 33 | if ( (is_orig && ! hook_request_bodies) || 34 | (! is_orig && ! hook_reply_bodies) ) 35 | return; 36 | 37 | if ( hook_host_pattern !in c$http$host ) 38 | return; 39 | 40 | c$http$body = ""; 41 | c$http$reassemble_body = T; 42 | } 43 | 44 | event http_entity_data(c: connection, is_orig: bool, length: count, 45 | data: string) 46 | { 47 | if ( ! c$http?$body ) 48 | return; 49 | 50 | c$http$body += data; 51 | 52 | if ( c$http$response_body_len < max_body_size ) 53 | return; 54 | 55 | c$http$reassemble_body = F; 56 | event http_body_complete(c); 57 | } 58 | 59 | event http_end_entity(c: connection, is_orig: bool) 60 | { 61 | if ( ! c$http?$body ) 62 | return; 63 | 64 | c$http$reassemble_body = F; 65 | event http_body_complete(c); 66 | } 67 | -------------------------------------------------------------------------------- /bro/duqu.bro: -------------------------------------------------------------------------------- 1 | ##! A Duqu detector. It builds a statemachine of the HTTP[S]-based C&C protocol 2 | ##! spoken by infected machines. 3 | ##! 4 | ##! Many thanks to Boldizsar Bencsath from CrySyS Lab for 5 | ##! providing Bro logs that that represent a Duqu session. 6 | ##! 7 | 8 | module HTTP; 9 | 10 | export { 11 | redef enum Notice::Type += { 12 | ## Indicates that we might have witnessed a Duqu infection. 13 | Potential_Duqu_Infection 14 | }; 15 | 16 | redef record Info += { 17 | cookie: string &log &optional; 18 | content_type: string &log &optional; 19 | }; 20 | 21 | ## The Duqu FSM. 22 | type DuquState: enum { 23 | JPEG_REQUEST, # Initial GET request 24 | JPEG_REPLY, # Response containing the JPEG 25 | START_EXFILTRATION, # POST request that initiates the data exfiltration 26 | ACK_EXFILTRATION, # POST request that initiates the data exfiltration 27 | EXFILTRATING # POST request containing the actual data 28 | }; 29 | } 30 | 31 | ## Keeps track of Duqu-infected machines. 32 | global duqus: table[addr] of DuquState &read_expire=1hr; 33 | 34 | event http_header(c: connection, is_orig: bool, name: string, value: string) 35 | { 36 | if ( is_orig && name == "COOKIE" ) 37 | c$http$cookie = value; 38 | 39 | if ( name == "CONTENT-TYPE" ) 40 | c$http$content_type = value; 41 | } 42 | 43 | event http_message_done(c: connection, is_orig: bool, stat: http_message_stat) 44 | { 45 | if ( is_orig ) 46 | { 47 | if ( c$http$method == "GET" && 48 | /^PHPSESSID=[[:alnum:]]+$/ in c$http$cookie && 49 | /([0-9]+){3}\.[0-9]+/ in c$http$host && 50 | c$http$uri == "/" ) 51 | duqus[c$id$orig_h] = JPEG_REQUEST; 52 | 53 | if ( c$http$method == "POST" && 54 | c$id$orig_h in duqus && 55 | duqus[c$id$orig_h] == JPEG_REPLY && 56 | /^PHPSESSID=[[:alnum:]]+$/ in c$http$cookie && 57 | /([0-9]+){3}\.[0-9]+/ in c$http$host && 58 | c$http$uri == "/" && 59 | # /multipart\/form-data/ in c$http$content_type && 60 | c$http$request_body_len == 0 ) 61 | duqus[c$id$orig_h] = START_EXFILTRATION; 62 | 63 | if ( c$id$orig_h in duqus && 64 | (duqus[c$id$orig_h] == ACK_EXFILTRATION || 65 | duqus[c$id$orig_h] == EXFILTRATING ) && 66 | c$http$request_body_len > 0 ) 67 | { 68 | duqus[c$id$orig_h] = EXFILTRATING; 69 | NOTICE([$note=Potential_Duqu_Infection, 70 | $msg=fmt("Duqu exfiltrated %d bytes", 71 | c$http$request_body_len), 72 | $conn=c]); 73 | } 74 | } 75 | else 76 | { 77 | if ( c$id$orig_h in duqus ) 78 | { 79 | if ( duqus[c$id$orig_h] == JPEG_REQUEST && 80 | c$http$status_code == 200 && 81 | /image\/jpeg/ in c$http$mime_type ) 82 | { 83 | duqus[c$id$orig_h] = JPEG_REPLY; 84 | NOTICE([$note=Potential_Duqu_Infection, 85 | $msg=fmt("Initial Duqu JPEG exchange"), 86 | $conn=c]); 87 | } 88 | else 89 | delete duqus[c$id$orig_h]; # Purge unnecessary state early. 90 | } 91 | 92 | 93 | if ( c$id$orig_h in duqus && 94 | duqus[c$id$orig_h] == START_EXFILTRATION && 95 | c$http$status_code == 200 && 96 | c$http$response_body_len == 0 ) 97 | duqus[c$id$orig_h] = ACK_EXFILTRATION; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /bro/meta.bro: -------------------------------------------------------------------------------- 1 | ##! This script handles a special ``meta_event`` that the core generates for 2 | ##! each raised event. It does not work with git/master, but requires the 3 | ##! branch ``topic/matthias/meta-analysis`` to work properly. 4 | ##! 5 | ##! Ths script is mainly intended to profile the event load that Bro 6 | ##! experiences, which can be of quite different nature than the packet stream. 7 | @load local 8 | 9 | module Meta; 10 | 11 | export { 12 | redef enum Log::ID += { LOG }; 13 | 14 | type Info: record { 15 | ts: time &log; ##< The timestamp when the event was generated. 16 | name: string &log; ##< The event name. 17 | size: count &log; ##< The size of the event in bytes. 18 | }; 19 | 20 | global log_meta: event(rec: Info); 21 | } 22 | 23 | event meta_event(name: string, timestamp: time, size: count) 24 | { 25 | local rec: Meta::Info = [$ts=timestamp, $name=name, $size=size]; 26 | Log::write(Meta::LOG, rec); 27 | } 28 | 29 | event bro_init() 30 | { 31 | Log::create_stream(Meta::LOG, [$columns=Info, $ev=log_meta]); 32 | } 33 | --------------------------------------------------------------------------------