Skip to content

Commit 12c5862

Browse files
authored
Merge branch 'master' into update_Charting_your_way_in_Helm_template_injection_c4c3d85e188cf37f
2 parents 37e8c75 + 7c46190 commit 12c5862

20 files changed

Lines changed: 1895 additions & 274 deletions

book.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ command = "python3 ./hacktricks-preprocessor.py"
1515
env = "prod"
1616

1717
[output.html]
18-
additional-css = ["theme/tabs.css", "theme/pagetoc.css"]
18+
additional-css = ["theme/tabs.css", "theme/pagetoc.css", "theme/discount.css"]
1919
additional-js = [
2020
"theme/tabs.js",
2121
"theme/pagetoc.js",
2222
"theme/ht_searcher.js",
2323
"theme/sponsor.js",
24+
"theme/discount.js",
2425
"theme/motion.js",
2526
"theme/ai.js"
2627
]
1.26 MB
Loading

src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,24 @@ You can read more about `gcloud` for containers [here](https://cloud.google.com/
7474

7575
This is a simple script to enumerate kubernetes in GCP: [https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum](https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum)
7676

77+
### Current GKE identity and metadata checks
78+
79+
When reviewing modern GKE clusters, separate Google Cloud IAM permissions, Kubernetes RBAC, pod workload identity, and node credentials. A Google principal can often retrieve cluster endpoint data with `container.clusters.get`, but the resulting Kubernetes requests still need to pass GKE/Kubernetes authorization and any network restrictions such as private endpoints or authorized networks.
80+
81+
Workload Identity Federation for GKE is the preferred way for pods to access Google Cloud APIs. Check whether the cluster has a workload pool and whether Kubernetes service accounts are mapped directly as IAM principals or are allowed to impersonate IAM service accounts:
82+
83+
```bash
84+
gcloud container clusters describe <cluster> --region <region> \
85+
--format='value(workloadIdentityConfig.workloadPool)'
86+
87+
kubectl get serviceaccounts -A -o yaml | grep -n 'iam.gke.io' -B 5 -A 8
88+
kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName'
89+
```
90+
91+
If a service account has the annotation `iam.gke.io/gcp-service-account`, review the IAM service account policy for `roles/iam.workloadIdentityUser` grants to Kubernetes service account principals. Also check IAM allow policies for direct workload identity principals or broad principal sets.
92+
93+
Metadata access depends on cluster mode, node pool configuration, and workload settings. Do not assume every pod can steal the node service account. In Workload Identity-enabled environments, ordinary pods should use the GKE metadata server to obtain the workload identity intended for their Kubernetes service account. Node compromise, `hostNetwork` pods in some Standard configurations, and legacy node metadata exposure can still change the blast radius, so verify the actual node pool metadata mode, node service account, OAuth scopes, and pod placement.
94+
7795
### TLS Boostrap Privilege Escalation
7896

7997
Initially this privilege escalation technique allowed to **privesc inside the GKE cluster** effectively allowing an attacker to **fully compromise it**.
@@ -104,4 +122,3 @@ Even if the API **doesn't allow to modify resources**, it could be possible to f
104122
{{#include ../../../banners/hacktricks-training.md}}
105123

106124

107-

src/pentesting-cloud/kubernetes-security/abusing-roles-clusterroles-in-kubernetes/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,8 @@ curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1
357357

358358
### Creating and Reading Secrets
359359

360-
There is a special kind of a Kubernetes secret of type **kubernetes.io/service-account-token** which stores serviceaccount tokens.
360+
There is a special kind of Kubernetes Secret of type **kubernetes.io/service-account-token** which stores service account tokens. Modern Kubernetes versions do **not** automatically create one long-lived Secret for every ServiceAccount; projected, bound TokenRequest tokens are the normal workload path. However, manually created service account token Secrets are still supported, and upgraded or legacy clusters may still contain long-lived token Secrets. Current clusters can also mark unused auto-generated legacy token Secrets invalid and eventually clean them up, leaving labels such as `kubernetes.io/legacy-token-invalid-since` and `kubernetes.io/legacy-token-last-used`.
361+
361362
If you have permissions to create and read secrets, and you also know the serviceaccount's name, you can create a secret as follows and then steal the victim serviceaccount's token from it:
362363

363364
```yaml
@@ -870,4 +871,3 @@ https://github.com/aquasecurity/kube-bench
870871
{{#include ../../../banners/hacktricks-training.md}}
871872

872873

873-

src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,19 @@ spec:
9595

9696
If you **don't specify** the **nodePort** in the yaml (it's the port that will be opened) a port in the **range 30000–32767 will be used**.
9797

98+
When reviewing NodePort or LoadBalancer Services, also inspect traffic-policy fields because they change which nodes and backends are useful from a given source:
99+
100+
```bash
101+
kubectl get services --all-namespaces \
102+
-o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,ETP:.spec.externalTrafficPolicy,ITP:.spec.internalTrafficPolicy,AFFINITY:.spec.sessionAffinity,DIST:.spec.trafficDistribution,NODEPORTS:.spec.ports[*].nodePort'
103+
```
104+
105+
- `externalTrafficPolicy: Local` preserves the original client source IP for NodePort/LoadBalancer traffic and avoids forwarding to endpoints on other nodes. A node without a local ready endpoint may drop the traffic even if the Service has endpoints elsewhere.
106+
- `externalTrafficPolicy: Cluster` is the default and can forward through any node, but backend logs may see node IPs instead of the real external client IP.
107+
- `internalTrafficPolicy: Local` limits in-cluster Service traffic to endpoints local to the source node. This is locality routing, not an authorization boundary.
108+
- `sessionAffinity: ClientIP` can make repeated tests from one client hit the same backend, hiding other ready endpoints during manual checks.
109+
- `trafficDistribution` and EndpointSlice topology hints can prefer same-zone or same-node endpoints on newer clusters; treat them as routing preferences rather than hard security policy.
110+
98111
### LoadBalancer
99112

100113
Exposes the Service externally **using a cloud provider's load balancer**. On GKE, this will spin up a [Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/) that will give you a single IP address that will forward all traffic to your service. In AWS it will launch a Load Balancer.
@@ -116,6 +129,8 @@ kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.nam
116129

117130
Traffic that ingresses into the cluster with the **external IP** (as **destination IP**), on the Service port, will be **routed to one of the Service endpoints**. `externalIPs` are not managed by Kubernetes and are the responsibility of the cluster administrator.
118131

132+
`externalIPs` is a sensitive route-control field because a user who can set it might claim traffic for an IP address the Service owner should not control if the surrounding network routes that IP to the cluster. Kubernetes announced the deprecation and planned removal of Service `externalIPs` in v1.36, so prefer controller-owned exposure mechanisms such as LoadBalancer integrations or Gateway API where possible, and restrict/admit this field carefully while it still exists.
133+
119134
In the Service spec, `externalIPs` can be specified along with any of the `ServiceTypes`. In the example below, "`my-service`" can be accessed by clients on "`80.11.12.10:80`" (`externalIP:port`)
120135

121136
```yaml
@@ -160,6 +175,21 @@ List all ExternalNames:
160175
kubectl get services --all-namespaces | grep ExternalName
161176
```
162177

178+
### EndpointSlices
179+
180+
EndpointSlices show the concrete backend addresses and ports that a Service currently routes to. They are especially useful when a Service has no selector, when labels do not explain the traffic path, or when only some backends are ready.
181+
182+
List EndpointSlices associated with Services:
183+
184+
```bash
185+
kubectl get endpointslices --all-namespaces
186+
kubectl get endpointslice -n <namespace> -l kubernetes.io/service-name=<service-name> -o yaml
187+
kubectl get endpointslice -n <namespace> -l kubernetes.io/service-name=<service-name> \
188+
-o custom-columns='NAME:.metadata.name,ADDR:.endpoints[*].addresses,READY:.endpoints[*].conditions.ready,PORTS:.ports[*].port'
189+
```
190+
191+
When reviewing exposure, compare the Service selector with the EndpointSlice `targetRef`, endpoint addresses, readiness conditions, and ports. A selectorless Service can be paired with manually managed EndpointSlices and route traffic to non-Pod or unexpected destinations.
192+
163193
### Ingress
164194

165195
Unlike all the above examples, **Ingress is NOT a type of service**. Instead, it sits **in front of multiple services and act as a “smart router”** or entrypoint into your cluster.
@@ -171,28 +201,37 @@ The default GKE ingress controller will spin up a [HTTP(S) Load Balancer](https:
171201
The YAML for a Ingress object on GKE with a [L7 HTTP Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) might look like this:
172202

173203
```yaml
174-
apiVersion: extensions/v1beta1
204+
apiVersion: networking.k8s.io/v1
175205
kind: Ingress
176206
metadata:
177207
name: my-ingress
178208
spec:
179-
backend:
180-
serviceName: other
181-
servicePort: 8080
209+
defaultBackend:
210+
service:
211+
name: other
212+
port:
213+
number: 8080
182214
rules:
183215
- host: foo.mydomain.com
184216
http:
185217
paths:
186-
- backend:
187-
serviceName: foo
188-
servicePort: 8080
218+
- path: /
219+
pathType: Prefix
220+
backend:
221+
service:
222+
name: foo
223+
port:
224+
number: 8080
189225
- host: mydomain.com
190226
http:
191227
paths:
192-
- path: /bar/*
228+
- path: /bar
229+
pathType: Prefix
193230
backend:
194-
serviceName: bar
195-
servicePort: 8080
231+
service:
232+
name: bar
233+
port:
234+
number: 8080
196235
```
197236

198237
List all the ingresses:
@@ -207,12 +246,31 @@ Although in this case it's better to get the info of each one by one to read it
207246
kubectl get ingresses --all-namespaces -o=yaml
208247
```
209248

249+
### Gateway API
250+
251+
Gateway API is the newer Kubernetes API for exposing Services. It separates infrastructure-owned Gateway objects from application-owned Route objects such as HTTPRoute. This is useful for delegation, but it also means exposure can be split across namespaces.
252+
253+
List Gateway API exposure objects:
254+
255+
```bash
256+
kubectl get gatewayclasses
257+
kubectl get gateways --all-namespaces
258+
kubectl get httproutes --all-namespaces
259+
kubectl get gateway -n <namespace> <gateway-name> -o yaml
260+
kubectl get httproute -n <namespace> <route-name> -o yaml
261+
```
262+
263+
Check Gateway listeners, allowed route namespaces, Route `parentRefs`, hostnames, filters, backend references, and status conditions such as whether the route was accepted. A Route that is accepted by a shared Gateway can expose a backend even when no legacy Ingress object exists.
264+
210265
### References
211266

212267
- [https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0](https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0)
213268
- [https://kubernetes.io/docs/concepts/services-networking/service/](https://kubernetes.io/docs/concepts/services-networking/service/)
269+
- [https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/](https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/)
270+
- [https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/](https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/)
271+
- [https://kubernetes.io/docs/tutorials/services/source-ip/](https://kubernetes.io/docs/tutorials/services/source-ip/)
272+
- [https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/](https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/)
273+
- [https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/)
274+
- [https://gateway-api.sigs.k8s.io/](https://gateway-api.sigs.k8s.io/)
214275

215276
{{#include ../../banners/hacktricks-training.md}}
216-
217-
218-

src/pentesting-cloud/kubernetes-security/kubernetes-basics.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ Note that as the might be several nodes (running several pods), there might also
3939

4040
When a pod creates data that shouldn't be lost when the pod disappear it should be stored in a physical volume. **Kubernetes allow to attach a volume to a pod to persist the data**. The volume can be in the local machine or in a **remote storage**. If you are running pods in different physical nodes you should use a remote storage so all the pods can access it.
4141

42+
Kubernetes also supports **image volumes** in recent versions. An `image` volume mounts an OCI image or artifact as a **read-only** filesystem source inside the Pod, using fields such as `volumes[].image.reference` and `volumes[].image.pullPolicy`. The kubelet pulls the artifact with the same credential sources used for container images, including node credentials, Pod `imagePullSecrets`, and ServiceAccount `imagePullSecrets`. During a security review, treat image volumes as runtime inputs and supply-chain dependencies: check whether the reference is pinned by digest, which registry credentials can fetch it, where it is mounted, and whether `subPath` limits the visible directory.
43+
4244
**Other configurations:**
4345

4446
- **ConfigMap**: You can configure **URLs** to access services. The pod will obtain data from here to know how to communicate with the rest of the services (pods). Note that this is not the recommended place to save credentials!
@@ -639,7 +641,7 @@ https://www.youtube.com/watch?v=X48VuDVv0do
639641
- [Argo CD projects (`AppProject` restrictions)](https://argo-cd.readthedocs.io/en/latest/user-guide/projects/)
640642
- [Argo CD multiple sources / external Helm value files](https://argo-cd.readthedocs.io/en/latest/user-guide/multiple_sources/#helm-value-files-from-external-git-repository)
641643
- [Kubernetes ValidatingAdmissionPolicy](https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/)
644+
- [https://kubernetes.io/docs/concepts/storage/volumes/#image](https://kubernetes.io/docs/concepts/storage/volumes/#image)
645+
- [https://kubernetes.io/docs/tasks/configure-pod-container/image-volumes/](https://kubernetes.io/docs/tasks/configure-pod-container/image-volumes/)
642646
643647
{{#include ../../banners/hacktricks-training.md}}
644-
645-

src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,23 @@ k api-resources --namespaced=false #Resources NOT specific to a namespace
192192
{{#endtab }}
193193
{{#endtabs }}
194194

195+
### Object metadata worth checking
196+
197+
When you can read an object, export the full YAML or JSON instead of relying only on table output or `describe`. The most useful security context is often in generic object fields that exist across many resource types:
198+
199+
```bash
200+
kubectl get pod <pod> -n <ns> -o yaml
201+
kubectl get deploy <deploy> -n <ns> -o json | jq '.metadata, .spec, .status'
202+
kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName,PHASE:.status.phase'
203+
```
204+
205+
- `metadata.uid`, `name`, `namespace`, `apiVersion` and `kind` identify the exact object and avoid confusion between objects with the same name in different namespaces or API groups.
206+
- `metadata.labels` and selectors connect Services, Deployments, ReplicaSets, Pods, NetworkPolicies and automation. Following selectors is often the fastest way to identify the real backend pods for a Service.
207+
- `metadata.annotations` can leak operational context such as ingress behavior, cloud load balancer settings, GitOps or Helm metadata, policy exemptions, and service mesh configuration. They should not contain secrets, but real clusters often expose useful clues there.
208+
- `metadata.ownerReferences` shows controller lineage. If a Pod is owned by a ReplicaSet owned by a Deployment, changing or deleting only the Pod usually does not fix the source.
209+
- `metadata.finalizers` and `metadata.deletionTimestamp` explain resources stuck in deletion and can reveal cleanup controllers or persistence/disruption tricks.
210+
- `status`, Events, and conditions can reveal node placement, pod IPs, image IDs, failure messages, scheduling issues, admission denials, and controller progress. They are useful clues, but audit logs are still required to prove who performed an action.
211+
195212
### Get Current Privileges
196213

197214
{{#tabs }}
@@ -330,7 +347,7 @@ kurl -k -v https://$APISERVER/api/v1/namespaces/{namespace}/serviceaccounts
330347

331348
### Get Deployments
332349

333-
The deployments specify the **components** that need to be **run**.
350+
Deployments specify the desired state for stateless application workloads. They create ReplicaSets, and those ReplicaSets create Pods.
334351

335352
{{#tabs }}
336353
{{#tab name="kubectl" }}
@@ -345,7 +362,30 @@ k get deployments -n custnamespace
345362
{{#tab name="API" }}
346363

347364
```bash
348-
kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/deployments/
365+
kurl -v https://$APISERVER/apis/apps/v1/namespaces/<namespace>/deployments/
366+
```
367+
368+
{{#endtab }}
369+
{{#endtabs }}
370+
371+
### Get StatefulSets
372+
373+
StatefulSets manage Pods that need stable names, ordered rollout behavior, and often per-replica persistent volumes.
374+
375+
{{#tabs }}
376+
{{#tab name="kubectl" }}
377+
378+
```bash
379+
k get statefulsets
380+
k get statefulsets -n custnamespace
381+
```
382+
383+
{{#endtab }}
384+
385+
{{#tab name="API" }}
386+
387+
```bash
388+
kurl -v https://$APISERVER/apis/apps/v1/namespaces/<namespace>/statefulsets/
349389
```
350390

351391
{{#endtab }}
@@ -391,7 +431,7 @@ k get services -n custnamespace
391431
{{#tab name="API" }}
392432

393433
```bash
394-
kurl -v https://$APISERVER/api/v1/namespaces/default/services/
434+
kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/services/
395435
```
396436

397437
{{#endtab }}
@@ -421,7 +461,7 @@ kurl -v https://$APISERVER/api/v1/nodes/
421461

422462
### Get DaemonSets
423463

424-
**DaeamonSets** allows to ensure that a **specific pod is running in all the nodes** of the cluster (or in the ones selected). If you delete the DaemonSet the pods managed by it will be also removed.
464+
**DaemonSets** ensure that a **specific Pod is running on all selected nodes** of the cluster. If you delete the DaemonSet, the Pods managed by it will also be removed.
425465

426466
{{#tabs }}
427467
{{#tab name="kubectl" }}
@@ -435,29 +475,53 @@ k get daemonsets
435475
{{#tab name="API" }}
436476

437477
```bash
438-
kurl -v https://$APISERVER/apis/extensions/v1beta1/namespaces/default/daemonsets
478+
kurl -v https://$APISERVER/apis/apps/v1/namespaces/<namespace>/daemonsets
479+
```
480+
481+
{{#endtab }}
482+
{{#endtabs }}
483+
484+
### Get Jobs
485+
486+
Jobs create Pods that run until completion. They are commonly used for migrations, backups, batch work, and one-off administrative tasks.
487+
488+
{{#tabs }}
489+
{{#tab name="kubectl" }}
490+
491+
```bash
492+
k get jobs
493+
k get jobs -n custnamespace
494+
```
495+
496+
{{#endtab }}
497+
498+
{{#tab name="API" }}
499+
500+
```bash
501+
kurl -v https://$APISERVER/apis/batch/v1/namespaces/<namespace>/jobs
439502
```
440503

441504
{{#endtab }}
442505
{{#endtabs }}
443506

444-
### Get cronjob
507+
### Get CronJobs
445508

446-
Cron jobs allows to schedule using crontab like syntax the launch of a pod that will perform some action.
509+
CronJobs use a crontab-like schedule to create Jobs that launch Pods for task-style execution.
447510

448511
{{#tabs }}
449512
{{#tab name="kubectl" }}
450513

451514
```bash
452515
k get cronjobs
516+
k get cronjobs -n custnamespace
453517
```
454518

455519
{{#endtab }}
456520

457521
{{#tab name="API" }}
458522

459523
```bash
460-
kurl -v https://$APISERVER/apis/batch/v1beta1/namespaces/<namespace>/cronjobs
524+
kurl -v https://$APISERVER/apis/batch/v1/namespaces/<namespace>/cronjobs
461525
```
462526

463527
{{#endtab }}
@@ -849,6 +913,3 @@ https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-metho
849913
{{#endref}}
850914

851915
{{#include ../../banners/hacktricks-training.md}}
852-
853-
854-

0 commit comments

Comments
 (0)