-
Notifications
You must be signed in to change notification settings - Fork 14.2k
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
Add Module For Kubernetes Pod Authenticated Code Execution #15733
Conversation
'uri' => http_client.normalize_uri("/api/v1/namespaces/#{namespace}/pods/#{name}/exec"), | ||
'query' => build_query_string({ | ||
'command' => command, | ||
'stdin' => !!options.delete('stdin'), |
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.
What is this guarding against? 👀
Strings are always truthy in Ruby, so I'm not sure what data type we're coercing here:
2.7.2 :006 > !!false
=> false
2.7.2 :007 > !!''
=> true
2.7.2 :008 > !!'false'
=> true
2.7.2 :009 > !!'true'
=> true
2.7.2 :010 > !!0
=> true
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 is ensuring that the values are boolean, which is required to make sure they're mapped to 'true'
and 'false'
strings relying on Ruby's #to_s
method. If a string value like "no"
was passed in, it would be sent as is to the REST API and is invalid in this context.
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.
If the string 'no' was passed in, it would still end up as being set to true? 👀
2.7.2 :006 > args = {stdin: 'no'}
=> { :stdin=>"no" }
2.7.2 :007 > !!args.delete(:stdin)
=> true
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.
Or rather, if it's not a boolean, something's gone wrong - as !!
will force most things to be a boolean true in Ruby - which doesn't seem like great fallback behavior for unintended types being passed in
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.
Yes that's intended because strings are truthy in Ruby. I could write this more explicitly as options.delete('stdin') ? 'true' : 'false'
if that would improve readability in your opinion.
{ | ||
name: random_identifiers[:volume_name], | ||
hostPath: { | ||
path: '/Users' |
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.
I threw in /Users
here as a test, but I think we should mount /
, make it configurable, or use a collection of potentially valid paths - as it's possible for the the kubernetes host to restrict the allowed mount locations
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.
Configuring a series of key values pairs wouldn't be easy with our datastore so using a collection of potentially valid paths would probably be a better way to go. You want to let me know what paths you think should be added?
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.
That sounds good to me to make it user configurable, but we'd definitely want to use /
as the default path - as /Users
was specific to my mac environment at the time 😄
print_status('Waiting for the pod to be ready...') | ||
10.times do | ||
sleep 3 | ||
phase = @kubernetes_client.get_pod(pod_name, namespace)&.dig(:status, :phase)&.downcase |
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.
I wonder if it would be useful to log the value of this at least once/occasionally to let the user know something's happening 🤔
The module will now iterate over identified image names by default and also allows an explicit image name to be specified using the new PodImage advanced option.
Release NotesAdds a new |
This adds a module to obtain authenticated code execution within a Kubernetes pod. The user must have a Kubernetes JWT token, and access to the Kubernetes REST API through some means (either direct or through a compromised pod). A session on an existing pod can be used to configure a few options, including the JWT token and RHOST / RPORT options. Some changes were made to the RHOSTS and SESSION validation code to honor instances in which they are marked as optional. When defined, the validation still takes place either way, but when they are marked as optional and blank the validation is skipped to allow the module to run.
When pivoting over a session, connections can timeout occassionally. There's an
HttpClientTimeout
datastore option that can be increased to adjust this, or just retry the module.WebSockets
This includes a new WebSocket implementation built on top of the existing
Rex::Proto::Http
client code. It includes a frame definition, convenient functions for sending and receiving raw frames, a dispatch loop and a channel wrapper for direct read / write operations. The dispatch loop will handle ping requests, unmasking requests, fragmentation of received data, and close requests. It allows the API consumer to just deal with the data frames.The WebSocket can be used to obtain an interactive shell session without delivering a Metasploit payload.
Verification
List the steps needed to make sure this thing works
msfconsole
use exploit/multi/kubernetes/exec
POD
datastore option blank, watch it be created automatically 🪄 )Demo
In this scenario, Metasploit has direct access to the Kubernetes API. A known token is used to execute a Python payload
within the
thinkphp-67f7c88cc9-tgpfh
pod.Next, the compromised session is used to access the internal Kubernetes endpoint, create a new pod and open a shell
directly via a WebSocket.