inguardians / peirates Goto Github PK
View Code? Open in Web Editor NEWPeirates - Kubernetes Penetration Testing tool
Home Page: https://www.inguardians.com/peirates/
License: GNU General Public License v2.0
Peirates - Kubernetes Penetration Testing tool
Home Page: https://www.inguardians.com/peirates/
License: GNU General Public License v2.0
[13] Request IAM credentials from GCP Metadata API [get-gcp-token] *
Feature request: let ParseLocalServerInfo() try kube DNS for a kubernetes service
allow any output to be saved into raw text and/or structured objects (YAML/JSON), save this output in a named object that the user can recall, parse with jq-type stuff.
Alternatively, have output from kubectl interactive commands write out to files that the user can refer to later and parse with their own tools.
Need a way to automate testing on each type of cluster.
Peirates:># enumerate-dns
Requesting SRV record any.any.svc.cluster.local - thank @raesene:
no services returned or some kind of error
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x1861b65]
goroutine 1 [running]:
github.com/inguardians/peirates.enumerateDNS()
/Users/jay/code/peirates/enumerate_dns.go:61 +0x125
github.com/inguardians/peirates.Main()
/Users/jay/code/peirates/peirates.go:1192 +0x2b4b
main.main()
/Users/jay/code/peirates/cmd/peirates/peirates.go:17 +0x1bc
if err != nil {
println("no services returned or some kind of error")
}
We need to make this return whenever we catch the error.
This is the error that I am getting when trying to list the pods:
Peirates:>#
3
[+] Printing a list of Pods in this namespace......
error: unable to read certificate-authority /run/secrets/kubernetes.io/serviceaccount/ca.crt for due to open /run/secrets/kubernetes.io/serviceaccount/ca.crt: no such file or directory
The service account secrets are actually located in /var/run/secrets/... Should the path be made configurable?
(Peirates version v1.0.20)
go vet
./kubeapi.go:80:15: assignment copies lock value to transport: net/http.Transport contains sync.Mutex
This code in config.go does not appear to actually get the discovered path into the kubeletKubeconfigFilePaths list, as the dynamically-found file path isn't used in the for loop on the last line.
kubeletKubeconfigFilePaths := make([]string, 0)
psDiscoveredPath, err := getKubeletKubeconfigPath()
if err != nil {
kubeletKubeconfigFilePaths = append(kubeletKubeconfigFilePaths, psDiscoveredPath)
}
kubeletKubeconfigFilePaths = append(kubeletKubeconfigFilePaths, "/var/lib/kubelet/kubeconfig")
kubeletKubeconfigFilePaths = append(kubeletKubeconfigFilePaths, "/etc/kubernetes/kubeconfig")
kubeletKubeconfigFilePaths = append(kubeletKubeconfigFilePaths, "/etc/kubernetes/kubelet.conf")
for _, path := range kubeletKubeconfigFilePaths {
Something like this, dependabot or equivalent please.
feature-request-abstracting-22-into-22-and-23
// [23] Use the kubelet to run a command in every pod
ExecuteCodeOnKubelet( , cmd )
// [22] Use the kubelet to gain the token in every pod where we can run a command
GetTokensFromKubeletExec( , &serviceAccounts)
GetIPAddressesOfNodes
foreach node
pods = GetParsedPods(node)
foreach pod in pods:
output = run_command_via_kubelet(pod,cat_token)
build_service_account(pod,output)
ExecuteCodeOnKubelet
Enter a node IP or select one from GetIPAddressesOfNodes
[1] All Nodes
[2] Enter Node IP address
[2] Discover Node IP addresses and choose a node IP
pods = GetParsedPods(node)
ListPods (pods)
[1] namespace1 - pod1 - container1
[2] namespace2 - pod2 - container2
Enter a pod to run on or enter All
Enter your command: user_command
if choice == All
set_of_pods = pods
else
set_of_pods = choice
for pod in set_of_pods:
run_command_via_kubelet(pod,user_command)
-----------+
Compromise |
-----------+
[20] Gain a reverse rootshell on a node by launching a hostPath-mounting pod
[21] Run command in one or all pods in this namespace via the API Server (RBAC permitting)
[22] Run a token-dumping command in all pods via Kubelets (authorization/Webhook permitting)
[30] Inject peirates into another pod via API Server (RBAC permitting)
-----------------+
Off-Menu +
-----------------+
[0] Run a kubectl command in the current namespace and service account context [beta]
[exit] Exit Peirates
----------------------------------------------------------------
Peirates:>#
20
What IP and Port will your netcat listener be listening on?
IP:
10.128.0.28
Port:
4444
Error from server (NotFound): pods "position-checker-74cd45fdf5-2v9fd" not found
root@position-checker-74cd45fdf5-2v9fd:/#
auth can-i should not prevent peirates from trying.
Rename/refactor MountRootFS so we get more capabilities in case the node does not run cron
Option 20 from main menu.
[0] Run a kubectl command in the current namespace and service account context [beta]
[exit] Exit Peirates
----------------------------------------------------------------
Peirates:>#
2
[1] List namespaces
[2] Switch namespace
[3] List namespaces then switch namespaces
1
Error from server (Forbidden): namespaces is forbidden: User "system:serviceaccount:default:default" cannot list resource "namespaces" in API group "" at the cluster scope
Am I the only getting all these errors?
go install -v github.com/inguardians/peirates/cmd/peirates@latest
github.com/inguardians/peirates
# github.com/inguardians/peirates
go/pkg/mod/github.com/inguardians/[email protected]/attack_create_hostfs_pod.go:12:6: attackHostPathMount redeclared in this block
go/pkg/mod/github.com/inguardians/[email protected]/attack-create-hostfs-pod.go:12:6: other declaration of attackHostPathMount
go/pkg/mod/github.com/inguardians/[email protected]/attack_create_hostfs_pod.go:45:6: MountRootFS redeclared in this block
go/pkg/mod/github.com/inguardians/[email protected]/attack-create-hostfs-pod.go:45:6: other declaration of MountRootFS
go/pkg/mod/github.com/inguardians/[email protected]/exec_in_pods.go:5:6: execInPodMenu redeclared in this block
go/pkg/mod/github.com/inguardians/[email protected]/exec-in-pods.go:5:6: other declaration of execInPodMenu
go/pkg/mod/github.com/inguardians/[email protected]/exec_in_pods.go:63:6: execInAllPods redeclared in this block
go/pkg/mod/github.com/inguardians/[email protected]/exec-in-pods.go:63:6: other declaration of execInAllPods
go/pkg/mod/github.com/inguardians/[email protected]/exec_in_pods.go:69:6: execInListPods redeclared in this block
go/pkg/mod/github.com/inguardians/[email protected]/exec-in-pods.go:69:6: other declaration of execInListPods
go/pkg/mod/github.com/inguardians/[email protected]/exec_via_kubelet_api.go:15:6: ExecuteCodeOnKubelet redeclared in this block
go/pkg/mod/github.com/inguardians/[email protected]/exec-via-kubelet-api.go:15:6: other declaration of ExecuteCodeOnKubelet
go/pkg/mod/github.com/inguardians/[email protected]/list_secrets.go:3:6: listSecrets redeclared in this block
go/pkg/mod/github.com/inguardians/[email protected]/listSecrets.go:3:6: other declaration of listSecrets
go/pkg/mod/github.com/inguardians/[email protected]/menu_cert_auth.go:11:6: setUpCompletionCertMenu redeclared in this block
go/pkg/mod/github.com/inguardians/[email protected]/menu-certauth.go:11:6: other declaration of setUpCompletionCertMenu
go/pkg/mod/github.com/inguardians/[email protected]/menu_cert_auth.go:19:6: certMenu redeclared in this block
go/pkg/mod/github.com/inguardians/[email protected]/menu-certauth.go:19:6: other declaration of certMenu
go/pkg/mod/github.com/inguardians/[email protected]/menu_namespaces.go:11:6: setUpCompletionNsMenu redeclared in this block
go/pkg/mod/github.com/inguardians/[email protected]/menu-namespaces.go:11:6: other declaration of setUpCompletionNsMenu
go/pkg/mod/github.com/inguardians/[email protected]/menu_namespaces.go:11:6: too many errors
Context:
go version go1.22.2 linux/amd64
Linux 6.8.0-35-generic
Ubuntu Noble 24.04 LTS
[12] Request IAM credentials from AWS Metadata API [get-aws-token] *
[13] Request IAM credentials from GCP Metadata API [get-gcp-token] *
[15] Pull Kubernetes service account tokens from kops' GCS bucket (Google Cloud only) [attack-kops-gcs-1] *
https://github.com/peterh/liner
or
https://github.com/candid82/liner
supports editing shortcuts, tab completion.
the second link is a fork of the first - need to look into what is better maintained
Here is a description of the problem with the naming of the gnostic module
From the top level:
~/w/peirates (dev34-20230101±) ▶ go get -t -u ./...
go get: github.com/googleapis/[email protected] updating to
github.com/googleapis/[email protected]: parsing go.mod:
module declares its path as: github.com/google/gnostic
but was required as: github.com/googleapis/gnostic
[15] Pull Kubernetes service account tokens from kops' GCS bucket (Google Cloud only) [attack-kops-gcs-1] *
// Run a command on a pod from the Kubelet
// Get a list of roles for this service account
// Get a list of roles available on the cluster
// Get a list of abilities for a role
// Request list of pods from a Kubelet
// Build YAML Manifests
We're potentially writing a new CA.CRT file every time we switch to a client cert-key pair - we could solve this with something like the following code in assignAuthenticationCertificateAndKeyToConnection()
and assignServiceAccountToConnection()
, but we'd need to back up the CA.CRT path whenever we switch to token again.
// If our current authz context had a cert auth path file (i.e., if we were using a cert-key pair), delete it
if len(info.ClientKeyPath) > 0 {
os.Remove(info.CAPath)
}
Feature request: let ParseLocalServerInfo() try kube DNS for a kubernetes service
Example - use option 0 to run kubectl commands:
[exit] Exit Peirates
----------------------------------------------------------------
Peirates:>#
0
This function allows you to run a kubectl command, with only a few restrictions.
Your command must not:
- change namespace
- specify a different service account
- change nameservers
- run for longer than a few seconds (as in kubectl exec)
Your command will crash this program if it is not permitted.
These requirements are dynamic - watch new versions for changes.
Leave off the "kubectl" part of the command. For example:
- get pods
- get pod podname -o yaml
- get secret secretname -o yaml
Please enter a kubectl command: get pods
Argument: get
Argument: pods
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:default" cannot list resource "pods" in API group "" in the namespace "default"
[11] Get a service account token from a secret
[12] Request IAM credentials from AWS Metadata API [AWS only]
[13] Request IAM credentials from GCP Metadata API [GCP only]
[14] Request kube-env from GCP Metadata API [GCP only]
[15] Pull Kubernetes service account tokens from Kop's bucket in GCS [GCP only]
--------------------------------+
Interrogate/Abuse Cloud API's |
--------------------------------+
[17] List AWS S3 Buckets accessible (Auto-Refreshing Metadata API credentials) [AWS]
[18] List contents of an AWS S3 Bucket (Auto-Refreshing Metadata API credentials) [AWS]
-----------+
Compromise |
-----------+
[20] Gain a reverse rootshell on a node by launching a hostPath-mounting pod
[21] Run command in one or all pods in this namespace via the API Server (RBAC permitting)
[22] Run a token-dumping command in all pods via Kubelets (authorization/Webhook permitting)
[30] Inject peirates into another pod via API Server (RBAC permitting)
-----------------+
Off-Menu +
-----------------+
[0] Run a kubectl command in the current namespace and service account context [beta]
[exit] Exit Peirates
----------------------------------------------------------------
Peirates:>#
11
Please enter the name of the secret for which you'd like the contents:
k
Error from server (NotFound): secrets "k" not found
Hello,
While testing peirates v1.1.13 on an AWS account which enforces using IMDSv2 (Instance Metadata Service version 2), get-aws-token
fails:
Peirates:># get-aws-token
[-] Error - problem with JSON unmarshal
IAM Credentials for user are:
aws_access_key_id =
aws_secret_access_key =
aws_session_token =
Press enter to continue
Moreover, the AWS autodiscovery failed:
Checking AWS...
[-] DoHTTPRequestAndGetBody got a 401 Unauthorized status instead of a successful 2XX status. Failing and printing response:
[-] GetRequest could not perform request to http://169.254.169.254/latest/ : DoHTTPRequestAndGetBody failed with status 401 Unauthorized
According to https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html, this is because requesting http://169.254.169.254/latest/meta-data/
requries a token (in HTTP header X-aws-ec2-metadata-token:
), which needs to be first requested with a HTTP PUT request to http://169.254.169.254/latest/api/token
. Moreover I confirm that doing these requests with curl
work fine in my environment.
Is there any plan to add support of AWS IMDSv2 to peirates?
[+] Transferring a copy of Peirates into pod: kube-controller-manager-master-us-central1-a-c3mm
[+] Transfer successful
Token location error: open /var/run/secrets/kubernetes.io/serviceaccount/token: no such file or directory
Namespaces location error open /var/run/secrets/kubernetes.io/serviceaccount/namespace: no such file or directory
[0/0]0x0
Usage of /tmp/peirates:
-L string
List of comma-seperated Pods: ex pod1,pod2,pod3
-c string
Command to run in pods (default "hostname")
-i string
API Server IP address: ex. 10.22.34.67
-p string
API Server Port: ex 443, 6443
2020/08/14 21:36:46 Error: must provide remote IP address (-i)
command terminated with exit code 1
IAM Credentials for user AWS Credentials from Environment Variables are:
aws_access_key_id =
aws_secret_access_key =
aws_session_token =
this is almost certainly the cause of #4 , #5 , #6 , #7
this command was never intended to be used as a library and we probably should not have done it.
here are the current usages, including commented out ones
peirates.go
41: responseJSON, _, err := runKubectlSimple(connectionString, "get", "pods", "-o", "json")
89: secretsJSON, _, err := runKubectlSimple(connectionString, "get", "secrets", "-o", "json")
188: NamespacesRaw, _, err := runKubectlSimple(connectionString, "get", "namespaces")
242: execInPodOut, _, err := runKubectlSimple(connectionString, "exec", "-it", execPod, "--", "/bin/sh", "-c", command)
264: copyIntoPod, _, err := runKubectlSimple(connectionString, "cp", filename, destination)
274: // runKubectlSimple (exec -it pod /tmp/peirates)
496: podDetailOut, _, err := runKubectlSimple(connectionString, "get", "pods", "-o", "json")
540: rolesOut, _, err := runKubectlSimple(connectionString, "get", "role", "-o", "json")
572: podDescriptionRaw, _, err := runKubectlSimple(connectionString, "describe", "pod", hostname)
619: getImagesRaw, _, err := runKubectlSimple(connectionString, "get", "pods", "-o", "wide", "--sort-by", "metadata.creationTimestamp")
670: _, _, err = runKubectlSimple(connectionString, "apply", "-f", "attack-pod.yaml")
678: //shadowFileBs, _, err := runKubectlSimple(connectionString, "exec", "-it", attackPodName, "grep", "root", "/root/etc/shadow")
679: //_, _, err := runKubectlSimple(connectionString, "exec", "-it", attackPodName, "grep", "root", "/root/etc/shadow")
683: err := runKubectlWithConfig(connectionString, stdin, &stdout, &stderr, "exec", "-it", attackPodName, "--", "/bin/sh", "-c", "cat >> /root/etc/crontab")
686: // BUG: when we remove that timer above and thus get an error condition, program crashes during the runKubectlSimple instead of reaching this message
817: podDetailOut, _, err := runKubectlSimple(connectionString, "get", "nodes", "-o", "json")
832: nodeDetailOut, _, err := runKubectlSimple(connectionString, "get", "nodes", "-o", "json")
1060: // func runKubectlSimple(cfg ServerInfo, cmdArgs ...string) ([]byte, []byte, error) {
1061: kubectlOutput, _, err := runKubectlSimple(connectionString, arguments...)
1189: secretJSON, _, err := runKubectlSimple(connectionString, "get", "secret", secretName, "-o", "json")
and here are the unique forms of usage:
apply -f <yaml>
cp <src> <destination>
describe pod <pod>
exec -it <pod> -- /bin/sh -c "cat >> /root/etc/crontab"
exec -it <pod> -- /bin/sh -c <command>
get namespaces
get nodes -o json
get pods -o json
get pods -o wide --sort-by "metadata.creationTimestamp"
get role -o json
get secret <secret> -o json
get secrets -o json
additionally, there is a command that allows for arbitrary kubectl commands.
I see a few paths moving forward.
exec
is harder. running arbitrary kubectl commands would be impossible without uploading a second binaryCould we make any Peirates e-mail addresses ([email protected]) go to this e-mail address instead please:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.