lib/ronin/sql/injection.rb in ronin-sql-0.2.4 vs lib/ronin/sql/injection.rb in ronin-sql-1.0.0
- old
+ new
@@ -1,11 +1,12 @@
#
-# Ronin SQL - A Ronin library providing support for SQL related security
-# tasks.
+# Ronin SQL - A Ruby DSL for crafting SQL Injections.
#
-# Copyright (c) 2007-2009 Hal Brodigan (postmodern.mod3 at gmail.com)
+# Copyright (c) 2007-2013 Hal Brodigan (postmodern.mod3 at gmail.com)
#
+# This file is part of Ronin SQL.
+#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
@@ -17,198 +18,165 @@
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
-require 'ronin/sql/error'
-require 'ronin/code/sql/injection'
-require 'ronin/sessions/http'
-require 'ronin/extensions/uri'
-require 'ronin/web/extensions/nokogiri'
-require 'ronin/web/spider'
+require 'ronin/sql/binary_expr'
+require 'ronin/sql/literals'
+require 'ronin/sql/clauses'
+require 'ronin/sql/statement_list'
-require 'parameters'
-require 'nokogiri'
-
module Ronin
module SQL
- class Injection
+ #
+ # Represents a SQL injection (SQLi).
+ #
+ class Injection < StatementList
- include Parameters
- include Sessions::HTTP
+ include Literals
+ include Clauses
- # The URL to inject upon
- attr_reader :url
+ # Default place holder values.
+ PLACE_HOLDERS = {
+ integer: 1,
+ decimal: 1.0,
+ string: '1',
+ list: [nil],
+ column: :id
+ }
- # The URL query param to inject into
- attr_reader :param
+ # The type of element to escape out of
+ attr_reader :escape
- # Options for crafting SQL injections
- attr_reader :sql_options
+ # The place holder data
+ attr_reader :place_holder
- # HTTP request method (either :get or :post)
- parameter :http_method,
- :default => :get,
- :description => 'HTTP request method to use'
+ # The expression that will be injected
+ attr_reader :expression
#
- # Creates a new Injection object with the specified _url_, _param_
- # to inject upon and the given _options_ which will be used
- # for crafting SQL injections.
+ # Initializes a new SQL injection.
#
- def initialize(url,param,options={})
- super()
-
- @url = url
- @param = param
- @sql_options = options
- end
-
+ # @param [Hash] options
+ # Additional injection options.
#
- # Spider a site starting at the specified _url_ using the given
- # _options_ and return an Array of URLs which are vulnerable to SQL
- # Injection. If a _block_ is given, it will be passed vulnerable SQL
- # Injection objects as they are found.
+ # @option options [:integer, :decimal, :string, :column] :escape (:integer)
+ # The type of element to escape out of.
#
- # Injection.spider('http://www.target.com/contact/')
- # # => [...]
+ # @option options [Boolean] :terminate
+ # Specifies whether to terminate the SQLi with a comment.
#
- # Injection.spider('http://www.target.com/') do |injection|
- # ...
- # end
+ # @option options [String, Symbol, Integer] :place_holder
+ # Place-holder data.
#
- def Injection.spider(url,options={},&block)
- injections = []
-
- Web::Spider.site(url,options) do |spider|
- spider.every_url_like(/\?[a-zA-Z0-9_]/) do |vuln_url|
- found = vuln_url.sql_injections
-
- found.each(&block) if block
- injections += found
- end
- end
-
- return injections
- end
-
+ # @yield [(injection)]
+ # If a block is given, it will be evaluated within the injection.
+ # If the block accepts an argument, the block will be called with the
+ # new injection.
#
- # Creates a new Code::SQL::Injection object using the given _options_
- # and _block_. The given _options_ will be merged with the injections
- # sql_options, to create a tailored Code::SQL::Injection object.
+ # @yieldparam [Injection] injection
+ # The new injection.
#
- def sql(options={},&block)
- Code::SQL::Injection.new(@sql_options.merge(options),&block)
- end
-
- def inject(options={},&block)
- injection = (options[:sql] || sql(options,&block))
-
- injection_url = URI(@url.to_s)
- injection_url.query_params[@param.to_s] = injection
-
- request_method = (options[:method] || @http_method)
- options = options.merge(:url => injection_url)
-
- if request_method == :post
- return http_post_body(options)
- else
- return http_get_body(options)
+ def initialize(options={},&block)
+ @escape = options.fetch(:escape,:integer)
+ @place_holder = options.fetch(:place_holder) do
+ PLACE_HOLDERS.fetch(@escape)
end
- end
- def inject_error(options={})
- inject({:sql => "'"}.merge(options))
- end
+ @expression = @place_holder
- def error(options={})
- inject_error(options).sql_error
+ super(&block)
end
- def has_error?(options={})
- Error.has_message?(inject_error(options))
- end
+ #
+ # Appends an `AND` expression to the injection.
+ #
+ # @yield [(injection)]
+ # The return value of the block will be used as the right-hand side
+ # operand. If the block accepts an argument, it will be called with
+ # the injection.
+ #
+ # @yieldparam [Injection] injection
+ #
+ # @return [self]
+ #
+ def and(&block)
+ value = case block.arity
+ when 0 then instance_eval(&block)
+ else block.call(self)
+ end
- def vulnerable?(options={})
- body1 = inject(options) { no_rows }
- body2 = inject(options) { all_rows }
-
- if (body1.sql_error? || body2.sql_error?)
- return false
- end
-
- body1 = Nokogiri::HTML(body1)
- body2 = Nokogiri::HTML(body2)
-
- return body1.total_children < body2.total_children
+ @expression = BinaryExpr.new(@expression,:AND,value)
+ return self
end
- def has_column?(column,options={})
- body1 = inject(options)
- body2 = inject(options.merge(:symbols => {:column => column})) do
- has_column?(column)
- end
+ #
+ # Appends an `OR` expression to the injection.
+ #
+ # @yield [(injection)]
+ # The return value of the block will be used as the right-hand side
+ # operand. If the block accepts an argument, it will be called with
+ # the injection.
+ #
+ # @yieldparam [Injection] injection
+ #
+ # @return [self]
+ #
+ def or(&block)
+ value = case block.arity
+ when 0 then instance_eval(&block)
+ else block.call(self)
+ end
- if (body1.sql_error? || body2.sql_error?)
- return false
- end
-
- body1 = Nokogiri::HTML(body1)
- body2 = Nokogiri::HTML(body2)
-
- return body1.total_children == body2.total_children
+ @expression = BinaryExpr.new(@expression,:OR,value)
+ return self
end
- def has_table?(table,options={})
- body1 = inject(options)
- body2 = inject(options.merge(:symbols => {:table => table})) do
- has_table?(table)
- end
+ #
+ # Converts the SQL injection to SQL.
+ #
+ # @param [Hash] options
+ # Additional options for {Emitter#initialize}.
+ #
+ # @option options [Boolean] :terminate
+ # Specifies whether to terminate the injection with `;--`.
+ #
+ # @return [String]
+ # The raw SQL.
+ #
+ def to_sql(options={})
+ emitter = emitter(options)
+ sql = ''
- if (body1.sql_error? || body2.sql_error?)
- return false
- end
+ sql << emitter.emit(@expression)
- body1 = Nokogiri::HTML(body1)
- body2 = Nokogiri::HTML(body2)
-
- return body1.total_children == body2.total_children
- end
-
- def uses_column?(column,options={})
- body1 = inject(options)
- body2 = inject(options.merge(:symbols => {:column => column})) do
- uses_column?(table)
+ unless clauses.empty?
+ sql << emitter.space << emitter.emit_clauses(clauses)
end
- if (body1.sql_error? || body2.sql_error?)
- return false
+ unless statements.empty?
+ sql << ';' << emitter.space << emitter.emit_statement_list(self)
end
- body1 = Nokogiri::HTML(body1)
- body2 = Nokogiri::HTML(body2)
+ case @escape
+ when :string, :list
+ if (options[:terminate] || (sql[0,1] != sql[-1,1]))
+ # terminate the expression
+ sql << ';--'
+ else
+ sql = sql[0..-2]
+ end
- return body1.total_children == body2.total_children
- end
-
- def uses_table?(table,options={})
- body1 = inject(options)
- body2 = inject(options.merge(:symbols => {:table => table})) do
- uses_table?(table)
+ # balance the quotes
+ sql = sql[1..-1]
+ else
+ if options[:terminate]
+ # terminate the expression
+ sql << ';--'
+ end
end
- if (body1.sql_error? || body2.sql_error?)
- return false
- end
-
- body1 = Nokogiri::HTML(body1)
- body2 = Nokogiri::HTML(body2)
-
- return body1.total_children == body2.total_children
- end
-
- def to_s
- @url.to_s
+ return sql
end
end
end
end