Git Product home page Git Product logo

peirates's Introduction

Peirates

Release gosec

Logo

What is Peirates?

Peirates, a Kubernetes penetration tool, enables an attacker to escalate privilege and pivot through a Kubernetes cluster. It automates known techniques to steal and collect service account tokens, secrets, obtain further code execution, and gain control of the cluster.

Where do I run Peirates?

You run Peirates from a container running on Kubernetes or from a Kubernetes node, outside the container.

Does Peirates attack a Kubernetes cluster?

Yes, it absolutely does. Talk to your lawyer and the cluster owners before using this tool in a Kubernetes cluster.

Who creates Peirates?

InGuardians' CTO Jay Beale first conceived of Peirates and put together a group of InGuardians developers to create it with him, including Faith Alderson, Adam Crompton and Dave Mayer. Faith convinced us to all learn Golang, so she could implement the tool's use of the kubectl library from the Kubernetes project. Adam persuaded the group to use a highly-interactive user interface. Dave brought contagious enthusiasm. Together, these four developers implemented attacks and began releasing this tool that we use on our penetration tests.

Other contributors have helped as well - see GitHub to see more, but please also review credits.md.

Do you welcome contributions?

Yes, we absolutely do. Submit a pull request and/or reach out to [email protected].

What license is this released under?

Peirates is released under the GPLv2 license.

Running Peirates

If you just want the peirates binary to start attacking things, grab the latest release from the releases page.

Peirates as a Container Image

You can find a useful alpine-peirates container image on Docker Hub, with a version number tag that tracks the Peirates version.

For example, for alpine-peirates:1.1.16, which contains peirates version 1.1.16, run:

docker pull bustakube/alpine-peirates:1.1.16

Building Peirates

However, if you want to build from source, read on!

Get peirates

go get -v "github.com/inguardians/peirates"

Get libary sources if you haven't already (Warning: this will take almost a gig of space because it needs the whole kubernetes repository)

go get -v "k8s.io/kubectl/pkg/cmd" "github.com/aws/aws-sdk-go"

Build the executable

cd $GOPATH/github.com/inguardians/peirates/scripts
./build.sh

This will generate an executable file named peirates in the same directory.

peirates's People

Contributors

3nc0d3r avatar dependabot[bot] avatar devsecfranklin avatar faithanalog avatar haxorthematrix avatar ianlee1521 avatar jaybeale avatar kaihoffman avatar rileydakota avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

peirates's Issues

get-aws-token does not support AWS IMDSv2

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?

enumerate_dns crashes peirates when it doesn't work

Output

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

Code where problem occurs

	if err != nil {
		println("no services returned or some kind of error")
	}

Correction suggestion

We need to make this return whenever we catch the error.

test harness

Need a way to automate testing on each type of cluster.

  • perhaps some terraform that stands things up
  • we could connect to existing clusters that are already running anyway perhaps

might be helpful to add tailscale

Test -m mode : get-aws-token

[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] *

replace or fix runKubectl

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.

  1. Use the API. this is far more reliable and forward compatible. most commands in use are data retrieval and trivial to replace. exec is harder. running arbitrary kubectl commands would be impossible without uploading a second binary
  2. Re-execute the peirates binary from within itself. Add a special switch to peirates that interprets the remaining arguments as a kubectl command and passes them directly to kubectl. If this works it will solve our crashing problems.
  3. Hybrid. Choose option 2 for now. replace things with API calls over time when possible to reduce our kubectl usage surface.

Peirates crashes when an action is forbidden

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"

Detect if we are pod, node, bare metal, or which cloud provider

  • no need to show AWS credentials summary when not using AWS
  • This dialog appears even if the cluster is not AWS/EKS
IAM Credentials for user AWS Credentials from Environment Variables are: 

aws_access_key_id = 
aws_secret_access_key = 
aws_session_token = 

Feature Request: Abstract 22 into 22 and 23

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)

Missing error handling in option 20 (Gain a reverse rootshell on a node by launching a hostPath-mounting pod)

-----------+
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:/# 

List namespaces causes an exit if not authorized

[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

Installation errors

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

Peirates unable to read secrets

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)

Feature requests - menu items

		// 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

Pod injection appears broken

[+] 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

Function 11 (Get a service account token from a secret) crashes when the secret name doesn't exist

[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

kubelet kubeconfig file path not used

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 {

FEATURE REQUEST: leave fewer temp files around from using kubeconfig files

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)
	}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.