-
Notifications
You must be signed in to change notification settings - Fork 14
Home
Ensnare is packaged as a gem plugin for Ruby on Rails and was developed to allow configuring and deploying a basic malicious behavior detection and response scheme in less than ten minutes.
Ensnare uses a combination of Honey Traps to entice malicious users, and a configurable suite of Trap Responses to confuse, allude, delay, or stop an attacker.
#Honey Trap Overview# For a really good primer on HoneyTraps, check out Ryan Barnett's blogposts here: Spiderlabs In the context of Ensnare, honeytraps are bobby-traps that are implemented in the application layer that don't actually provide any application functionality but allow Ensnare to track when those 'traps' have been triggered.
In the current release of Ensnare, these traps can be placed as benign cookies, parameters, bad paths, or even regular expressions.
The power of honey traps are in identifying a legitimate user from an attacker. The traps are located in places transparent to a general user. Headers and POST parameters can only be viewed with a proxy or packet capture. Bad paths like /admin /wp-admin should never be forced browse by a legitimate user. Expressions like <script>alert(1)</script>
should also never be sent. When we see evidence that these traps are being tampered with, Ensnare can react accordingly as defined in your Threshold Groups
This makes it extremely effective at reducing false positive triggers and since they will only be triggered by scanners and attackers, the number of alerts should be manageable.
An example would be the following POST request to add an item to a shopping cart:
We can see all of the headers that a user should not have access too. If an attacker was to modify the uuid cookie to another value, we could log that violation and act accordingly.
#How Ensnare Works# Ensnare is integrated into the base Rails application functionality. This allows Ensnare to sit alongside your application. The flow of requests and responses handled by Ensnare is captured in the diagram below:
###Step 1: Check for Violations### This step determines if the request is potentially malicious depending on what traps you have configured (Parameter, Cookie, Path, Regex, etc.).
If the request triggers a trap, log the violation (for either the user
, session
, ip
or all
depending on your configuration file)
###Step 2: Determine Threshold###
A lookup is performed on the user
, session
, ip
or a combination of those to determine if the user has reached a violation count that would put them in a Threshold group. If the attacker enters a Threshold group, we will move to Step 3.
###Step 3: Run the Responses### These only run if the attacker enters a threshold group. Responses could include a combination of modifying the response, replacing the response (captcha, random data), delaying the response, changing the http status code of the response, blocking the response, etc.
###Step 4: Set Traps### During this step the honey traps are inserted in the response. This may include setting cookies and inserting form parameters.
###Step 5: Render Response### Depending on which response is selected in the Threshold Group, the response is rendered for the attacker.
#Traps# Ensnare currently supports four types of traps:
cookies
parameters
-
routing error
(also known as 'bad paths') regular expressions
custom
For each trap, you can also specify an optional weight if you want certain trap types to cause more violation counts.
More details can be found in the Configuration File Overview.
###Cookies### Ensnare will send cookies in each response as specified in the configuration file. You can choose from some predefined cookies or specify your own. You can also provide either a static cookie value or define your own function to make the cookie values (adding to the randomness/unpredictability).
###Parameters### Ensnare will send parameters in each response for pages with POST forms. Currently, ensnare does not support honey parameters in GET requests. You can choose from some predefined parameters or specify your own. You can also provide either a static parameter value or define your own function to make the parameter values (adding to the randomness/unpredictability).
###Routing Error###
With this trap, you can specify a list of paths that will trigger a violation. Some examples could be /admin
, /debug
, /destroy
.
###Regular Expressions### With regular expressions, you can specify a pattern to match that will trigger a violation. One way to effectively use this is to take a look at an application security scanners signature files, and add those signatures to this trap type.
###Custom Traps### Ensnare also allows creating custom trap configurations. Custom traps should be placed in the applications lib/ensnare/traps folder. A sample can be found here: https://github.com/ahoernecke/ensnare/blob/master/test/dummy/lib/ensnare/traps/custom.rb_sample
Custom traps need to implement the initialize and run methods and need to be enabled inside the config/initializers/ensnare.rb config file.
###Logging Violations Outside of Traps### Ensnare allow logging violations from anywhere within the application. For example, you may want to log violations when a user violations the application's authorization controls. This can be done by creating a violation record (Ensnare::Violation.create) with the options (all required):
- :ip_address
- :session_id
- :user_id
- :violation_type (String that identifies the type of violation. ex. "Authorization failure")
- :name (The name of the object/control/method that was violated. ex. "recipes#destroy")
- :expected (The expected value, if any)
- :observed (The observed value, if any)
- :weight (How much weight to apply to the violations.)
A sample can be seen below:
Ensnare::Violation.create(:ip_address=>request.remote_ip.to_s,
:violation_type=>"Delete Widget",
:session_id=>session.try(:[],"session_id").to_s,
:user_id=>current_user.id,
:expected=>"None",
:name=>params[:id].to_s,
:observed=>"Delete",
:weight=>5)
#Thresholds# Thresholds allow Ensnare to define the flexibility and handling of users that trigger violations. One way to think about these are as a way to gauge how bad an attacker is. The more violations that occur from a user, the more confident Ensnare can be that the user is malicious and act accordingly.
We want to ensure general users of an application are not punished, so if a user triggers a 404 or a violation, they probably shouldn't enter a Threshold group. If a user is repeatedly trigger violations by running a SQL injection tool, that should definitely result in the user entering a Threshold group.
Threshold groups are a collection of these policies that control how to handle trap violations. They contain the following elements:
-
trap_count
- How many traps must be triggered to enter the group -
timer
- How long should the attacker be in the threshold -
responses
- What responses should Ensnare render for the user once they enter this group.
Here is a very simple example of how an attacker may enter a threshold group:
We can assume that in this diagram the attacker has triggered 5 violations. The attacker would enter the threshold for 600 seconds. For each request/response pair, the responses would have the following characteristics:
20% of responses have a 10-15 second delay 30% redirect the user to '/' 50% do nothing
If the attacker continues and makes a total of 30 violations, they enter threshold group 2. The attacker will enter this threshold group for 1600 seconds. The following responses would occur:
60% of responses have a 20 second delay 20% of responses result in a 500 server error 20% do nothing.
Many more configuration options and response types are available and described in this documentation. For more information on how Threshold groups are structured can be found in the Configuration File Overview below.
#Response Types# The following are response types currently supported by Ensnare:
- none
- message
- redirect
- redirect_loop
- throttle
- captcha
- not_found
- server_error
- random_content
- block
###none### This response does nothing at all! Use this response to simply control the likelihood of responses being triggered. By setting the weight on this response higher, the likelihood of other responses being triggered when a violation threshold is reached decreases. This may help confuse an attacker and adds a bit of randomness.
none settings:
:weight => int(specify the likelihood of this response being triggered in a violation thresholds)
###message### This response displays a message you specify to warn the user to stop their malicious activity.
message settings:
:weight => int(specify the likelihood of this response being triggered in a violation threshold)`
:content => str(content to display in the warning message)
###redirect### This response simply redirects the user to either your webroot or a website you specify. This seemingly innocuous response can be very effective if setup on a violation threshold that includes multiple responses. Imagine trying to navigate a website that intermittently redirects you out of the expected application flow. This would make it very hard to attack multi step forms.
redirect settings:
:weight => int(specify the likelihood of this response being triggered in a violation threshold)
:url => str(url or path to redirect the attacker to. default is webroot)
###redirect_loop### The redirect loop response puts the response into an endless 301 loop. This times out in most modern browsers pretty quickly, however it fills up the proxy log (if the user is doing a manual attack) and it is generally confusing. Often times, attackers will restart their web browsers or terminate a session if an application exhibits this type of behavior.
redirect_loop settings:
:weight => int(specify the likelihood of this response being triggered in a violation threshold)
:parameter => str(parameter to send nonce too)
###throttle### One of the most effective responses, the throttle implements a random or fixed delay time when requesting a page. Long delays can result in application scanner instability and sometimes result in false positives being generated such as time based sql injection.
throttle settings:
:weight => int(specify the likelihood of this response being triggered in a violation threshold)
:min_delay => int(the minimum amount of time in seconds to delay the application when the response is triggered)
:max_delay => int(the maximum amount of time in seconds to delay the application when the response is triggered)
Note: you can set the min and max delay to the same time to create a fixed delay.
###captcha### One of the most effective responses for scanners, a captcha image must be solved before facilitating a request. Long delays can result in application scanner instability and sometimes result in false positives being generated such as time based sql injection. The ensnare captcha responses uses recaptcha which needs to be setup separately. Please see the recaptcha gem
captcha settings:
:weight => int(specify the likelihood of this response being triggered in a violation threshold)
:persist => boolean(if True, every request this function gets called until the user solves the captcha)
Note: We don't suggest you set weight AND persist. This response is most effective when just set as persistent.
In order for captcha to work, Ensnare requires the 'recaptcha' gem to be installed and configured:
Add the following to your Gemfile:
gem 'recaptcha'
You will also need to setup an initializer file as directed by the recaptcha gem. For example
#config/catpcha.rb
Recaptcha.configure do |config|
config.public_key = "<snip>"
config.private_key = "<snip>"
end
##not_found## This response will result in responses as a 404 page. 404 pages may confuse application scanners about if the requested resource or request is valid. Some scanners may remove the request from scope if they receive 404 errors.
404 settings:
:weight => int(specify the likelihood of this response being triggered in a violation threshold)
:location => str(specify the location of the 404 page to display. default is /public/404.html)
##server_error## This response sends a 500 page to as the response to a request. 500 errors are usually displayed when the application framework cannot handle a request. This can be especially lucrative for a manual attacker, who may think he/she is effectively causing an application error. This may result in the attacker spending unnecessary time investigating a false positive.
server_error settings:
:weight => int(specify the likelihood of this response being triggered in a violation threshold)
:location => str(specify the location of the 505 page to display. default is /public/505.html)
##random_content## This response returns random content when triggered. Simply used to confuse the manual attacker, garbled strings of varying length and nonsense will be returned.
random_content settings:
:weight => int(specify the likelihood of this response being triggered in a violation threshold)
:min_size => int(specify the minimum size in bytes of the randomly generated response)
:max_size => int(specify the maximum size in bytes of the randomly generated response)
#Configuration File Overview# The configuration file is located in config/initializes/ensnare.rb. The following sections describe the types of configuration settings you can set in the config file. The configuration sample has plenty of context and examples, so if you are unsure about a configuration setting please see the sample files located in dashboard.
###Config Type###
config.type = :custom
This is used by the Dashboard only, just ensure if you are making your own config to specify :custom otherwise you can copy another template form the dashboard that has the correct type definition.
###Config Mode###
config.mode = :log
Ensnare can run in three modes of operation:
:disabled
:log
:enforce
:log mode only logs exceptions and is extremely useful in establishing a workflow that works for you.
###What to Trap On###
config.trap_on = [:ip, :user, :session]
Here you can specify how you will log violations. You can set it to log violations based on an IP address, a user object, or a session object (or any combination of the above).
###Current User Method###
config.current_user_method = :current_user
If you are going to use the user object for trapping, you can specify the method used to retrieve the currently logged-in user.
###Current User Identifier###
config.current_user_identifier = :id
If you are going to use the user object for trapping, you can specify the the identifier of currently logged-in user. This must be set if you are trapping on users.
###Restrict Access to Dashboard### To access the dashboard this must be set.
To prevent unnecessary users from accessing the dashboard:
config.dashboard_user_method = :current_user
config.dashboard_authorization_method = :admin?
NOTE: you need to have a method defined to check if the user is the admin.
###Global Timer###
config.global_timer = 400000
The global timer method specifies the number of time in seconds which must pass before violations are cleared for an ip, user, or session object. The timer resets each time a violation is triggered.
###Enabled Traps###
config.enabled_traps = [
{:type=>:cookie,
:options=>{
:cookie_names=>{:oracle_001 => "s-2fslsasflkjflkjasfs2-f", :ool0020093 => "a4d1fc354f49c40c74c00b963adc13"},
:predefined_cookies=>[:admin, :random, :google, :uid, :debug]
}
},
{:type=>:parameter,
:options=>{
:parameter_names=>{:coupon_code => "84763949", :exp_csrf_token => config.randomizer},
:predefined_parameters=>[:uid, :admin, :debug, :random]
}
},
{:type=>:routing_error,
:options=>{
:bad_paths=>["/admin", "/debug", "/robots", "/destroy"],
:violation_weight=>10
}
}
]
Ensnare supports four trap types: cookies, parameters, routing_errors, and custom. You can specify your own named values for cookies and parameters, or use a set of predefined settings which are listed below:
:admin => this sets a cookie named admin with a boolean value set to false
:debug => this sets a cookie named debug with a boolean value set to false
:random => this generates a random N character cookie with a random encrypted value
:google => this generates 4 random cookies that look like Google tracking
:uid => this sets a cookie that look like a UID
:gid => this sets a cookie that looks like a GID
As you can see with the :exp_csrf_token parameter
, you can also specify a function to generate your cookie or parameter values, allowing you to totally customize and create more random looking values.
The :routing_error
trap type allows you to specify paths that will trigger a violation. In addition, you can specify the optional :violation_weight
parameter which results in n
violations being logged each time that particular trap is triggered.
###Threshold Groups###
config.thresholds = []
# Example 1:
config.thresholds << {:timer=>600, :trap_count=>2,
:traps=>[
{:trap=>"redirect",:weight=>50,:url=>"/"},
{:trap=>"throttle",:weight=>40,:min_delay=>10,:max_delay=>20},
{:trap=>"none", :weight=>10}
]}
config.thresholds << {:timer=>900, :trap_count=>15,
:traps=>[
{:trap=>"captcha", :persist=>true },
{:trap=>"redirect",:weight=>10,:url=>"/"},
{:trap=>"throttle",:weight=>30,:min_delay=>15,:max_delay=>20},
{:trap=>"not_found",:weight=>10},
{:trap=>"server_error", :weight=>20},
{:trap=>"redirect_loop",:weight=>5, :parameter=>'test'},
{:trap=>"random_content",:weight=>5,:min_size=>500,:max_size=>1500},
{:trap=>"none", :weight=>20}
]}
Lets walk through how we group together our responses into Threshold Groups, which is really the beauty and power of Ensnare.
As you recall from the Thresholds overview, we define N number of threshold groups to be enforced when certain :trap_counts
are hit. Each subsequent threshold group must have a higher trap_count
. This allows Ensnare to enforce trap responses in a Waterfall style. Lets define what a waterfall effect looks like in Ensnare:
Lets assume we have two threshold groups. We will pseudo code for simplicity sake:
Global Timer = 2 Hours
Threshold1 << {Trap_Count = 2 violations, Timer = 30 seconds}
Threshold2 << {Trap_Count = 5 violations, Timer = 5 minues}
If an attacker triggers 2 violations, they will be in Threshold1
for 30 seconds. If the same attacker stops triggering traps, they will leave Threshold1
after 30 seconds. If the user triggers any traps again before the Global Timer
expires, the attacker will enter Threshold2
for 5 minutes. If the user does not trigger any more traps during that time, the user will fall into Threshold1
for 30 seconds. If the user triggers any traps during that time, the attacker will reenter the appropriate Threshold group. This continues until the user triggers no more traps and the Global Timer
expires.
#Dashboard#
The Dashboard consists of four pages: mode
, configuration
, metrics
, and violations
.
###Mode###
The mode allows you to make non-persistent changes to the ensnare.rb
configuration file. You can disable Ensnare, turn on log only mode, or enforce ensnare.
###Configuration###
This page provides a bunch of example configuration files you can use for your project. It will also let you know which configuration file you are currently using. IF you are feeling adventurous, try out the custom configuration file and create your own based on your specific needs!
###Metrics###
The metrics page provides you with information on where the violations are occurring, a count of violations, and which ip addresses and sessions have violations.
###Violations###
The violations page provides raw details on the violations captured in the database. This is useful for profiling attacker behavior.
#Internals#
Ensnare works by registered a before_filter that runs for each request. This method does the following (depending on configuration):
- Run the trap methods
- Trap methods are passed attributes of the request such as the cookies and parameters for inspection
- Trap methods may log violations, depending on the details of the request
- Trap methods may also setup cookie values to be set for the user's session
- Determine Threshold Group
- Ensnare will determine if the application has seen violations from the user in the based (based on session, ip, user--if configured)
- Based on the number of violations previously, seen, ensnare determines the threshold group
- Run Responses (based on user's threshold group)
- Ensnare will run all persistent responses
- Ensnare will run other responses based on the configuration
- If a response has run which renders or redirects, ensure will prevent the request from being fulfilled by the Rails application
- Finish the response--If ensnare did not render a response, pass the request off to the Rails application
#Performance#
We have tried to keep Ensnare as light-weight as possible to prevent significant performance impact. Our performance testing has resulted in the metrics below. Of course, YMMV and it's important to test the impact on your own application!
Test # | w Ensnare (avg, ms) | w/o Ensnare (avg, ms) | Difference (avg, ms) | Note |
---|---|---|---|---|
1 | 172.6 | 166.3 | 6.3 | Longer running request |
2 | 155.8 | 148.2 | 7.6 | Basic request |
3 | 155.2 | 148.7 | 6.4 | Basic request |
4 | 154.2 | 142.5 | 11.7 | Includes violation |
Avg | 159.4 | 151.4 | 8.0 |
#Todo# We would like to see the following additions made to Ensnare (help us!):
- Rails 4 Edition
- More trap types (GET parameters, other headers, Http Methods like Trace)
- Optional config to delay a user entering a threshold (to make it harder to correlate which requests resulted in an application state change)
- Whitelisting
- More response types
- Filtering/queuing on violation data
- More metrics with graph and query functionality