this closes issue #9
This commit is contained in:
@@ -102,4 +102,8 @@ class TutorialsController < ApplicationController
|
|||||||
@good_code_2 = %q{<td class="ssn"><%= @user.work_info.last_four %></td>}
|
@good_code_2 = %q{<td class="ssn"><%= @user.work_info.last_four %></td>}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def mass_assignment
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -99,11 +99,14 @@
|
|||||||
<li id="guard">
|
<li id="guard">
|
||||||
<%= link_to "Guard", guard_tutorials_path %>
|
<%= link_to "Guard", guard_tutorials_path %>
|
||||||
</li>
|
</li>
|
||||||
<li id="info_dislosure">
|
<li>
|
||||||
<a href="#">Session Secret</a>
|
<a href="#">Session Secret</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li id="info_dislosure">
|
||||||
<%= link_to "Info Dislosure", info_disclosure_tutorials_path %>
|
<%= link_to "Info Dislosure", info_disclosure_tutorials_path %>
|
||||||
|
</li>
|
||||||
|
<li id="mass_assignment">
|
||||||
|
<%= link_to "Mass Assignment", mass_assignment_tutorials_path %>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#">DB Sessions</a>
|
<a href="#">DB Sessions</a>
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
<div class="widget">
|
||||||
|
<div class="widget-header">
|
||||||
|
<div class="title">
|
||||||
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Mass Assignment (Admin role)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="widget-body">
|
||||||
|
<div id="accordion1" class="accordion no-margin">
|
||||||
|
<div class="accordion-group">
|
||||||
|
<div class="accordion-heading">
|
||||||
|
<a href="#collapseOne" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
||||||
|
<i class="icon-info icon-white">
|
||||||
|
</i>
|
||||||
|
Description
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
||||||
|
<div class="accordion-inner">
|
||||||
|
<p class="desc">
|
||||||
|
The application allows the admin attribute of a User model to be set through a mass assignment call. This vulnerability exists because a developer has indicated it is acceptable to set or change the admin value through the use of the attr_accessible setting. Any action that uses mass assignment to create a user or modify a user's settings is susceptible to this attack which would allow vertical privilege escalation.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="accordion-group">
|
||||||
|
<div class="accordion-heading">
|
||||||
|
<a href="#collapseTwo" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
||||||
|
<i class="icon-bug icon-white">
|
||||||
|
</i>
|
||||||
|
Bug
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
||||||
|
<div class="accordion-inner">
|
||||||
|
<p>
|
||||||
|
The bug is introduced within app/models/user.rb, seen on line 3 (:admin):
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<pre class="ruby">
|
||||||
|
<%= %q{
|
||||||
|
class User < ActiveRecord::Base
|
||||||
|
attr_accessible :email, :password, :admin, :password_confirmation, :first_name, :last_name
|
||||||
|
} %>
|
||||||
|
</pre>
|
||||||
|
</p>
|
||||||
|
<p class="desc">
|
||||||
|
Any attribute added to the attr_accessible setting can be used during a mass assignment call. What this means is that conceptually, the following is allowed:
|
||||||
|
</p>
|
||||||
|
<pre class="ruby">
|
||||||
|
# Note the string "true"/"false" or 1/0, etc. can be added to specify the boolean attribute...
|
||||||
|
# is true or false thanks to ActiveRecord
|
||||||
|
User.new(:email => "email@email.com",
|
||||||
|
:admin => "true",
|
||||||
|
:password => "h4xx0r",
|
||||||
|
:first_name => "Captain",
|
||||||
|
:last_name => "Crunch"
|
||||||
|
)
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="accordion-group">
|
||||||
|
<div class="accordion-heading">
|
||||||
|
<a href="#collapseThree" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
||||||
|
<i class="icon-lightning icon-white">
|
||||||
|
</i>
|
||||||
|
Solution
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
||||||
|
<div class="accordion-inner">
|
||||||
|
<p><b> Mass Assignment ATTACK:</b></p>
|
||||||
|
<p class="desc">
|
||||||
|
Through the use of an intercepting proxy, we are able to capture our form submission after entering our information on the sign up page. The request looks like this...
|
||||||
|
</p>
|
||||||
|
<pre class="ruby">
|
||||||
|
POST /users HTTP/1.1
|
||||||
|
Host: railsgoat.dev
|
||||||
|
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
|
||||||
|
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
||||||
|
Accept-Language: en-US,en;q=0.5
|
||||||
|
Accept-Encoding: gzip, deflate
|
||||||
|
Referer: http://railsgoat.dev/signup
|
||||||
|
Cookie: _railsgoat_session=[redacted]
|
||||||
|
Connection: keep-alive
|
||||||
|
Content-Type: application/x-www-form-urlencoded
|
||||||
|
Content-Length: 248
|
||||||
|
|
||||||
|
utf8=â&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&user[email]=test@test.com&user[first_name]=test&user[last_name]=test&user[password]=testtest&user[password_confirmation]=testtest&commit=Submit
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
...and the attack is quite simple. Append a parameter to the body of this POST request that specifies the admin value is true.
|
||||||
|
</p>
|
||||||
|
<pre class="ruby"> utf8=â&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&user[email]=test@test.com&user[first_name]=test&user[last_name]=test&user[password]=testtest&user[password_confirmation]=testtest&commit=Submit&<span style="background-color: yellow">user[admin]=true</span>
|
||||||
|
</pre>
|
||||||
|
<p class="desc">
|
||||||
|
So when the request is received by the create method within the user controller (code shown below), the admin attribute is set to true upon user creation.
|
||||||
|
</p>
|
||||||
|
<pre class="ruby">
|
||||||
|
def create
|
||||||
|
<span style="background-color: yellow">user = User.new(params[:user])</span>
|
||||||
|
user.build_retirement(POPULATE_RETIREMENTS.shuffle.first)
|
||||||
|
user.build_paid_time_off(POPULATE_PAID_TIME_OFF.shuffle.first).schedule.build(POPULATE_SCHEDULE.shuffle.first)
|
||||||
|
user.build_work_info(POPULATE_WORK_INFO.shuffle.first)
|
||||||
|
user.performance.build(POPULATE_PERFORMANCE.shuffle.first)
|
||||||
|
if user.save
|
||||||
|
session[:user_id] = user.user_id
|
||||||
|
redirect_to home_dashboard_index_path
|
||||||
|
else
|
||||||
|
@user = user
|
||||||
|
render :new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
</pre>
|
||||||
|
<p class="desc">
|
||||||
|
The last thing to mention here is that this can be done either through the signup page or when you edit your account settings.
|
||||||
|
</p>
|
||||||
|
<p><b> Mass Assignment SOLUTION:</b></p>
|
||||||
|
<p class="desc">
|
||||||
|
The solution is fairly simple, remove the admin attribute from the attr_accessible method. The following code shows what we mean:
|
||||||
|
</p>
|
||||||
|
<pre class="ruby">
|
||||||
|
<span style="background-color:yellow"># Note that the admin attr has been removed </span>
|
||||||
|
<%= %q{
|
||||||
|
class User < ActiveRecord::Base
|
||||||
|
attr_accessible :email, :password, :password_confirmation, :first_name, :last_name
|
||||||
|
} %>
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<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">
|
||||||
|
</i>
|
||||||
|
Hint
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
|
||||||
|
<div class="accordion-inner">
|
||||||
|
<p>
|
||||||
|
Did you register your account correctly? How about when you updated your settings?
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
function openSub(){
|
function openSub(){
|
||||||
$('li[id="submenu"]').addClass('open');
|
$('li[id="submenu"]').addClass('open');
|
||||||
|
$('li[id="submenu"]').addClass('active open');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(openSub);
|
$(document).ready(openSub);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="info_dislosure"]').addClass('active');
|
$('li[id="info_dislosure"]').addClass('active');
|
||||||
|
$('li[id="submenu"]').addClass('active open');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive);
|
$(document).ready(makeActive);
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<div class="dashboard-wrapper">
|
||||||
|
<div class="main-container">
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span12">
|
||||||
|
<%= render :partial => ("layouts/tutorial/mass_assignment/admin_mass_assign")%>
|
||||||
|
</div> <!-- End Span12-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
function makeActive(){
|
||||||
|
$('li[id="mass_assignment"]').addClass('active');
|
||||||
|
$('li[id="submenu"]').addClass('active open');
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).ready(makeActive);
|
||||||
|
</script>
|
||||||
@@ -40,6 +40,7 @@ resources :tutorials do
|
|||||||
get "redirects"
|
get "redirects"
|
||||||
get "guard"
|
get "guard"
|
||||||
get "info_disclosure"
|
get "info_disclosure"
|
||||||
|
get "mass_assignment"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user