Convert file indentation to spaces
This commit is contained in:
@@ -16,9 +16,9 @@
|
||||
</div>
|
||||
<div class="accordion-body in collapse" id="collapseCompOne" style="height: auto;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
<p class="desc">
|
||||
A timing attack can exist in several forms. This specific case relates to username (email address) enumeration. By leveraging an automated tool, an attacker can review any subtle variation in response times after submitting a login request to determine if the application is performing a computationally intense function. Meaning, if a function is run once a user is discovered, even if the password is incorrect, this information provides the user with valid or invalid usernames.
|
||||
</p>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -32,28 +32,28 @@
|
||||
</div>
|
||||
<div class="accordion-body collapse" id="collapseCompTwo" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p>
|
||||
Within app/models/user.rb
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def self.authenticate(email, password)
|
||||
auth = nil
|
||||
<span style="background-color: yellow"> user = find_by_email(email)</span>
|
||||
raise "#{email} doesn't exist!" if !(user)
|
||||
<span style="background-color: yellow">if user.password == Digest::MD5.hexdigest(password)</span>
|
||||
auth = user
|
||||
else
|
||||
raise "Incorrect Password!"
|
||||
end
|
||||
return auth
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Ignore for a moment that the application actually tells you whether or not an email address exists :-). Instead, let's look at what would happen if this error message wasn't so specific. Even if the error message vulnerability was mitigated (because it indicates whether or not a user exists), there will be some variations in the application's response between a user that exists and one that does not (however so slight, considering MD5 is in use).
|
||||
</p>
|
||||
<p class="desc">
|
||||
To understand why, let's follow the flow of this code example. Firstly, the application look for a user by email. If not found, nothing else really happens. No further processing, password comparison, etc. If a user <i>is</i> found, we will perform a password comparison and process as normal.
|
||||
</p>
|
||||
<p>
|
||||
Within app/models/user.rb
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def self.authenticate(email, password)
|
||||
auth = nil
|
||||
<span style="background-color: yellow"> user = find_by_email(email)</span>
|
||||
raise "#{email} doesn't exist!" if !(user)
|
||||
<span style="background-color: yellow">if user.password == Digest::MD5.hexdigest(password)</span>
|
||||
auth = user
|
||||
else
|
||||
raise "Incorrect Password!"
|
||||
end
|
||||
return auth
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Ignore for a moment that the application actually tells you whether or not an email address exists :-). Instead, let's look at what would happen if this error message wasn't so specific. Even if the error message vulnerability was mitigated (because it indicates whether or not a user exists), there will be some variations in the application's response between a user that exists and one that does not (however so slight, considering MD5 is in use).
|
||||
</p>
|
||||
<p class="desc">
|
||||
To understand why, let's follow the flow of this code example. Firstly, the application look for a user by email. If not found, nothing else really happens. No further processing, password comparison, etc. If a user <i>is</i> found, we will perform a password comparison and process as normal.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -68,29 +68,29 @@
|
||||
<div class="accordion-body collapse" id="collapseCompThree" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p><b>Lack of Password Complexity - SOLUTION</b></p>
|
||||
<p>
|
||||
Within app/models/user.rb:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def self.authenticate(email, password)
|
||||
<span style="background-color: yellow">user = find_by_email(email) || User.new(:password => "")</span>
|
||||
<span style="background-color: yellow">if Rack::Utils.secure_compare(user.password, Digest::MD5.hexdigest(password))</span>
|
||||
return user
|
||||
else
|
||||
raise "Incorrect username or password"
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
To mitigate this attack and shore up our weakness, we do two things. The first is to find a user by email, if they don't exist, create a new user object in memory (not in the database) and assign it a blank password value. This means, regardless of whether or not a user exists, we will have a user to perform some processing on. The next is, we take the input from the user and match it against the user object's password leveraging secure_compare. This is a function (secure_compare) used to ensure that when a comparison happens, it will always take the same amount of time.
|
||||
</p>
|
||||
<p class="desc">
|
||||
In summary, we have ensured that regardless of whether or not a user exists, a password comparison will always occur and it will take the same amount of time to complete.
|
||||
</p>
|
||||
<p>
|
||||
Within app/models/user.rb:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def self.authenticate(email, password)
|
||||
<span style="background-color: yellow">user = find_by_email(email) || User.new(:password => "")</span>
|
||||
<span style="background-color: yellow">if Rack::Utils.secure_compare(user.password, Digest::MD5.hexdigest(password))</span>
|
||||
return user
|
||||
else
|
||||
raise "Incorrect username or password"
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
To mitigate this attack and shore up our weakness, we do two things. The first is to find a user by email, if they don't exist, create a new user object in memory (not in the database) and assign it a blank password value. This means, regardless of whether or not a user exists, we will have a user to perform some processing on. The next is, we take the input from the user and match it against the user object's password leveraging secure_compare. This is a function (secure_compare) used to ensure that when a comparison happens, it will always take the same amount of time.
|
||||
</p>
|
||||
<p class="desc">
|
||||
In summary, we have ensured that regardless of whether or not a user exists, a password comparison will always occur and it will take the same amount of time to complete.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseCompFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
||||
<i class="icon-aid icon-white">
|
||||
@@ -100,9 +100,9 @@
|
||||
</div>
|
||||
<div class="accordion-body collapse" id="collapseCompFour" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
Timing is everything. Authenticating is important too.
|
||||
</p>
|
||||
<p class="desc">
|
||||
Timing is everything. Authenticating is important too.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
<div class="accordion-body in collapse" id="collapsePwdOne" style="height: auto;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
Password complexity is incredibly important and highly debated subject. Other factors play a part in the stringency of the enforcement policy applied. If a username can be enumerated, a CAPTCHA on the login form is not present or other methods to deter a brute-force password guessing campaign are not in place, at least password complexity enforcement policy can make it a that much more difficult for an attacker to guess users passwords.
|
||||
</p>
|
||||
Password complexity is incredibly important and highly debated subject. Other factors play a part in the stringency of the enforcement policy applied. If a username can be enumerated, a CAPTCHA on the login form is not present or other methods to deter a brute-force password guessing campaign are not in place, at least password complexity enforcement policy can make it a that much more difficult for an attacker to guess users passwords.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -32,18 +32,18 @@
|
||||
</div>
|
||||
<div class="accordion-body collapse" id="collapsePwdTwo" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p>
|
||||
<p>
|
||||
Within app/models/User.rb
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
validates :password, :presence => true,
|
||||
:confirmation => true,
|
||||
:length => {:within => 6..40},
|
||||
:on => :create
|
||||
</pre>
|
||||
<p class="desc">
|
||||
The application validates only the password length and nothing else. Developers can leverage the format option to apply a regular expression that checks the password has sufficient complexity.
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
validates :password, :presence => true,
|
||||
:confirmation => true,
|
||||
:length => {:within => 6..40},
|
||||
:on => :create
|
||||
</pre>
|
||||
<p class="desc">
|
||||
The application validates only the password length and nothing else. Developers can leverage the format option to apply a regular expression that checks the password has sufficient complexity.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -58,28 +58,28 @@
|
||||
<div class="accordion-body collapse" id="collapsePwdThree" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p><b>Lack of Password Complexity - ATTACK</b></p>
|
||||
<p class="desc">
|
||||
Leverage a tool such as BurpSuite's intruder to brute-force the passwords of the users. The highest privileged account that you an attacker can compromise is the admin. The password is very simple ("admin1234"), username is ("admin@metacorp.com").
|
||||
</p>
|
||||
<p><b>Lack of Password Complexity - SOLUTION</b></p>
|
||||
<p class="desc">
|
||||
This regular expression validates the password has the following requirements:
|
||||
<li>1 digit</li>
|
||||
<li>1 lowercase alphabet</li>
|
||||
<li>1 uppercase alphabet</li>
|
||||
<li>1 special character</li>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
<p class="desc">
|
||||
Leverage a tool such as BurpSuite's intruder to brute-force the passwords of the users. The highest privileged account that you an attacker can compromise is the admin. The password is very simple ("admin1234"), username is ("admin@metacorp.com").
|
||||
</p>
|
||||
<p><b>Lack of Password Complexity - SOLUTION</b></p>
|
||||
<p class="desc">
|
||||
This regular expression validates the password has the following requirements:
|
||||
<li>1 digit</li>
|
||||
<li>1 lowercase alphabet</li>
|
||||
<li>1 uppercase alphabet</li>
|
||||
<li>1 special character</li>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
validates :password, :presence => true,
|
||||
:confirmation => true,
|
||||
:if => :password,
|
||||
:format => {:with => /\A.*(?=.{10,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\@\#\$\%\^\&\+\=]).*\z/}
|
||||
|
||||
</pre>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a style="background-color: rgb(181, 121, 158)" href="#collapsePwdFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
||||
<i class="icon-aid icon-white">
|
||||
@@ -89,9 +89,9 @@ validates :password, :presence => true,
|
||||
</div>
|
||||
<div class="accordion-body collapse" id="collapsePwdFour" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
I wonder how strong the administrator's password is?
|
||||
</p>
|
||||
<p class="desc">
|
||||
I wonder how strong the administrator's password is?
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
Overly verbose error messages that indicate whether or not a user exists can assist an attacker with brute-forcing accounts. In attempting to harvest valid usernames for a password-guessing campaign, these messages can prove very useful.
|
||||
</p>
|
||||
Overly verbose error messages that indicate whether or not a user exists can assist an attacker with brute-forcing accounts. In attempting to harvest valid usernames for a password-guessing campaign, these messages can prove very useful.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -33,50 +33,50 @@
|
||||
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p><b>Username and Password Enumeration</b></p>
|
||||
<p><b>Within /app/models/user.rb:</b><p>
|
||||
<p><b>Within /app/models/user.rb:</b><p>
|
||||
|
||||
|
||||
<pre class="ruby">
|
||||
def self.authenticate(email, password)
|
||||
auth = nil
|
||||
user = find_by_email(email)
|
||||
# I heard something about hashing, dunno, why bother really. Nobody will get access to my stuff!
|
||||
if user
|
||||
if user.password == password
|
||||
auth = user
|
||||
else
|
||||
raise "Incorrect Password!"
|
||||
end
|
||||
else
|
||||
raise "#{email} doesn't exist!"
|
||||
end
|
||||
return auth
|
||||
end
|
||||
</pre>
|
||||
<p> On lines 9 and 12 you'll notice that the application generates two error messages. </p>
|
||||
<p><b>Within /app/controllers/sessions_controller.rb:</b><p>
|
||||
<pre class="ruby">
|
||||
def create
|
||||
<pre class="ruby">
|
||||
def self.authenticate(email, password)
|
||||
auth = nil
|
||||
user = find_by_email(email)
|
||||
# I heard something about hashing, dunno, why bother really. Nobody will get access to my stuff!
|
||||
if user
|
||||
if user.password == password
|
||||
auth = user
|
||||
else
|
||||
raise "Incorrect Password!"
|
||||
end
|
||||
else
|
||||
raise "#{email} doesn't exist!"
|
||||
end
|
||||
return auth
|
||||
end
|
||||
</pre>
|
||||
<p> On lines 9 and 12 you'll notice that the application generates two error messages. </p>
|
||||
<p><b>Within /app/controllers/sessions_controller.rb:</b><p>
|
||||
<pre class="ruby">
|
||||
def create
|
||||
|
||||
begin
|
||||
user = User.authenticate(params[:email], params[:password])
|
||||
rescue Exception => e
|
||||
end
|
||||
begin
|
||||
user = User.authenticate(params[:email], params[:password])
|
||||
rescue Exception => e
|
||||
end
|
||||
|
||||
if user
|
||||
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
|
||||
redirect_to home_dashboard_index_path
|
||||
else
|
||||
flash[:error] = e.message
|
||||
render "new"
|
||||
end
|
||||
if user
|
||||
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
|
||||
redirect_to home_dashboard_index_path
|
||||
else
|
||||
flash[:error] = e.message
|
||||
render "new"
|
||||
end
|
||||
|
||||
end
|
||||
</pre>
|
||||
<p> On line 5 you see the exception message object "e" is created. On line 11, the message is displayed. </p>
|
||||
<p class="desc">
|
||||
One of these messages indicates the email address (username) doesn't exist on the system. The other indicates that the password is incorrect. Although the application will render both error messages, either one of the error messages would be harmful by itself. This type of information can be used by an attacker to harvest email addresses or usernames. Once that list is gathered, passwords can be guessed for each account. If the username being enumerated is actually an email address, a phishing campaign could ensue with emails made to look like they are originating from the vulnerable site.
|
||||
</p>
|
||||
end
|
||||
</pre>
|
||||
<p> On line 5 you see the exception message object "e" is created. On line 11, the message is displayed. </p>
|
||||
<p class="desc">
|
||||
One of these messages indicates the email address (username) doesn't exist on the system. The other indicates that the password is incorrect. Although the application will render both error messages, either one of the error messages would be harmful by itself. This type of information can be used by an attacker to harvest email addresses or usernames. Once that list is gathered, passwords can be guessed for each account. If the username being enumerated is actually an email address, a phishing campaign could ensue with emails made to look like they are originating from the vulnerable site.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,34 +91,34 @@
|
||||
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p>
|
||||
<b> Username and Password Enumeration - SOLUTION</b>
|
||||
</p>
|
||||
<p> Within /app/controllers/sessions_controller.rb</p>
|
||||
<b> Username and Password Enumeration - SOLUTION</b>
|
||||
</p>
|
||||
<p> Within /app/controllers/sessions_controller.rb</p>
|
||||
<pre class="ruby">
|
||||
def create
|
||||
def create
|
||||
|
||||
begin
|
||||
user = User.authenticate(params[:email], params[:password])
|
||||
rescue Exception => e
|
||||
end
|
||||
begin
|
||||
user = User.authenticate(params[:email], params[:password])
|
||||
rescue Exception => e
|
||||
end
|
||||
|
||||
if user
|
||||
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
|
||||
redirect_to home_dashboard_index_path
|
||||
else
|
||||
flash[:error] = "Either your username and password is incorrect" #e.message
|
||||
render "new"
|
||||
end
|
||||
if user
|
||||
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
|
||||
redirect_to home_dashboard_index_path
|
||||
else
|
||||
flash[:error] = "Either your username and password is incorrect" #e.message
|
||||
render "new"
|
||||
end
|
||||
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Although this fix is neither systemic nor does it address the problematic code at its core (within the user model), it does provide a quick solution. On line 12, we comment out the "e.message code" and instead provide a very generic error message that lacks specificity on what credential was incorrectly entered.
|
||||
</p>
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Although this fix is neither systemic nor does it address the problematic code at its core (within the user model), it does provide a quick solution. On line 12, we comment out the "e.message code" and instead provide a very generic error message that lacks specificity on what credential was incorrectly entered.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
||||
<i class="icon-aid icon-white">
|
||||
@@ -128,10 +128,10 @@
|
||||
</div>
|
||||
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
Enter an email address that wouldn't likely exist into the login form. Analyze the result.<br/><br/>
|
||||
Can you leverage this to gain unauthorized access?
|
||||
</p>
|
||||
<p class="desc">
|
||||
Enter an email address that wouldn't likely exist into the login form. Analyze the result.<br/><br/>
|
||||
Can you leverage this to gain unauthorized access?
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user