├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING-ARCHIVED.md ├── LICENSE.txt ├── README.md ├── SECURITY.md ├── __load__.zeek ├── app-exfil-conn.zeek ├── main.zeek └── threaded_exfil.zeek /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Comment line immediately above ownership line is reserved for related other information. Please be careful while editing. 2 | #ECCN:Open Source 3 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Salesforce Open Source Community Code of Conduct 2 | 3 | ## About the Code of Conduct 4 | 5 | Equality is a core value at Salesforce. We believe a diverse and inclusive 6 | community fosters innovation and creativity, and are committed to building a 7 | culture where everyone feels included. 8 | 9 | Salesforce open-source projects are committed to providing a friendly, safe, and 10 | welcoming environment for all, regardless of gender identity and expression, 11 | sexual orientation, disability, physical appearance, body size, ethnicity, nationality, 12 | race, age, religion, level of experience, education, socioeconomic status, or 13 | other similar personal characteristics. 14 | 15 | The goal of this code of conduct is to specify a baseline standard of behavior so 16 | that people with different social values and communication styles can work 17 | together effectively, productively, and respectfully in our open source community. 18 | It also establishes a mechanism for reporting issues and resolving conflicts. 19 | 20 | All questions and reports of abusive, harassing, or otherwise unacceptable behavior 21 | in a Salesforce open-source project may be reported by contacting the Salesforce 22 | Open Source Conduct Committee at ossconduct@salesforce.com. 23 | 24 | ## Our Pledge 25 | 26 | In the interest of fostering an open and welcoming environment, we as 27 | contributors and maintainers pledge to making participation in our project and 28 | our community a harassment-free experience for everyone, regardless of gender 29 | identity and expression, sexual orientation, disability, physical appearance, 30 | body size, ethnicity, nationality, race, age, religion, level of experience, education, 31 | socioeconomic status, or other similar personal characteristics. 32 | 33 | ## Our Standards 34 | 35 | Examples of behavior that contributes to creating a positive environment 36 | include: 37 | 38 | * Using welcoming and inclusive language 39 | * Being respectful of differing viewpoints and experiences 40 | * Gracefully accepting constructive criticism 41 | * Focusing on what is best for the community 42 | * Showing empathy toward other community members 43 | 44 | Examples of unacceptable behavior by participants include: 45 | 46 | * The use of sexualized language or imagery and unwelcome sexual attention or 47 | advances 48 | * Personal attacks, insulting/derogatory comments, or trolling 49 | * Public or private harassment 50 | * Publishing, or threatening to publish, others' private information—such as 51 | a physical or electronic address—without explicit permission 52 | * Other conduct which could reasonably be considered inappropriate in a 53 | professional setting 54 | * Advocating for or encouraging any of the above behaviors 55 | 56 | ## Our Responsibilities 57 | 58 | Project maintainers are responsible for clarifying the standards of acceptable 59 | behavior and are expected to take appropriate and fair corrective action in 60 | response to any instances of unacceptable behavior. 61 | 62 | Project maintainers have the right and responsibility to remove, edit, or 63 | reject comments, commits, code, wiki edits, issues, and other contributions 64 | that are not aligned with this Code of Conduct, or to ban temporarily or 65 | permanently any contributor for other behaviors that they deem inappropriate, 66 | threatening, offensive, or harmful. 67 | 68 | ## Scope 69 | 70 | This Code of Conduct applies both within project spaces and in public spaces 71 | when an individual is representing the project or its community. Examples of 72 | representing a project or community include using an official project email 73 | address, posting via an official social media account, or acting as an appointed 74 | representative at an online or offline event. Representation of a project may be 75 | further defined and clarified by project maintainers. 76 | 77 | ## Enforcement 78 | 79 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 80 | reported by contacting the Salesforce Open Source Conduct Committee 81 | at ossconduct@salesforce.com. All complaints will be reviewed and investigated 82 | and will result in a response that is deemed necessary and appropriate to the 83 | circumstances. The committee is obligated to maintain confidentiality with 84 | regard to the reporter of an incident. Further details of specific enforcement 85 | policies may be posted separately. 86 | 87 | Project maintainers who do not follow or enforce the Code of Conduct in good 88 | faith may face temporary or permanent repercussions as determined by other 89 | members of the project's leadership and the Salesforce Open Source Conduct 90 | Committee. 91 | 92 | ## Attribution 93 | 94 | This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant-home], 95 | version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html. 96 | It includes adaptions and additions from [Go Community Code of Conduct][golang-coc], 97 | [CNCF Code of Conduct][cncf-coc], and [Microsoft Open Source Code of Conduct][microsoft-coc]. 98 | 99 | This Code of Conduct is licensed under the [Creative Commons Attribution 3.0 License][cc-by-3-us]. 100 | 101 | [contributor-covenant-home]: https://www.contributor-covenant.org (https://www.contributor-covenant.org/) 102 | [golang-coc]: https://golang.org/conduct 103 | [cncf-coc]: https://github.com/cncf/foundation/blob/master/code-of-conduct.md 104 | [microsoft-coc]: https://opensource.microsoft.com/codeofconduct/ 105 | [cc-by-3-us]: https://creativecommons.org/licenses/by/3.0/us/ 106 | -------------------------------------------------------------------------------- /CONTRIBUTING-ARCHIVED.md: -------------------------------------------------------------------------------- 1 | # ARCHIVED 2 | 3 | This project is `Archived` and is no longer actively maintained; 4 | We are not accepting contributions or Pull Requests. 5 | 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Salesforce.com, Inc. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 11 | 12 | 3. Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multithreaded Exfiltration Detection 2 | 3 | An expansion of the exfil module designed by [Reservior Labs](https://github.com/reservoirlabs/bro-scripts/tree/master/exfil-detection-framework), these improved Zeek (formerly Bro) scripts are capable of detecting exfil that leverages multithreading. By aggregating the numerous data streams created by multithreading, exfiltration attempts that previously evaded network detection through many small data streams will now be noticed by Zeek. 4 | 5 | 6 | ## Detection Logic 7 | To understand the basic logic of the main script, check out the [original exfil module README](https://github.com/reservoirlabs/bro-scripts/blob/master/exfil-detection-framework/README.md). 8 | 9 | The original exfil module assumed exfiltration occurs over a single, large TCP connection, but exfiltration can occur over many smaller connections via multithreading. This repo's scripts detect the latter scenario by aggregating the byte counts of multiple connections to determine if exfiltration is occurring over many different bytestreams. If multiple outbound data streams with the same source IP, destination IP, and destination port appear in quick succession, the data streams their byte counts are aggregated and treated as a possible exfiltration attempt. If the aggregated byte count surpasses a given byte threshold, an alert is written to the Zeek notice log. There are two main variables that can be tuned to fit detection needs: `thread_check_interval` and `file_thresh`. The `thread_check_interval` defines a period of time to allow other connections to populate that incidicate multithreaded exfil. The `file_thresh` defines the minimum number of bytes that must be exfiltrated before a notice is written. 10 | 11 | ## Illustrative Diagram Example: 12 | In this example, 15 MB of data is being exfiltrated over 5 unique data streams that were created because the task was split into 5 threads. 13 | ### Multithreaded Exfil Evades Traditional Detection Logic 14 | ``` | 15 | | 16 | | 17 | |xxxxxxxxxxxxxxxxxxxxxxxx 6 MB byte exfil threshold 18 | | * - Data stream from thread 1: 3 MB 19 | bytes | * % # ^ $ % - Data stream from thread 2: 3 MB 20 | | / / / / / # - Data stream from thread 3: 3 MB 21 | | / / / / / ^ - Data stream from thread 4: 3 MB 22 | |/_/__/___/___/_________ $ - Data stream from thread 5: 3 MB 23 | time 24 | ``` 25 | Even though the total number of exfiltrated bytes has exceeded the threshold, because each thread consituted its own data stream, none of them individually reach the threshold byte number and no alerts are raised. 26 | 27 | ### Detecting Multithreaded Exfil by Aggregation 28 | ``` | / 29 | | / # 30 | | / 31 | |xxxxx/xxxxxxxxxxxxxxxxx 6 MB byte exfil threshold 32 | | / % * - Data stream from thread 1: 3 MB 33 | bytes | / % - Data stream from thread 2: 3 MB 34 | | / # - Data stream from thread 3: 3 MB 35 | | / * 36 | |/______________________ 37 | time 38 | ``` 39 | Using the Zeek scripts in this repo, the total number of bytes from each of the five threads (three pictured in the diagram) is aggregated and a notice is raised because the byte threshold is crossed. 40 | 41 | ## Script Explanations 42 | 1. **main.bro** - The primary script that drives the Exfil Framework. 43 | 2. **app-exfil-conn.bro** - The script that attaches the Exfil Framework to connections. You will want to edit the redefs exported by this script to choose which connections get monitored for file uploads. **Note:** Start small. If this script is attached to a lot of connections, it may negatively impact the amount of traffic your Zeek sensor can process. 44 | 3. **threaded_exfil.zeek** - Adds events that are necessary for aggregating threads. 45 | 4. **__load__.zeek** - Loads all the Exfil Framework scripts. You will not need to edit this file. 46 | 47 | 48 | ## Dependencies 49 | * [JA3](https://github.com/salesforce/ja3) - The exfiltration script refers to the popular JA3 Zeek script, so [ja3.zeek](https://github.com/salesforce/ja3/blob/master/zeek/ja3.zeek) will be required. 50 | 51 | ## Credits 52 | * [Manju Lalwani](https://www.linkedin.com/in/manjulalwani/) - Research & Project Lead 53 | * [Caleb Yu](https://www.linkedin.com/in/caleb-yu/) - Zeek script programmer and tester 54 | * [Reservior Labs](https://www.reservoir.com/) - Exfil framework baseline 55 | * Salesforce threat detection team 56 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Security 2 | 3 | Please report any security issue to [security@salesforce.com](mailto:security@salesforce.com) 4 | as soon as it is discovered. This library limits its runtime dependencies in 5 | order to reduce the total cost of ownership as much as can be, but all consumers 6 | should remain vigilant and have their security stakeholders review all third-party 7 | products (3PP) like this one and their dependencies. 8 | -------------------------------------------------------------------------------- /__load__.zeek: -------------------------------------------------------------------------------- 1 | @load ./ja3.zeek 2 | @load ./main 3 | @load ./threaded_exfil.zeek 4 | @load ./app-exfil-conn.zeek 5 | -------------------------------------------------------------------------------- /app-exfil-conn.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, salesforce.com, inc. 2 | # All rights reserved. 3 | # Licensed under the BSD 3-Clause license. 4 | # For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause 5 | # 6 | # Contributed by Bob Rotsted @ Reservoir Labs 7 | # 8 | # Small Business Innovation Research (SBIR) Data Rights. 9 | # 10 | # These SBIR data are furnished with SBIR rights under Contract 11 | # No. DE-SC0004400 and DE-SC0006343. For a period of 4 years 12 | # (expiring August, 2018), unless extended in accordance with 13 | # FAR 27.409(h), subject to Section 8 of the SBA SBIR Policy 14 | # Directive of August 6, 2012, after acceptance of all items to 15 | # be delivered under this contract, theGovernment will use these 16 | # data for Government purposes only, and theyshall not be 17 | # disclosed outside the Government (including disclosure for 18 | # procurement purposes) during such period without permission of 19 | # the Contractor, except that, subject to the foregoing use and 20 | # disclosure prohibitions, these data may be disclosed for use 21 | # by support Contractors. After the protection period, the 22 | # Government has a paid-up license to use, and to authorize 23 | # others to use on its behalf, these data for Government 24 | # purposes, but is relieved of all disclosure prohibitions and 25 | # assumes no liability for unauthorized use of these data by 26 | # third parties. This notice shall be affixed to any 27 | # reproductions of these data, in whole or in part. 28 | # 29 | ## Modified for SFDC by John Althouse 30 | # 31 | ## Modified for detecting multithreaded exfiltration by Manju Lalwani and Caleb Yu 32 | # 33 | ##! Watch all TCP,UDP,ICMP flows for Data Exfil 34 | 35 | module Exfil; 36 | 37 | export { 38 | ## Defines which subnets are monitored for data exfiltration 39 | global watched_subnets_conn: set[subnet] = { 40 | 10.0.0.0/8, 41 | 172.16.0.0/12, 42 | 192.168.0.0/16, 43 | } &redef; 44 | 45 | ## Defines whether connections with local destinations should be monitored for data exfiltration 46 | global ignore_local_dest_conn: bool = T &redef; 47 | 48 | ## Defines the thresholds and polling interval for the exfil framework. See main.bro for more details. 49 | global settings_conn: Settings &redef; 50 | } 51 | 52 | event connection_established (c: connection) { 53 | 54 | if (ignore_local_dest_conn == T && (c$id$resp_h in watched_subnets_conn)) 55 | return; 56 | 57 | if (c$id$orig_h !in watched_subnets_conn ) 58 | return; 59 | 60 | Exfil::watch_connection(c , settings_conn); 61 | 62 | } 63 | -------------------------------------------------------------------------------- /main.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, salesforce.com, inc. 2 | # All rights reserved. 3 | # Licensed under the BSD 3-Clause license. 4 | # For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause 5 | 6 | # Contributed by Bob Rotsted @ Reservoir Labs 7 | # 8 | # Small Business Innovation Research (SBIR) Data Rights. 9 | # 10 | # These SBIR data are furnished with SBIR rights under Contract 11 | # No. DE-SC0004400 and DE-SC0006343. For a period of 4 years 12 | # (expiring August, 2018), unless extended in accordance with 13 | # FAR 27.409(h), subject to Section 8 of the SBA SBIR Policy 14 | # Directive of August 6, 2012, after acceptance of all items to 15 | # be delivered under this contract, theGovernment will use these 16 | # data for Government purposes only, and theyshall not be 17 | # disclosed outside the Government (including disclosure for 18 | # procurement purposes) during such period without permission of 19 | # the Contractor, except that, subject to the foregoing use and 20 | # disclosure prohibitions, these data may be disclosed for use 21 | # by support Contractors. After the protection period, the 22 | # Government has a paid-up license to use, and to authorize 23 | # others to use on its behalf, these data for Government 24 | # purposes, but is relieved of all disclosure prohibitions and 25 | # assumes no liability for unauthorized use of these data by 26 | # third parties. This notice shall be affixed to any 27 | # reproductions of these data, in whole or in part. 28 | # 29 | ## Modified to include JA3 by John Althouse 30 | # 31 | ## Modified for detecting multithreaded exfiltration by Manju Lalwani and Caleb Yu 32 | # 33 | ##! This is the base module of the Exfil Framework. The Exfil Framework is a library for the detection of outbound bytestreams. 34 | 35 | module Exfil; 36 | 37 | export { 38 | 39 | redef enum Log::ID += { LOG, DEBUG }; 40 | 41 | ## Define the default Notice::Type for Exfil events 42 | redef enum Notice::Type += { 43 | File_Transfer, 44 | }; 45 | 46 | type Info: record { 47 | 48 | ## Timestamp 49 | ts: time &log; 50 | ## UID 51 | uid: string &log; 52 | ## The connection record 53 | id: conn_id &log; 54 | ## Transport Protocol 55 | proto: transport_proto &log; 56 | ## Service 57 | service: set[string] &log &optional; 58 | ## Host name if cleartext 59 | host: string &log &optional; 60 | ## SSL Server Name 61 | server_name: string &log &optional; 62 | ## Subject of the X.509 certificate 63 | subject: string &log &optional; 64 | ## JA3 SSL Client Fingerprint 65 | ja3: string &log &optional; 66 | ## SSL Certificate Validation Status 67 | validation_status: string &log &optional; 68 | ## How many bytes were sent? 69 | orig_bytes: count &log; 70 | ## what happened? 71 | note: Notice::Type &log; 72 | 73 | }; 74 | 75 | type DebugInfo: record { 76 | 77 | ts: time &log; 78 | id: conn_id &log; 79 | uid: string &log; 80 | byte_rate: double &log; 81 | reported: bool &log; 82 | byte_count: count &log; 83 | 84 | }; 85 | 86 | ## A public data structure for defining thresholds and reporting Settings 87 | type Settings: record { 88 | 89 | ## How often should we poll this connection. A smaller value leads to more accurate detection and file size estimation. 90 | checkup_interval: interval &default=1sec; 91 | ## How soon should we check for another connection with the same source IP, destination IP, and destination port? 92 | thread_checkup_interval: interval &default=5sec; 93 | ## What must the byte rate be to flag it as a transfer. Note: We have found that 2000/bytes per second is a good threshold. If you wish to change 94 | ## the checkup interval or byte_rate_thresh, you may want to increase the byte_rate_thresh in 2000/bytes per second increments. 95 | byte_rate_thresh: count &default=2000; 96 | ## How many bytes constitute a file transfer. 97 | file_thresh: count &default=65536; 98 | ## How many unique connections should be seen to consider the possibility of multithreading. 99 | unique_thread_thresh: count &default=20; 100 | ## Deliver this to the notice framework? 101 | notice: bool &default=F; 102 | ## Define notice type for this transfer 103 | note: Notice::Type &default=Exfil::File_Transfer; 104 | 105 | }; 106 | 107 | ## This turns on the 'debug' log, shows byte rates by connection 108 | global debug: bool = F &redef; 109 | 110 | ## The logging event for the Exfil::Log 111 | global log_exfil: event(rec: Info); 112 | 113 | ## The logging event for the Exfil::Log 114 | global log_debug: event(rec: DebugInfo); 115 | 116 | ## Event associated with addition to state table 117 | global watching_connection: event(c: connection); 118 | 119 | ## Event associated with removal from state table 120 | global forgetting_connection: event(c: connection); 121 | 122 | ## Event associated with the beginning of a heuristically detected transfer 123 | global heuristic_transfer_begin: event(c: connection); 124 | 125 | ## Event associated with the end of a heuristically detected transfer 126 | global heuristic_transfer_end: event(c: connection); 127 | 128 | ## Event assoicated with a flow surpassing the byte threshold defined in file_thresh of Settings 129 | global transfer_above_file_thresh: event(c: connection); 130 | 131 | ## Event assoicated with a flow below the byte threshold defined in file_thresh of Settings 132 | global transfer_below_file_thresh: event(c: connection); 133 | 134 | ## Public interface for Exfil Framework 135 | global watch_connection: function(c: connection, s: Settings); 136 | 137 | ## Checks to see if a set of threads has exfiltrated a file 138 | global thread_check: event(c: connection, s: Settings, b: count); 139 | 140 | ## Aggregates multiple connections that are likely threads of a single process 141 | global add_conn_to_threads: function(c: connection, s: Settings, new_thread: bool); 142 | 143 | } 144 | 145 | ## A private data structure for tracking byte counts and reporting state 146 | type connection_stats: record { 147 | src_ip: addr; 148 | src_p: port; 149 | dst_ip: addr; 150 | dst_p: port; 151 | orig_byte_cnt: count &default=0; 152 | last_byte_cnt: count &default=0; 153 | reported: bool &default=F; 154 | 155 | }; 156 | 157 | type thread_aggregation_stats: record { 158 | thread_cnt: count &default=0; 159 | total_byte_cnt: count &default=0; 160 | timed_byte_cnt: count &default=0; 161 | }; 162 | 163 | ## This is the data structure that holds our tracked sessions 164 | type sessions: table[string] of connection_stats &create_expire=1day; 165 | 166 | ## This is the data strucutre that holds our aggregated threads 167 | type threading_stats: table[string] of thread_aggregation_stats &create_expire=1hr; 168 | 169 | ## This holds all connections tracked by the framework 170 | global tracked_sessions: sessions; 171 | 172 | ## This holds all the aggregated threadeds tracked by the framework 173 | global thread_collection: threading_stats; 174 | 175 | event zeek_init() &priority=5 { 176 | 177 | Log::create_stream(Exfil::LOG, [$columns=Info, $ev=log_exfil]); 178 | Log::create_stream(Exfil::DEBUG, [$columns=DebugInfo, $ev=log_debug]); 179 | 180 | } 181 | 182 | function alert( c: connection, byte_cnt: count, n: Notice::Type, notice: bool) { 183 | 184 | local rec = Exfil::Info($ts=network_time(), $uid=c$uid, $id=c$id, $proto=(get_port_transport_proto(c$id$orig_p)), $service=c$service, $orig_bytes=byte_cnt, $note=n); 185 | if (c?$ssl) { 186 | if (c$ssl?$subject) { 187 | rec$subject = c$ssl$subject; 188 | } 189 | rec$server_name = c$ssl$server_name; 190 | rec$ja3 = c$ssl$ja3; 191 | if (c$ssl?$validation_status) { 192 | rec$validation_status = c$ssl$validation_status; 193 | } 194 | } 195 | if (c?$http) rec$host = c$http$host; 196 | 197 | Log::write(Exfil::LOG, rec); 198 | 199 | if (notice) { 200 | # if ( $orig_bytes > 262144000 ) 201 | # if ( $orig_bytes != 18446744073709551615 ) { 202 | 203 | NOTICE([$note=n, 204 | $id=c$id, 205 | $msg=fmt("Large file upload detected from %s to %s. This file is approximately %s bytes.", c$id$orig_h, c$id$resp_h, byte_cnt), 206 | $sub=c$ssl$subject]); 207 | } 208 | 209 | } 210 | 211 | event Exfil::regular_check(c: connection, s: Settings) { 212 | # Does this connection still exist? If it doesn't remove it from tracking and don't schedule a checkup 213 | local byte_cnt: count; 214 | 215 | if (! connection_exists(c$id)) { 216 | if (c$uid in tracked_sessions) { 217 | if ( tracked_sessions[c$uid]$reported ) { 218 | byte_cnt = (tracked_sessions[c$uid]$last_byte_cnt - tracked_sessions[c$uid]$orig_byte_cnt); 219 | event Exfil::heuristic_transfer_end(c); 220 | if ( byte_cnt >= s$file_thresh) { 221 | # Fire an event that signals that this event has ended 222 | event Exfil::transfer_above_file_thresh(c); 223 | alert(c,byte_cnt,s$note,s$notice); 224 | # If byte_cnt < notification threshold but the burst is over fire a transfer_below_file_thresh event 225 | } else { 226 | event Exfil::transfer_below_file_thresh(c); 227 | } 228 | } 229 | event Exfil::forgetting_connection(c); 230 | delete tracked_sessions[c$uid]; 231 | } 232 | return; 233 | } 234 | 235 | local new_thread: bool = F; 236 | # Has this connection been tracked yet? If not track it, set counters to default vals and return 237 | if (c$uid !in tracked_sessions) { 238 | 239 | event Exfil::watching_connection(c); 240 | tracked_sessions[c$uid] = [$last_byte_cnt = c$orig$size, $src_ip=c$id$orig_h, $src_p=c$id$orig_p, $dst_ip=c$id$resp_h, $dst_p=c$id$resp_p]; 241 | # Assume the new connection is a single thread of many 242 | new_thread = T; 243 | add_conn_to_threads(c, s, new_thread); 244 | local conn_key = (fmt("%s->%s:%s", c$id$orig_h, c$id$resp_h, c$id$resp_p)); 245 | # Schedule another check and check for more connections with the same source and destination IP 5 seconds later. 246 | schedule s$checkup_interval { Exfil::regular_check(c, s) }; 247 | schedule s$thread_checkup_interval { Exfil::thread_check(c, s, thread_collection[conn_key]$total_byte_cnt) }; 248 | return; 249 | 250 | } 251 | 252 | local session = tracked_sessions[c$uid]; 253 | # get latest information from c$id 254 | lookup_connection(c$id); 255 | 256 | # byte_rate = (current byte count) - (byte count at last poll) / (polling interval) 257 | local byte_rate: double = 258 | ((c$orig$size - session$last_byte_cnt) + 0.0 / interval_to_double(s$checkup_interval)); 259 | 260 | # set last_byte_cnt to current byte count 261 | session$last_byte_cnt = c$orig$size; 262 | 263 | # if the current byte_rate is greater than our byte rate threshold 264 | if (byte_rate > s$byte_rate_thresh && session$reported == F) { 265 | 266 | # Flag this connection for reporting 267 | session$reported = T; 268 | session$orig_byte_cnt = c$orig$size; 269 | 270 | event Exfil::heuristic_transfer_begin(c); 271 | 272 | } 273 | 274 | # determine how much data has been seen 275 | byte_cnt = (c$orig$size - session$orig_byte_cnt); 276 | 277 | # If debugging is turned on, write the debug log 278 | if (debug) { 279 | local debug_rec: Exfil::DebugInfo = [$ts=network_time(), $id=c$id, $uid=c$uid, $byte_rate=byte_rate, 280 | $reported=session$reported, $byte_count=byte_cnt]; 281 | Log::write(Exfil::DEBUG, debug_rec); 282 | } 283 | 284 | # if the current byte_rate has returned from the burst, notice, log, etc. 285 | if ( session$reported && byte_rate < s$byte_rate_thresh ) { 286 | 287 | event Exfil::heuristic_transfer_end(c); 288 | if ( byte_cnt >= s$file_thresh) { 289 | 290 | # Fire an event that signals that this event has ended 291 | event Exfil::transfer_above_file_thresh(c); 292 | alert(c,byte_cnt,s$note,s$notice); 293 | 294 | # If byte_cnt < notification threshold but the burst is over fire a transfer_below_file_thresh event 295 | } else { 296 | event Exfil::transfer_below_file_thresh(c); 297 | } 298 | 299 | # Return connection to "unreported" state 300 | session$reported = F; 301 | 302 | } 303 | 304 | 305 | add_conn_to_threads(c, s, new_thread); 306 | local new_key = (fmt("%s->%s:%s", c$id$orig_h, c$id$resp_h, c$id$resp_p)); 307 | 308 | # Schedule next checkup and a checkup to see if the connection is one of many threads 309 | schedule s$thread_checkup_interval { Exfil::thread_check(c, s, thread_collection[new_key]$total_byte_cnt) }; 310 | schedule s$checkup_interval { Exfil::regular_check(c, s) }; 311 | #Schedule something new and if there is no additional entry, delete it. 312 | } 313 | 314 | 315 | ## A public interface for attaching connections to the analyzer 316 | function watch_connection (c: connection, s: Settings) { 317 | 318 | schedule s$checkup_interval { Exfil::regular_check(c, s) }; 319 | 320 | } 321 | -------------------------------------------------------------------------------- /threaded_exfil.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, salesforce.com, inc. 2 | # All rights reserved. 3 | # Licensed under the BSD 3-Clause license. 4 | # For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause 5 | 6 | module Exfil; 7 | 8 | ## This function determines if exfiltration has occurred via multithreading 9 | event Exfil::thread_check(c: connection, s: Settings, b: count) { 10 | local c_key = (fmt("%s->%s:%s", c$id$orig_h, c$id$resp_h, c$id$resp_p)); 11 | 12 | if (c_key in thread_collection) 13 | { 14 | # If there is only a single connection with a particular source IP, destination IP, and destination port over 5 seconds 15 | # threading is not happening, so the connection can be removed. 16 | if (thread_collection[c_key]$thread_cnt == 1) 17 | { 18 | delete thread_collection[c_key]; 19 | } 20 | 21 | # Once the byte count given by the function is equal to the byte count given by the last time the function was run 22 | # it is safe to assume all threads have been completed. If the byte count is high enough, raise a notice. 23 | else if (b == thread_collection[c_key]$timed_byte_cnt) 24 | { 25 | if ((thread_collection[c_key]$thread_cnt >= s$unique_thread_thresh) && (thread_collection[c_key]$total_byte_cnt > s$file_thresh)) 26 | { 27 | NOTICE([ 28 | $id=c$id, 29 | $src=c$id$orig_h, 30 | $dst=c$id$resp_h, 31 | $p=c$id$resp_p, 32 | $note=Exfil::File_Transfer, 33 | $msg=fmt("A total of %d bytes have gone from %s to %s:%s over %d unique streams", (thread_collection[c_key]$total_byte_cnt), (c$id$orig_h), (c$id$resp_h), (c$id$resp_p), (thread_collection[c_key]$thread_cnt)), 34 | $sub="This is multithreaded exfiltration"]); 35 | delete thread_collection[c_key]; 36 | return; 37 | 38 | } 39 | # If the byte count is not high enough, delete the connection from the table 40 | else 41 | { 42 | delete thread_collection[c_key]; 43 | return; 44 | } 45 | } 46 | else 47 | { 48 | thread_collection[c_key]$timed_byte_cnt = (thread_collection[c_key]$total_byte_cnt); 49 | return; 50 | } 51 | } 52 | return; 53 | 54 | } 55 | 56 | 57 | # This function adds a new connection to the threads table 58 | function add_conn_to_threads (c: connection, s: Settings, new_thread: bool) { 59 | 60 | # It gathers information in the same way as it would for any connection 61 | local session = tracked_sessions[c$uid]; 62 | # get latest information from c$id 63 | lookup_connection(c$id); 64 | # set last_byte_cnt to current byte count 65 | session$last_byte_cnt = c$orig$size; 66 | 67 | # The table tracked_sessions uses relies on the source IP, destination IP, and destination port as a key 68 | local new_key = (fmt("%s->%s:%s", c$id$orig_h, c$id$resp_h, c$id$resp_p)); 69 | local add_entry = T; 70 | 71 | for (x, y in tracked_sessions) 72 | { 73 | for (key in thread_collection) 74 | { 75 | if (key == new_key) 76 | { 77 | add_entry = F; 78 | local new_bytes = (y$last_byte_cnt - y$orig_byte_cnt); 79 | if (new_thread == T) 80 | { 81 | thread_collection[new_key]$thread_cnt += 1; 82 | new_thread = F; 83 | } 84 | thread_collection[new_key]$total_byte_cnt += new_bytes; 85 | y$orig_byte_cnt = y$last_byte_cnt; 86 | break; 87 | } 88 | } 89 | if (add_entry == T) 90 | { 91 | thread_collection[new_key] = [$thread_cnt=1, $total_byte_cnt=y$last_byte_cnt, $timed_byte_cnt=0]; 92 | y$orig_byte_cnt = y$last_byte_cnt; 93 | new_thread = F; 94 | } 95 | } 96 | } 97 | 98 | --------------------------------------------------------------------------------