Merge branch 'master' of github.com:OWASP/railsgoat into pr-108
This commit is contained in:
@@ -8,3 +8,4 @@
|
||||
/public/data
|
||||
*.png
|
||||
coverage
|
||||
.tags
|
||||
|
||||
@@ -40,8 +40,36 @@ $("pre.ruby").snippet("ruby",{style:"rand01",transparent:true,showNum:true});
|
||||
// with a transparent background
|
||||
// without showing line numbers.
|
||||
|
||||
|
||||
|
||||
$("pre.javascript").snippet("javascript",{style:"rand01",transparent:true,showNum:true});
|
||||
// Finds <pre> elements with the class "js"
|
||||
// and snippet highlights the JAVASCRIPT code within
|
||||
// using a random style from the selection of 39
|
||||
// with a transparent background
|
||||
// without showing line numbers.
|
||||
|
||||
};
|
||||
|
||||
var rAmp = /&/g,
|
||||
rLt = /</g,
|
||||
rGt = />/g,
|
||||
rApos = /\'/g,
|
||||
rQuot = /\"/g,
|
||||
hChars = /[&<>\"\']/;
|
||||
|
||||
function hoganEscape(str) {
|
||||
str = coerceToString(str);
|
||||
return hChars.test(str) ?
|
||||
str
|
||||
.replace(rAmp, '&')
|
||||
.replace(rLt, '<')
|
||||
.replace(rGt, '>')
|
||||
.replace(rApos, ''')
|
||||
.replace(rQuot, '"') :
|
||||
str;
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
rubyCodeFormat()
|
||||
});
|
||||
|
||||
@@ -6,6 +6,23 @@ class AdminController < ApplicationController
|
||||
def dashboard
|
||||
end
|
||||
|
||||
def analytics
|
||||
if params[:field].nil?
|
||||
fields = "*"
|
||||
else
|
||||
fields = params[:field].map {|k,v| k }.join(",")
|
||||
# This seems to be a bit safer
|
||||
#fields = params[:field].map {|k,v| Analytics.parse_field(k) }.join(",")
|
||||
end
|
||||
|
||||
if params[:ip]
|
||||
@analytics = Analytics.hits_by_ip(params[:ip], fields)
|
||||
else
|
||||
@analytics = Analytics.all
|
||||
end
|
||||
render "layouts/admin/_analytics"
|
||||
end
|
||||
|
||||
def get_all_users
|
||||
@users = User.all
|
||||
render :partial => "layouts/admin/get_all_users"
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
class Api::V1::MobileController < ApplicationController
|
||||
|
||||
skip_before_filter :authenticated
|
||||
before_filter :mobile_request?
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
if params[:class]
|
||||
model = params[:class].classify.constantize
|
||||
respond_with model.find(params[:id]).to_json
|
||||
end
|
||||
end
|
||||
|
||||
def index
|
||||
if params[:class]
|
||||
model = params[:class].classify.constantize
|
||||
respond_with model.all.to_json
|
||||
else
|
||||
respond_with nil.to_json
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def mobile_request?
|
||||
if session[:mobile_param]
|
||||
session[:mobile_param] == "1"
|
||||
else
|
||||
request.user_agent =~ /ios|android/i
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,7 +1,7 @@
|
||||
class ApplicationController < ActionController::Base
|
||||
|
||||
before_filter :authenticated, :has_info
|
||||
helper_method :current_user, :is_admin?
|
||||
before_filter :authenticated, :has_info, :create_analytic
|
||||
helper_method :current_user, :is_admin?, :sanitize_font
|
||||
|
||||
# Our security guy keep talking about sea-surfing, cool story bro.
|
||||
# protect_from_forgery
|
||||
@@ -45,4 +45,13 @@ class ApplicationController < ActionController::Base
|
||||
redirect_to home_dashboard_index_path if redirect
|
||||
end
|
||||
|
||||
def create_analytic
|
||||
Analytics.create({ :ip_address => request.remote_ip, :referrer => request.referrer, :user_agent => request.user_agent})
|
||||
end
|
||||
|
||||
def sanitize_font(css)
|
||||
css
|
||||
# css if css.match(/\A[0-9]+([\%]|pt)\z/)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -7,7 +7,7 @@ class BenefitFormsController < ApplicationController
|
||||
|
||||
def download
|
||||
begin
|
||||
path = Rails.root.join('public', 'docs', params[:name])
|
||||
path = params[:name]
|
||||
file = params[:type].constantize.new(path)
|
||||
send_file file, :disposition => 'attachment'
|
||||
rescue
|
||||
|
||||
@@ -4,6 +4,11 @@ class DashboardController < ApplicationController
|
||||
|
||||
def home
|
||||
@user = current_user
|
||||
|
||||
# See if the user has a font preference
|
||||
if params[:font]
|
||||
cookies[:font] = params[:font]
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
class Analytics < ActiveRecord::Base
|
||||
attr_accessible :ip_address, :referrer, :user_agent
|
||||
|
||||
scope :hits_by_ip, ->(ip,col="*") { select("#{col}").where(:ip_address => ip).order("id DESC")}
|
||||
|
||||
def self.count_by_col(col)
|
||||
calculate(:count, col)
|
||||
end
|
||||
|
||||
def self.parse_field(field)
|
||||
valid_fields = ["ip_address", "referrer", "user_agent"]
|
||||
|
||||
if valid_fields.include?(field)
|
||||
field
|
||||
else
|
||||
"1"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4,7 +4,10 @@ class Message < ActiveRecord::Base
|
||||
validates_presence_of :creator_id, :receiver_id, :message
|
||||
|
||||
def creator_name
|
||||
creator = User.where(:id => self.creator_id).first
|
||||
if creator = User.where(:user_id => self.creator_id).first
|
||||
creator.full_name
|
||||
else
|
||||
"<b>Name unavailable</b>".html_safe
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -13,7 +13,7 @@
|
||||
<!-- Begin Widget Body -->
|
||||
<div class="widget-body">
|
||||
Click on PDF to download<br/><br/>
|
||||
<%= link_to download_path(:type => "File", :name => "Health_n_Stuff.pdf") do %>
|
||||
<%= link_to download_path(:type => "File", :name => "public/docs/Health_n_Stuff.pdf") do %>
|
||||
<div class="doc-icons-container">
|
||||
<div class="icon light-blue hidden-tablet">
|
||||
<span class="fs1 doc-icon" aria-hidden="true" data-icon=""></span>
|
||||
@@ -39,7 +39,7 @@
|
||||
<!-- Begin Widget Body -->
|
||||
<div class="widget-body">
|
||||
Click on PDF to download<br/><br/>
|
||||
<%= link_to download_path(:type => "File", :name => "Dental_n_Stuff.pdf") do %>
|
||||
<%= link_to download_path(:type => "File", :name => "public/docs/Dental_n_Stuff.pdf") do %>
|
||||
<div class="doc-icons-container">
|
||||
<div class="icon light-blue hidden-tablet">
|
||||
<span class="fs1 doc-icon" aria-hidden="true" data-icon=""></span>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<form action="">
|
||||
Search by IP: <input type="text" name="ip"><br />
|
||||
<input type="checkbox" value="" name="field[ip_address]"> IP Address<br />
|
||||
<input type="checkbox" value="" name="field[referrer]"> Referrer<br />
|
||||
<input type="checkbox" value="" name="field[user_agent]"> User Agent
|
||||
</form>
|
||||
|
||||
<div id="dt_example" class="example_alt_pagination">
|
||||
<table class="table table-striped table-hover table-bordered pull-left" id="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<%
|
||||
count = (params[:field] ? params[:field].count : 3)
|
||||
count.times do %>
|
||||
<td> </td>
|
||||
<% end %>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @analytics.each do |a|%>
|
||||
<tr>
|
||||
<% a.attributes.each do |k,v| %>
|
||||
<td><%= v %></td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="editAcct" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true">
|
||||
</div>
|
||||
<div class="clearfix">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function dataTablePagination(){
|
||||
$('#data-table').dataTable({
|
||||
"sPaginationType": "full_numbers"
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$(document).ready(dataTablePagination());
|
||||
</script>
|
||||
@@ -6,13 +6,13 @@
|
||||
<%= javascript_include_tag "application" %>
|
||||
<%= csrf_meta_tags %> <!-- <~ What is this for? I hear it helps w/ JS and Sea-surfing.....whatevz -->
|
||||
<!-- bootstrap css -->
|
||||
<script type="text/javascript" src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<!--[if lte IE 7]>
|
||||
<script src="assets/fonts/lte-ie7.js">
|
||||
</script>
|
||||
<![endif]-->
|
||||
|
||||
<!-- Google Visualization JS -->
|
||||
<%
|
||||
if cookies[:font]
|
||||
%>
|
||||
<style>body { font-size:<%= cookies[:font] %> !important;}</style>
|
||||
<%
|
||||
end
|
||||
%>
|
||||
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
|
||||
|
||||
</head>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<header>
|
||||
<a href="#l" class="logo">
|
||||
|
||||
</a>
|
||||
|
||||
<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">
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
<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">
|
||||
<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="collapseFive" style="height: auto;">
|
||||
<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.
|
||||
@@ -24,13 +24,13 @@
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseSix" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
||||
<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="collapseSix" style="height: 0px;">
|
||||
<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.
|
||||
@@ -81,13 +81,13 @@
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a href="#collapseSeven" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
||||
<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="collapseSeven" style="height: 0px;">
|
||||
<div class="accordion-body collapse" id="collapseEleven" style="height: 0px;">
|
||||
<div class="accordion-inner">
|
||||
<p><b>Command Injection - ATTACK</b></p>
|
||||
<p class="desc">
|
||||
@@ -139,13 +139,13 @@
|
||||
</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">
|
||||
<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="collapseEight" style="height: 0px;">
|
||||
<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>
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
<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>
|
||||
@@ -0,0 +1,125 @@
|
||||
<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,3 +1,19 @@
|
||||
<div align="right">
|
||||
<!-- 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>" + paramValue + "</OPTION>");
|
||||
} catch(err) {
|
||||
}
|
||||
//document.write("</select>");
|
||||
</script>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<h2 align="center">MetaCorp</h2>
|
||||
<h3 align="center">A GoatGroup Company</h3>
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
<%= 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"%>
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
<%= 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>
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ Railsgoat::Application.routes.draw do
|
||||
post "delete_user"
|
||||
put "update_user"
|
||||
get "get_all_users"
|
||||
get "analytics"
|
||||
end
|
||||
|
||||
resources :dashboard do
|
||||
@@ -93,6 +94,7 @@ Railsgoat::Application.routes.draw do
|
||||
namespace :api, defaults: {format: 'json'} do
|
||||
namespace :v1 do
|
||||
resources :users
|
||||
resources :mobile
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
class CreateAnalytics < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :analytics do |t|
|
||||
t.string :ip_address
|
||||
t.string :referrer
|
||||
t.string :user_agent
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
||||
+9
-1
@@ -11,7 +11,15 @@
|
||||
#
|
||||
# It's strongly recommended to check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 20140315002730) do
|
||||
ActiveRecord::Schema.define(:version => 20140408185601) do
|
||||
|
||||
create_table "analytics", :force => true do |t|
|
||||
t.string "ip_address"
|
||||
t.string "referrer"
|
||||
t.string "user_agent"
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
end
|
||||
|
||||
create_table "benefits", :force => true do |t|
|
||||
t.datetime "created_at", :null => false
|
||||
|
||||
@@ -11,7 +11,7 @@ feature 'insecure direct object reference' do
|
||||
|
||||
visit "/users/#{@normal_user.user_id}/benefit_forms"
|
||||
download_url = first('.widget-body a')[:href]
|
||||
visit download_url.sub(/name=(.*?)&/, 'name=../../config/database.yml&')
|
||||
visit download_url.sub(/name=(.*?)&/, 'name=config/database.yml&')
|
||||
|
||||
pending(:if => verifying_fixed?) {
|
||||
page.status_code.should == 200
|
||||
|
||||
Reference in New Issue
Block a user