├── LICENSE ├── README.md ├── __load__.bro ├── auth_bruteforcing.bro ├── bugzilla_bruteforce.bro ├── chrome-sha1.bro ├── cipher_stats.bro ├── conn-add-country.bro ├── conn-peer.bro ├── conn_bad_subnet.bro ├── conn_bad_subnet_input.bro ├── counttable.bro ├── detect-bruteforcing-ext.bro ├── detect_open_proxies.bro ├── dhcpr.bro ├── dlp.bro ├── dlp_input.bro ├── dlp_input.txt ├── excessive_http_errors_topk.bro ├── extract-interesting-files.bro ├── filter_input.bro ├── filter_input.zeek ├── filter_noise_conn.bro ├── filter_noise_conn.zeek ├── filter_noise_dns.bro ├── filter_noise_dns.zeek ├── filter_noise_files.bro ├── filter_noise_files.zeek ├── filter_noise_http.bro ├── filter_noise_http.zeek ├── filter_noise_intel.bro ├── filter_noise_intel.zeek ├── filter_noise_mysql.bro ├── filter_noise_mysql.zeek ├── filter_noise_ssl.bro ├── filter_noise_ssl.zeek ├── filter_noise_x509.bro ├── filter_noise_x509.zeek ├── find_non_aes_clients.bro ├── find_non_aes_servers.bro ├── heartbleed_mozillaca.bro ├── http_auth_base64.bro ├── http_headers_flash_tmp.bro ├── http_headers_lb.bro ├── intel-dns.bro ├── intel-ext ├── LICENSE ├── __load__.bro ├── conn-udp-icmp.bro ├── dns-answers.bro ├── ftp-username.bro ├── radius.bro ├── smtp-subject.bro └── ssl.bro ├── livecheck.bro ├── logfilter_ip.txt ├── metasploit_ssl.bro ├── missing-roots.bro ├── perfect_forward_secrecy.bro ├── radius_bruteforcing.bro ├── relengawsportstopk.bro ├── relengawstopk.bro ├── sqli.bro ├── sshverlong.bro ├── ssl-ciphers.bro ├── ssl-log-ext-firefox-mitm.bro ├── ssl-log-ext.bro ├── ssl-log-ext1.bro ├── sslproto_stats.bro ├── sslverlong.bro ├── subnettopk.bro ├── unix_commands.bro ├── unusual_http_methods.bro ├── validate-certs-cache-intermediates.bro ├── verify_wpad.bro ├── weak-keys-mozilla.bro ├── weak_ciphers.bro ├── weak_protocols.bro ├── whitelist_scan_detection.bro ├── whitelist_scan_detection_input.bro └── whitelist_scan_ip.txt /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License, version 2.0 2 | 3 | 1. Definitions 4 | 5 | 1.1. “Contributor” 6 | 7 | means each individual or legal entity that creates, contributes to the 8 | creation of, or owns Covered Software. 9 | 10 | 1.2. “Contributor Version” 11 | 12 | means the combination of the Contributions of others (if any) used by a 13 | Contributor and that particular Contributor’s Contribution. 14 | 15 | 1.3. “Contribution” 16 | 17 | means Covered Software of a particular Contributor. 18 | 19 | 1.4. “Covered Software” 20 | 21 | means Source Code Form to which the initial Contributor has attached the 22 | notice in Exhibit A, the Executable Form of such Source Code Form, and 23 | Modifications of such Source Code Form, in each case including portions 24 | thereof. 25 | 26 | 1.5. “Incompatible With Secondary Licenses” 27 | means 28 | 29 | a. that the initial Contributor has attached the notice described in 30 | Exhibit B to the Covered Software; or 31 | 32 | b. that the Covered Software was made available under the terms of version 33 | 1.1 or earlier of the License, but not also under the terms of a 34 | Secondary License. 35 | 36 | 1.6. “Executable Form” 37 | 38 | means any form of the work other than Source Code Form. 39 | 40 | 1.7. “Larger Work” 41 | 42 | means a work that combines Covered Software with other material, in a separate 43 | file or files, that is not Covered Software. 44 | 45 | 1.8. “License” 46 | 47 | means this document. 48 | 49 | 1.9. “Licensable” 50 | 51 | means having the right to grant, to the maximum extent possible, whether at the 52 | time of the initial grant or subsequently, any and all of the rights conveyed by 53 | this License. 54 | 55 | 1.10. “Modifications” 56 | 57 | means any of the following: 58 | 59 | a. any file in Source Code Form that results from an addition to, deletion 60 | from, or modification of the contents of Covered Software; or 61 | 62 | b. any new file in Source Code Form that contains any Covered Software. 63 | 64 | 1.11. “Patent Claims” of a Contributor 65 | 66 | means any patent claim(s), including without limitation, method, process, 67 | and apparatus claims, in any patent Licensable by such Contributor that 68 | would be infringed, but for the grant of the License, by the making, 69 | using, selling, offering for sale, having made, import, or transfer of 70 | either its Contributions or its Contributor Version. 71 | 72 | 1.12. “Secondary License” 73 | 74 | means either the GNU General Public License, Version 2.0, the GNU Lesser 75 | General Public License, Version 2.1, the GNU Affero General Public 76 | License, Version 3.0, or any later versions of those licenses. 77 | 78 | 1.13. “Source Code Form” 79 | 80 | means the form of the work preferred for making modifications. 81 | 82 | 1.14. “You” (or “Your”) 83 | 84 | means an individual or a legal entity exercising rights under this 85 | License. For legal entities, “You” includes any entity that controls, is 86 | controlled by, or is under common control with You. For purposes of this 87 | definition, “control” means (a) the power, direct or indirect, to cause 88 | the direction or management of such entity, whether by contract or 89 | otherwise, or (b) ownership of more than fifty percent (50%) of the 90 | outstanding shares or beneficial ownership of such entity. 91 | 92 | 93 | 2. License Grants and Conditions 94 | 95 | 2.1. Grants 96 | 97 | Each Contributor hereby grants You a world-wide, royalty-free, 98 | non-exclusive license: 99 | 100 | a. under intellectual property rights (other than patent or trademark) 101 | Licensable by such Contributor to use, reproduce, make available, 102 | modify, display, perform, distribute, and otherwise exploit its 103 | Contributions, either on an unmodified basis, with Modifications, or as 104 | part of a Larger Work; and 105 | 106 | b. under Patent Claims of such Contributor to make, use, sell, offer for 107 | sale, have made, import, and otherwise transfer either its Contributions 108 | or its Contributor Version. 109 | 110 | 2.2. Effective Date 111 | 112 | The licenses granted in Section 2.1 with respect to any Contribution become 113 | effective for each Contribution on the date the Contributor first distributes 114 | such Contribution. 115 | 116 | 2.3. Limitations on Grant Scope 117 | 118 | The licenses granted in this Section 2 are the only rights granted under this 119 | License. No additional rights or licenses will be implied from the distribution 120 | or licensing of Covered Software under this License. Notwithstanding Section 121 | 2.1(b) above, no patent license is granted by a Contributor: 122 | 123 | a. for any code that a Contributor has removed from Covered Software; or 124 | 125 | b. for infringements caused by: (i) Your and any other third party’s 126 | modifications of Covered Software, or (ii) the combination of its 127 | Contributions with other software (except as part of its Contributor 128 | Version); or 129 | 130 | c. under Patent Claims infringed by Covered Software in the absence of its 131 | Contributions. 132 | 133 | This License does not grant any rights in the trademarks, service marks, or 134 | logos of any Contributor (except as may be necessary to comply with the 135 | notice requirements in Section 3.4). 136 | 137 | 2.4. Subsequent Licenses 138 | 139 | No Contributor makes additional grants as a result of Your choice to 140 | distribute the Covered Software under a subsequent version of this License 141 | (see Section 10.2) or under the terms of a Secondary License (if permitted 142 | under the terms of Section 3.3). 143 | 144 | 2.5. Representation 145 | 146 | Each Contributor represents that the Contributor believes its Contributions 147 | are its original creation(s) or it has sufficient rights to grant the 148 | rights to its Contributions conveyed by this License. 149 | 150 | 2.6. Fair Use 151 | 152 | This License is not intended to limit any rights You have under applicable 153 | copyright doctrines of fair use, fair dealing, or other equivalents. 154 | 155 | 2.7. Conditions 156 | 157 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in 158 | Section 2.1. 159 | 160 | 161 | 3. Responsibilities 162 | 163 | 3.1. Distribution of Source Form 164 | 165 | All distribution of Covered Software in Source Code Form, including any 166 | Modifications that You create or to which You contribute, must be under the 167 | terms of this License. You must inform recipients that the Source Code Form 168 | of the Covered Software is governed by the terms of this License, and how 169 | they can obtain a copy of this License. You may not attempt to alter or 170 | restrict the recipients’ rights in the Source Code Form. 171 | 172 | 3.2. Distribution of Executable Form 173 | 174 | If You distribute Covered Software in Executable Form then: 175 | 176 | a. such Covered Software must also be made available in Source Code Form, 177 | as described in Section 3.1, and You must inform recipients of the 178 | Executable Form how they can obtain a copy of such Source Code Form by 179 | reasonable means in a timely manner, at a charge no more than the cost 180 | of distribution to the recipient; and 181 | 182 | b. You may distribute such Executable Form under the terms of this License, 183 | or sublicense it under different terms, provided that the license for 184 | the Executable Form does not attempt to limit or alter the recipients’ 185 | rights in the Source Code Form under this License. 186 | 187 | 3.3. Distribution of a Larger Work 188 | 189 | You may create and distribute a Larger Work under terms of Your choice, 190 | provided that You also comply with the requirements of this License for the 191 | Covered Software. If the Larger Work is a combination of Covered Software 192 | with a work governed by one or more Secondary Licenses, and the Covered 193 | Software is not Incompatible With Secondary Licenses, this License permits 194 | You to additionally distribute such Covered Software under the terms of 195 | such Secondary License(s), so that the recipient of the Larger Work may, at 196 | their option, further distribute the Covered Software under the terms of 197 | either this License or such Secondary License(s). 198 | 199 | 3.4. Notices 200 | 201 | You may not remove or alter the substance of any license notices (including 202 | copyright notices, patent notices, disclaimers of warranty, or limitations 203 | of liability) contained within the Source Code Form of the Covered 204 | Software, except that You may alter any license notices to the extent 205 | required to remedy known factual inaccuracies. 206 | 207 | 3.5. Application of Additional Terms 208 | 209 | You may choose to offer, and to charge a fee for, warranty, support, 210 | indemnity or liability obligations to one or more recipients of Covered 211 | Software. However, You may do so only on Your own behalf, and not on behalf 212 | of any Contributor. You must make it absolutely clear that any such 213 | warranty, support, indemnity, or liability obligation is offered by You 214 | alone, and You hereby agree to indemnify every Contributor for any 215 | liability incurred by such Contributor as a result of warranty, support, 216 | indemnity or liability terms You offer. You may include additional 217 | disclaimers of warranty and limitations of liability specific to any 218 | jurisdiction. 219 | 220 | 4. Inability to Comply Due to Statute or Regulation 221 | 222 | If it is impossible for You to comply with any of the terms of this License 223 | with respect to some or all of the Covered Software due to statute, judicial 224 | order, or regulation then You must: (a) comply with the terms of this License 225 | to the maximum extent possible; and (b) describe the limitations and the code 226 | they affect. Such description must be placed in a text file included with all 227 | distributions of the Covered Software under this License. Except to the 228 | extent prohibited by statute or regulation, such description must be 229 | sufficiently detailed for a recipient of ordinary skill to be able to 230 | understand it. 231 | 232 | 5. Termination 233 | 234 | 5.1. The rights granted under this License will terminate automatically if You 235 | fail to comply with any of its terms. However, if You become compliant, 236 | then the rights granted under this License from a particular Contributor 237 | are reinstated (a) provisionally, unless and until such Contributor 238 | explicitly and finally terminates Your grants, and (b) on an ongoing basis, 239 | if such Contributor fails to notify You of the non-compliance by some 240 | reasonable means prior to 60 days after You have come back into compliance. 241 | Moreover, Your grants from a particular Contributor are reinstated on an 242 | ongoing basis if such Contributor notifies You of the non-compliance by 243 | some reasonable means, this is the first time You have received notice of 244 | non-compliance with this License from such Contributor, and You become 245 | compliant prior to 30 days after Your receipt of the notice. 246 | 247 | 5.2. If You initiate litigation against any entity by asserting a patent 248 | infringement claim (excluding declaratory judgment actions, counter-claims, 249 | and cross-claims) alleging that a Contributor Version directly or 250 | indirectly infringes any patent, then the rights granted to You by any and 251 | all Contributors for the Covered Software under Section 2.1 of this License 252 | shall terminate. 253 | 254 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user 255 | license agreements (excluding distributors and resellers) which have been 256 | validly granted by You or Your distributors under this License prior to 257 | termination shall survive termination. 258 | 259 | 6. Disclaimer of Warranty 260 | 261 | Covered Software is provided under this License on an “as is” basis, without 262 | warranty of any kind, either expressed, implied, or statutory, including, 263 | without limitation, warranties that the Covered Software is free of defects, 264 | merchantable, fit for a particular purpose or non-infringing. The entire 265 | risk as to the quality and performance of the Covered Software is with You. 266 | Should any Covered Software prove defective in any respect, You (not any 267 | Contributor) assume the cost of any necessary servicing, repair, or 268 | correction. This disclaimer of warranty constitutes an essential part of this 269 | License. No use of any Covered Software is authorized under this License 270 | except under this disclaimer. 271 | 272 | 7. Limitation of Liability 273 | 274 | Under no circumstances and under no legal theory, whether tort (including 275 | negligence), contract, or otherwise, shall any Contributor, or anyone who 276 | distributes Covered Software as permitted above, be liable to You for any 277 | direct, indirect, special, incidental, or consequential damages of any 278 | character including, without limitation, damages for lost profits, loss of 279 | goodwill, work stoppage, computer failure or malfunction, or any and all 280 | other commercial damages or losses, even if such party shall have been 281 | informed of the possibility of such damages. This limitation of liability 282 | shall not apply to liability for death or personal injury resulting from such 283 | party’s negligence to the extent applicable law prohibits such limitation. 284 | Some jurisdictions do not allow the exclusion or limitation of incidental or 285 | consequential damages, so this exclusion and limitation may not apply to You. 286 | 287 | 8. Litigation 288 | 289 | Any litigation relating to this License may be brought only in the courts of 290 | a jurisdiction where the defendant maintains its principal place of business 291 | and such litigation shall be governed by laws of that jurisdiction, without 292 | reference to its conflict-of-law provisions. Nothing in this Section shall 293 | prevent a party’s ability to bring cross-claims or counter-claims. 294 | 295 | 9. Miscellaneous 296 | 297 | This License represents the complete agreement concerning the subject matter 298 | hereof. If any provision of this License is held to be unenforceable, such 299 | provision shall be reformed only to the extent necessary to make it 300 | enforceable. Any law or regulation which provides that the language of a 301 | contract shall be construed against the drafter shall not be used to construe 302 | this License against a Contributor. 303 | 304 | 305 | 10. Versions of the License 306 | 307 | 10.1. New Versions 308 | 309 | Mozilla Foundation is the license steward. Except as provided in Section 310 | 10.3, no one other than the license steward has the right to modify or 311 | publish new versions of this License. Each version will be given a 312 | distinguishing version number. 313 | 314 | 10.2. Effect of New Versions 315 | 316 | You may distribute the Covered Software under the terms of the version of 317 | the License under which You originally received the Covered Software, or 318 | under the terms of any subsequent version published by the license 319 | steward. 320 | 321 | 10.3. Modified Versions 322 | 323 | If you create software not governed by this License, and you want to 324 | create a new license for such software, you may create and use a modified 325 | version of this License if you rename the license and remove any 326 | references to the name of the license steward (except to note that such 327 | modified license differs from this License). 328 | 329 | 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses 330 | If You choose to distribute Source Code Form that is Incompatible With 331 | Secondary Licenses under the terms of this version of the License, the 332 | notice described in Exhibit B of this License must be attached. 333 | 334 | Exhibit A - Source Code Form License Notice 335 | 336 | This Source Code Form is subject to the 337 | terms of the Mozilla Public License, v. 338 | 2.0. If a copy of the MPL was not 339 | distributed with this file, You can 340 | obtain one at 341 | http://mozilla.org/MPL/2.0/. 342 | 343 | If it is not possible or desirable to put the notice in a particular file, then 344 | You may include the notice in a location (such as a LICENSE file in a relevant 345 | directory) where a recipient would be likely to look for such a notice. 346 | 347 | You may add additional accurate notices of copyright ownership. 348 | 349 | Exhibit B - “Incompatible With Secondary Licenses” Notice 350 | 351 | This Source Code Form is “Incompatible 352 | With Secondary Licenses”, as defined by 353 | the Mozilla Public License, v. 2.0. 354 | 355 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | bro-gramming 2 | ============ 3 | 4 | Bro IDS programs collection. 5 | 6 | Special and a big thank you for the guidance, ideas and code snippets to: 7 | Seth Hall, Bro/ICSI, Broala 8 | Justin Azoff, Bro/NCSA 9 | Johanna Amann, Bro/ICSI 10 | And the rest of the Bro/Zeek Team 11 | Anthony Verez 12 | 13 | auth_bruteforcing - detect HTTP bruteforcing (Base64) 14 | bugzilla_bruteforce - an example how to parse raw HTTP data to implement an application level bruteforcing detection 15 | chrome-sha1 - warn on certificates issued before a certain timestamp and with SHA1 16 | cipher_stats - generates a periodic statistics of cipher suites used in your network. Useful to measure an impact when disabling yet another vulnerable ciphersuite 17 | conn-add-country - add the country code to each connection record 18 | conn-peer - for each connection record add the full worker name that processed that connection. Useful for troubleshooting packet loss. 19 | conn_bad_subnet - an early attempt to implement the Intelligence-like framework but for subnets 20 | conn_bad_subnet_input - a parser for the above code's config file (i.e. what to watch for) 21 | counttable - Johanna Amann's script to count the number of times $str has been seen, to be used with the SUMSTAT framework (see the ciper_stats script how to use that) 22 | detect-bruteforcing-ext - SSH bruteforcing detection on the wire 23 | detect_open_proxies - whitelist all known proxies, detect servers behaving like proxies you do not know about 24 | dhcpr - whitelist known DHCP servers, alert on unknown servers that look like they are sending DHCP answers 25 | dlp - a naive attempt to implement a DLP-like functionality, complete with whitelisting support 26 | excessive_http_errors_topk - a SUMSTATS script that's been highly successful detecting abusers of web services. Tune it to your liking. 27 | extract-interesting-files - an example script answering a popular question 'how to extract files of a certain MIME type' 28 | filter_input - a parser for the filter_noise_conn script. IP addresses for which traffic should not be logged can be put there 29 | filter_noise_conn - an example how to prevent some connections from logging 30 | filter_noise_dns - an example how to prevent some DNS queries from logging 31 | filter_noise_files - an example how to prevent some MIME types from logging (avoids the X509 certificates double-logging) 32 | filter_noise_http - an example how to prevent some HTTP transactions from logging 33 | filter_noise_intel - filter out noisy connections from the intel.log 34 | filter_noise_mysql - a filter that prevents ANY form of MySQL logging other than one crossing the private-public boundry 35 | filter_noise_ssl - filter out some SSL transactions and do not log them 36 | filter_noise_x509 - filter out some X509 certificates from log 37 | find_non_aes_clients - alert on SSL communication from clients using weak ciphersuite. Detects obsolete clients initating weak connections from your network 38 | find_non_aes_clients - alert on SSL communication from servers using weak ciphersuite. Detects weak ciphersuites negotiated by your servers 39 | heartbleed_mozillaca - an old example kept here to show how to alert based on certificate's data, including the time when the certificate was issued. Useful for detecting certificates from a compromised CA. 40 | http_auth_base64 - there is no place for HTTP+Base64 authentication and this scripts alerts on such traffic 41 | http_headers_lb - an example how to find a custom HTTP header in your traffic (here - from the load-balancer), add it to logs and use content in the Intel framework 42 | intel-dns - a script written by the Corelight team, that alerts on an actual connection to an IP associated with a domain that had had an Intel hit 43 | intel-ext - a collection of scripts extending the Intel framework, sources from Crowdstrike and modified for Mozilla 44 | livecheck - a small script that logs how much the logger process falls behind the connection processing, useful for troubleshooting 45 | perfect_forward_secrecy - adds the 'pfs' field to the SSL record if the connection uses PFS 46 | radius_bruteforcing - a small script to detect Radius auth bruteforcing 47 | sqli - a script to detect SQLi attempts 48 | sshverlong - detect a suspiciously long SSH client/server version string 49 | ssl-ciphers - written by Johanna Amann, this script calculates the percentage of the use of the different TLS cipher suites for each host in the local network 50 | ssl-log-ext1 - Add list of SSL/TLS cipher suites supported by clients to ssl log file - written by Johanna Amann 51 | ssl-log-ext - Add list of SSL/TLS cipher suites supported by clients to ssl log file - written by Johanna Amann 52 | sslproto_stats - yet another script creating a breakdown by SSL protocols seen on the wire 53 | subnettopk - a script that has been proven useful in the DDoS combat. Creates a log file with statistics about connections and bytes send/received per subnet 54 | unix_commands - a script to detect Unix command injection attempts 55 | unusual_http_methods - a script to detect the most unusual HTTP methods used, useful for tunneling detection, scanner detection, etc 56 | validate-certs-cache-intermediates - perform full certificate chain validation for SSL certificates. Also caches all intermediate certificates encountered so far and use them for future validations - Johanna Amann 57 | verify_wpad - alert on unknown (or all) WPAD queries and answers 58 | weak-keys-mozilla - generate notices when SSL/TLS connections use certificates or DH parameters that have potentially unsafe key lengths 59 | weak_ciphers - SslWeakCiphers give percentage of SSL weak ciphers used (< 2048 bits key except for ECDHE) 60 | weak_protocols - SslWeakProtocols give percentage of SSL weak protocols used (<= SSL2) 61 | whitelist_scan_detection - script to read in a list of IP addresses that will be whitelisted from scan detection (ignore as a source of a scan). 62 | whitelist_scan_detection_input - script to read in a list of IP addresses that will be whitelisted from scan detection (ignore as a source of a scan). 63 | -------------------------------------------------------------------------------- /__load__.bro: -------------------------------------------------------------------------------- 1 | @load ./http_headers_lb 2 | @load ./auth_bruteforcing 3 | @load ./dhcpr 4 | @load ./excessive_http_errors_topk 5 | @load ./perfect_forward_secrecy 6 | @load ./weak-keys-mozilla 7 | @load ./filter_input 8 | @load ./filter_noise_conn 9 | @load ./filter_noise_dns 10 | @load ./filter_noise_http 11 | @load ./filter_noise_x509 12 | @load ./filter_noise_files 13 | @load ./filter_noise_mysql 14 | @load ./filter_noise_intel 15 | @load ./filter_noise_ssl 16 | @load ./missing-roots 17 | @load ./verify_wpad 18 | @load ./unusual_http_methods 19 | @load ./intel-ext 20 | @load ./detect-bruteforcing-ext 21 | @load ./livecheck 22 | @load ./bugzilla_bruteforce 23 | @load ./conn-add-country 24 | @load ./conn-peer 25 | @load ./intel-dns 26 | @load ./whitelist_scan_detection_input 27 | @load ./whitelist_scan_detection 28 | @load ./dlp_input.bro 29 | @load ./dlp.bro 30 | -------------------------------------------------------------------------------- /auth_bruteforcing.bro: -------------------------------------------------------------------------------- 1 | # Script to detect HTTP auth bruteforcing 2 | # write http auth stuff in http_auth.log for forensics 3 | # Inspired by policy/protocols/http/detect-sqli.bro 4 | # 5 | # This Source Code Form is subject to the terms of the Mozilla Public 6 | # License, v. 2.0. If a copy of the MPL was not distributed with this 7 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | # 9 | # Software distributed under the License is distributed on an "AS IS" basis, 10 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 | # for the specific language governing rights and limitations under the 12 | # License. 13 | # 14 | # The Initial Developer of the Original Code is 15 | # Mozilla Corporation 16 | # Portions created by the Initial Developer are Copyright (C) 2014 17 | # the Initial Developer. All Rights Reserved. 18 | # 19 | # Contributor(s): 20 | # Anthony Verez averez@mozilla.com 21 | # Michal Purzynski mpurzynski@mozilla.com 22 | 23 | @load base/frameworks/notice 24 | @load base/frameworks/sumstats 25 | @load base/protocols/http 26 | 27 | module AuthBruteforcing; 28 | 29 | export { 30 | redef enum Notice::Type += { 31 | ## Indicates that a host performing HTTP requests leading to 32 | ## excessive HTTP auth errors was detected. 33 | HTTP_AuthBruteforcing_Attacker, 34 | ## Indicates that a host was seen to respond excessive HTTP 35 | ## auth errors. This is tracked by IP address as opposed to 36 | ## hostname. 37 | HTTP_AuthBruteforcing_Victim, 38 | }; 39 | 40 | # Let's tag the http item 41 | redef enum HTTP::Tags += { 42 | ## HTTP status code 401, describing a HTTP auth error 43 | HTTP_AUTH_ERROR, 44 | ## HTTP describing a successful HTTP auth 45 | HTTP_AUTH_SUCCESS, 46 | }; 47 | 48 | redef enum Log::ID += { LOG }; 49 | 50 | type Info: record { 51 | ts: time &log; 52 | uid: string &log; 53 | id: conn_id &log &optional; 54 | cluster_client_ip: string &log &optional; 55 | status_code: count &log &optional; 56 | host: string &log &optional; 57 | uri: string &log &optional; 58 | username: string &log &optional; 59 | auth_success: bool &log &optional; 60 | }; 61 | 62 | global log_auth: event(rec: Info); 63 | 64 | ## Defines the threshold that determines if a auth bruteforcing attack 65 | ## is ongoing based on the number of requests that appear to be 66 | ## attacks. 67 | const auth_errors_threshold: double = 10.0 &redef; 68 | 69 | ## Interval at which to watch for the 70 | ## :bro:id:`AuthBruteforcing::auth_errors_requests_threshold` variable to be crossed. 71 | ## At the end of each interval the counter is reset. 72 | const auth_errors_interval = 2min &redef; 73 | 74 | ## Interval at which to watch for the 75 | ## :bro:id:`AuthBruteforcing::excessive_auth_errors_threshold` variable to be 76 | ## crossed. At the end of each interval the counter is reset. 77 | const excessive_auth_errors_interval = 1min &redef; 78 | 79 | const internal_space: subnet = 10.0.0.0/8 &redef; 80 | const public_space: subnet = 10.0.0.0/20 &redef; 81 | 82 | const ignore_host_resp: set[addr] = { } &redef; 83 | const ignore_host_orig: set[addr] = { } &redef; 84 | } 85 | 86 | event bro_init() &priority=3 87 | { 88 | # Create auth_bruteforcing.log 89 | Log::create_stream(AuthBruteforcing::LOG, [$columns=Info, $ev=log_auth]); 90 | 91 | # HTTP auth errors for requests FROM the same host 92 | local r1: SumStats::Reducer = [$stream="http.auth_errors.attacker", $apply=set(SumStats::SUM)]; 93 | SumStats::create([$name="auth-http-errors-attackers", 94 | $epoch=auth_errors_interval, 95 | $reducers=set(r1), 96 | $threshold_val(key: SumStats::Key, result: SumStats::Result) = { 97 | return result["http.auth_errors.attacker"]$sum; 98 | }, 99 | $threshold=auth_errors_threshold, 100 | $threshold_crossed(key: SumStats::Key, result: SumStats::Result) = { 101 | NOTICE([$note=HTTP_AuthBruteforcing_Attacker, 102 | $msg=fmt("HTTP auth bruteforcing from attacker %s", key$host), 103 | $sub=fmt("%.0f auth failed in %s", result["http.auth_errors.attacker"]$sum, auth_errors_interval), 104 | $src=key$host, 105 | $n=to_count(fmt("%.0f", result["http.auth_errors.attacker"]$sum)) 106 | ]); 107 | }]); 108 | 109 | # HTTP errors for requests TO the same host 110 | local r2: SumStats::Reducer = [$stream="http.auth_errors.victim", $apply=set(SumStats::SUM)]; 111 | SumStats::create([$name="auth-http-errors-victims", 112 | $epoch=auth_errors_interval, 113 | $reducers=set(r2), 114 | $threshold_val(key: SumStats::Key, result: SumStats::Result) = { 115 | return result["http.auth_errors.victim"]$sum; 116 | }, 117 | $threshold=auth_errors_threshold, 118 | $threshold_crossed(key: SumStats::Key, result: SumStats::Result) = { 119 | NOTICE([$note=HTTP_AuthBruteforcing_Victim, 120 | $msg=fmt("HTTP auth bruteforcing to victim %s", key$host), 121 | $sub=fmt("%.0f auth failed in %s", result["http.auth_errors.victim"]$sum, auth_errors_interval), 122 | $src=key$host, 123 | $n=to_count(fmt("%.0f", result["http.auth_errors.victim"]$sum)) 124 | ]); 125 | }]); 126 | } 127 | 128 | # Make sure we have all the http info before looking for auth errors 129 | event http_message_done(c: connection, is_orig: bool, stat: http_message_stat) 130 | { 131 | # only conns we want 132 | local ports_ext: set[port] = { 80/tcp }; 133 | local ports_int: set[port] = { 80/tcp, 81/tcp, 443/tcp }; 134 | 135 | if (c$id$resp_h in ignore_host_resp) 136 | return; 137 | if (c$id$orig_h in ignore_host_orig) 138 | return; 139 | 140 | if (((c$id$resp_h in internal_space) && (c$id$resp_p in ports_int)) || ((c$id$resp_h in public_space) && (c$id$resp_p in ports_ext))) { 141 | 142 | if (c$http?$username && c$http?$status_code) { 143 | local auth_success : bool = T; 144 | if (c$http$status_code == 401) { 145 | auth_success = F; 146 | add c$http$tags[HTTP_AUTH_ERROR]; 147 | } 148 | else if (c$http$status_code < 400) { 149 | auth_success = T; 150 | add c$http$tags[HTTP_AUTH_SUCCESS]; 151 | } 152 | if(!auth_success) { 153 | SumStats::observe("http.auth_errors.attacker", 154 | [$host=to_addr(c$http$cluster_client_ip)], 155 | []); 156 | if ( c?$conn ) 157 | SumStats::observe("http.auth_errors.victim", 158 | [$host=c$conn$id$resp_h], 159 | []); 160 | } 161 | } 162 | } 163 | } 164 | 165 | -------------------------------------------------------------------------------- /bugzilla_bruteforce.bro: -------------------------------------------------------------------------------- 1 | module BugzBruteforcing; 2 | 3 | export { 4 | redef enum Notice::Type += { 5 | ## Indicates that a host performing HTTP requests leading to 6 | ## excessive HTTP auth errors was detected. 7 | HTTP_BugzBruteforcing_Attacker, 8 | ## Indicates that a host was seen to respond excessive HTTP 9 | ## auth errors. This is tracked by IP address as opposed to 10 | ## hostname. 11 | HTTP_BugzBruteforcing_Victim, 12 | }; 13 | 14 | const ports_int: set[port] = { 80/tcp, 443/tcp } &redef; 15 | 16 | redef enum Log::ID += { LOG }; 17 | 18 | # Let's tag the http item 19 | redef enum HTTP::Tags += { 20 | ## HTTP status code 401, describing a HTTP auth error 21 | HTTP_AUTH_ERROR, 22 | ## HTTP describing a successful HTTP auth 23 | HTTP_AUTH_SUCCESS, 24 | }; 25 | 26 | type Info: record { 27 | ts: time &log; 28 | uid: string &log; 29 | id: conn_id &log &optional; 30 | cluster_client_ip: string &log &optional; 31 | status_code: count &log &optional; 32 | host: string &log &optional; 33 | uri: string &log &optional; 34 | username: string &log &optional; 35 | auth_success: bool &log &optional; 36 | }; 37 | 38 | const auth_errors_threshold: double = 10.0 &redef; 39 | const auth_errors_interval = 15min &redef; 40 | } 41 | 42 | event bro_init() 43 | { 44 | Log::create_stream(BugzBruteforcing::LOG, [$columns=Info]); 45 | 46 | # HTTP auth errors for requests FROM the same host 47 | local r1: SumStats::Reducer = [$stream="bugz.auth_errors.attacker", $apply=set(SumStats::SUM)]; 48 | SumStats::create([$name="bugz-http-errors-attackers", 49 | $epoch=auth_errors_interval, 50 | $reducers=set(r1), 51 | $threshold_val(key: SumStats::Key, result: SumStats::Result) = { 52 | return result["bugz.auth_errors.attacker"]$sum; 53 | }, 54 | $threshold=auth_errors_threshold, 55 | $threshold_crossed(key: SumStats::Key, result: SumStats::Result) = { 56 | NOTICE([$note=HTTP_BugzBruteforcing_Attacker, 57 | $msg=fmt("HTTP auth bruteforcing from attacker %s", key$host), 58 | $sub=fmt("%.0f auth failed in %s", result["bugz.auth_errors.attacker"]$sum, auth_errors_interval), 59 | $src=key$host, 60 | $n=to_count(fmt("%.0f", result["bugz.auth_errors.attacker"]$sum)) 61 | ]); 62 | }]); 63 | 64 | # HTTP errors for requests TO the same host 65 | local r2: SumStats::Reducer = [$stream="bugz.auth_errors.victim", $apply=set(SumStats::SUM)]; 66 | SumStats::create([$name="bugz-http-errors-victims", 67 | $epoch=auth_errors_interval, 68 | $reducers=set(r2), 69 | $threshold_val(key: SumStats::Key, result: SumStats::Result) = { 70 | return result["bugz.auth_errors.victim"]$sum; 71 | }, 72 | $threshold=auth_errors_threshold, 73 | $threshold_crossed(key: SumStats::Key, result: SumStats::Result) = { 74 | NOTICE([$note=HTTP_BugzBruteforcing_Victim, 75 | $msg=fmt("HTTP auth bruteforcing to victim %s", key$str), 76 | $sub=fmt("%s auth failed in %s", result["bugz.auth_errors.victim"]$sum, auth_errors_interval), 77 | $n=to_count(fmt("%.0f", result["bugz.auth_errors.victim"]$sum)) 78 | ]); 79 | }]); 80 | } 81 | 82 | event http_entity_data(c: connection, is_orig: bool, length: count, data: string) { 83 | # if (!Site::is_local_addr(c$id$resp_h)) 84 | # return; 85 | if (c$id$resp_p !in ports_int) 86 | return; 87 | if (!c$http?$method || c$http$method != "POST") 88 | return; 89 | if (/(index|show_bug)\.cgi/ !in c$http$uri) 90 | return; 91 | 92 | local meta_table: string = sub_bytes(data, 250, 260); 93 | if (/Invalid\ Username\ Or\ Password/ in meta_table) { 94 | SumStats::observe("bugz.auth_errors.attacker", [$host=to_addr(c$http$cluster_client_ip)], SumStats::Observation($num=1)); 95 | SumStats::observe("bugz.auth_errors.victim", [$str=c$http$host], SumStats::Observation($num=1)); 96 | add c$http$tags[HTTP_AUTH_ERROR]; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /chrome-sha1.bro: -------------------------------------------------------------------------------- 1 | # This script identifies certificates on the local network which will be 2 | # impacted by the Chrome SHA-1 sunset changes. For more details, please 3 | # see http://googleonlinesecurity.blogspot.com/2014/09/gradually-sunsetting-sha-1.html 4 | # 5 | # Questions -> johanna@icir.org 6 | 7 | @load base/protocols/ssl 8 | @load base/frameworks/notice 9 | 10 | module Chrome; 11 | 12 | export { 13 | redef enum Notice::Type += { 14 | ## Indicates that the certificate of a host will be impacted by the google 15 | ## SHA-1 sunset changes. 16 | SHA1_Sunset 17 | }; 18 | } 19 | 20 | global recently_checked_certs: set[string] = set(); 21 | 22 | event ssl_established(c: connection) 23 | { 24 | if (!Site::is_local_addr(c$id$resp_h)) 25 | return; 26 | 27 | # If there aren't any certs we can't validate the chain. 28 | if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| == 0 || 29 | ! c$ssl$cert_chain[0]?$x509 ) 30 | return; 31 | 32 | local chain_id = ""; 33 | local chain: vector of opaque of x509 = vector(); 34 | local chain_hashes: table[string] of string; 35 | 36 | for ( i in c$ssl$cert_chain ) 37 | { 38 | chain_id = cat(chain_id, c$ssl$cert_chain[i]$sha1); 39 | if ( c$ssl$cert_chain[i]?$x509 ) 40 | { 41 | chain[i] = c$ssl$cert_chain[i]$x509$handle; 42 | chain_hashes[c$ssl$cert_chain[i]$sha1] = c$ssl$cert_chain[i]$fuid; 43 | } 44 | } 45 | 46 | if ( chain_id in recently_checked_certs ) 47 | return; 48 | 49 | add recently_checked_certs[chain_id]; 50 | 51 | # This only applies to certificates with an expiry after 2016-01-01. 52 | local cutoff: time = double_to_time(1451606400.0); 53 | 54 | if ( c$ssl$cert_chain[0]$x509$certificate$not_valid_after < cutoff ) 55 | return; 56 | 57 | local result = x509_verify(chain, SSL::root_certs); 58 | 59 | # If we cannot validate, we cannot tell anything in any case... 60 | if ( result$result_string != "ok" ) 61 | return; 62 | 63 | local vchain = result$chain_certs; 64 | for ( i in vchain ) 65 | { 66 | local cert = x509_parse(vchain[i]); 67 | 68 | if ( cert$subject == cert$issuer ) 69 | # skip the root, it is allowed to use whatever hash algorithm it wants to. 70 | next; 71 | 72 | if ( /^sha1With/ in cert$sig_alg ) 73 | { 74 | local msg: string = "An intermediate CA certificate in the chain uses SHA-1. Chrome will consider this unsafe in the future."; 75 | if ( i == 0 ) 76 | msg = "The host certificate uses SHA-1. Chrome will consider this unsafe in the future."; 77 | 78 | local n: Notice::Info = [$note=SHA1_Sunset, 79 | $msg=msg, 80 | $sub=fmt("Subject: %s, Issuer: %s, Signature algorithm: %s", cert$subject, cert$issuer, cert$sig_alg), 81 | $conn=c, $n=int_to_count(i), 82 | $identifier=cat(c$id$resp_h,c$id$resp_p,i), 83 | $suppress_for=7 days 84 | ]; 85 | 86 | local cert_hash = sha1_hash(x509_get_certificate_string(vchain[i])); 87 | if ( cert_hash in chain_hashes ) 88 | n$fuid = chain_hashes[cert_hash]; 89 | 90 | NOTICE(n); 91 | } 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /cipher_stats.bro: -------------------------------------------------------------------------------- 1 | # Script to calculate the statistics about the SSL/TLS ciphersuites used in your network. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | # 7 | # Contributor(s): 8 | # Michal Purzynski mpurzynski@mozilla.com 9 | 10 | @load base/protocols/ssl 11 | 12 | module SSLCipherStat; 13 | 14 | export { 15 | redef enum Log::ID += { LOG1 }; 16 | redef enum Log::ID += { LOG2 }; 17 | 18 | type Info_C: record { 19 | ## Timestamp when the log line was finished and written. 20 | ts: time &log; 21 | ## Time interval that the log line covers. 22 | ts_delta: interval &log; 23 | resp_h: addr &log; 24 | ecdhe_c: double &log; 25 | dhe_c: double &log; 26 | dh_c: double &log; 27 | aes_c: double &log; 28 | ecdhe_s: double &log; 29 | dhe_s: double &log; 30 | dh_s: double &log; 31 | aes_s: double &log; 32 | }; 33 | 34 | type Info_S: record { 35 | ## Timestamp when the log line was finished and written. 36 | ts: time &log; 37 | ## Time interval that the log line covers. 38 | ts_delta: interval &log; 39 | resp_h: addr &log; 40 | ecdhe: double &log; 41 | dhe: double &log; 42 | dh: double &log; 43 | aes: double &log; 44 | }; 45 | 46 | ## The frequency of logging the stats collected by this script. 47 | const break_interval = 15mins &redef; 48 | 49 | ## Monitored hosts for weak SSL ciphers 50 | const cert_tracking = ALL_HOSTS &redef; 51 | } 52 | 53 | redef record SSL::Info += { 54 | ## Ciphers available for the client 55 | weak_cipher: bool &log &optional; 56 | }; 57 | 58 | event bro_init() 59 | { 60 | Log::create_stream(SSLCipherStat::LOG1, [$columns=Info_C]); 61 | Log::create_stream(SSLCipherStat::LOG2, [$columns=Info_S]); 62 | 63 | local cr1: SumStats::Reducer = [$stream="ssl_cipher_stat.ssl_hits", $apply=set(SumStats::SUM)]; 64 | local cr2: SumStats::Reducer = [$stream="ssl_cipher_stat.cipher_hits", $apply=set(SumStats::SUM)]; 65 | local cr3: SumStats::Reducer = [$stream="ssl_cipher_stat.ecdhe_hits", $apply=set(SumStats::SUM)]; 66 | local cr4: SumStats::Reducer = [$stream="ssl_cipher_stat.dhe_hits", $apply=set(SumStats::SUM)]; 67 | local cr5: SumStats::Reducer = [$stream="ssl_cipher_stat.dh_hits", $apply=set(SumStats::SUM)]; 68 | local cr6: SumStats::Reducer = [$stream="ssl_cipher_stat.aes_hits", $apply=set(SumStats::SUM)]; 69 | local cr7: SumStats::Reducer = [$stream="ssl_cipher_stat.client_dh", $apply=set(SumStats::SUM)]; 70 | local cr8: SumStats::Reducer = [$stream="ssl_cipher_stat.client_dhe", $apply=set(SumStats::SUM)]; 71 | local cr9: SumStats::Reducer = [$stream="ssl_cipher_stat.client_ecdhe", $apply=set(SumStats::SUM)]; 72 | local cr10: SumStats::Reducer = [$stream="ssl_cipher_stat.client_aes", $apply=set(SumStats::SUM)]; 73 | 74 | local sr1: SumStats::Reducer = [$stream="ssl_cipher_negotiated.ssl_hits", $apply=set(SumStats::SUM)]; 75 | local sr2: SumStats::Reducer = [$stream="ssl_cipher_negotiated.ecdhe_hits", $apply=set(SumStats::SUM)]; 76 | local sr3: SumStats::Reducer = [$stream="ssl_cipher_negotiated.dhe_hits", $apply=set(SumStats::SUM)]; 77 | local sr4: SumStats::Reducer = [$stream="ssl_cipher_negotiated.dh_hits", $apply=set(SumStats::SUM)]; 78 | local sr5: SumStats::Reducer = [$stream="ssl_cipher_negotiated.aes_hits", $apply=set(SumStats::SUM)]; 79 | 80 | SumStats::create([$name="ssl_cipher_stat.ssl_hits", 81 | $epoch=break_interval, 82 | $reducers=set(cr1,cr2,cr3,cr4,cr5,cr6,cr7,cr8,cr9,cr10), 83 | $epoch_result(ts: time, key: SumStats::Key, result: SumStats::Result) = 84 | { 85 | local l: Info_C; 86 | 87 | l$resp_h = key$host; 88 | l$ts = network_time(); 89 | l$ts_delta = break_interval; 90 | if ("ssl_cipher_stat.ecdhe_hits" in result) 91 | l$ecdhe_c = result["ssl_cipher_stat.ecdhe_hits"]$sum/result["ssl_cipher_stat.cipher_hits"]$sum; 92 | if ("ssl_cipher_stat.dhe_hits" in result) 93 | l$dhe_c = result["ssl_cipher_stat.dhe_hits"]$sum/result["ssl_cipher_stat.cipher_hits"]$sum; 94 | if ("ssl_cipher_stat.dh_hits" in result) 95 | l$dh_c = result["ssl_cipher_stat.dh_hits"]$sum/result["ssl_cipher_stat.cipher_hits"]$sum; 96 | if ("ssl_cipher_stat.aes_hits" in result) 97 | l$aes_c = result["ssl_cipher_stat.aes_hits"]$sum/result["ssl_cipher_stat.cipher_hits"]$sum; 98 | if ("ssl_cipher_stat.ecdhe_hits" in result) 99 | l$ecdhe_s = result["ssl_cipher_stat.client_ecdhe"]$sum/result["ssl_cipher_stat.ssl_hits"]$sum; 100 | if ("ssl_cipher_stat.dhe_hits" in result) 101 | l$dhe_s = result["ssl_cipher_stat.client_dhe"]$sum/result["ssl_cipher_stat.ssl_hits"]$sum; 102 | if ("ssl_cipher_stat.dh_hits" in result) 103 | l$dh_s = result["ssl_cipher_stat.client_dh"]$sum/result["ssl_cipher_stat.ssl_hits"]$sum; 104 | if ("ssl_cipher_stat.aes_hits" in result) 105 | l$aes_s = result["ssl_cipher_stat.client_aes"]$sum/result["ssl_cipher_stat.ssl_hits"]$sum; 106 | 107 | Log::write(LOG1,l); 108 | } 109 | ]); 110 | 111 | SumStats::create([$name="ssl_cipher_negotiated.ssl_hits", 112 | $epoch=break_interval, 113 | $reducers=set(sr1,sr2,sr3,sr4,sr5), 114 | $epoch_result(ts: time, key: SumStats::Key, result: SumStats::Result) = 115 | { 116 | local l: Info_S; 117 | 118 | l$resp_h = key$host; 119 | l$ts = network_time(); 120 | l$ts_delta = break_interval; 121 | if ("ssl_cipher_negotiated.ecdhe_hits" in result) 122 | l$ecdhe = result["ssl_cipher_negotiated.ecdhe_hits"]$sum/result["ssl_cipher_negotiated.ssl_hits"]$sum; 123 | if ("ssl_cipher_negotiated.dhe_hits" in result) 124 | l$dhe = result["ssl_cipher_negotiated.dhe_hits"]$sum/result["ssl_cipher_negotiated.ssl_hits"]$sum; 125 | if ("ssl_cipher_negotiated.dh_hits" in result) 126 | l$dh = result["ssl_cipher_negotiated.dh_hits"]$sum/result["ssl_cipher_negotiated.ssl_hits"]$sum; 127 | if ("ssl_cipher_negotiated.aes_hits" in result) { 128 | l$aes = result["ssl_cipher_negotiated.aes_hits"]$sum/result["ssl_cipher_negotiated.ssl_hits"]$sum; 129 | } 130 | 131 | Log::write(LOG2,l); 132 | } 133 | ]); 134 | 135 | } 136 | 137 | event ssl_client_hello(c: connection, version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec) 138 | { 139 | if (Site::is_local_addr(c$id$orig_h)) 140 | return; 141 | 142 | local dh_ok = F; 143 | local dhe_ok = F; 144 | local ecdhe_ok = F; 145 | local aes_ok = F; 146 | 147 | SumStats::observe("ssl_cipher_stat.ssl_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 148 | 149 | for (cipher in ciphers) { 150 | SumStats::observe("ssl_cipher_stat.cipher_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 151 | if (/_ECDHE_/ in SSL::cipher_desc[ciphers[cipher]]) { 152 | SumStats::observe("ssl_cipher_stat.ecdhe_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 153 | ecdhe_ok = T; 154 | } 155 | if (/_DHE_/ in SSL::cipher_desc[ciphers[cipher]]) { 156 | SumStats::observe("ssl_cipher_stat.dhe_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 157 | dhe_ok = T; 158 | } 159 | if (/_DH_/ in SSL::cipher_desc[ciphers[cipher]]) { 160 | SumStats::observe("ssl_cipher_stat.dh_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 161 | dh_ok = T; 162 | } 163 | if (/_AES_/ in SSL::cipher_desc[ciphers[cipher]]) { 164 | SumStats::observe("ssl_cipher_stat.aes_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 165 | aes_ok = T; 166 | } 167 | } 168 | 169 | if (dh_ok = T) 170 | SumStats::observe("ssl_cipher_stat.client_dh", [$host=c$id$resp_h], SumStats::Observation($num=1)); 171 | if (dhe_ok = T) 172 | SumStats::observe("ssl_cipher_stat.client_dhe", [$host=c$id$resp_h], SumStats::Observation($num=1)); 173 | if (ecdhe_ok = T) 174 | SumStats::observe("ssl_cipher_stat.client_ecdhe", [$host=c$id$resp_h], SumStats::Observation($num=1)); 175 | if (aes_ok = T) 176 | SumStats::observe("ssl_cipher_stat.client_aes", [$host=c$id$resp_h], SumStats::Observation($num=1)); 177 | 178 | } 179 | 180 | event ssl_server_hello(c: connection, version: count, possible_ts: time, server_random: string, session_id: string, cipher: count, comp_method: count) 181 | { 182 | local dh_ok = F; 183 | local dhe_ok = F; 184 | local ecdhe_ok = F; 185 | local aes_ok = F; 186 | 187 | SumStats::observe("ssl_cipher_negotiated.ssl_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 188 | 189 | if (/_AES_/ in SSL::cipher_desc[cipher]) 190 | SumStats::observe("ssl_cipher_negotiated.aes_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 191 | if (/_DHE_/ in SSL::cipher_desc[cipher]) 192 | SumStats::observe("ssl_cipher_negotiated.dhe_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 193 | if (/_ECDHE_/ in SSL::cipher_desc[cipher]) 194 | SumStats::observe("ssl_cipher_negotiated.ecdhe_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 195 | if (/_DH_/ in SSL::cipher_desc[cipher]) 196 | SumStats::observe("ssl_cipher_negotiated.dh_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 197 | } 198 | -------------------------------------------------------------------------------- /conn-add-country.bro: -------------------------------------------------------------------------------- 1 | ##! Add country codes for the originator and responder to each connection in the conn log. 2 | 3 | redef record Conn::Info += { 4 | orig_cc: string &log &optional; 5 | resp_cc: string &log &optional; 6 | }; 7 | 8 | event connection_state_remove(c: connection) 9 | { 10 | local orig_loc = lookup_location(c$id$orig_h); 11 | if ( orig_loc?$country_code ) 12 | c$conn$orig_cc = orig_loc$country_code; 13 | 14 | local resp_loc = lookup_location(c$id$resp_h); 15 | if ( resp_loc?$country_code ) 16 | c$conn$resp_cc = resp_loc$country_code; 17 | } 18 | -------------------------------------------------------------------------------- /conn-peer.bro: -------------------------------------------------------------------------------- 1 | ##! Add the peer to the connection logs. 2 | 3 | module Conn; 4 | 5 | export { 6 | redef record Conn::Info += { 7 | peer: string &optional &log; 8 | }; 9 | } 10 | 11 | event connection_state_remove(c: connection) { 12 | c$conn$peer = peer_description; 13 | } 14 | -------------------------------------------------------------------------------- /conn_bad_subnet.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | # Script to detect connections to subnet given as a table index. Yields metadata. 9 | 10 | module ConnBadSubnet; 11 | 12 | redef enum Notice::Type += { 13 | IN_ORIG, 14 | IN_RESP, 15 | }; 16 | 17 | event new_connection(c: connection) 18 | { 19 | if ( ! c?$id ) 20 | return; 21 | if ( ! c$id?$orig_h ) 22 | return; 23 | 24 | if ( c$id$orig_h in bad_subnets ) 25 | NOTICE([$note=IN_ORIG, 26 | $msg=fmt("Suspicious IP %s known from %s attacks initated connection to our host %s ", cat(c$id$orig_h), bad_subnets[c$id$orig_h], cat(c$id$resp_h)), 27 | $uid=c$uid, 28 | $id=c$id, 29 | $identifier=cat(c$uid)]); 30 | 31 | if ( c$id$resp_h in bad_subnets ) 32 | NOTICE([$note=IN_RESP, 33 | $msg=fmt("Our host %s initiated connection to suspicious %s known from %s attacks", cat(c$id$orig_h), cat(c$id$resp_h), bad_subnets[c$id$resp_h]), 34 | $uid=c$uid, 35 | $id=c$id, 36 | $identifier=cat(c$uid)]); 37 | } 38 | 39 | -------------------------------------------------------------------------------- /conn_bad_subnet_input.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | # Script to detect connections to subnet given as a table index. Yields metadata. 9 | 10 | module ConnBadSubnet; 11 | 12 | type Idx: record { 13 | bad_subnet: subnet; 14 | }; 15 | 16 | type Val: record { 17 | timestamp: time; 18 | description: string; 19 | }; 20 | 21 | global bad_subnets: table[subnet] of Val = table(); 22 | 23 | event bro_init() 24 | { 25 | Input::add_table([$source="/opt/bro/share/bro/intelzilla/bad_subnets.txt", $name="bad_subnets_list", $idx=Idx, $val=Val, $destination=bad_subnets, $mode=Input::REREAD]); 26 | } 27 | 28 | -------------------------------------------------------------------------------- /counttable.bro: -------------------------------------------------------------------------------- 1 | # This script provives a COUNTTABLE type for sumstats. This type is basically like 2 | # SUM, with the difference that you have to provide a $str in the observation, and 3 | # the SUM is calculated independently for each $str. 4 | # 5 | # This makes it optimal to sum up small number of keys per host like, for example, 6 | # all the TLS ciphers you saw in use for hosts on the local host. 7 | # 8 | # Do not try to use this with a big number of different $str values, especially 9 | # in a cluster setup. It will probably lead to excessive resource use. 10 | # Johanna Amann, Bro/ICSI - johanna@icir.org 11 | 12 | @load base/frameworks/sumstats 13 | 14 | module SumStats; 15 | 16 | export { 17 | redef enum Calculation += { COUNTTABLE }; 18 | 19 | redef record ResultVal += { 20 | counttable: table[string] of count &optional; 21 | }; 22 | } 23 | 24 | function add_ct_entry(mytable: table[string] of count, str: string, num: count) 25 | { 26 | if ( str !in mytable ) 27 | mytable[str] = 0; 28 | 29 | mytable[str] += num; 30 | } 31 | 32 | hook register_observe_plugins() 33 | { 34 | register_observe_plugin(COUNTTABLE, function(r: Reducer, val: double, obs: Observation, rv: ResultVal) 35 | { 36 | if ( ! obs?$str ) 37 | { 38 | Reporter::error("COUNTTABLE sumstats plugin needs str in observation"); 39 | return; 40 | } 41 | 42 | local increment = 1; 43 | if ( obs?$num ) 44 | increment = obs$num; 45 | 46 | if ( ! rv?$counttable ) 47 | rv$counttable = table(); 48 | 49 | add_ct_entry(rv$counttable, obs$str, increment); 50 | }); 51 | } 52 | 53 | hook compose_resultvals_hook(result: ResultVal, rv1: ResultVal, rv2: ResultVal) 54 | { 55 | if ( ! (rv1?$counttable || rv2?$counttable ) ) 56 | return; 57 | 58 | if ( !rv1?$counttable ) 59 | { 60 | result$counttable = copy(rv2$counttable); 61 | return; 62 | } 63 | 64 | if ( !rv2?$counttable ) 65 | { 66 | result$counttable = copy(rv1$counttable); 67 | return; 68 | } 69 | 70 | result$counttable = copy(rv1$counttable); 71 | 72 | for ( i in rv2$counttable ) 73 | add_ct_entry(result$counttable, i, rv2$counttable[i]); 74 | } 75 | 76 | -------------------------------------------------------------------------------- /detect-bruteforcing-ext.bro: -------------------------------------------------------------------------------- 1 | ##! Detect hosts which are doing password guessing attacks and/or password 2 | ##! bruteforcing over SSH. 3 | 4 | @load base/protocols/ssh 5 | @load base/frameworks/sumstats 6 | @load base/frameworks/notice 7 | @load base/frameworks/intel 8 | 9 | module SSH; 10 | 11 | export { 12 | redef enum Notice::Type += { 13 | ## Indicates that a host has been identified as crossing the 14 | ## :bro:id:`SSH::password_guesses_limit` threshold with 15 | ## failed logins. 16 | Password_Guessing, 17 | ## Indicates that a host previously identified as a "password 18 | ## guesser" has now had a successful login 19 | ## attempt. This is not currently implemented. 20 | Login_By_Password_Guesser, 21 | }; 22 | 23 | redef enum Intel::Where += { 24 | ## An indicator of the login for the intel framework. 25 | SSH::SUCCESSFUL_LOGIN, 26 | }; 27 | 28 | ## The number of failed SSH connections before a host is designated as 29 | ## guessing passwords. 30 | const password_guesses_limit: double = 30 &redef; 31 | 32 | ## The amount of time to remember presumed non-successful logins to 33 | ## build a model of a password guesser. 34 | const guessing_timeout = 30 mins &redef; 35 | 36 | ## This value can be used to exclude hosts or entire networks from being 37 | ## tracked as potential "guessers". The index represents 38 | ## client subnets and the yield value represents server subnets. 39 | const ignore_guessers: table[subnet] of set[subnet] &redef; 40 | } 41 | 42 | event bro_init() 43 | { 44 | local r1: SumStats::Reducer = [$stream="ssh.login.failure", $apply=set(SumStats::SUM, SumStats::SAMPLE), $num_samples=5]; 45 | SumStats::create([$name="detect-ssh-bruteforcing", 46 | $epoch=guessing_timeout, 47 | $reducers=set(r1), 48 | $threshold_val(key: SumStats::Key, result: SumStats::Result) = 49 | { 50 | return result["ssh.login.failure"]$sum; 51 | }, 52 | $threshold=password_guesses_limit, 53 | $threshold_crossed(key: SumStats::Key, result: SumStats::Result) = 54 | { 55 | local r = result["ssh.login.failure"]; 56 | local sub_msg = fmt("Sampled servers: "); 57 | local samples = r$samples; 58 | for ( i in samples ) 59 | { 60 | if ( samples[i]?$str ) 61 | sub_msg = fmt("%s%s %s", sub_msg, i==0 ? "":",", samples[i]$str); 62 | } 63 | # Generate the notice. 64 | NOTICE([$note=Password_Guessing, 65 | $msg=fmt("%s appears to be guessing SSH passwords (seen in %d connections).", key$host, r$num), 66 | $sub=sub_msg, 67 | $src=key$host, 68 | $identifier=cat(key$host)]); 69 | }]); 70 | } 71 | 72 | event ssh_auth_successful(c: connection, auth_method_none: bool) 73 | { 74 | local id = c$id; 75 | 76 | Intel::seen([$host=id$orig_h, 77 | $conn=c, 78 | $where=SSH::SUCCESSFUL_LOGIN]); 79 | } 80 | 81 | event ssh_auth_failed(c: connection) 82 | { 83 | local id = c$id; 84 | 85 | # Add data to the FAILED_LOGIN metric unless this connection should 86 | # be ignored. 87 | if ( ! (id$orig_h in ignore_guessers && 88 | id$resp_h in ignore_guessers[id$orig_h]) ) 89 | SumStats::observe("ssh.login.failure", [$host=id$orig_h], [$str=cat(id$resp_h)]); 90 | } 91 | -------------------------------------------------------------------------------- /detect_open_proxies.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # The Initial Developer of the Original Code is 6 | # Mozilla Corporation 7 | # Portions created by the Initial Developer are Copyright (C) 2014 8 | # the Initial Developer. All Rights Reserved. 9 | # 10 | # Contributor(s): 11 | # Michal Purzynski mpurzynski@mozilla.com 12 | 13 | @load base/frameworks/notice 14 | @load base/protocols/http 15 | 16 | module MozillaDetectOpenProxies; 17 | 18 | export { 19 | redef enum Notice::Type += { 20 | Possible_Open_Proxy, 21 | Possible_New_Proxy, 22 | }; 23 | 24 | redef enum HTTP::Tags += { 25 | HTTP_PROXY_OPEN, 26 | HTTP_PROXY_NEW, 27 | }; 28 | 29 | const whitelist_proxies_lb: set[addr] &redef; 30 | } 31 | 32 | event http_header(c: connection, is_orig: bool, name: string, value: string) 33 | { 34 | if ( Site::is_local_addr(c$id$orig_h) == T && c$id$orig_h ! in whitelist_proxies_lb ) 35 | if ( name == "X-FORWARDED-FOR" ) { 36 | add c$http$tags[HTTP_PROXY_NEW]; 37 | NOTICE([$note=Possible_New_Proxy, 38 | $msg=fmt("%s has sent outbound HTTP request with the X-FORWARDED-FOR header, looks like a proxy", c$id$orig_h), 39 | $uid=c$uid, 40 | $id=c$id, 41 | $identifier=cat(c$uid)]); 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /dhcpr.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | # Script to detect not authorized DHCP servers. Bro needs to see DHCP traffic of course. Will not work when servers and clients are in the same Vlan, unless you mirror that traffic. 9 | 10 | @load base/protocols/dhcp/utils 11 | 12 | module UnauthorizedDHCP; 13 | 14 | redef enum Notice::Type += { 15 | ServerOffer, 16 | ServerAck, 17 | ServerRoutersInOffer, 18 | ServerRoutersInAck, 19 | }; 20 | 21 | export { 22 | global trusted_dhcpd: set[addr] = { } &redef; 23 | global trusted_gw: set[addr] = { } &redef; 24 | } 25 | 26 | event dhcp_offer(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) 27 | { 28 | local clientip: addr; 29 | if (serv_addr !in trusted_dhcpd) { 30 | if ( DHCP::reverse_ip(msg$yiaddr) != 0.0.0.0 ) 31 | clientip = DHCP::reverse_ip(msg$yiaddr); 32 | else 33 | clientip = c$id$orig_h; 34 | NOTICE([$note=ServerOffer, 35 | $msg=fmt("%s is sending unauthorized DHCP offers", serv_addr), 36 | $sub=cat(DHCP::reverse_ip(msg$yiaddr)), 37 | $uid=c$uid, 38 | $id=c$id, 39 | $identifier=cat(c$uid)]); 40 | } 41 | if (|router| > 1 || (|router| == 1) && router[1] !in trusted_gw ) 42 | { 43 | local rtr: count; 44 | local routers: string = ""; 45 | for (rtr in router) { 46 | routers += cat(router[rtr]) + ","; 47 | } 48 | NOTICE([$note=ServerRoutersInOffer, 49 | $msg=fmt("%s is sending suspicious DHCP router list - %s", serv_addr, routers), 50 | $sub=cat(serv_addr), 51 | $uid=c$uid, 52 | $id=c$id, 53 | $identifier=cat(c$uid)]); 54 | } 55 | } 56 | event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) 57 | { 58 | local clientip: addr; 59 | if (serv_addr !in trusted_dhcpd) { 60 | if ( DHCP::reverse_ip(msg$yiaddr) != 0.0.0.0 ) 61 | clientip = DHCP::reverse_ip(msg$yiaddr); 62 | else 63 | clientip = c$id$orig_h; 64 | NOTICE([$note=ServerAck, 65 | $msg=fmt("%s is sending unauthorized DHCP ack", serv_addr), 66 | $sub=cat(DHCP::reverse_ip(msg$yiaddr)), 67 | $uid=c$uid, 68 | $id=c$id, 69 | $identifier=cat(c$uid)]); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /dlp.bro: -------------------------------------------------------------------------------- 1 | module ConnAnomaly; 2 | 3 | redef enum Notice::Type += { 4 | ConnLong, 5 | ConnBig, 6 | ConnBigUp, 7 | ConnBigDown, 8 | }; 9 | 10 | export { 11 | const max_duration: interval = 3600sec &redef; 12 | const size_threshold = 1024*1024*100 &redef; 13 | 14 | global ConnAnomaly::whitelist_connlong_policy: hook(wlconn: conn_id); 15 | global ConnAnomaly::whitelist_connbigup_policy: hook(wlconn: conn_id); 16 | global ConnAnomaly::whitelist_connbigdown_policy: hook(wlconn: conn_id); 17 | } 18 | 19 | # global wl_dlp_table: table[subnet] of table[subnet] of port = table() &synchronized; 20 | hook whitelist_connbig_policy(wlconn: conn_id) 21 | { 22 | if (wlconn$orig_h in wl_dlp_table) { 23 | if (wlconn$resp_h in wl_dlp_table[wlconn$orig_h]) 24 | if (wlconn$resp_p in wl_dlp_table[wlconn$orig_h][wlconn$resp_h]) { 25 | break; 26 | } 27 | } 28 | } 29 | 30 | #hook whitelist_connbigup_policy(wlconn: conn_id) 31 | #{ 32 | # if (wlconn$orig_h in wl_dlp_table) { 33 | # if (wlconn$resp_h in wl_dlp_table[wlconn$orig_h]$wl_dst) 34 | # if (wlconn$resp_p == wl_dlp_table[wlconn$orig_h]$wl_port) { 35 | # break; 36 | # } 37 | # } 38 | #} 39 | # 40 | #hook whitelist_connbigdown_policy(wlconn: conn_id) 41 | #{ 42 | # if (wlconn$orig_h in wl_dlp_table) { 43 | # if (wlconn$resp_h in wl_dlp_table[wlconn$orig_h]$wl_dst) 44 | # if (wlconn$resp_p == wl_dlp_table[wlconn$orig_h]$wl_port) { 45 | # break; 46 | # } 47 | # } 48 | #} 49 | 50 | event ConnThreshold::bytes_threshold_crossed(c: connection, threshold: count, is_orig: bool) 51 | { 52 | if ( ( c$orig$num_bytes_ip >= size_threshold || c$resp$num_bytes_ip >= size_threshold) && ( hook whitelist_connbig_policy(c$id) )) { 53 | #add c["bigconn"]; 54 | NOTICE([ 55 | $note=ConnBig, 56 | $msg=fmt("%s received/sent more than %d bytes over a single connection", c$id$orig_h, threshold), 57 | $uid=c$uid, 58 | $id=c$id, 59 | $identifier=c$uid + cat(c$orig$num_bytes_ip) + cat(c$resp$num_bytes_ip), 60 | $conn = c, 61 | $suppress_for=0sec 62 | ]); 63 | 64 | ConnThreshold::set_bytes_threshold(c, threshold*2, F); 65 | ConnThreshold::set_bytes_threshold(c, threshold*2, T); 66 | } 67 | } 68 | 69 | event connection_established(c: connection) &priority=-3 70 | { 71 | ConnThreshold::set_bytes_threshold(c, size_threshold, T); 72 | ConnThreshold::set_bytes_threshold(c, size_threshold, F); 73 | } 74 | 75 | #event Conn::log_conn(rec: Conn::Info) 76 | #{ 77 | # if ( ( rec?$duration ) && ( rec$duration > max_duration ) && ( hook whitelist_connlong_policy(rec$id) ) ) 78 | # NOTICE([$note=ConnLong, 79 | # $msg=fmt("%s had a connection opened for %s minutes", rec$id$orig_h, rec$duration/60sec), 80 | # $uid=rec$uid, 81 | # $id=rec$id, 82 | # $identifier=cat(rec$uid) 83 | # ]); 84 | #} 85 | 86 | -------------------------------------------------------------------------------- /dlp_input.bro: -------------------------------------------------------------------------------- 1 | module ConnAnomaly; 2 | 3 | type Val: record { 4 | wl_src: subnet; 5 | wl_dst: subnet; 6 | wl_port: port &type_column="t"; 7 | wl_comment: string &optional; 8 | }; 9 | 10 | global wl_dlp_table: table[subnet] of table[subnet] of set[port] = table() &synchronized; 11 | 12 | event line(description: Input::EventDescription, tpe: Input::Event, value: Val) 13 | { 14 | if (tpe != Input::EVENT_REMOVED) { 15 | if (value$wl_src !in wl_dlp_table) { 16 | wl_dlp_table[value$wl_src] = table(); 17 | } 18 | if (value$wl_dst !in wl_dlp_table[value$wl_src]) { 19 | wl_dlp_table[value$wl_src][value$wl_dst] = set(); 20 | } 21 | if (value$wl_port !in wl_dlp_table[value$wl_src][value$wl_dst]) { 22 | add wl_dlp_table[value$wl_src][value$wl_dst][value$wl_port]; 23 | } 24 | } else { 25 | delete wl_dlp_table[value$wl_src][value$wl_dst]; 26 | } 27 | } 28 | 29 | event bro_init() 30 | { 31 | Input::add_event([$source="/opt/bro/share/bro/brozilla/dlp_input.txt", 32 | $name="wl_dlp_table_input", 33 | $fields=Val, 34 | $ev=line, 35 | $mode=Input::REREAD]); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /dlp_input.txt: -------------------------------------------------------------------------------- 1 | #fields wl_src wl_dst wl_port t wl_comment 2 | 10.55.55.0/24 10.77.77.88/32 445 tcp Big SMB Example 3 | -------------------------------------------------------------------------------- /excessive_http_errors_topk.bro: -------------------------------------------------------------------------------- 1 | # Script to detect excessive HTTP errors from the same source IP 2 | # Inspired by policy/protocols/http/detect-sqli.bro 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | # Contributor(s): 9 | # Bro IDS team (detect-sqli inspiration) 10 | # Anthony Verez averez@mozilla.com 11 | # Michal Purzynski mpurzynski@mozilla.com 12 | 13 | @load base/frameworks/notice 14 | @load base/frameworks/sumstats 15 | @load base/protocols/http 16 | 17 | module MozillaHTTPErrors; 18 | 19 | export { 20 | redef enum Notice::Type += { 21 | ## Indicates that a host performing HTTP requests leading to 22 | ## excessive HTTP errors was detected. 23 | Excessive_HTTP_Errors_Attacker, 24 | ## Indicates that a host was seen to respond excessive HTTP 25 | ## errors. This is tracked by IP address as opposed to 26 | ## hostname. 27 | Excessive_HTTP_Errors_Victim, 28 | }; 29 | 30 | # Let's tag the http item 31 | redef enum HTTP::Tags += { 32 | ## HTTP status code >= 400, describing a HTTP error 33 | HTTP_ERROR, 34 | }; 35 | 36 | ## Interval at which to watch for the 37 | ## :bro:id:`HTTP::excessive_http_errors_threshold` variable to be 38 | ## crossed. At the end of each interval the counter is reset. 39 | const excessive_http_errors_interval = 15mins &redef; 40 | const report_threshold_attacker = 50 &redef; 41 | const report_threshold_victim = 100 &redef; 42 | const suppress_attacker = 15mins &redef; 43 | const suppress_victim = 15mins &redef; 44 | 45 | const topk_attacker_howmuch = 30 &redef; 46 | const topk_attacker_size = 1000 &redef; 47 | 48 | const topk_victim_howmuch = 30 &redef; 49 | const topk_victim_size = 1000 &redef; 50 | 51 | const monitor_ip_spaces: set[subnet] &redef; 52 | const monitor_ports: set[port] &redef; 53 | const ignore_hosts_orig: set[subnet] &redef; 54 | const ignore_hosts_resp: set[subnet] &redef; 55 | const ignore_user_agents: pattern &redef; 56 | const ignore_referrers: pattern &redef; 57 | const ignore_host_fields: pattern &redef; 58 | } 59 | 60 | event bro_init() 61 | { 62 | # Add filters to the metrics so that the metrics framework knows how to 63 | # determine when it looks like an actual attack and how to respond when 64 | # thresholds are crossed. 65 | 66 | # HTTP errors for requests FROM the same host 67 | local r1: SumStats::Reducer = [$stream="http.excessive_errors.attacker", $apply=set(SumStats::TOPK), $topk_size=topk_attacker_size]; 68 | SumStats::create([$name="excessive-http-errors-attackers", 69 | $epoch=excessive_http_errors_interval, 70 | $reducers=set(r1), 71 | $epoch_result(ts: time, key: SumStats::Key, result: SumStats::Result) = 72 | { 73 | if ("http.excessive_errors.attacker" in result) { 74 | local r = result["http.excessive_errors.attacker"]; 75 | local s: vector of SumStats::Observation; 76 | s = topk_get_top(r$topk, topk_attacker_howmuch); 77 | 78 | for (attacker in s) { 79 | local errors = topk_count(r$topk, s[attacker]); 80 | if (errors > report_threshold_attacker) { 81 | NOTICE([$note=Excessive_HTTP_Errors_Attacker, 82 | $msg=fmt("Excessive HTTP errors for requests from %s", s[attacker]$str), 83 | $sub=fmt("%d in %s, eps: %d", errors, excessive_http_errors_interval, topk_epsilon(r$topk, s[attacker])), 84 | $src=to_addr(s[attacker]$str), 85 | $identifier=s[attacker]$str, 86 | $suppress_for=suppress_attacker 87 | ]); 88 | } 89 | } 90 | } 91 | }]); 92 | 93 | # HTTP errors for requests TO the same host 94 | local r2: SumStats::Reducer = [$stream="http.excessive_errors.victim", $apply=set(SumStats::TOPK), $topk_size=topk_victim_size]; 95 | SumStats::create([$name="excessive-http-errors-victims", 96 | $epoch=excessive_http_errors_interval, 97 | $reducers=set(r2), 98 | $epoch_result(ts: time, key: SumStats::Key, result: SumStats::Result) = 99 | { 100 | if ("http.excessive_errors.victim" in result) { 101 | local r = result["http.excessive_errors.victim"]; 102 | local s: vector of SumStats::Observation; 103 | s = topk_get_top(r$topk, topk_victim_howmuch); 104 | 105 | for (victim in s) { 106 | local errors = topk_count(r$topk, s[victim]); 107 | if (errors > report_threshold_victim) { 108 | NOTICE([$note=Excessive_HTTP_Errors_Victim, 109 | $msg=fmt("Excessive HTTP errors for requests to %s", s[victim]$str), 110 | $sub=fmt("%d in %s, eps: %d", victim, excessive_http_errors_interval, topk_epsilon(r$topk, s[victim])), 111 | $src=to_addr(s[victim]$str), 112 | $identifier=s[victim]$str, 113 | $suppress_for=suppress_victim 114 | ]); 115 | } 116 | } 117 | } 118 | }]); 119 | } 120 | 121 | 122 | 123 | event http_reply(c: connection, version: string, code: count, 124 | reason: string) &priority=3 125 | { 126 | if ( c$id$resp_h !in monitor_ip_spaces ) 127 | return; 128 | if ( c$id$resp_p !in monitor_ports ) 129 | return; 130 | if ( c$id$resp_h in ignore_hosts_resp ) 131 | return; 132 | if ( c$id$orig_h in ignore_hosts_orig ) 133 | return; 134 | if ( ! c?$http ) 135 | return; 136 | if ( ( c$http?$cluster_client_ip ) && ( to_addr(c$http$cluster_client_ip) in ignore_hosts_orig ) ) 137 | return; 138 | if ( ( c$http?$user_agent ) && ( ignore_user_agents in c$http$user_agent ) ) 139 | return; 140 | if ( ( c$http?$referrer ) && ( ignore_referrers in c$http$referrer ) ) 141 | return; 142 | if ( ( c$http?$host ) && ( ignore_host_fields in c$http$host ) ) 143 | return; 144 | if ( code >= 400 ) { 145 | add c$http$tags[HTTP_ERROR]; 146 | SumStats::observe("http.excessive_errors.victim", [], 147 | [$str=fmt("%s", c$id$resp_h)]); 148 | if ( ( c?$http ) && ( c$http?$cluster_client_ip ) ) 149 | SumStats::observe("http.excessive_errors.attacker", [], 150 | [$str=fmt("%s", c$http$cluster_client_ip)]); 151 | } 152 | } 153 | 154 | -------------------------------------------------------------------------------- /extract-interesting-files.bro: -------------------------------------------------------------------------------- 1 | # Extract files of specific MIME types 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | # 7 | # Contributor(s): 8 | # Someone who originally wrote this script, I didn't 9 | 10 | global ext_map: table[string] of string = { 11 | ["application/x-dosexec"] = "exe", 12 | ["application/octet-stream"] = "bin", 13 | } &default =""; 14 | 15 | event file_new(f: fa_file) 16 | { 17 | local am_i_orig: bool; 18 | local ext = ""; 19 | 20 | for (cid in f$conns) { 21 | if (Site::is_local_addr(cid$orig_h)) { 22 | am_i_orig = T; 23 | break; 24 | } 25 | } 26 | 27 | if (!f?$mime_type) 28 | f$mime_type = "application/octet-stream"; 29 | if ( f?$mime_type && f$mime_type in ext_map) 30 | ext = ext_map[f$mime_type]; 31 | 32 | local fname = fmt("/nsm/bro/extracted/%s-%s.%s", f$source, f$id, ext); 33 | Files::add_analyzer(f, Files::ANALYZER_EXTRACT, [$extract_filename=fname]); 34 | } 35 | -------------------------------------------------------------------------------- /filter_input.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | # Script to read in a list of IP addresses that won't be logged to any log file 9 | # 10 | 11 | module LogFilter; 12 | 13 | type Idx: record { 14 | drop_ip: addr; 15 | }; 16 | 17 | type Val: record { 18 | description: string; 19 | }; 20 | 21 | global drop_ip_from_log: table[addr] of Val = table(); 22 | 23 | event bro_init() 24 | { 25 | Input::add_table([$source="/opt/bro/share/bro/brozilla/logfilter_ip.txt", 26 | $name="drop_ip_list", 27 | $idx=Idx, 28 | $val=Val, 29 | $destination=drop_ip_from_log, 30 | $mode=Input::REREAD]); 31 | } 32 | -------------------------------------------------------------------------------- /filter_input.zeek: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | # Script to read in a list of IP addresses that won't be logged to any log file 9 | # 10 | 11 | module LogFilter; 12 | 13 | type Idx: record { 14 | drop_ip: addr; 15 | }; 16 | 17 | type Val: record { 18 | description: string; 19 | }; 20 | 21 | global drop_ip_from_log: table[addr] of Val = table(); 22 | 23 | event zeek_init() 24 | { 25 | Input::add_table([$source="/opt/zeek/share/zeek/zeekzilla/logfilter_ip.txt", 26 | $name="drop_ip_list", 27 | $idx=Idx, 28 | $val=Val, 29 | $destination=drop_ip_from_log, 30 | $mode=Input::REREAD]); 31 | } 32 | -------------------------------------------------------------------------------- /filter_noise_conn.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | 9 | module LogFilter; 10 | 11 | const ignore_ports_resp: set[port] = {53/udp, 53/tcp, 123/udp, 137/udp, 161/udp, 514/udp, 514/tcp, 5355/udp, 5666/tcp, 8443/tcp} &redef; 12 | const ignore_services: set[string] = {"dns"} &redef; 13 | 14 | event bro_init() 15 | { 16 | Log::remove_default_filter(Conn::LOG); 17 | Log::add_filter(Conn::LOG, [$name = "conn-noise", 18 | $pred(rec: Conn::Info) = { 19 | #if (/^RSTO|^S0$|^SH$|^SHR$/ in rec$conn_state) { 20 | # return T; 21 | #} else { 22 | if (rec$id$resp_p in ignore_ports_resp) { 23 | return F; 24 | } 25 | if (rec?$service && rec$service in ignore_services) { 26 | return F; 27 | } 28 | if ((rec$id$orig_h in drop_ip_from_log) || (rec$id$resp_h in drop_ip_from_log)) 29 | return F; 30 | return T; 31 | } 32 | ]); 33 | } 34 | 35 | # If you have enough CPU power and just want to send them to a separate file, use this. 36 | #event bro_init() 37 | #{ 38 | # Log::remove_default_filter(Conn::LOG); 39 | # Log::add_filter(Conn::LOG, [$name = "conn-noise", 40 | # $path_func(id: Log::ID, path: string, rec: Conn::Info) = { 41 | # 42 | # return (rec$id$resp_p in ignore_ports_resp) ? "conn-noise" : "conn"; 43 | # }]); 44 | #} 45 | -------------------------------------------------------------------------------- /filter_noise_conn.zeek: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | 9 | module LogFilter; 10 | 11 | const ignore_ports_resp: set[port] = {53/udp, 53/tcp, 123/udp, 137/udp, 161/udp, 514/udp, 514/tcp, 5355/udp, 5666/tcp, 8443/tcp} &redef; 12 | const ignore_services: set[string] = {"dns"} &redef; 13 | 14 | event zeek_init() 15 | { 16 | Log::remove_default_filter(Conn::LOG); 17 | Log::add_filter(Conn::LOG, [$name = "conn-noise", 18 | $pred(rec: Conn::Info) = { 19 | #if (/^RSTO|^S0$|^SH$|^SHR$/ in rec$conn_state) { 20 | # return T; 21 | #} else { 22 | if (rec$id$resp_p in ignore_ports_resp) { 23 | return F; 24 | } 25 | if (rec?$service && rec$service in ignore_services) { 26 | return F; 27 | } 28 | if ((rec$id$orig_h in drop_ip_from_log) || (rec$id$resp_h in drop_ip_from_log)) 29 | return F; 30 | return T; 31 | } 32 | ]); 33 | } 34 | 35 | # If you have enough CPU power and just want to send them to a separate file, use this. 36 | #event zeek_init() 37 | #{ 38 | # Log::remove_default_filter(Conn::LOG); 39 | # Log::add_filter(Conn::LOG, [$name = "conn-noise", 40 | # $path_func(id: Log::ID, path: string, rec: Conn::Info) = { 41 | # 42 | # return (rec$id$resp_p in ignore_ports_resp) ? "conn-noise" : "conn"; 43 | # }]); 44 | #} 45 | -------------------------------------------------------------------------------- /filter_noise_dns.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | 9 | module LogFilter; 10 | 11 | event bro_init() 12 | { 13 | Log::remove_default_filter(DNS::LOG); 14 | Log::add_filter(DNS::LOG, [$name = "dns-noise", 15 | $path_func(id: Log::ID, path: string, rec: DNS::Info) = { 16 | return (rec?$query && /.mozilla.(org|net|com)$|.newrelic.com$|.github.(io|com)$|.allizom.org$|.local$|^localhost_prl$/ in rec$query) ? "dns-noise" : "dns"; 17 | }]); 18 | } 19 | 20 | -------------------------------------------------------------------------------- /filter_noise_dns.zeek: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | 9 | module LogFilter; 10 | 11 | event zeek_init() 12 | { 13 | Log::remove_default_filter(DNS::LOG); 14 | Log::add_filter(DNS::LOG, [$name = "dns-noise", 15 | $path_func(id: Log::ID, path: string, rec: DNS::Info) = { 16 | return (rec?$query && /.mozilla.(org|net|com)$|.newrelic.com$|.github.(io|com)$|.allizom.org$|.local$|^localhost_prl$/ in rec$query) ? "dns-noise" : "dns"; 17 | }]); 18 | } 19 | 20 | -------------------------------------------------------------------------------- /filter_noise_files.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | 9 | module LogFilter; 10 | 11 | event bro_init() 12 | { 13 | Log::remove_default_filter(Files::LOG); 14 | Log::add_filter(Files::LOG, [$name = "files-noise", 15 | $pred(rec: Files::Info) = { 16 | for (tx_host in rec$tx_hosts) { 17 | if ((rec?$mime_type) && ((rec$mime_type == "application/pkix-cert") || (rec$mime_type == "application/x-x509-ca-cert") || (rec$mime_type == "application/x-x509-user-cert") )) 18 | return F; 19 | return T; 20 | } 21 | return T; 22 | } 23 | ]); 24 | } 25 | -------------------------------------------------------------------------------- /filter_noise_files.zeek: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | 9 | module LogFilter; 10 | 11 | event zeek_init() 12 | { 13 | Log::remove_default_filter(Files::LOG); 14 | Log::add_filter(Files::LOG, [$name = "files-noise", 15 | $pred(rec: Files::Info) = { 16 | for (tx_host in rec$tx_hosts) { 17 | if ((rec?$mime_type) && ((rec$mime_type == "application/pkix-cert") || (rec$mime_type == "application/x-x509-ca-cert") || (rec$mime_type == "application/x-x509-user-cert") )) 18 | return F; 19 | return T; 20 | } 21 | return T; 22 | } 23 | ]); 24 | } 25 | -------------------------------------------------------------------------------- /filter_noise_http.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | 9 | module LogFilter; 10 | 11 | event bro_init() 12 | { 13 | Log::remove_default_filter(HTTP::LOG); 14 | Log::add_filter(HTTP::LOG, [$name = "http-noise", 15 | $path_func(id: Log::ID, path: string, rec: HTTP::Info) = { 16 | return (rec?$user_agent && /HTTP-Monitor/ in rec$user_agent) ? "http-noise" : "http"; 17 | }]); 18 | } 19 | 20 | -------------------------------------------------------------------------------- /filter_noise_http.zeek: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | 9 | module LogFilter; 10 | 11 | event zeek_init() 12 | { 13 | Log::remove_default_filter(HTTP::LOG); 14 | Log::add_filter(HTTP::LOG, [$name = "http-noise", 15 | $path_func(id: Log::ID, path: string, rec: HTTP::Info) = { 16 | return (rec?$user_agent && /HTTP-Monitor/ in rec$user_agent) ? "http-noise" : "http"; 17 | }]); 18 | } 19 | 20 | -------------------------------------------------------------------------------- /filter_noise_intel.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | # Redirect intel hits from the usual noise generators like DNS servers to a separate log file. 9 | 10 | module Intel; 11 | 12 | export { 13 | const ignore_intel_src: set[addr] &redef; 14 | } 15 | 16 | event bro_init() 17 | { 18 | Log::remove_default_filter(Intel::LOG); 19 | Log::add_filter(Intel::LOG, [$name = "intel-noise", 20 | $path_func(id: Log::ID, path: string, rec: Intel::Info) = { 21 | return (rec?$id && rec$id?$orig_h && rec$id$orig_h in ignore_intel_src) ? "intel-noise" : "intel"; 22 | }]); 23 | } 24 | 25 | -------------------------------------------------------------------------------- /filter_noise_intel.zeek: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | # Redirect intel hits from the usual noise generators like DNS servers to a separate log file. 9 | 10 | module Intel; 11 | 12 | export { 13 | const ignore_intel_src: set[addr] &redef; 14 | } 15 | 16 | event zeek_init() 17 | { 18 | Log::remove_default_filter(Intel::LOG); 19 | Log::add_filter(Intel::LOG, [$name = "intel-noise", 20 | $path_func(id: Log::ID, path: string, rec: Intel::Info) = { 21 | return (rec?$id && rec$id?$orig_h && rec$id$orig_h in ignore_intel_src) ? "intel-noise" : "intel"; 22 | }]); 23 | } 24 | 25 | -------------------------------------------------------------------------------- /filter_noise_mysql.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | 9 | module LogFilter; 10 | 11 | event bro_init() 12 | { 13 | Log::remove_default_filter(mysql::LOG); 14 | Log::add_filter(mysql::LOG, [$name = "mysql-noise", 15 | $pred(rec: MySQL::Info) = { 16 | if ((Site::is_local_addr(rec$id$orig_h) == F) || (Site::is_local_addr(rec$id$resp_h)) == F) 17 | return T; 18 | else 19 | return F; 20 | }]); 21 | } 22 | -------------------------------------------------------------------------------- /filter_noise_mysql.zeek: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | 9 | module LogFilter; 10 | 11 | event zeek_init() 12 | { 13 | Log::remove_default_filter(mysql::LOG); 14 | Log::add_filter(mysql::LOG, [$name = "mysql-noise", 15 | $pred(rec: MySQL::Info) = { 16 | if ((Site::is_local_addr(rec$id$orig_h) == F) || (Site::is_local_addr(rec$id$resp_h)) == F) 17 | return T; 18 | else 19 | return F; 20 | }]); 21 | } 22 | -------------------------------------------------------------------------------- /filter_noise_ssl.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | 9 | module LogFilter; 10 | 11 | const ignore_ports_resp: set[port] = {53/udp, 53/tcp, 123/udp, 137/udp, 161/udp, 514/udp, 514/tcp, 5355/udp, 5666/tcp, 8443/tcp} &redef; 12 | 13 | event bro_init() 14 | { 15 | Log::remove_default_filter(SSL::LOG); 16 | Log::add_filter(SSL::LOG, [$name = "ssl-noise", 17 | $pred(rec: SSL::Info) = { 18 | if (rec$id$resp_p in ignore_ports_resp) 19 | return F; 20 | if ((rec$id$orig_h in drop_ip_from_log) || (rec$id$resp_h in drop_ip_from_log)) 21 | return F; 22 | if ((rec?$server_name) && (/aus3\.mozilla\.org|aus4\.mozilla\.org|aus5\.mozilla\.org/ in rec$server_name)) 23 | return F; 24 | return T; 25 | } 26 | ]); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /filter_noise_ssl.zeek: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | 9 | module LogFilter; 10 | 11 | const ignore_ports_resp: set[port] = {53/udp, 53/tcp, 123/udp, 137/udp, 161/udp, 514/udp, 514/tcp, 5355/udp, 5666/tcp, 8443/tcp} &redef; 12 | 13 | event zeek_init() 14 | { 15 | Log::remove_default_filter(SSL::LOG); 16 | Log::add_filter(SSL::LOG, [$name = "ssl-noise", 17 | $pred(rec: SSL::Info) = { 18 | if (rec$id$resp_p in ignore_ports_resp) 19 | return F; 20 | if ((rec$id$orig_h in drop_ip_from_log) || (rec$id$resp_h in drop_ip_from_log)) 21 | return F; 22 | if ((rec?$server_name) && (/aus3\.mozilla\.org|aus4\.mozilla\.org|aus5\.mozilla\.org/ in rec$server_name)) 23 | return F; 24 | return T; 25 | } 26 | ]); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /filter_noise_x509.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Johanna Amann, Bro/ICSI - johanna@icir.org 7 | # Michal Purzynski mpurzynski@mozilla.com 8 | # 9 | 10 | module LogFilter; 11 | 12 | redef record X509::Info += { 13 | local_cert: bool &default=F; 14 | }; 15 | 16 | event file_state_remove(f: fa_file) &priority=6 17 | { 18 | if ( ! f$info?$x509 ) 19 | return; 20 | 21 | for ( i in f$info$tx_hosts ) 22 | { 23 | if ( Site::is_local_addr(i) ) 24 | f$info$x509$local_cert = T; 25 | } 26 | } 27 | 28 | function no_local_certs(rec: X509::Info): bool 29 | { 30 | return ! rec$local_cert; 31 | } 32 | 33 | event bro_init () &priority=-5 34 | { 35 | local f = Log::get_filter(X509::LOG, "default"); 36 | Log::remove_filter(X509::LOG, "default"); 37 | f$pred=no_local_certs; 38 | Log::add_filter(X509::LOG, f); 39 | } 40 | 41 | #event bro_init() 42 | #{ 43 | # Log::remove_default_filter(X509::LOG); 44 | # Log::add_filter(X509::LOG, [$name = "X509-noise", 45 | # $pred(rec: X509::Info) = { 46 | # if ((rec$certificate?$cn) && (/mozilla\.(com|org|net)$/ in rec$certificate$cn)) 47 | # return F; 48 | # } 49 | # ]); 50 | #} 51 | -------------------------------------------------------------------------------- /filter_noise_x509.zeek: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Johanna Amann, Bro/ICSI - johanna@icir.org 7 | # Michal Purzynski mpurzynski@mozilla.com 8 | # 9 | 10 | module LogFilter; 11 | 12 | redef record X509::Info += { 13 | local_cert: bool &default=F; 14 | }; 15 | 16 | event file_state_remove(f: fa_file) &priority=6 17 | { 18 | if ( ! f$info?$x509 ) 19 | return; 20 | 21 | for ( i in f$info$tx_hosts ) 22 | { 23 | if ( Site::is_local_addr(i) ) 24 | f$info$x509$local_cert = T; 25 | } 26 | } 27 | 28 | function no_local_certs(rec: X509::Info): bool 29 | { 30 | return ! rec$local_cert; 31 | } 32 | 33 | event zeek_init () &priority=-5 34 | { 35 | local f = Log::get_filter(X509::LOG, "default"); 36 | Log::remove_filter(X509::LOG, "default"); 37 | f$pred=no_local_certs; 38 | Log::add_filter(X509::LOG, f); 39 | } 40 | 41 | #event zeek_init() 42 | #{ 43 | # Log::remove_default_filter(X509::LOG); 44 | # Log::add_filter(X509::LOG, [$name = "X509-noise", 45 | # $pred(rec: X509::Info) = { 46 | # if ((rec$certificate?$cn) && (/mozilla\.(com|org|net)$/ in rec$certificate$cn)) 47 | # return F; 48 | # } 49 | # ]); 50 | #} 51 | -------------------------------------------------------------------------------- /find_non_aes_clients.bro: -------------------------------------------------------------------------------- 1 | # Script that logs clients that do not support AES in the SSL ciphers that are 2 | # announced in their CLIENT HELLO 3 | # 4 | # Version: MPL 1.1/GPL 2.0/LGPL 2.1 5 | # 6 | # The contents of this file are subject to the Mozilla Public License Version 7 | # 1.1 (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # http://www.mozilla.org/MPL/ 10 | # 11 | # Software distributed under the License is distributed on an "AS IS" basis, 12 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 13 | # for the specific language governing rights and limitations under the 14 | # License. 15 | # 16 | # The Initial Developer of the Original Code is 17 | # Mozilla Corporation 18 | # Portions created by the Initial Developer are Copyright (C) 2014 19 | # the Initial Developer. All Rights Reserved. 20 | # 21 | # Contributor(s): 22 | # Michal Purzynski mpurzynski@mozilla.com 23 | # Julien Vehent jvehent@mozilla.com [:ulfr] 24 | # 25 | # Alternatively, the contents of this file may be used under the terms of 26 | # either the GNU General Public License Version 2 or later (the "GPL"), or 27 | # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 28 | # in which case the provisions of the GPL or the LGPL are applicable instead 29 | # of those above. If you wish to allow use of your version of this file only 30 | # under the terms of either the GPL or the LGPL, and not to allow others to 31 | # use your version of this file under the terms of the MPL, indicate your 32 | # decision by deleting the provisions above and replace them with the notice 33 | # and other provisions required by the GPL or the LGPL. If you do not delete 34 | # the provisions above, a recipient may use your version of this file under 35 | # the terms of any one of the MPL, the GPL or the LGPL. 36 | 37 | @load base/protocols/conn 38 | @load base/protocols/ssl 39 | 40 | module SSL; 41 | 42 | redef enum Notice::Type += { 43 | SSL_NonAES_Client, 44 | }; 45 | 46 | event ssl_client_hello(c: connection, version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec) 47 | { 48 | local hasAES:bool = F; 49 | for (cipher in ciphers) { 50 | if (/AES/ in SSL::cipher_desc[ciphers[cipher]]) { 51 | hasAES = T; 52 | } 53 | } 54 | if ( !hasAES ) { 55 | if (c$id$orig_h in Site::local_nets) { 56 | local cs = ""; 57 | for (cipher in ciphers) { 58 | cs += SSL::cipher_desc[ciphers[cipher]] + ","; 59 | } 60 | NOTICE([$note=SSL_NonAES_Client, 61 | $msg=fmt("%s does not support AES cipher.", c$id$orig_h), 62 | $sub=cs, 63 | $uid=c$uid, 64 | $id=c$id, 65 | $identifier=cat(c$uid)]); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /find_non_aes_servers.bro: -------------------------------------------------------------------------------- 1 | # Script that logs clients that do not support AES in the SSL ciphers that are 2 | # announced in their CLIENT HELLO 3 | # 4 | # Version: MPL 1.1/GPL 2.0/LGPL 2.1 5 | # 6 | # The contents of this file are subject to the Mozilla Public License Version 7 | # 1.1 (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # http://www.mozilla.org/MPL/ 10 | # 11 | # Software distributed under the License is distributed on an "AS IS" basis, 12 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 13 | # for the specific language governing rights and limitations under the 14 | # License. 15 | # 16 | # The Initial Developer of the Original Code is 17 | # Mozilla Corporation 18 | # Portions created by the Initial Developer are Copyright (C) 2014 19 | # the Initial Developer. All Rights Reserved. 20 | # 21 | # Contributor(s): 22 | # Michal Purzynski mpurzynski@mozilla.com 23 | # Julien Vehent jvehent@mozilla.com [:ulfr] 24 | # 25 | # Alternatively, the contents of this file may be used under the terms of 26 | # either the GNU General Public License Version 2 or later (the "GPL"), or 27 | # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 28 | # in which case the provisions of the GPL or the LGPL are applicable instead 29 | # of those above. If you wish to allow use of your version of this file only 30 | # under the terms of either the GPL or the LGPL, and not to allow others to 31 | # use your version of this file under the terms of the MPL, indicate your 32 | # decision by deleting the provisions above and replace them with the notice 33 | # and other provisions required by the GPL or the LGPL. If you do not delete 34 | # the provisions above, a recipient may use your version of this file under 35 | # the terms of any one of the MPL, the GPL or the LGPL. 36 | 37 | @load base/protocols/conn 38 | @load base/protocols/ssl 39 | 40 | module SSL; 41 | 42 | redef enum Notice::Type += { 43 | SSL_NonAES_Server, 44 | }; 45 | 46 | event ssl_server_handshake(c: connection, version: count, possible_ts: time, server_random: string, session_id: string, cipher: count, comp_method: count) 47 | { 48 | local hasAES:bool = F; 49 | if (/AES/ in SSL::cipher_desc[cipher]) { 50 | hasAES = T; 51 | } 52 | if ( !hasAES ) { 53 | if (c$id$resp_h in Site::local_nets) { 54 | NOTICE([$note=SSL_NonAES_Server, 55 | $msg=fmt("%s server negotiated non-AES cipher.", c$id$orig_h), 56 | $sub=SSL::cipher_desc[cipher], 57 | $uid=c$uid, 58 | $id=c$id, 59 | $identifier=cat(c$uid)]); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /heartbleed_mozillaca.bro: -------------------------------------------------------------------------------- 1 | # Script to detect certs issued Mozilla CA before than 2014-04-11 00:00:00 CA time 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | # 7 | # Software distributed under the License is distributed on an "AS IS" basis, 8 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 9 | # for the specific language governing rights and limitations under the 10 | # License. 11 | # 12 | # The Initial Developer of the Original Code is 13 | # Mozilla Corporation 14 | # Portions created by the Initial Developer are Copyright (C) 2014 15 | # the Initial Developer. All Rights Reserved. 16 | # 17 | # Contributor(s): 18 | # Anthony Verez averez@mozilla.com 19 | 20 | @load base/frameworks/notice 21 | @load base/protocols/ssl 22 | 23 | module HeartbleedMozillaCA; 24 | 25 | export { 26 | redef enum Notice::Type += { 27 | HeartbleedMozillaCA_NeedChangeCert 28 | }; 29 | } 30 | 31 | event x509_certificate(c: connection, is_orig: bool, cert: X509, chain_idx: count, chain_len: count, der_cert: string) &priority=3 32 | { 33 | # 2014-04-11 00:00:00 CA time 34 | local time_before = 1397199600.0; 35 | 36 | if (/[mM][oO][zZ][iI][lL][lL][aA]/ in cert$issuer && time_before > time_to_double(cert$not_valid_before)) { 37 | NOTICE([$note=HeartbleedMozillaCA_NeedChangeCert, 38 | $conn=c, $suppress_for=15mins, 39 | $msg=fmt("Moz CA-signed Certificate %s has not been changed after Heartbleed (valid since %T)", cert$subject, cert$not_valid_before), 40 | $identifier=cat(c$id$resp_h, c$id$resp_p, c$ssl$cert_hash)]); 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /http_auth_base64.bro: -------------------------------------------------------------------------------- 1 | # Script to detect Basic HTTP auth (using base64) 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | # 7 | # Software distributed under the License is distributed on an "AS IS" basis, 8 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 9 | # for the specific language governing rights and limitations under the 10 | # License. 11 | # 12 | # The Initial Developer of the Original Code is 13 | # Mozilla Corporation 14 | # Portions created by the Initial Developer are Copyright (C) 2014 15 | # the Initial Developer. All Rights Reserved. 16 | # 17 | # Contributor(s): 18 | # Anthony Verez averez@mozilla.com 19 | 20 | @load base/frameworks/notice 21 | @load base/protocols/http 22 | 23 | module AuthBasic; 24 | 25 | export { 26 | redef enum Notice::Type += { 27 | Basic_Auth_Server, 28 | Basic_Auth_Client 29 | }; 30 | 31 | const filter_port_resp: set[port] = { 80/tcp } &redef; 32 | 33 | const ignore_host_resp: set[addr] = { } &redef; 34 | const ignore_host_orig: set[addr] = { } &redef; 35 | } 36 | 37 | event http_header(c: connection, is_orig: bool, name: string, value: string) 38 | { 39 | if ((c$id$resp_p in filter_port_resp) && (c$id$resp_h !in ignore_host_resp) && (c$id$orig_h !in ignore_host_orig)) { 40 | if (/WWW-Authenticate/ in name && /.*Basic.*/ in value) { 41 | NOTICE([$note=Basic_Auth_Server, 42 | $msg="Server identified on which Basic Access Authentication is in use.", 43 | $conn=c 44 | # $identifier=cat(c$id$resp_h,c$id$resp_p), 45 | # $suppress_for=1day 46 | ]); 47 | } 48 | if (/Authorization/ in name && /.*Basic.*/ in value) 49 | { 50 | local parts = split1(decode_base64(sub_bytes(value, 7, |value|)), /:/); 51 | if (|parts| == 2) 52 | NOTICE([$note=Basic_Auth_Client, 53 | $msg="Client using Basic Access Authentication.", 54 | $sub=fmt("username: %s", parts[1]), 55 | $conn=c 56 | # $identifier=cat(c$id$resp_h,c$id$resp_p), 57 | # $suppress_for=1day 58 | ]); 59 | } 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /http_headers_flash_tmp.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | 8 | # Detect browser plugins as they leak through requests to some 9 | # advertising servers. 10 | # Scripts inspired by the original Bro code - policy/protocols/http/software-browser-plugins 11 | 12 | @load base/protocols/http 13 | @load base/frameworks/software 14 | @load policy/protocols/http/software-browser-plugins 15 | 16 | module HTTP; 17 | 18 | event http_header(c: connection, is_orig: bool, name: string, value: string) 19 | { 20 | if ( ! is_orig ) 21 | return; 22 | if ( ! c?$http ) 23 | return; 24 | 25 | if ( name == "X-REQUESTED-WITH" && /^ShockwaveFlash/ in value ) 26 | Software::found(c$id, [$unparsed_version=value, $host=c$id$orig_h, $software_type=BROWSER_PLUGIN]); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /http_headers_lb.bro: -------------------------------------------------------------------------------- 1 | # Script to extract value of various HTTP headers provided by load balancers 2 | # and add it to the HTTP log stream 3 | # Inspired by policy/protocols/http/header-names.bro 4 | # 5 | # This Source Code Form is subject to the terms of the Mozilla Public 6 | # License, v. 2.0. If a copy of the MPL was not distributed with this 7 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | # 9 | # Software distributed under the License is distributed on an "AS IS" basis, 10 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 | # for the specific language governing rights and limitations under the 12 | # License. 13 | # 14 | # The Initial Developer of the Original Code is 15 | # Mozilla Corporation 16 | # Portions created by the Initial Developer are Copyright (C) 2014 17 | # the Initial Developer. All Rights Reserved. 18 | # 19 | # Contributor(s): 20 | # Anthony Verez averez@mozilla.com 21 | # Michal Purzynski mpurzynski@mozilla.com 22 | 23 | @load base/protocols/http/main 24 | 25 | module MozillaHTTPHeaders; 26 | 27 | export { 28 | redef record Intel::Info += { 29 | ## True client IP address added by our ZLBs 30 | cluster_client_ip: string &log &optional; 31 | }; 32 | 33 | redef record Intel::Seen += { 34 | ## Log value of the X-CLUSTER-CLIENT-IP 35 | ## True client IP address added by our ZLBs 36 | cluster_client_ip: string &log &optional; 37 | }; 38 | 39 | redef record HTTP::Info += { 40 | ## Log value of the X-CLUSTER-CLIENT-IP 41 | ## True client IP address added by our ZLBs 42 | cluster_client_ip: string &log &optional; 43 | ## Log which backend server handled the connection. 44 | ## Might be useful to know where to look for more logs or which server might be under the load 45 | backend_server: string &log &optional; 46 | version: string &log &optional; 47 | redirect_dst: string &log &optional; 48 | }; 49 | 50 | redef enum Intel::Where += { 51 | HTTP::IN_X_CLUSTER_CLIENT_IP_HEADER, 52 | HTTP::IN_X_BACKEND_SERVER_HEADER, 53 | }; 54 | 55 | ## A boolean value to determine if you log the value of X-CLUSTER-CLIENT-IP headers 56 | const log_cluster_client_ip = T &redef; 57 | ## A boolean value to determine if you log the value of X-BACKEND-SERVER headers 58 | const log_backend_server = T &redef; 59 | } 60 | 61 | event Intel::match(s: Intel::Seen, items: set[Intel::Item]) 62 | { 63 | if ( ( s?$conn ) && ( s$conn?$http ) && ( s$conn$http?$cluster_client_ip ) ) 64 | s$cluster_client_ip = s$conn$http$cluster_client_ip; 65 | } 66 | 67 | event http_header(c: connection, is_orig: bool, name: string, value: string) 68 | { 69 | if (!c?$http) 70 | return; 71 | 72 | if (name == "LOCATION") { 73 | c$http$redirect_dst = value; 74 | # Intel::seen([$host=to_addr(value), 75 | # $indicator_type=Intel::DOMAIN, 76 | # $conn=c, 77 | # $where=HTTP::IN_X_BACKEND_SERVER_HEADER]); 78 | } 79 | if (name == "X-CLUSTER-CLIENT-IP" ) { 80 | #if (is_valid_ip(value)) { 81 | c$http$cluster_client_ip = value; 82 | Intel::seen([$host=to_addr(value), 83 | $indicator_type=Intel::ADDR, 84 | $conn=c, 85 | $where=HTTP::IN_X_CLUSTER_CLIENT_IP_HEADER]); 86 | #} 87 | } 88 | if (name == "X-BACKEND-SERVER") { 89 | c$http$backend_server = value; 90 | # this has no right to be true. EVER. 91 | # Intel::seen([$host=to_addr(value), 92 | # $indicator_type=Intel::DOMAIN, 93 | # $conn=c, 94 | # $where=HTTP::IN_X_BACKEND_SERVER_HEADER]); 95 | } 96 | } 97 | 98 | event http_request(c: connection, method: string, original_URI: string, unescaped_URI: string, version: string) 99 | { 100 | c$http$version = version; 101 | } 102 | -------------------------------------------------------------------------------- /intel-dns.bro: -------------------------------------------------------------------------------- 1 | ##! When we get an intel hit for a DNS query, store the IP answer(s) and 2 | ##! generate a notice if anyone connects to one of those IPs. 3 | 4 | @load policy/frameworks/intel/seen/where-locations 5 | 6 | module Broala; 7 | 8 | export { 9 | redef enum Notice::Type += { 10 | ## A connection was seen to an IP associated with a domain name from the Intel framework 11 | Connection_to_Intel_Domain 12 | }; 13 | 14 | global dns_intel_match: event(s: Intel::Seen, items: set[Intel::Item]); 15 | } 16 | 17 | type DNS_Query: record { 18 | ## The connection of the DNS query 19 | conn: connection; 20 | ## The query itself 21 | query: string; 22 | ## The TTL 23 | ttl : interval; 24 | ## The intelligence item(s) associated with this 25 | intel: set[Intel::Item]; 26 | }; 27 | 28 | global get_ttl: function(t: table[addr] of DNS_Query, idx: any): interval; 29 | global intel_dns_watchlist: table[addr] of DNS_Query &create_expire=0secs &expire_func=get_ttl; 30 | 31 | redef record Intel::Seen += { 32 | ## The associated DNS answer IP 33 | dns_ip: addr &optional; 34 | ## The associated DNS answer TTL 35 | dns_ttl: interval &optional; 36 | }; 37 | 38 | function get_ttl(t: table[addr] of DNS_Query, idx: any): interval 39 | { 40 | if ( t[idx]$ttl < 5min ) return 5min; 41 | return t[idx]$ttl; 42 | } 43 | 44 | function dns_do_seen(c: connection, ans: dns_answer, a: addr) 45 | { 46 | # Pulling this out to avoid repeating code. 47 | if ( c$dns?$query ) 48 | { 49 | Intel::seen([$indicator=c$dns$query, $indicator_type=Intel::DOMAIN, $conn=c, $where=DNS::IN_RESPONSE, 50 | $dns_ip=a, $dns_ttl=ans$TTL]); 51 | # The query in the reply can be different; consider both if different 52 | if ( c$dns$query != ans$query ) 53 | Intel::seen([$indicator=ans$query, $indicator_type=Intel::DOMAIN, $conn=c, $where=DNS::IN_RESPONSE, 54 | $dns_ip=a, $dns_ttl=ans$TTL]); 55 | } 56 | else 57 | { 58 | Intel::seen([$indicator=ans$query, $indicator_type=Intel::DOMAIN, $conn=c, $where=DNS::IN_RESPONSE, 59 | $dns_ip=a, $dns_ttl=ans$TTL]); 60 | } 61 | } 62 | 63 | event dns_A_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr) 64 | { 65 | dns_do_seen(c, ans, a); 66 | } 67 | 68 | event dns_AAAA_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr) 69 | { 70 | dns_do_seen(c, ans, a); 71 | } 72 | 73 | event Intel::match(s: Intel::Seen, items: set[Intel::Item]) 74 | { 75 | # This is handled by the manager. Send it to the worker. 76 | event Broala::dns_intel_match(s, items); 77 | } 78 | 79 | event dns_intel_match(s: Intel::Seen, items: set[Intel::Item]) 80 | { 81 | if ( s$where != DNS::IN_RESPONSE ) return; 82 | if ( s?$conn && s?$dns_ip && s$dns_ip !in intel_dns_watchlist ) 83 | intel_dns_watchlist[s$dns_ip] = [$conn=s$conn, $query=s$indicator, $ttl=s$dns_ttl, $intel=items]; 84 | } 85 | 86 | event connection_state_remove(c: connection) 87 | { 88 | if ( c$id$resp_h in intel_dns_watchlist ) 89 | { 90 | local d = intel_dns_watchlist[c$id$resp_h]; 91 | 92 | # Let's create a comma-seperated string of the intel sources that this domain was from 93 | local sources: table[count] of string = table(); 94 | for ( i in d$intel ) 95 | sources[|sources|+1] = i$meta$source; 96 | 97 | NOTICE([$note=Connection_to_Intel_Domain, 98 | $msg=fmt("Connection to IP associated with Intel Domain '%s'", d$query), 99 | $sub=join_string_array(",", sources), $conn=c, $suppress_for=30mins, 100 | $identifier=cat(c$id$orig_h, d$query)]); 101 | } 102 | } 103 | 104 | redef Cluster::manager2worker_events += /Broala::dns_intel_match/; 105 | -------------------------------------------------------------------------------- /intel-ext/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, CrowdStrike, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /intel-ext/__load__.bro: -------------------------------------------------------------------------------- 1 | @load ./conn-udp-icmp 2 | @load ./dns-answers 3 | @load ./ftp-username 4 | @load ./radius 5 | @load ./smtp-subject 6 | @load ./ssl 7 | -------------------------------------------------------------------------------- /intel-ext/conn-udp-icmp.bro: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 CrowdStrike 2014 2 | # josh.liburdi@crowdstrike.com 3 | # Modifications and bugs by: 4 | # Michal Purzynski mpurzynski@mozilla.com 5 | # 6 | # This file has been initially imported from https://github.com/CrowdStrike/NetworkDetection/tree/master/bro-scripts/intel-extensions/seen 7 | # 8 | # Support for UDP, ICMP. This will only generate Intel matches when a connection is removed from Bro. 9 | # Will not send local link IPv6 events to the Intel Framework because it generates false positives here. 10 | # Also omit the non-established TCP connections. 11 | 12 | @load base/frameworks/intel 13 | @load policy/frameworks/intel/seen/where-locations 14 | 15 | const ip6_local: set[subnet] = set([fe80:0000::]/10, [ff02::]/16); 16 | 17 | event connection_state_remove(c: connection) 18 | { 19 | if ( ( c$conn?$proto ) && ( c$conn$proto != tcp ) ) { 20 | if ( ( c$id$orig_h in ip6_local ) || ( c$id$resp_h in ip6_local ) ) 21 | return; 22 | 23 | Intel::seen([$indicator=cat(c$id$orig_h), $indicator_type=Intel::ADDR, $host=c$id$orig_h, $where=Conn::IN_ORIG, $conn=c]); 24 | Intel::seen([$indicator=cat(c$id$resp_h), $indicator_type=Intel::ADDR, $host=c$id$orig_h, $where=Conn::IN_RESP, $conn=c]); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /intel-ext/dns-answers.bro: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 CrowdStrike 2014 2 | # josh.liburdi@crowdstrike.com 3 | # 4 | # This file has been initially imported from https://github.com/CrowdStrike/NetworkDetection/tree/master/bro-scripts/intel-extensions/seen 5 | # 6 | # Intel framework support for DNS answers 7 | 8 | @load base/protocols/dns 9 | @load base/frameworks/intel 10 | @load policy/frameworks/intel/seen/where-locations 11 | 12 | event dns_end(c: connection, msg: dns_msg) 13 | { 14 | if ( ( c?$dns ) && ( c$dns?$answers ) ) 15 | { 16 | local ans = c$dns$answers; 17 | for ( i in ans ) 18 | { 19 | if ( ans[i] == ip_addr_regex ) 20 | Intel::seen([$host=to_addr(ans[i]), 21 | $conn=c, 22 | $where=DNS::IN_RESPONSE]); 23 | else 24 | Intel::seen([$indicator=ans[i], 25 | $indicator_type=Intel::DOMAIN, 26 | $conn=c, 27 | $where=DNS::IN_RESPONSE]); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /intel-ext/ftp-username.bro: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 CrowdStrike 2014 2 | # josh.liburdi@crowdstrike.com 3 | # Modifications and bugs by: 4 | # Michal Purzynski mpurzynski@mozilla.com 5 | # 6 | # This file has been initially imported from https://github.com/CrowdStrike/NetworkDetection/tree/master/bro-scripts/intel-extensions/seen 7 | # 8 | # Intel framework support for FTP usernames 9 | 10 | @load base/protocols/ftp 11 | @load base/frameworks/intel 12 | @load policy/frameworks/intel/seen/where-locations 13 | 14 | @load base/frameworks/intel 15 | 16 | module Intel; 17 | 18 | export { 19 | redef enum Intel::Where += { 20 | FTP::IN_USER_NAME, 21 | }; 22 | } 23 | 24 | event ftp_request(c: connection, command: string, arg: string) 25 | { 26 | if ( command == "USER" ) 27 | Intel::seen([$indicator=arg, 28 | $indicator_type=Intel::USER_NAME, 29 | $conn=c, 30 | $where=FTP::IN_USER_NAME]); 31 | } 32 | -------------------------------------------------------------------------------- /intel-ext/radius.bro: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 CrowdStrike 2014 2 | # josh.liburdi@crowdstrike.com 3 | # Modifications and bugs by: 4 | # Michal Purzynski mpurzynski@mozilla.com 5 | # 6 | # This file has been initially imported from https://github.com/CrowdStrike/NetworkDetection/tree/master/bro-scripts/intel-extensions/seen 7 | # 8 | # Radius username and MAC address support for the Intel Framework 9 | 10 | @load base/protocols/radius 11 | @load base/frameworks/intel 12 | @load policy/frameworks/intel/seen/where-locations 13 | 14 | module Intel; 15 | 16 | export { 17 | redef enum Intel::Type += { 18 | PHY_ADDR 19 | }; 20 | 21 | redef enum Intel::Where += { 22 | RADIUS::IN_SUCCESSFUL_AUTH, 23 | RADIUS::IN_FAILED_AUTH 24 | }; 25 | } 26 | 27 | event RADIUS::log_radius(rec: RADIUS::Info) 28 | { 29 | if ( rec?$username && rec?$result ) { 30 | 31 | if ( rec$result == "success" ) { 32 | Intel::seen([$indicator=rec$mac, 33 | $indicator_type=Intel::PHY_ADDR, 34 | $where=RADIUS::IN_SUCCESSFUL_AUTH]); 35 | Intel::seen([$indicator=rec$username, 36 | $indicator_type=Intel::USER_NAME, 37 | $where=RADIUS::IN_SUCCESSFUL_AUTH]); 38 | } 39 | 40 | if ( rec$result == "failed" ) { 41 | Intel::seen([$indicator=rec$mac, 42 | $indicator_type=Intel::PHY_ADDR, 43 | $where=RADIUS::IN_FAILED_AUTH]); 44 | Intel::seen([$indicator=rec$username, 45 | $indicator_type=Intel::USER_NAME, 46 | $where=RADIUS::IN_FAILED_AUTH]); 47 | } 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /intel-ext/smtp-subject.bro: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 CrowdStrike 2014 2 | # josh.liburdi@crowdstrike.com 3 | # 4 | # This file has been initially imported from https://github.com/CrowdStrike/NetworkDetection/tree/master/bro-scripts/intel-extensions/seen 5 | # 6 | # Intel framework support for SMTP subjects 7 | 8 | @load base/protocols/smtp 9 | @load base/frameworks/intel 10 | @load policy/frameworks/intel/seen/where-locations 11 | 12 | @load base/frameworks/intel 13 | 14 | module Intel; 15 | 16 | export { 17 | redef enum Intel::Type += { 18 | EMAIL_SUBJECT 19 | }; 20 | 21 | redef enum Intel::Where += { 22 | SMTP::IN_SUBJECT, 23 | }; 24 | } 25 | 26 | event smtp_request(c: connection, is_orig: bool, command: string, arg: string) 27 | { 28 | if ( c$smtp?$subject ) 29 | Intel::seen([$indicator=c$smtp$subject, 30 | $indicator_type=Intel::EMAIL_SUBJECT, 31 | $conn=c, 32 | $where=SMTP::IN_SUBJECT]); 33 | } 34 | -------------------------------------------------------------------------------- /intel-ext/ssl.bro: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 CrowdStrike 2014 2 | # josh.liburdi@crowdstrike.com 3 | # Modifications and bugs by: 4 | # Michal Purzynski mpurzynski@mozilla.com 5 | # 6 | # This file has been initially imported from https://github.com/CrowdStrike/NetworkDetection/tree/master/bro-scripts/intel-extensions/seen 7 | # 8 | # SSL Issuer and Subject field support for the Intel Framework 9 | 10 | @load base/protocols/ssl 11 | @load base/frameworks/intel 12 | @load policy/frameworks/intel/seen/where-locations 13 | 14 | module Intel; 15 | 16 | export { 17 | redef enum Intel::Type += { 18 | CERT_ISSUER, 19 | CERT_SUBJECT, 20 | CERT_SERIAL 21 | }; 22 | 23 | redef enum Intel::Where += { 24 | SSL::IN_SERVER_CERT, 25 | SSL::IN_CLIENT_CERT 26 | }; 27 | } 28 | 29 | event ssl_established(c: connection) 30 | { 31 | if ( ! c?$ssl ) 32 | return; 33 | 34 | if ( ( c$ssl?$issuer ) && ( c$ssl?$subject ) ) { 35 | Intel::seen([$indicator=c$ssl$issuer, 36 | $indicator_type=Intel::CERT_ISSUER, 37 | $conn=c, 38 | $where=SSL::IN_SERVER_CERT 39 | ]); 40 | Intel::seen([$indicator=c$ssl$subject, 41 | $indicator_type=Intel::CERT_SUBJECT, 42 | $conn=c, 43 | $where=SSL::IN_SERVER_CERT 44 | ]); 45 | } 46 | 47 | if ( ( c$ssl?$issuer ) && ( c$ssl?$client_subject ) ) { 48 | Intel::seen([$indicator=c$ssl$issuer, 49 | $indicator_type=Intel::CERT_ISSUER, 50 | $conn=c, 51 | $where=SSL::IN_CLIENT_CERT 52 | ]); 53 | Intel::seen([$indicator=c$ssl$client_subject, 54 | $indicator_type=Intel::CERT_SUBJECT, 55 | $conn=c, 56 | $where=SSL::IN_CLIENT_CERT 57 | ]); 58 | } 59 | } 60 | 61 | event x509_certificate(f: fa_file, cert_ref: opaque of x509, cert: X509::Certificate) 62 | { 63 | Intel::seen([$indicator=cert$serial, $indicator_type=Intel::CERT_SERIAL, $f=f, $where=X509::IN_CERT]); 64 | if ( ! cert?$serial ) 65 | return; 66 | 67 | Intel::seen([$indicator=cert$serial, $indicator_type=Intel::CERT_SERIAL, $f=f, $where=X509::IN_CERT]); 68 | } 69 | 70 | event file_hash(f: fa_file, kind: string, hash: string) 71 | { 72 | if ( ! f?$info || ! f$info?$x509 || kind != "sha1" ) 73 | return; 74 | 75 | Intel::seen([$indicator=hash, 76 | $indicator_type=Intel::CERT_HASH, 77 | $f=f, 78 | $where=X509::IN_CERT]); 79 | } 80 | -------------------------------------------------------------------------------- /livecheck.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Software distributed under the License is distributed on an "AS IS" basis, 6 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 7 | # for the specific language governing rights and limitations under the 8 | # License. 9 | # 10 | # The Initial Developer of the Original Code is 11 | # Mozilla Corporation 12 | # Portions created by the Initial Developer are Copyright (C) 2014 13 | # the Initial Developer. All Rights Reserved. 14 | # 15 | # Contributor(s): 16 | # Michal Purzynski mpurzynski@mozilla.com 17 | 18 | @load base/frameworks/notice 19 | 20 | module MozillaAlive; 21 | 22 | export { 23 | redef enum Notice::Type += { 24 | Bro_Is_Watching_You, 25 | }; 26 | 27 | global wpad_dat_sum: set[string] &redef; 28 | } 29 | 30 | event MozillaAlive::check() 31 | { 32 | local message = current_time() - network_time(); 33 | 34 | NOTICE([$note=Bro_Is_Watching_You, 35 | $msg=fmt("Aliveness check succeeded"), 36 | $sub=fmt("falling behind by %s", cat(message))]); 37 | 38 | schedule 5mins { MozillaAlive::check() }; 39 | } 40 | 41 | function start_liveness_check() 42 | { 43 | schedule 5mins { MozillaAlive::check() }; 44 | } 45 | 46 | event bro_init() 47 | { 48 | start_liveness_check(); 49 | } 50 | 51 | -------------------------------------------------------------------------------- /logfilter_ip.txt: -------------------------------------------------------------------------------- 1 | #fields drop_ip description 2 | 63.245.214.82 v-1030.fw1.releng.scl3.mozilla.net 3 | 63.245.214.162 nat-fw1.scl3.mozilla.com 4 | 63.245.214.159 nat-neo-bughunter.scl3.mozilla.com 5 | 10.22.75.42 nagios1.private.scl3.mozilla.com 6 | 169.254.169.254 aws.metadata.service 7 | -------------------------------------------------------------------------------- /metasploit_ssl.bro: -------------------------------------------------------------------------------- 1 | ## Detect Default Metasploit SSL Random Cert (includes /meterpreter/reverse_https and browser exploits with SSL) 2 | ## Version 2 (8/2/2015) 3 | ## Copywrite 2015 John B. Althouse III 4 | ## Modifications and bugs by Michal Purzynski 5 | 6 | module MSF_SSL; 7 | 8 | export { 9 | redef enum Notice::Type += { 10 | Metasploit_SSL_Cert, 11 | }; 12 | const ignored_subnets: set[subnet] &redef; 13 | } 14 | 15 | const falselist += { 16 | "CN=localhost", 17 | }; 18 | 19 | event ssl_established(c: connection ) 20 | { 21 | if ( c$id$resp_h in ignored_subnets ) 22 | return; 23 | if ( ! c$ssl?$subject ) 24 | return; 25 | if ( ! c$ssl?$issuer ) 26 | return; 27 | if ( c$ssl$subject != c$ssl$issuer ) 28 | return; 29 | if ( c$ssl$subject in falselist ) 30 | return; 31 | 32 | if ( ( /^CN=[a-z]{2,10}$/ == c$ssl$subject ) && ( /^.+SHA256$/ == c$ssl$cipher ) ) 33 | NOTICE([$note=Metasploit_SSL_Cert, 34 | $conn=c, 35 | $msg=fmt("Metasploit Style Randomly Generated SSL Cert, '%s'", c$ssl$subject), 36 | $sub=c$ssl$issuer]); 37 | } 38 | 39 | -------------------------------------------------------------------------------- /perfect_forward_secrecy.bro: -------------------------------------------------------------------------------- 1 | #! Add field pfs (perfect forward secrecy) to ssl log file 2 | #! Attackers in possession of a private ssl/tls key want be able 3 | #! to decrypt old communications if perfect forward secrecy is used. 4 | #! We consider we have perfect forward secrecy if we use DHE 5 | #! (Diffie-Hellman) of ECDHE (Elliptic-curve Diffie-Hellman) 6 | 7 | @load base/protocols/ssl 8 | 9 | module PerfectForwardSecrecy; 10 | 11 | export { 12 | ## Monitored hosts for PFS 13 | const cert_tracking = ALL_HOSTS &redef; 14 | } 15 | 16 | redef record SSL::Info += { 17 | ## Perfect Forward Secrecy 18 | pfs: bool &log &optional; 19 | }; 20 | 21 | event ssl_established(c: connection) 22 | { 23 | if (addr_matches_host(c$id$resp_h, cert_tracking)) 24 | { 25 | c$ssl$pfs = F; 26 | if ( (/_DHE_/ in c$ssl$cipher) || (/_ECDHE_/ in c$ssl$cipher) ) 27 | { 28 | c$ssl$pfs = T; 29 | } 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /radius_bruteforcing.bro: -------------------------------------------------------------------------------- 1 | # Script to detect Radius auth bruteforcing 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | # 7 | # Contributor(s): 8 | # Michal Purzynski mpurzynski@mozilla.com 9 | 10 | @load base/frameworks/notice 11 | @load base/frameworks/sumstats 12 | @load base/protocols/http 13 | 14 | module MozillaRadiusErrors; 15 | 16 | export { 17 | redef enum Notice::Type += { 18 | Auth_Bruteforcing_User, 19 | Auth_Bruteforcing_MAC, 20 | }; 21 | 22 | const auth_errors_threshold: double = 3.0 &redef; 23 | const auth_errors_interval = 1min &redef; 24 | } 25 | 26 | event bro_init() 27 | { 28 | local r1: SumStats::Reducer = [$stream="radius.auth_errors.user", $apply=set(SumStats::SUM)]; 29 | local r2: SumStats::Reducer = [$stream="radius.auth_errors.mac", $apply=set(SumStats::SUM)]; 30 | SumStats::create([$name="radius-auth-errors-users", 31 | $epoch=auth_errors_interval, 32 | $reducers=set(r1), 33 | $threshold_val(key: SumStats::Key, result: SumStats::Result) = { 34 | return result["radius.auth_errors.user"]$sum; 35 | }, 36 | $threshold=auth_errors_threshold, 37 | $threshold_crossed(key: SumStats::Key, result: SumStats::Result) = { 38 | NOTICE([$note=Auth_Bruteforcing_User, 39 | $msg=fmt("Radius auth bruteforcing for user %s", key$str), 40 | $sub=fmt("%.0f auth failed in %s", result["radius.auth_errors.user"]$sum, auth_errors_interval), 41 | $n=to_count(fmt("%.0f", result["radius.auth_errors.user"]$sum)) 42 | ]); 43 | }]); 44 | SumStats::create([$name="radius-auth-errors-macs", 45 | $epoch=auth_errors_interval, 46 | $reducers=set(r2), 47 | $threshold_val(key: SumStats::Key, result: SumStats::Result) = { 48 | return result["radius.auth_errors.mac"]$sum; 49 | }, 50 | $threshold=auth_errors_threshold, 51 | $threshold_crossed(key: SumStats::Key, result: SumStats::Result) = { 52 | NOTICE([$note=Auth_Bruteforcing_MAC, 53 | $msg=fmt("Radius auth bruteforcing for MAC %s", key$str), 54 | $sub=fmt("%.0f auth failed in %s", result["radius.auth_errors.mac"]$sum, auth_errors_interval), 55 | $n=to_count(fmt("%.0f", result["radius.auth_errors.mac"]$sum)) 56 | ]); 57 | }]); 58 | } 59 | 60 | event RADIUS::log_radius(rec: RADIUS::Info) 61 | { 62 | if ( ( rec?$result ) && ( rec$result != "success" ) ) { 63 | if ( ( rec?$username ) && ( |rec$username| > 1 ) ) 64 | SumStats::observe("radius.auth_errors.user", 65 | [$str=rec$username], 66 | SumStats::Observation($num=1)); 67 | if ( ( rec?$connect_info ) && ( |rec$connect_info| > 1 ) ) 68 | SumStats::observe("radius.auth_errors.mac", 69 | [$str=rec$connect_info], 70 | SumStats::Observation($num=1)); 71 | } 72 | } 73 | 74 | -------------------------------------------------------------------------------- /relengawsportstopk.bro: -------------------------------------------------------------------------------- 1 | module RelengVpc; 2 | 3 | export { 4 | redef enum Log::ID += { LOG1, LOG2 }; 5 | 6 | type Info: record { 7 | ts: time &log; 8 | host: string &log; 9 | inbytes: double &log; 10 | outbytes: double &log; 11 | }; 12 | 13 | global log_rsubnet: event( rec: Info ); 14 | } 15 | 16 | event bro_init() { 17 | 18 | local r11: SumStats::Reducer = [$stream="inbytes1", $apply=set(SumStats::SUM)]; 19 | local r21: SumStats::Reducer = [$stream="outbytes1", $apply=set(SumStats::SUM)]; 20 | 21 | SumStats::create([ 22 | $name="rsubnet-measurement", 23 | $epoch=10mins, 24 | $reducers=set(r11, r21), 25 | $epoch_result(ts: time, key: SumStats::Key, result: SumStats::Result) = { 26 | local out1: RelengVpc::Info; 27 | out1$ts = ts; 28 | out1$inbytes = result["inbytes1"]$sum; 29 | out1$outbytes = result["outbytes1"]$sum; 30 | out1$host = key$str; 31 | Log::write(RelengVpc::LOG1, out1); 32 | } 33 | ]); 34 | 35 | Log::create_stream(RelengVpc::LOG1, [$columns=Info, $ev=log_rsubnet]); 36 | Log::set_buf(RelengVpc::LOG1, F); 37 | 38 | local r12: SumStats::Reducer = [$stream="inbytes2", $apply=set(SumStats::SUM)]; 39 | local r22: SumStats::Reducer = [$stream="outbytes2", $apply=set(SumStats::SUM)]; 40 | 41 | SumStats::create([ 42 | $name="rsubnet-measurement2", 43 | $epoch=10mins, 44 | $reducers=set(r12, r22), 45 | $epoch_result(ts: time, key: SumStats::Key, result: SumStats::Result) = { 46 | local out2: RelengVpc::Info; 47 | out2$ts = ts; 48 | out2$inbytes = result["inbytes2"]$sum; 49 | out2$outbytes = result["outbytes2"]$sum; 50 | out2$host = key$str; 51 | Log::write(RelengVpc::LOG2, out2); 52 | } 53 | ]); 54 | 55 | Log::create_stream(RelengVpc::LOG2, [$columns=Info, $ev=log_rsubnet]); 56 | Log::set_buf(RelengVpc::LOG2, F); 57 | 58 | } 59 | 60 | 61 | event connection_state_remove(c: connection) { 62 | 63 | const relengvpc1: subnet = 10.132.0.0/16 &redef; 64 | const relengvpc2: subnet = 10.134.0.0/16 &redef; 65 | 66 | if ((c$id$resp_h in relengvpc1) || (c$id$resp_h in relengvpc2) || (c$id$orig_h in relengvpc1) || (c$id$orig_h in relengvpc2)) { 67 | if ( c$conn$proto == tcp && c$conn$conn_state == "SF" ) { 68 | local key1: string; 69 | key1 = fmt("%s-%s", c$id$resp_h,c$id$resp_p); 70 | SumStats::observe("inbytes1", [$str=key1], [$num=c$conn$orig_bytes]); 71 | SumStats::observe("outbytes1", [$str=key1], [$num=c$conn$resp_bytes]); 72 | local key2: string; 73 | key2 = fmt("%s-%s-%s", c$id$orig_h,c$id$resp_h,c$id$resp_p); 74 | SumStats::observe("inbytes2", [$str=key2], [$num=c$conn$orig_bytes]); 75 | SumStats::observe("outbytes2", [$str=key2], [$num=c$conn$resp_bytes]); 76 | } 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /relengawstopk.bro: -------------------------------------------------------------------------------- 1 | module RelEngVPC; 2 | 3 | export { 4 | redef enum Log::ID += { LOG }; 5 | 6 | type Info: record { 7 | ts: time &log; 8 | host: addr &log; 9 | inbytes: double &log; 10 | outbytes: double &log; 11 | }; 12 | 13 | global log_rsubnet: event( rec: Info ); 14 | } 15 | 16 | event bro_init() { 17 | 18 | local r1: SumStats::Reducer = [$stream="inbytes", $apply=set(SumStats::SUM)]; 19 | local r2: SumStats::Reducer = [$stream="outbytes", $apply=set(SumStats::SUM)]; 20 | 21 | SumStats::create([ 22 | $name="rsubnet-measurement", 23 | $epoch=10mins, 24 | $reducers=set(r1, r2), 25 | $epoch_result(ts: time, key: SumStats::Key, result: SumStats::Result) = { 26 | local out: RelEngVPC::Info; 27 | out$ts = ts; 28 | out$inbytes = result["inbytes"]$sum; 29 | out$outbytes = result["outbytes"]$sum; 30 | out$host = key$host; 31 | Log::write(RelEngVPC::LOG, out); 32 | } 33 | ]); 34 | 35 | Log::create_stream(RelEngVPC::LOG, [$columns=Info, $ev=log_rsubnet]); 36 | Log::set_buf(RelEngVPC::LOG, F); 37 | } 38 | 39 | 40 | event connection_state_remove(c: connection) { 41 | 42 | const relengvpc1: subnet = 10.132.0.0/16 &redef; 43 | const relengvpc2: subnet = 10.134.0.0/16 &redef; 44 | if ((c$id$resp_h in relengvpc1) || (c$id$resp_h in relengvpc2) || (c$id$orig_h in relengvpc1) || (c$id$orig_h in relengvpc2)) { 45 | if ( c$conn$proto == tcp && c$conn$conn_state == "SF" ) { 46 | if ( Site::is_local_addr(c$id$resp_h) ) { 47 | SumStats::observe("outbytes", [$host=c$id$resp_h], [$num=c$conn$orig_bytes]); 48 | SumStats::observe("inbytes", [$host=c$id$resp_h], [$num=c$conn$resp_bytes]); 49 | } else if ( Site::is_local_addr(c$id$orig_h) ) { 50 | SumStats::observe("outbytes", [$host=c$id$orig_h], [$num=c$conn$orig_bytes]); 51 | SumStats::observe("inbytes", [$host=c$id$orig_h], [$num=c$conn$resp_bytes]); 52 | } 53 | } 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /sqli.bro: -------------------------------------------------------------------------------- 1 | # Script to detect SQLi attempts 2 | # inspired by detect_sqli for sumstats part 3 | # creates a sqli.log file with all attacks detected for forensics 4 | # 5 | # This Source Code Form is subject to the terms of the Mozilla Public 6 | # License, v. 2.0. If a copy of the MPL was not distributed with this 7 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | # 9 | # Software distributed under the License is distributed on an "AS IS" basis, 10 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 | # for the specific language governing rights and limitations under the 12 | # License. 13 | # 14 | # The Initial Developer of the Original Code is 15 | # Mozilla Corporation 16 | # Portions created by the Initial Developer are Copyright (C) 2014 17 | # the Initial Developer. All Rights Reserved. 18 | # 19 | # Contributor(s): 20 | # Anthony Verez averez@mozilla.com 21 | 22 | @load base/frameworks/notice 23 | @load base/frameworks/sumstats 24 | @load base/protocols/http 25 | 26 | module SQLi; 27 | 28 | export { 29 | # sqli.log 30 | # Let's tag our items 31 | 32 | redef enum HTTP::Tags += { 33 | ## SQLi detected with Bro signatures 34 | SQLI_SIG_BRO, 35 | }; 36 | 37 | type Tags: enum { 38 | ## SQLi detected with Bro signatures 39 | SIG_BRO, 40 | } &redef; 41 | 42 | redef enum Log::ID += { LOG }; 43 | 44 | type Info: record { 45 | ts: time &log; 46 | uid: string &log; 47 | id: conn_id &log; 48 | cluster_client_ip: string &log &optional; 49 | status_code: count &log &optional; 50 | host: string &log &optional; 51 | uri: string &log &optional; 52 | sig_id: string &log &optional; 53 | tags: set[Tags] &log &optional; 54 | }; 55 | 56 | global log_sqli: event(rec: Info); 57 | 58 | # Sumstats and alerts in notice 59 | redef enum Notice::Type += { 60 | ## Indicates that a host performing SQLi attacks was 61 | ## detected. 62 | SQLi_Attacker, 63 | ## Indicates that a host was seen to have SQLi attacks 64 | ## against it. This is tracked by IP address as opposed to 65 | ## hostname. 66 | SQLi_Victim, 67 | }; 68 | 69 | ## Defines the threshold that determines if a SQLi attack 70 | ## is ongoing based on the number of requests that appear to be SQLi 71 | ## attacks. 72 | const sqli_requests_threshold: double = 2.0 &redef; 73 | 74 | ## Interval at which to watch for the 75 | ## :bro:id:`SQLi::sqli_requests_threshold` variable to be crossed. 76 | ## At the end of each interval the counter is reset. 77 | const sqli_requests_interval = 1min &redef; 78 | 79 | const mozilla_internal_space: subnet = 10.0.0.0/8 &redef; 80 | const mozilla_public_space: subnet = 63.245.208.0/20 &redef; 81 | # From https://github.com/mozilla/nmap-differential-scan/blob/master/targets.txt 82 | const mozilla_pek1_office_space: subnet = 223.202.6.1/27 &redef; 83 | 84 | const ignore_host_resp: set[addr] = { } &redef; 85 | const ignore_host_orig: set[addr] = { } &redef; 86 | 87 | type Sig: record { 88 | regex: pattern; 89 | name: string; 90 | }; 91 | 92 | type SigVec: vector of Sig; 93 | 94 | # Please add signatures at the END of this list and to preserve rule ids 95 | # (?i:javascriptt) for case-insensitive is not working atm, so we need to do it old school :S 96 | # see https://bro-tracker.atlassian.net/browse/BIT-26 97 | global sigs = SigVec( 98 | [$regex=/.*[\?&][^[:blank:]\x00-\x37\|]+?=[\-[:alnum:]%]+([[:blank:]\x00-\x37]|\/\*.*?\*\/)*['"]?([[:blank:]\x00-\x37]|\/\*.*?\*\/|\)?;)+.*?([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])([[:blank:]\x00-\x37]|\/\*.*?\*\/)+.*/, 99 | $name="bro-1"], 100 | [$regex=/.*[\?&][^[:blank:]\x00-\x37\|]+?=[\-0-9%]+([[:blank:]\x00-\x37]|\/\*.*?\*\/)*['"]?([[:blank:]\x00-\x37]|\/\*.*?\*\/|\)?;)+([xX]?[oO][rR]|[nN]?[aA][nN][dD])([[:blank:]\x00-\x37]|\/\*.*?\*\/)+['"]?(([^a-zA-Z&]+)?=|[eE][xX][iI][sS][tT][sS]).*/, 101 | $name="bro-2"], 102 | [$regex=/.*[\?&][^[:blank:]\x00-\x37]+?=[\-0-9%]*([[:blank:]\x00-\x37]|\/\*.*?\*\/)*['"]([[:blank:]\x00-\x37]|\/\*.*?\*\/)*(-|=|\+|\|\|)([[:blank:]\x00-\x37]|\/\*.*?\*\/)*([0-9]|\(?[cC][oO][nN][vV][eE][rR][tT]|[cC][aA][sS][tT]).*/, 103 | $name="bro-3"], 104 | [$regex=/.*[\?&][^[:blank:]\x00-\x37\|]+?=([[:blank:]\x00-\x37]|\/\*.*?\*\/)*['"]([[:blank:]\x00-\x37]|\/\*.*?\*\/|;)*([xX]?[oO][rR]|[nN]?[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])([[:blank:]\x00-\x37]|\/\*.*?\*\/|[\[(])+[a-zA-Z&]{2,}.*/, 105 | $name="bro-4"], 106 | [$regex=/.*[\?&][^[:blank:]\x00-\x37]+?=[^\.]*?([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])\(.*/, 107 | $name="bro-5"], 108 | [$regex=/.*\/\*![[:digit:]]{5}.*?\*\/.*/, 109 | $name="bro-6"] 110 | ); 111 | } 112 | 113 | function subn_norm(key: SumStats::Key): SumStats::Key { 114 | return [$str=cat(mask_addr(key$host, 24))]; 115 | } 116 | 117 | event bro_init() &priority=5 118 | { 119 | # Create sqli.log 120 | Log::create_stream(SQLi::LOG, [$columns=Info, $ev=log_sqli]); 121 | 122 | # Add filters to the metrics so that the metrics framework knows how to 123 | # determine when it looks like an actual attack and how to respond when 124 | # thresholds are crossed. 125 | # local r1: SumStats::Reducer = [$stream="http.sqli.attacker", $apply=set(SumStats::SUM), $normalize_key=subn_norm]; 126 | # SumStats::create([$name="sqli-attackers", 127 | # $epoch=sqli_requests_interval, 128 | # $reducers=set(r1), 129 | # $threshold_val(key: SumStats::Key, result: SumStats::Result) = { 130 | # return result["http.sqli.attacker"]$sum; 131 | # }, 132 | # $threshold=sqli_requests_threshold, 133 | # $threshold_crossed(key: SumStats::Key, result: SumStats::Result) = { 134 | # NOTICE([$note=SQLi_Attacker, 135 | # $msg=fmt("SQLi attack from attacker subnet %s", key$str), 136 | # $sub=fmt("%.0f requests in %s", result["http.sqli.attacker"]$sum, sqli_requests_interval), 137 | # $n=to_count(fmt("%.0f", result["http.sqli.attacker"]$sum)) 138 | # ]); 139 | # }]); 140 | # local r2: SumStats::Reducer = [$stream="http.sqli.victim", $apply=set(SumStats::SUM), $normalize_key=subn_norm]; 141 | # SumStats::create([$name="sqli-victims", 142 | # $epoch=sqli_requests_interval, 143 | # $reducers=set(r2), 144 | # $threshold_val(key: SumStats::Key, result: SumStats::Result) = { 145 | # return result["http.sqli.victim"]$sum; 146 | # }, 147 | # $threshold=sqli_requests_threshold, 148 | # $threshold_crossed(key: SumStats::Key, result: SumStats::Result) = { 149 | # NOTICE([$note=SQLi_Victim, 150 | # $msg=fmt("SQLi attack to victim subnet %s", key$str), 151 | # $sub=fmt("%.0f requests in %s", result["http.sqli.victim"]$sum, sqli_requests_interval), 152 | # $n=to_count(fmt("%.0f", result["http.sqli.victim"]$sum)) 153 | # ]); 154 | # }]); 155 | } 156 | 157 | # Make sure we have all the http info before looking for SQLi 158 | event http_message_done(c: connection, is_orig: bool, stat: http_message_stat) 159 | { 160 | # only conns we want 161 | local ports_ext: set[port] = { 80/tcp }; 162 | local ports_int: set[port] = { 80/tcp, 81/tcp, 443/tcp }; 163 | 164 | if (((c$id$resp_h in mozilla_internal_space) && (c$id$resp_p in ports_int)) || ((c$id$resp_h in mozilla_public_space) && (c$id$resp_p in ports_ext)) && (c$id$resp_h !in ignore_host_resp) && (c$id$orig_h !in ignore_host_orig)) { 165 | if (c$http?$cluster_client_ip && to_addr(c$http$cluster_client_ip) !in mozilla_pek1_office_space) { 166 | for(tid in sigs) { 167 | if(sigs[tid]$regex in c$http$uri) { 168 | local tags: set[Tags]; 169 | 170 | add tags[SIG_BRO]; 171 | add c$http$tags[SQLI_SIG_BRO]; 172 | 173 | local rec: SQLi::Info = [ 174 | $ts=network_time(), 175 | $uid=c$uid, 176 | $id=c$id, 177 | $cluster_client_ip=c$http$cluster_client_ip, 178 | $status_code=c$http$status_code, 179 | $host=c$http$host, 180 | $uri=c$http$uri, 181 | $sig_id=sigs[tid]$name, 182 | $tags=tags 183 | ]; 184 | 185 | Log::write(SQLi::LOG, rec); 186 | 187 | # SumStats::observe("http.sqli.attacker", 188 | # [$host=to_addr(c$http$cluster_client_ip)], 189 | # []); 190 | # SumStats::observe("http.sqli.victim", 191 | # [$host=c$conn$id$resp_h], 192 | # []); 193 | break; 194 | } 195 | } 196 | } 197 | } 198 | } 199 | 200 | -------------------------------------------------------------------------------- /sshverlong.bro: -------------------------------------------------------------------------------- 1 | # This scripts send an alert on SSH client or server longer than N chars. Used to detect some types of malware CnC communication, not longer in the wild. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | # 7 | # Contributor(s): 8 | # Michal Purzynski mpurzynski@mozilla.com 9 | 10 | module SSH_VER_LONG; 11 | 12 | redef enum Notice::Type += { 13 | ClientVersionStringLong, 14 | ServerVersionStringLong, 15 | }; 16 | 17 | event ssh_client_version(c: connection, version: string) 18 | { 19 | if (|version| > 40) 20 | NOTICE([$note=ClientVersionStringLong, 21 | $msg=fmt("%s seems to use a very long SSH client version.", c$id$orig_h), 22 | $sub=version, 23 | $uid=c$uid, 24 | $id=c$id, 25 | $identifier=cat(c$uid)]); 26 | } 27 | 28 | event ssh_server_version(c: connection, version: string) 29 | { 30 | if (|version| > 40) 31 | NOTICE([$note=ServerVersionStringLong, 32 | $msg=fmt("%s seems to use a very long SSH server version.", c$id$orig_h), 33 | $sub=version, 34 | $uid=c$uid, 35 | $id=c$id, 36 | $identifier=cat(c$uid)]); 37 | } 38 | -------------------------------------------------------------------------------- /ssl-ciphers.bro: -------------------------------------------------------------------------------- 1 | # This script calculates the percentage of the use of the different 2 | # TLS cipher suites for each host in the local network. 3 | # Written by Johanna Amann 4 | 5 | @load base/protocols/ssl 6 | @load ./counttable 7 | 8 | module SSLCiphers; 9 | 10 | export { 11 | redef enum Log::ID += { LOG }; 12 | 13 | type Info: record { 14 | ts: time &log &default=network_time(); 15 | resp_h: addr &log; 16 | cipher: string &log; 17 | percent: double &log; 18 | connections: count &log; 19 | }; 20 | 21 | ## The frequency of logging the stats collected by this script. 22 | const epoch_interval = 60mins &redef; 23 | } 24 | 25 | event bro_init() 26 | { 27 | Log::create_stream(LOG, [$columns=Info]); 28 | 29 | local r1: SumStats::Reducer = [$stream="ciphers.conns", $apply=set(SumStats::SUM)]; 30 | local r2: SumStats::Reducer = [$stream="ciphers.ciphers", $apply=set(SumStats::COUNTTABLE)]; 31 | 32 | SumStats::create([$name="ciphers", 33 | $epoch=epoch_interval, 34 | $reducers=set(r1,r2), 35 | $epoch_result(ts: time, key: SumStats::Key, result: SumStats::Result) = 36 | { 37 | # both of these always have to be in the result set 38 | if ( "ciphers.conns" !in result ) 39 | return; 40 | if ( "ciphers.ciphers" !in result ) 41 | return; 42 | 43 | local hits = result["ciphers.conns"]$sum; 44 | local ciphers = result["ciphers.ciphers"]$counttable; 45 | 46 | for ( cipher in ciphers ) 47 | { 48 | local line: Info = [$resp_h=key$host, $cipher=cipher, $connections=ciphers[cipher], $percent=(ciphers[cipher]+0.0)/hits]; 49 | Log::write(LOG,line); 50 | } 51 | 52 | } 53 | ]); 54 | } 55 | 56 | event ssl_server_hello(c: connection, version: count, possible_ts: time, server_random: string, session_id: string, cipher: count, comp_method: count) 57 | { 58 | if (!Site::is_local_addr(c$id$resp_h)) 59 | return; 60 | 61 | SumStats::observe("ciphers.conns", [$host=c$id$resp_h], []); 62 | 63 | local cipher_str = SSL::cipher_desc[cipher]; 64 | SumStats::observe("ciphers.ciphers", [$host=c$id$resp_h], [$str=cipher_str]); 65 | } 66 | 67 | -------------------------------------------------------------------------------- /ssl-log-ext-firefox-mitm.bro: -------------------------------------------------------------------------------- 1 | # Add list of SSL/TLS cipher suites supported by clients to ssl log file 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | # 7 | # The Initial Developer of the Original Code is 8 | # Mozilla Corporation 9 | # Portions created by the Initial Developer are Copyright (C) 2014 10 | # the Initial Developer. All Rights Reserved. 11 | # 12 | # Contributor(s): 13 | # Original code and idea - Johanna Amann, Bro/ICSI - johanna@icir.org 14 | # Random bugs and extensions support - Michal Purzynski mpurzynski@mozilla.com 15 | 16 | @load base/protocols/ssl 17 | 18 | module SSL; 19 | 20 | export { 21 | redef record SSL::Info += { 22 | client_ciphers: index_vec &log &optional; 23 | client_curves: index_vec &log &optional; 24 | extensions: index_vec &log &optional; 25 | point_formats: index_vec &log &optional; 26 | }; 27 | } 28 | 29 | event ssl_client_hello (c: connection, version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec) 30 | { 31 | if (!c?$ssl) 32 | return; 33 | if (|ciphers| == 0) 34 | return; 35 | 36 | c$ssl$client_ciphers = ciphers; 37 | } 38 | 39 | event ssl_extension (c: connection, is_orig: bool, code: count, val: string) 40 | { 41 | if (!c?$ssl) 42 | return; 43 | if (!is_orig) 44 | return; 45 | 46 | if (!c$ssl?$extensions) 47 | c$ssl$extensions = vector(code); 48 | else 49 | c$ssl$extensions[|c$ssl$extensions|] = code; 50 | } 51 | 52 | event ssl_extension_elliptic_curves (c: connection, is_orig: bool, curves: index_vec) 53 | { 54 | if (!c?$ssl) 55 | return; 56 | if (|curves| == 0) 57 | return; 58 | if (!is_orig) 59 | return; 60 | 61 | c$ssl$client_curves = curves; 62 | } 63 | 64 | event ssl_extension_ec_point_formats(c: connection, is_orig: bool, point_formats: index_vec) 65 | { 66 | if (!c?$ssl) 67 | return; 68 | if (|point_formats| == 0) 69 | return; 70 | if (!is_orig) 71 | return; 72 | 73 | c$ssl$point_formats = point_formats; 74 | } 75 | -------------------------------------------------------------------------------- /ssl-log-ext.bro: -------------------------------------------------------------------------------- 1 | # Add list of SSL/TLS cipher suites supported by clients to ssl log file 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | # 7 | # The Initial Developer of the Original Code is 8 | # Mozilla Corporation 9 | # Portions created by the Initial Developer are Copyright (C) 2014 10 | # the Initial Developer. All Rights Reserved. 11 | # 12 | # Contributor(s): 13 | # Original code and idea - Johanna Amann, Bro/ICSI - johanna@icir.org 14 | # Random bugs and extensions support - Michal Purzynski mpurzynski@mozilla.com 15 | 16 | @load base/protocols/ssl 17 | 18 | module SSL; 19 | 20 | export { 21 | redef record SSL::Info += { 22 | client_ciphers: set[string] &log &optional &default=string_set(); 23 | client_curves: set[string] &log &optional &default=string_set(); 24 | extensions: set[string] &log &optional &default=string_set(); 25 | point_formats: set[string] &log &optional &default=string_set(); 26 | }; 27 | } 28 | 29 | event ssl_client_hello (c: connection, version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec) 30 | { 31 | if (!c?$ssl) 32 | return; 33 | if (|ciphers| == 0) 34 | return; 35 | 36 | for (cipher in ciphers) { 37 | add c$ssl$client_ciphers[SSL::cipher_desc[ciphers[cipher]]]; 38 | } 39 | } 40 | 41 | event ssl_extension (c: connection, is_orig: bool, code: count, val: string) 42 | { 43 | if (!c?$ssl) 44 | return; 45 | 46 | add c$ssl$extensions[SSL::extensions[code]]; 47 | } 48 | 49 | event ssl_extension_elliptic_curves (c: connection, is_orig: bool, curves: index_vec) 50 | { 51 | if (!c?$ssl) 52 | return; 53 | if (|curves| == 0) 54 | return; 55 | 56 | for (curve in curves) { 57 | add c$ssl$client_curves[ec_curves[curves[curve]]]; 58 | } 59 | } 60 | 61 | event ssl_extension_ec_point_formats(c: connection, is_orig: bool, point_formats: index_vec) 62 | { 63 | if (!c?$ssl) 64 | return; 65 | if (|point_formats| == 0) 66 | return; 67 | 68 | for (point in point_formats) { 69 | add c$ssl$point_formats[ec_point_formats[point_formats[point]]]; 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /ssl-log-ext1.bro: -------------------------------------------------------------------------------- 1 | # Add list of SSL/TLS cipher suites supported by clients to ssl log file 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | # 7 | # The Initial Developer of the Original Code is 8 | # Mozilla Corporation 9 | # Portions created by the Initial Developer are Copyright (C) 2014 10 | # the Initial Developer. All Rights Reserved. 11 | # 12 | # Contributor(s): 13 | # Original code and idea - Johanna Amann, Bro/ICSI - johanna@icir.org 14 | # Random bugs and extensions support - Michal Purzynski mpurzynski@mozilla.com 15 | 16 | @load base/protocols/ssl 17 | 18 | module SSL; 19 | 20 | export { 21 | redef record SSL::Info += { 22 | client_ciphers: index_vec &log &optional; 23 | client_curves: index_vec &log &optional; 24 | extensions: index_vec &log &optional; 25 | point_formats: index_vec &log &optional; 26 | }; 27 | } 28 | 29 | event ssl_client_hello (c: connection, version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec) 30 | { 31 | if (!c?$ssl) 32 | return; 33 | if (|ciphers| == 0) 34 | return; 35 | 36 | c$ssl$client_ciphers = ciphers; 37 | } 38 | 39 | event ssl_extension (c: connection, is_orig: bool, code: count, val: string) 40 | { 41 | if (!c?$ssl) 42 | return; 43 | if (!is_orig) 44 | return; 45 | 46 | if (!c$ssl?$extensions) 47 | c$ssl$extensions = vector(code); 48 | else 49 | c$ssl$extensions[|c$ssl$extensions|] = code; 50 | } 51 | 52 | event ssl_extension_elliptic_curves (c: connection, is_orig: bool, curves: index_vec) 53 | { 54 | if (!c?$ssl) 55 | return; 56 | if (|curves| == 0) 57 | return; 58 | if (!is_orig) 59 | return; 60 | 61 | c$ssl$client_curves = curves; 62 | } 63 | 64 | event ssl_extension_ec_point_formats(c: connection, is_orig: bool, point_formats: index_vec) 65 | { 66 | if (!c?$ssl) 67 | return; 68 | if (|point_formats| == 0) 69 | return; 70 | if (!is_orig) 71 | return; 72 | 73 | c$ssl$point_formats = point_formats; 74 | } 75 | -------------------------------------------------------------------------------- /sslproto_stats.bro: -------------------------------------------------------------------------------- 1 | @load base/protocols/ssl 2 | 3 | module SSLProtoStat; 4 | 5 | export { 6 | redef enum Log::ID += { LOG1 }; 7 | redef enum Log::ID += { LOG2 }; 8 | 9 | type Info_C: record { 10 | ## Timestamp when the log line was finished and written. 11 | ts: time &log; 12 | ## Time interval that the log line covers. 13 | ts_delta: interval &log; 14 | resp_h: addr &log; 15 | tls12: double &log; 16 | tls11: double &log; 17 | tls10: double &log; 18 | ssl3: double &log; 19 | ssl2: double &log; 20 | }; 21 | 22 | type Info_S: record { 23 | ## Timestamp when the log line was finished and written. 24 | ts: time &log; 25 | ## Time interval that the log line covers. 26 | ts_delta: interval &log; 27 | resp_h: addr &log; 28 | tls12: double &log; 29 | tls11: double &log; 30 | tls10: double &log; 31 | ssl3: double &log; 32 | ssl2: double &log; 33 | }; 34 | 35 | ## The frequency of logging the stats collected by this script. 36 | const break_interval = 15mins &redef; 37 | 38 | ## Monitored hosts for weak SSL ciphers 39 | const cert_tracking = ALL_HOSTS &redef; 40 | } 41 | 42 | event bro_init() 43 | { 44 | Log::create_stream(SSLProtoStat::LOG1, [$columns=Info_C]); 45 | Log::create_stream(SSLProtoStat::LOG2, [$columns=Info_S]); 46 | 47 | local cr1: SumStats::Reducer = [$stream="ssl_proto_stat.ssl_hits", $apply=set(SumStats::SUM)]; 48 | local cr2: SumStats::Reducer = [$stream="ssl_proto_stat.tls12_hits", $apply=set(SumStats::SUM)]; 49 | local cr3: SumStats::Reducer = [$stream="ssl_proto_stat.tls11_hits", $apply=set(SumStats::SUM)]; 50 | local cr4: SumStats::Reducer = [$stream="ssl_proto_stat.tls10_hits", $apply=set(SumStats::SUM)]; 51 | local cr5: SumStats::Reducer = [$stream="ssl_proto_stat.ssl3_hits", $apply=set(SumStats::SUM)]; 52 | local cr6: SumStats::Reducer = [$stream="ssl_proto_stat.ssl2_hits", $apply=set(SumStats::SUM)]; 53 | 54 | local sr1: SumStats::Reducer = [$stream="ssl_proto_negotiated.ssl_hits", $apply=set(SumStats::SUM)]; 55 | local sr2: SumStats::Reducer = [$stream="ssl_proto_negotiated.tls12_hits", $apply=set(SumStats::SUM)]; 56 | local sr3: SumStats::Reducer = [$stream="ssl_proto_negotiated.tls11_hits", $apply=set(SumStats::SUM)]; 57 | local sr4: SumStats::Reducer = [$stream="ssl_proto_negotiated.tls10_hits", $apply=set(SumStats::SUM)]; 58 | local sr5: SumStats::Reducer = [$stream="ssl_proto_negotiated.ssl3_hits", $apply=set(SumStats::SUM)]; 59 | local sr6: SumStats::Reducer = [$stream="ssl_proto_negotiated.ssl2_hits", $apply=set(SumStats::SUM)]; 60 | 61 | SumStats::create([$name="ssl_proto_stat.ssl_hits", 62 | $epoch=break_interval, 63 | $reducers=set(cr1,cr2,cr3,cr4,cr5,cr6), 64 | $epoch_result(ts: time, key: SumStats::Key, result: SumStats::Result) = 65 | { 66 | local l: Info_C; 67 | 68 | l$resp_h = key$host; 69 | l$ts = network_time(); 70 | l$ts_delta = break_interval; 71 | if ("ssl_proto_stat.tls12_hits" in result) 72 | l$tls12 = result["ssl_proto_stat.tls12_hits"]$sum/result["ssl_proto_stat.ssl_hits"]$sum; 73 | if ("ssl_proto_stat.tls11_hits" in result) 74 | l$tls11 = result["ssl_proto_stat.tls11_hits"]$sum/result["ssl_proto_stat.ssl_hits"]$sum; 75 | if ("ssl_proto_stat.tls10_hits" in result) 76 | l$tls10 = result["ssl_proto_stat.tls10_hits"]$sum/result["ssl_proto_stat.ssl_hits"]$sum; 77 | if ("ssl_proto_stat.ssl3_hits" in result) 78 | l$ssl3 = result["ssl_proto_stat.ssl3_hits"]$sum/result["ssl_proto_stat.ssl_hits"]$sum; 79 | if ("ssl_proto_stat.ssl2_hits" in result) 80 | l$ssl2 = result["ssl_proto_stat.ssl2_hits"]$sum/result["ssl_proto_stat.ssl_hits"]$sum; 81 | 82 | Log::write(LOG1,l); 83 | } 84 | ]); 85 | 86 | SumStats::create([$name="ssl_proto_negotiated.ssl_hits", 87 | $epoch=break_interval, 88 | $reducers=set(sr1,sr2,sr3,sr4,sr5,sr6), 89 | $epoch_result(ts: time, key: SumStats::Key, result: SumStats::Result) = 90 | { 91 | local l: Info_S; 92 | 93 | l$resp_h = key$host; 94 | l$ts = network_time(); 95 | l$ts_delta = break_interval; 96 | if ("ssl_proto_negotiated.tls12_hits" in result) 97 | l$tls12 = result["ssl_proto_negotiated.tls12_hits"]$sum/result["ssl_proto_negotiated.ssl_hits"]$sum; 98 | if ("ssl_proto_negotiated.tls11_hits" in result) 99 | l$tls11 = result["ssl_proto_negotiated.tls11_hits"]$sum/result["ssl_proto_negotiated.ssl_hits"]$sum; 100 | if ("ssl_proto_negotiated.tls10_hits" in result) 101 | l$tls10 = result["ssl_proto_negotiated.tls10_hits"]$sum/result["ssl_proto_negotiated.ssl_hits"]$sum; 102 | if ("ssl_proto_negotiated.ssl3_hits" in result) 103 | l$ssl3 = result["ssl_proto_negotiated.ssl3_hits"]$sum/result["ssl_proto_negotiated.ssl_hits"]$sum; 104 | if ("ssl_proto_negotiated.ssl2_hits" in result) 105 | l$ssl2 = result["ssl_proto_negotiated.ssl2_hits"]$sum/result["ssl_proto_negotiated.ssl_hits"]$sum; 106 | 107 | Log::write(LOG2,l); 108 | } 109 | ]); 110 | 111 | } 112 | 113 | event ssl_client_hello(c: connection, version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec) 114 | { 115 | if (Site::is_local_addr(c$id$orig_h)) 116 | return; 117 | 118 | SumStats::observe("ssl_proto_stat.ssl_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 119 | 120 | if (/TLSv12/ in SSL::version_strings[version]) 121 | SumStats::observe("ssl_proto_stat.tls12_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 122 | if (/TLSv11/ in SSL::version_strings[version]) 123 | SumStats::observe("ssl_proto_stat.tls11_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 124 | if (/TLSv10/ in SSL::version_strings[version]) 125 | SumStats::observe("ssl_proto_stat.tls10_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 126 | if (/SSLv3/ in SSL::version_strings[version]) 127 | SumStats::observe("ssl_proto_stat.sslv3_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 128 | if (/SSLv2/ in SSL::version_strings[version]) 129 | SumStats::observe("ssl_proto_stat.sslv2_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 130 | } 131 | 132 | event ssl_server_hello(c: connection, version: count, possible_ts: time, server_random: string, session_id: string, cipher: count, comp_method: count) 133 | { 134 | SumStats::observe("ssl_proto_negotiated.ssl_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 135 | 136 | if (/TLSv12/ in SSL::version_strings[version]) 137 | SumStats::observe("ssl_proto_negotiated.tls12_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 138 | if (/TLSv11/ in SSL::version_strings[version]) 139 | SumStats::observe("ssl_proto_negotiated.tls11_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 140 | if (/TLSv10/ in SSL::version_strings[version]) 141 | SumStats::observe("ssl_proto_negotiated.tls10_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 142 | if (/SSLv3/ in SSL::version_strings[version]) 143 | SumStats::observe("ssl_proto_negotiated.ssl3_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 144 | if (/SSLv2/ in SSL::version_strings[version]) 145 | SumStats::observe("ssl_proto_negotiated.ssl2_hits", [$host=c$id$resp_h], SumStats::Observation($num=1)); 146 | } 147 | -------------------------------------------------------------------------------- /sslverlong.bro: -------------------------------------------------------------------------------- 1 | module SSH_VER_LONG; 2 | 3 | redef enum Notice::Type += { 4 | ClientVersionStringLong, 5 | ServerVersionStringLong, 6 | }; 7 | 8 | event ssh_client_version(c: connection, version: string) 9 | { 10 | if (|version| > 40) 11 | NOTICE([$note=ClientVersionStringLong, 12 | $msg=fmt("%s seems to use a very long SSH client version.", c$id$orig_h), 13 | $sub=version, 14 | $uid=c$uid, 15 | $id=c$id, 16 | $identifier=cat(c$uid)]); 17 | } 18 | 19 | event ssh_server_version(c: connection, version: string) 20 | { 21 | if (|version| > 40) 22 | NOTICE([$note=ServerVersionStringLong, 23 | $msg=fmt("%s seems to use a very long SSH server version.", c$id$orig_h), 24 | $sub=version, 25 | $uid=c$uid, 26 | $id=c$id, 27 | $identifier=cat(c$uid)]); 28 | } 29 | -------------------------------------------------------------------------------- /subnettopk.bro: -------------------------------------------------------------------------------- 1 | module Subnet; 2 | 3 | export { 4 | redef enum Log::ID += { LOG }; 5 | 6 | type Info: record { 7 | ts: time &log; 8 | net: string &log; 9 | inbytes: double &log; 10 | outbytes: double &log; 11 | }; 12 | 13 | global log_subnet: event( rec: Info ); 14 | } 15 | 16 | function subn_norm(key: SumStats::Key): SumStats::Key { 17 | return [$str=cat(mask_addr(key$host, 24))]; 18 | } 19 | 20 | event bro_init() { 21 | 22 | local r1: SumStats::Reducer = [$stream="inbytes", $apply=set(SumStats::SUM), $normalize_key=subn_norm]; 23 | local r2: SumStats::Reducer = [$stream="outbytes", $apply=set(SumStats::SUM), $normalize_key=subn_norm]; 24 | 25 | SumStats::create([ 26 | $name="subnet-measurement", 27 | $epoch=60mins, 28 | $reducers=set(r1, r2), 29 | $epoch_result(ts: time, key: SumStats::Key, result: SumStats::Result) = { 30 | local out: Subnet::Info; 31 | out$ts = ts; 32 | out$inbytes = result["inbytes"]$sum; 33 | out$outbytes = result["outbytes"]$sum; 34 | out$net = key$str; 35 | Log::write(Subnet::LOG, out); 36 | } 37 | ]); 38 | 39 | Log::create_stream(Subnet::LOG, [$columns=Info, $ev=log_subnet]); 40 | Log::set_buf(Subnet::LOG, F); 41 | } 42 | 43 | 44 | event connection_state_remove(c: connection) { 45 | if ( c$conn$proto == tcp && c$conn$conn_state == "SF" ) { 46 | if ( Site::is_local_addr(c$id$resp_h) ) { 47 | SumStats::observe("outbytes", [$host=c$id$resp_h], [$num=c$conn$orig_bytes]); 48 | SumStats::observe("inbytes", [$host=c$id$resp_h], [$num=c$conn$resp_bytes]); 49 | } else if ( Site::is_local_addr(c$id$orig_h) ) { 50 | SumStats::observe("outbytes", [$host=c$id$orig_h], [$num=c$conn$orig_bytes]); 51 | SumStats::observe("inbytes", [$host=c$id$orig_h], [$num=c$conn$resp_bytes]); 52 | } 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /unix_commands.bro: -------------------------------------------------------------------------------- 1 | # Script to detect Unix command injection attempts 2 | # inspired by detect_sqli for sumstats part 3 | # creates a unix_command.log file with all attacks detected for forensics 4 | # 5 | # This Source Code Form is subject to the terms of the Mozilla Public 6 | # License, v. 2.0. If a copy of the MPL was not distributed with this 7 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | # 9 | # Software distributed under the License is distributed on an "AS IS" basis, 10 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 | # for the specific language governing rights and limitations under the 12 | # License. 13 | # 14 | # The Initial Developer of the Original Code is 15 | # Mozilla Corporation 16 | # Portions created by the Initial Developer are Copyright (C) 2014 17 | # the Initial Developer. All Rights Reserved. 18 | # 19 | # Contributor(s): 20 | # Anthony Verez averez@mozilla.com 21 | 22 | @load base/frameworks/notice 23 | @load base/frameworks/sumstats 24 | @load base/protocols/http 25 | 26 | module UnixCommand; 27 | 28 | export { 29 | # unix_command.log 30 | # Let's tag our items 31 | 32 | redef enum HTTP::Tags += { 33 | ## Unix command detected with arcsight signatures 34 | UNIX_COMMAND_SIG_ARCSIGHT, 35 | }; 36 | 37 | type Tags: enum { 38 | ## Unix command detected with arcsight signatures 39 | SIG_ARCSIGHT, 40 | } &redef; 41 | 42 | redef enum Log::ID += { LOG }; 43 | 44 | type Info: record { 45 | ts: time &log; 46 | uid: string &log; 47 | id: conn_id &log; 48 | cluster_client_ip: string &log &optional; 49 | status_code: count &log &optional; 50 | host: string &log &optional; 51 | uri: string &log &optional; 52 | sig_id: string &log &optional; 53 | tags: set[Tags] &log &optional; 54 | }; 55 | 56 | global log_unix: event(rec: Info); 57 | 58 | # Sumstats and alerts in notice 59 | redef enum Notice::Type += { 60 | ## Indicates that a host performing Unix Command injection 61 | ## attacks was detected. 62 | UnixCommand_Attacker, 63 | ## Indicates that a host was seen to have Unix Command injection 64 | ## attacks against it. This is tracked by IP address as opposed to 65 | ## hostname. 66 | UnixCommand_Victim, 67 | }; 68 | 69 | ## Defines the threshold that determines if a unix command injection 70 | ## attack is ongoing based on the number of requests that appear to be 71 | ## unix command injection attacks. 72 | const unix_requests_threshold: double = 2.0 &redef; 73 | 74 | ## Interval at which to watch for the 75 | ## :bro:id:`UnixCommand::unix_requests_threshold` variable to be crossed. 76 | ## At the end of each interval the counter is reset. 77 | const unix_requests_interval = 1min &redef; 78 | 79 | const mozilla_internal_space: subnet = 10.0.0.0/8 &redef; 80 | const mozilla_public_space: subnet = 63.245.208.0/20 &redef; 81 | # From https://github.com/mozilla/nmap-differential-scan/blob/master/targets.txt 82 | const mozilla_pek1_office_space: subnet = 223.202.6.1/27 &redef; 83 | 84 | const ignore_host_resp: set[addr] = { } &redef; 85 | const ignore_host_orig: set[addr] = { } &redef; 86 | 87 | type Sig: record { 88 | regex: pattern; 89 | name: string; 90 | }; 91 | 92 | type SigVec: vector of Sig; 93 | 94 | # Please add signatures at the END of this list and to preserve rule ids 95 | # (?i:javascriptt) for case-insensitive is not working atm, so we need to do it old school :S 96 | # see https://bro-tracker.atlassian.net/browse/BIT-26 97 | global sigs = SigVec( 98 | [$regex=/.*\.\.\/\.\.\/.*/, 99 | $name="unix_arcsight-1"], 100 | [$regex=/.*\/etc\/shadow.*/, 101 | $name="unix_arcsight-2"], 102 | [$regex=/.*\/etc\/passwd.*/, 103 | $name="unix_arcsight-3"] 104 | ); 105 | } 106 | 107 | #function subn_norm(key: SumStats::Key): SumStats::Key { 108 | # return [$str=cat(mask_addr(key$host, 24))]; 109 | #} 110 | 111 | event bro_init() &priority=5 112 | { 113 | # Create unix_command.log 114 | Log::create_stream(UnixCommand::LOG, [$columns=Info, $ev=log_unix]); 115 | 116 | # Add filters to the metrics so that the metrics framework knows how to 117 | # determine when it looks like an actual attack and how to respond when 118 | # thresholds are crossed. 119 | # local r1: SumStats::Reducer = [$stream="http.unix.attacker", $apply=set(SumStats::SUM), $normalize_key=subn_norm]; 120 | # SumStats::create([$name="unix-attackers", 121 | # $epoch=unix_requests_interval, 122 | # $reducers=set(r1), 123 | # $threshold_val(key: SumStats::Key, result: SumStats::Result) = { 124 | # return result["http.unix.attacker"]$sum; 125 | # }, 126 | # $threshold=unix_requests_threshold, 127 | # $threshold_crossed(key: SumStats::Key, result: SumStats::Result) = { 128 | # NOTICE([$note=UnixCommand_Attacker, 129 | # $msg=fmt("Unix command attack from attack subnet %s", key$str), 130 | # $sub=fmt("%.0f requests in %s", result["http.unix.attacker"]$sum, unix_requests_interval), 131 | # $n=to_count(fmt("%.0f", result["http.unix.attacker"]$sum)) 132 | # ]); 133 | # }]); 134 | # local r2: SumStats::Reducer = [$stream="http.unix.victim", $apply=set(SumStats::SUM), $normalize_key=subn_norm]; 135 | # SumStats::create([$name="unix-victims", 136 | # $epoch=unix_requests_interval, 137 | # $reducers=set(r2), 138 | # $threshold_val(key: SumStats::Key, result: SumStats::Result) = { 139 | # return result["http.unix.victim"]$sum; 140 | # }, 141 | # $threshold=unix_requests_threshold, 142 | # $threshold_crossed(key: SumStats::Key, result: SumStats::Result) = { 143 | # NOTICE([$note=UnixCommand_Victim, 144 | # $msg=fmt("Unix command attack to victim subnet %s", key$str), 145 | # $sub=fmt("%.0f requests in %s", result["http.unix.victim"]$sum, unix_requests_interval), 146 | # $n=to_count(fmt("%.0f", result["http.unix.victim"]$sum)) 147 | # ]); 148 | # }]); 149 | } 150 | 151 | # Make sure we have all the http info before looking for unix commands 152 | event http_message_done(c: connection, is_orig: bool, stat: http_message_stat) 153 | { 154 | # only conns we want 155 | local ports_ext: set[port] = { 80/tcp }; 156 | local ports_int: set[port] = { 80/tcp, 81/tcp, 443/tcp }; 157 | 158 | if (((c$id$resp_h in mozilla_internal_space) && (c$id$resp_p in ports_int)) || ((c$id$resp_h in mozilla_public_space) && (c$id$resp_p in ports_ext)) && (c$id$resp_h !in ignore_host_resp) && (c$id$orig_h !in ignore_host_orig)) { 159 | if (c$http?$cluster_client_ip && to_addr(c$http$cluster_client_ip) !in mozilla_pek1_office_space) { 160 | for(tid in sigs) { 161 | if(sigs[tid]$regex in c$http$uri) { 162 | local tags: set[Tags]; 163 | 164 | add tags[SIG_ARCSIGHT]; 165 | add c$http$tags[UNIX_COMMAND_SIG_ARCSIGHT]; 166 | 167 | local rec: UnixCommand::Info = [ 168 | $ts=network_time(), 169 | $uid=c$uid, 170 | $id=c$id, 171 | $cluster_client_ip=c$http$cluster_client_ip, 172 | $status_code=c$http$status_code, 173 | $host=c$http$host, 174 | $uri=c$http$uri, 175 | $sig_id=sigs[tid]$name, 176 | $tags=tags 177 | ]; 178 | 179 | Log::write(UnixCommand::LOG, rec); 180 | 181 | # SumStats::observe("http.unix.attacker", 182 | # [$host=to_addr(c$http$cluster_client_ip)], 183 | # []); 184 | # SumStats::observe("http.unix.victim", 185 | # [$host=c$conn$id$resp_h], 186 | # []); 187 | break; 188 | } 189 | } 190 | } 191 | } 192 | } 193 | 194 | -------------------------------------------------------------------------------- /unusual_http_methods.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Software distributed under the License is distributed on an "AS IS" basis, 6 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 7 | # for the specific language governing rights and limitations under the 8 | # License. 9 | # 10 | # The Initial Developer of the Original Code is 11 | # Mozilla Corporation 12 | # Portions created by the Initial Developer are Copyright (C) 2014 13 | # the Initial Developer. All Rights Reserved. 14 | # 15 | # Contributor(s): 16 | # Michal Purzynski mpurzynski@mozilla.com 17 | 18 | @load base/frameworks/notice 19 | @load base/protocols/http 20 | 21 | module MozillaUnusualHTTP; 22 | 23 | export { 24 | redef enum Notice::Type += { 25 | Interesting_HTTP_Method_Success, 26 | Interesting_HTTP_Method_Fail, 27 | }; 28 | 29 | redef enum HTTP::Tags += { 30 | HTTP_BAD_METHOD_OK, 31 | HTTP_BAD_METHOD_FAIL, 32 | }; 33 | 34 | global whitelist_hosts_methods: table[addr, string] of set[subnet] = table() &redef; 35 | 36 | const suspicious_http_methods: set[string] = { 37 | "DELETE", "TRACE", "CONNECT", 38 | "PROPPATCH", "MKCOL", "SEARCH", 39 | "COPY", "MOVE", "LOCK", "UNLOCK", 40 | "POLL", "REPORT", "SUBSCRIBE", "BMOVE" 41 | } &redef; 42 | 43 | const monitor_ip_spaces: set[subnet] &redef; 44 | const monitor_ports: set[port] &redef; 45 | const ignore_hosts_orig: set[subnet] &redef; 46 | const ignore_hosts_resp: set[subnet] &redef; 47 | } 48 | 49 | event http_reply(c: connection, version: string, code: count, reason: string) 50 | { 51 | 52 | local cluster_client_ip: addr; 53 | local http_host: string; 54 | 55 | if ( ! c?$http ) 56 | return; 57 | if ( ! c$http?$method ) 58 | return; 59 | if ( c$http?$host ) 60 | http_host = c$http$host; 61 | else 62 | http_host = "NONE"; 63 | if ( c$id$resp_h !in monitor_ip_spaces ) 64 | return; 65 | if ( c$id$resp_p !in monitor_ports ) 66 | return; 67 | if ( c$id$resp_h in ignore_hosts_resp ) 68 | return; 69 | if ( c$id$orig_h in ignore_hosts_orig ) 70 | return; 71 | if ( ! c$http?$cluster_client_ip ) 72 | cluster_client_ip = c$id$orig_h; 73 | else 74 | cluster_client_ip = to_addr(c$http$cluster_client_ip); 75 | if ( ( c$http?$cluster_client_ip ) && ( to_addr(c$http$cluster_client_ip) in ignore_hosts_orig ) ) 76 | return; 77 | if ( c$http$method ! in suspicious_http_methods ) 78 | return; 79 | 80 | if ( [c$id$resp_h, c$http$method] in whitelist_hosts_methods ) { 81 | if ( c$id$orig_h in whitelist_hosts_methods[c$id$resp_h, c$http$method] ) 82 | return; 83 | if ( cluster_client_ip in whitelist_hosts_methods[c$id$resp_h, c$http$method] ) 84 | return; 85 | } else { 86 | if ( c$http$status_code < 300 ) { 87 | add c$http$tags[HTTP_BAD_METHOD_OK]; 88 | NOTICE([$note=Interesting_HTTP_Method_Success, 89 | $msg=fmt("%s successfully used method %s on %s host %s", cluster_client_ip, c$http$method, c$id$resp_h, http_host), 90 | $uid=c$uid, 91 | $id=c$id, 92 | $identifier=cat(http_host,c$http$method,cluster_client_ip)]); 93 | } else { 94 | add c$http$tags[HTTP_BAD_METHOD_FAIL]; 95 | NOTICE([$note=Interesting_HTTP_Method_Fail, 96 | $msg=fmt("%s failed to used method %s on %s host %s", cluster_client_ip, c$http$method, c$id$resp_h, http_host), 97 | $uid=c$uid, 98 | $id=c$id, 99 | $identifier=cat(http_host,c$http$method,cluster_client_ip)]); 100 | } 101 | } 102 | } 103 | 104 | -------------------------------------------------------------------------------- /validate-certs-cache-intermediates.bro: -------------------------------------------------------------------------------- 1 | ##! Perform full certificate chain validation for SSL certificates. 2 | # Also caches all intermediate certificates encountered so far and use them 3 | # for future validations. 4 | # Johanna Amann, Bro/ICSI - johanna@icir.org 5 | 6 | @load base/frameworks/notice 7 | @load base/protocols/ssl 8 | 9 | module SSL; 10 | 11 | export { 12 | redef enum Notice::Type += { 13 | ## This notice indicates that the result of validating the 14 | ## certificate along with its full certificate chain was 15 | ## invalid. 16 | Invalid_Server_Cert 17 | }; 18 | 19 | redef record Info += { 20 | ## Result of certificate validation for this connection. 21 | validation_status: string &log &optional; 22 | }; 23 | 24 | ## MD5 hash values for recently validated chains along with the 25 | ## validation status are kept in this table to avoid constant 26 | ## validation every time the same certificate chain is seen. 27 | global recently_validated_certs: table[string] of string = table() 28 | &read_expire=5mins &redef; 29 | 30 | ## Event from a worker to the manager that it has encountered a new 31 | ## valid intermediate 32 | global intermediate_add: event(key: string, value: vector of opaque of x509); 33 | 34 | ## Event from the manager to the workers that a new intermediate chain 35 | ## is to be added 36 | global new_intermediate: event(key: string, value: vector of opaque of x509); 37 | } 38 | 39 | global intermediate_cache: table[string] of vector of opaque of x509; 40 | 41 | @if ( Cluster::is_enabled() ) 42 | @load base/frameworks/cluster 43 | redef Cluster::manager2worker_events += /SSL::intermediate_add/; 44 | redef Cluster::worker2manager_events += /SSL::new_intermediate/; 45 | @endif 46 | 47 | 48 | function add_to_cache(key: string, value: vector of opaque of x509) 49 | { 50 | intermediate_cache[key] = value; 51 | @if ( Cluster::is_enabled() ) 52 | event SSL::new_intermediate(key, value); 53 | @endif 54 | } 55 | 56 | @if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER ) 57 | event SSL::intermediate_add(key: string, value: vector of opaque of x509) 58 | { 59 | intermediate_cache[key] = value; 60 | } 61 | @endif 62 | 63 | @if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) 64 | event SSL::new_intermediate(key: string, value: vector of opaque of x509) 65 | { 66 | if ( key in intermediate_cache ) 67 | return; 68 | 69 | intermediate_cache[key] = value; 70 | event SSL::intermediate_add(key, value); 71 | } 72 | @endif 73 | 74 | function cache_validate(chain: vector of opaque of x509): string 75 | { 76 | local chain_hash: vector of string = vector(); 77 | 78 | for ( i in chain ) 79 | chain_hash[i] = sha1_hash(x509_get_certificate_string(chain[i])); 80 | 81 | local chain_id = join_string_vec(chain_hash, "."); 82 | 83 | # If we tried this certificate recently, just return the cached result. 84 | if ( chain_id in recently_validated_certs ) 85 | return recently_validated_certs[chain_id]; 86 | 87 | local result = x509_verify(chain, root_certs); 88 | recently_validated_certs[chain_id] = result$result_string; 89 | 90 | # if we have a working chain where we did not store the intermediate certs 91 | # in our cache yet - do so 92 | if ( result$result_string == "ok" && result?$chain_certs && |result$chain_certs| > 2 ) 93 | { 94 | local result_chain = result$chain_certs; 95 | local icert = x509_parse(result_chain[1]); 96 | if ( icert$subject !in intermediate_cache ) 97 | { 98 | local cachechain: vector of opaque of x509; 99 | for ( i in result_chain ) 100 | { 101 | if ( i >=1 && i<=|result_chain|-2 ) 102 | cachechain[i-1] = result_chain[i]; 103 | } 104 | add_to_cache(icert$subject, cachechain); 105 | } 106 | } 107 | 108 | return result$result_string; 109 | } 110 | 111 | event ssl_established(c: connection) &priority=3 112 | { 113 | # If there aren't any certs we can't very well do certificate validation. 114 | if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| == 0 || 115 | ! c$ssl$cert_chain[0]?$x509 ) 116 | return; 117 | 118 | local intermediate_chain: vector of opaque of x509 = vector(); 119 | local issuer = c$ssl$cert_chain[0]$x509$certificate$issuer; 120 | local result: string; 121 | 122 | # look if we already have a working chain for the issuer of this cert. 123 | # If yes, try this chain first instead of using the chain supplied from 124 | # the server. 125 | if ( issuer in intermediate_cache ) 126 | { 127 | intermediate_chain[0] = c$ssl$cert_chain[0]$x509$handle; 128 | for ( i in intermediate_cache[issuer] ) 129 | intermediate_chain[i+1] = intermediate_cache[issuer][i]; 130 | 131 | result = cache_validate(intermediate_chain); 132 | if ( result == "ok" ) 133 | { 134 | c$ssl$validation_status = result; 135 | return; 136 | } 137 | } 138 | 139 | # validation with known chains failed or there was no fitting intermediate 140 | # in our store. 141 | # Fall back to validating the certificate with the server-supplied chain 142 | local chain: vector of opaque of x509 = vector(); 143 | for ( i in c$ssl$cert_chain ) 144 | { 145 | if ( c$ssl$cert_chain[i]?$x509 ) 146 | chain[i] = c$ssl$cert_chain[i]$x509$handle; 147 | } 148 | 149 | result = cache_validate(chain); 150 | c$ssl$validation_status = result; 151 | 152 | if ( result != "ok" ) 153 | { 154 | local message = fmt("SSL certificate validation failed with (%s)", c$ssl$validation_status); 155 | NOTICE([$note=Invalid_Server_Cert, $msg=message, 156 | $sub=c$ssl$subject, $conn=c, 157 | $identifier=cat(c$id$resp_h,c$id$resp_p,c$ssl$validation_status)]); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /verify_wpad.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Software distributed under the License is distributed on an "AS IS" basis, 6 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 7 | # for the specific language governing rights and limitations under the 8 | # License. 9 | # 10 | # The Initial Developer of the Original Code is 11 | # Mozilla Corporation 12 | # Portions created by the Initial Developer are Copyright (C) 2014 13 | # the Initial Developer. All Rights Reserved. 14 | # 15 | # Contributor(s): 16 | # Michal Purzynski mpurzynski@mozilla.com 17 | # 18 | # TODO: application/x-ns-proxy-autoconfig 19 | # "site-local" option 252 ("auto-proxy-config") 20 | # maybe check for HTTP download from DNS answers only 21 | # proxy.pac 22 | 23 | @load policy/frameworks/files/hash-all-files 24 | @load base/frameworks/notice 25 | @load base/protocols/http 26 | @load base/protocols/dns 27 | 28 | module MozillaVerifyWPAD; 29 | 30 | export { 31 | redef enum Notice::Type += { 32 | New_WPAD_In_DNS, 33 | New_WPAD_In_HTTP, 34 | }; 35 | 36 | global whitelist_hosts: set[string] &redef; 37 | global wpad_dat_sum: set[string] &redef; 38 | const ignore_subnets: set[subnet] &redef; 39 | } 40 | 41 | event file_hash(f: fa_file, kind: string, hash: string) 42 | { 43 | local is_whitelisted: bool; 44 | local error_code: int; 45 | local lastconn: connection; 46 | 47 | 48 | for ( cid in f$conns ) { 49 | if ( cid$resp_h in ignore_subnets ) 50 | return; 51 | if ( cid$orig_h in ignore_subnets ) 52 | return; 53 | if ( ! f$conns[cid]?$http ) 54 | return; 55 | if ( ! f$conns[cid]$http?$method ) 56 | return; 57 | lastconn = f$conns[cid]; 58 | if ( f$conns[cid]$http$method != "GET" ) 59 | return; 60 | else { 61 | if ( /^\/wpad\.dat/ ! in f$conns[cid]$http$uri ) { 62 | return; 63 | } 64 | } 65 | } 66 | 67 | for ( cid in f$conns ) { 68 | if ( cat(cid$resp_h) in whitelist_hosts ) { 69 | is_whitelisted = T; 70 | error_code = 0; 71 | } else { 72 | is_whitelisted = F; 73 | error_code = 1; 74 | break; 75 | } 76 | } 77 | 78 | if ( is_whitelisted == T ) { 79 | if ( hash ! in wpad_dat_sum ) 80 | error_code = 2; 81 | } 82 | 83 | local message: string; 84 | if ( error_code == 0 ) 85 | return; 86 | else if ( error_code == 1 ) 87 | message = "Unknown HTTP server"; 88 | else if ( error_code == 2 ) 89 | message = "WPAD checksum fail"; 90 | else 91 | message = "Something went wrong"; 92 | 93 | NOTICE([$note=New_WPAD_In_HTTP, 94 | $msg=fmt("Unauthorized WPAD detected"), 95 | $sub=message, 96 | $uid=lastconn$uid, 97 | $id=lastconn$id, 98 | $identifier=cat(lastconn$uid)]); 99 | } 100 | 101 | event DNS::log_dns(rec: DNS::Info) 102 | { 103 | local is_whitelisted: bool; 104 | 105 | if ( ! rec?$qtype_name ) 106 | return; 107 | if ( ! rec?$query ) 108 | return; 109 | if ( ! rec?$answers ) 110 | return; 111 | 112 | if ( /CNAME|^A|AAAA/ ! in rec$qtype_name ) 113 | return; 114 | # put your domain name in here 115 | if ( /^wpad\..*mozilla\.(net|org|com)/ ! in rec$query ) 116 | return; 117 | 118 | for ( vecidx in rec$answers ) { 119 | if ( rec$answers[vecidx] in whitelist_hosts ) 120 | is_whitelisted = T; 121 | else { 122 | is_whitelisted = F; 123 | break; 124 | } 125 | } 126 | 127 | if ( is_whitelisted == T ) 128 | return; 129 | else { 130 | NOTICE([$note=New_WPAD_In_DNS, 131 | $msg=fmt("Unauthorized WPAD detected"), 132 | $sub=fmt("Host %s sends WPAD answers with unknown proxies", cat(rec$id$resp_h)), 133 | $uid=rec$uid, 134 | $id=rec$id, 135 | $identifier=cat(rec$uid)]); 136 | } 137 | } 138 | 139 | -------------------------------------------------------------------------------- /weak-keys-mozilla.bro: -------------------------------------------------------------------------------- 1 | ##! Generate notices when SSL/TLS connections use certificates or DH parameters 2 | ##! that have potentially unsafe key lengths. 3 | # Original author lost in the battle - either Johanna Amann, Bro/ICSI - johanna@icir.org or Michal Purzynski 4 | 5 | @load base/protocols/ssl 6 | @load base/frameworks/notice 7 | @load base/utils/directions-and-hosts 8 | 9 | module SSL; 10 | 11 | export { 12 | redef enum Notice::Type += { 13 | ## Indicates that a server is using a potentially unsafe key. 14 | Weak_Key, 15 | }; 16 | 17 | ## The category of hosts you would like to be notified about which have 18 | ## certificates that are going to be expiring soon. By default, these 19 | ## notices will be suppressed by the notice framework for 1 day after a particular 20 | ## certificate has had a notice generated. Choices are: LOCAL_HOSTS, REMOTE_HOSTS, 21 | ## ALL_HOSTS, NO_HOSTS 22 | const notify_weak_keys = LOCAL_HOSTS &redef; 23 | 24 | ## The minimal key length in bits that is considered to be safe. Any shorter 25 | ## (non-EC) key lengths will trigger the notice. 26 | const notify_minimal_key_length = 1024 &redef; 27 | ## A separate key length for DH because some Java versions cannot use 2048 bits 28 | const notify_minimal_dh_key_length = 1024 &redef; 29 | 30 | ## Warn if the DH key length is smaller than the certificate key length. This is 31 | ## potentially unsafe because it gives a wrong impression of safety due to the 32 | ## certificate key length. However, it is very common and cannot be avoided in some 33 | ## settings (e.g. with old jave clients). 34 | const notify_dh_length_shorter_cert_length = T &redef; 35 | 36 | const ports_ignore: set[port] = {5666/tcp} &redef; 37 | } 38 | 39 | # We check key lengths only for DSA or RSA certificates. For others, we do 40 | # not know what is safe (e.g. EC is safe even with very short key lengths). 41 | event ssl_established(c: connection) &priority=3 42 | { 43 | # If there are no certificates or we are not interested in the server, just return. 44 | if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| == 0 || 45 | ! addr_matches_host(c$id$resp_h, notify_weak_keys) || 46 | ! c$ssl$cert_chain[0]?$x509 ) 47 | return; 48 | 49 | local fuid = c$ssl$cert_chain_fuids[0]; 50 | local cert = c$ssl$cert_chain[0]$x509$certificate; 51 | 52 | if ( !cert?$key_type || !cert?$key_length ) 53 | return; 54 | 55 | if ( cert$key_type != "dsa" && cert$key_type != "rsa" ) 56 | return; 57 | 58 | if (cert?$curve) 59 | return; 60 | 61 | if (c$id$resp_p in ports_ignore) 62 | return; 63 | 64 | local key_length = cert$key_length; 65 | 66 | if ( key_length < notify_minimal_key_length ) 67 | NOTICE([$note=Weak_Key, 68 | $msg=fmt("Host uses weak certificate with %d bit key", key_length), 69 | $conn=c, $suppress_for=1day, 70 | $identifier=cat(c$id$orig_h, c$id$orig_p, key_length) 71 | ]); 72 | } 73 | 74 | event ssl_dh_server_params(c: connection, p: string, q: string, Ys: string) &priority=3 75 | { 76 | if ( ! addr_matches_host(c$id$resp_h, notify_weak_keys) ) 77 | return; 78 | 79 | local key_length = |p| * 8; # key length in bits 80 | 81 | if (c$id$resp_p in ports_ignore) 82 | return; 83 | 84 | if ( key_length < notify_minimal_dh_key_length ) 85 | NOTICE([$note=Weak_Key, 86 | $msg=fmt("Host uses weak DH parameters with %d key bits", key_length), 87 | $conn=c, $suppress_for=1day, 88 | $identifier=cat(c$id$orig_h, c$id$orig_p, key_length) 89 | ]); 90 | 91 | if ( notify_dh_length_shorter_cert_length && 92 | c?$ssl && c$ssl?$cert_chain && |c$ssl$cert_chain| > 0 && c$ssl$cert_chain[0]?$x509 && 93 | c$ssl$cert_chain[0]$x509?$certificate && c$ssl$cert_chain[0]$x509$certificate?$key_type && 94 | (c$ssl$cert_chain[0]$x509$certificate$key_type == "rsa" || 95 | c$ssl$cert_chain[0]$x509$certificate$key_type == "dsa" )) 96 | { 97 | if ( c$ssl$cert_chain[0]$x509$certificate?$key_length && 98 | c$ssl$cert_chain[0]$x509$certificate$key_length > key_length ) 99 | NOTICE([$note=Weak_Key, 100 | $msg=fmt("DH key length of %d bits is smaller certificate key length of %d bits", 101 | key_length, c$ssl$cert_chain[0]$x509$certificate$key_length), 102 | $conn=c, $suppress_for=1day, 103 | $identifier=cat(c$id$orig_h, c$id$orig_p) 104 | ]); 105 | } 106 | } 107 | 108 | -------------------------------------------------------------------------------- /weak_ciphers.bro: -------------------------------------------------------------------------------- 1 | #! SslWeakCiphers give percentage of SSL weak ciphers used (< 2048 bits key except for ECDHE) 2 | #! Depends on protocols/ssl/client_ciphers.bro 3 | # Original author lost in the battle - either Johanna Amann, Bro/ICSI - johanna@icir.org or Michal Purzynski 4 | 5 | @load base/protocols/ssl 6 | 7 | module SslWeakCiphers; 8 | 9 | export { 10 | redef enum Log::ID += { LOG }; 11 | 12 | type Info: record { 13 | ## Timestamp when the log line was finished and written. 14 | ts: time &log; 15 | ## Time interval that the log line covers. 16 | ts_delta: interval &log; 17 | ## Percentage of weak SSL ciphers used 18 | percent_weak_ciphers: double &log; 19 | }; 20 | 21 | ## The frequency of logging the stats collected by this script. 22 | const break_interval = 15mins &redef; 23 | 24 | ## Monitored hosts for weak SSL ciphers 25 | const cert_tracking = ALL_HOSTS &redef; 26 | } 27 | 28 | redef record SSL::Info += { 29 | ## Ciphers available for the client 30 | weak_cipher: bool &log &optional; 31 | }; 32 | 33 | event bro_init() &priority=3 34 | { 35 | Log::create_stream(SslWeakCiphers::LOG, [$columns=Info]); 36 | 37 | local r1: SumStats::Reducer = [$stream="ssl_weak_ciphers.weak_hits", $apply=set(SumStats::UNIQUE)]; 38 | local r2: SumStats::Reducer = [$stream="ssl_weak_ciphers.ssl_hits", $apply=set(SumStats::UNIQUE)]; 39 | SumStats::create([$name="ssl_weak_ciphers-metrics", 40 | $epoch=break_interval, 41 | $reducers=set(r1,r2), 42 | $epoch_result(ts: time, key: SumStats::Key, result: SumStats::Result) = 43 | { 44 | local l: Info; 45 | l$ts = network_time(); 46 | l$ts_delta = break_interval; 47 | if ("ssl_weak_ciphers.weak_hits" in result && "ssl_weak_ciphers.ssl_hits" in result) 48 | l$percent_weak_ciphers = result["ssl_weak_ciphers.weak_hits"]$num * 100 / result["ssl_weak_ciphers.ssl_hits"]$num; 49 | else 50 | l$percent_weak_ciphers = 0; 51 | Log::write(LOG, l); 52 | }]); 53 | } 54 | 55 | event ssl_established(c: connection) 56 | { 57 | # Only look at monitored hosts 58 | if (addr_matches_host(c$id$resp_h, cert_tracking)) 59 | { 60 | local strong_key = F; 61 | c$ssl$weak_cipher = F; 62 | SumStats::observe("ssl_weak_ciphers.ssl_hits", [], []); 63 | # If the cipher key used is weak 64 | if ( !(/256/ in c$ssl$cipher) && !(/ECDHE/ in c$ssl$cipher) ) 65 | { 66 | # Does the client browser support 256 bytes SSL/TLS cipher key? 67 | local client_browser_weak = F; 68 | for(cipher in c$ssl$available_ciphers_client) 69 | { 70 | if(/256/ in cipher || /ECDHE/ in cipher) 71 | { 72 | client_browser_weak = T; 73 | break; 74 | } 75 | } 76 | # If the server does not support strong SSL/TLS cipher but client does 77 | if (!client_browser_weak) 78 | { 79 | SumStats::observe("ssl_weak_ciphers.weak_hits", [], []); 80 | c$ssl$weak_cipher = T; 81 | } 82 | } 83 | } 84 | } 85 | 86 | -------------------------------------------------------------------------------- /weak_protocols.bro: -------------------------------------------------------------------------------- 1 | #! SslWeakProtocols give percentage of SSL weak protocols used (<= SSL2) 2 | # Original author lost in the battle - either Johanna Amann, Bro/ICSI - johanna@icir.org or Michal Purzynski 3 | 4 | @load base/protocols/ssl 5 | @load base/frameworks/sumstats 6 | 7 | module SslWeakProtocols; 8 | 9 | export { 10 | redef enum Log::ID += { LOG }; 11 | 12 | type Info: record { 13 | ## Timestamp when the log line was finished and written. 14 | ts: time &log; 15 | ## Time interval that the log line covers. 16 | ts_delta: interval &log; 17 | ## Percentage of weak SSL protocols used 18 | percent_weak_protocols: double &log; 19 | }; 20 | 21 | ## The frequency of logging the stats collected by this script. 22 | const break_interval = 15mins &redef; 23 | 24 | ## Monitored hosts for weak SSL protocols 25 | const cert_tracking = ALL_HOSTS &redef; 26 | } 27 | 28 | event bro_init() &priority=3 29 | { 30 | Log::create_stream(SslWeakProtocols::LOG, [$columns=Info]); 31 | 32 | local r1: SumStats::Reducer = [$stream="ssl_weak_protocols.weak_hits", $apply=set(SumStats::UNIQUE)]; 33 | local r2: SumStats::Reducer = [$stream="ssl_weak_protocols.ssl_hits", $apply=set(SumStats::UNIQUE)]; 34 | SumStats::create([$name="ssl_weak_protocols-metrics", 35 | $epoch=break_interval, 36 | $reducers=set(r1,r2), 37 | $epoch_result(ts: time, key: SumStats::Key, result: SumStats::Result) = 38 | { 39 | local l: Info; 40 | l$ts = network_time(); 41 | l$ts_delta = break_interval; 42 | if ("ssl_weak_protocols.ssl_hits" in result && "ssl_weak_protocols.weak_hits" in result) 43 | l$percent_weak_protocols = result["ssl_weak_protocols.weak_hits"]$num * 100 / result["ssl_weak_protocols.ssl_hits"]$num; 44 | else 45 | l$percent_weak_protocols = 0; 46 | Log::write(LOG, l); 47 | }]); 48 | } 49 | 50 | event ssl_established(c: connection) 51 | { 52 | # Only look at monitored hosts 53 | if (addr_matches_host(c$id$resp_h, cert_tracking)) 54 | { 55 | SumStats::observe("ssl_weak_protocols.ssl_hits", [], []); 56 | # If the protocol used is weak 57 | if ( /SSLv2/ in c$ssl$version ) 58 | { 59 | SumStats::observe("ssl_weak_protocols.weak_hits", [], []); 60 | } 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /whitelist_scan_detection.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | # Script to read in a list of IP addresses that will be whitelisted from scan detection (ignore as a source of a scan). 9 | # 10 | 11 | module Scan; 12 | 13 | global whitelist_scan_port: set[port] = {5223/udp, 5223/tcp, 7000/udp, 7000/tcp, 4001/tcp, 3283/tcp, 11375/tcp, 4242/tcp, 13040/tcp, 6850/tcp, 10380/tcp, 1900/tcp } &redef; 14 | 15 | hook scan_policy(scanner: addr, victim: addr, scanned_port: port) 16 | { 17 | if (( victim in whitelist_scan_ip) || ( scanner in whitelist_scan_ip ) || ( scanned_port in whitelist_scan_port)) 18 | break; 19 | } 20 | -------------------------------------------------------------------------------- /whitelist_scan_detection_input.bro: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Contributor(s): 6 | # Michal Purzynski mpurzynski@mozilla.com 7 | # 8 | # Script to read in a list of IP addresses that will be whitelisted from scan detection (ignore as a source of a scan). 9 | # 10 | 11 | module Scan; 12 | 13 | type Idx: record { 14 | whitelist_ip: subnet; 15 | }; 16 | 17 | global whitelist_scan_ip: set[subnet] = {} &synchronized; 18 | 19 | event bro_init() 20 | { 21 | Input::add_table([$source="/etc/bro/scripts/brozilla/whitelist_scan_ip.txt", 22 | $name="whitelist_scan_ip", 23 | $idx=Idx, 24 | $destination=whitelist_scan_ip, 25 | $mode=Input::REREAD]); 26 | } 27 | -------------------------------------------------------------------------------- /whitelist_scan_ip.txt: -------------------------------------------------------------------------------- 1 | #fields whitelist_ip 2 | 239.255.255.250/32 3 | 255.255.255.255/32 4 | 10.11.22.0/24 5 | 10.22.33.21/32 6 | 169.254.0.0/16 7 | --------------------------------------------------------------------------------