Recon
Basic
DNS enum
- find out nameserver
cat /etc/resolv.conf
# search k8s-lan-party.svc.cluster.local svc.cluster.local cluster.local us-west-1.compute.internal
# nameserver 10.100.120.34
# options ndots:5
- if you already know the hostname you want to resolve and have the necessary utility to make a request:
dig @10.100.120.34 getflag-service.k8s-lan-party.svc.cluster.local | grep -A1 "ANSWER SECTION"
# ;; ANSWER SECTION:
# getflag-service.k8s-lan-party.svc.cluster.local. 30 IN A 10.100.136.254
- if you know the IP address you want to get a DNS name of perform a reverse DNS lookup (PTR record):
dig @10.100.120.34 -x 10.100.136.254 | grep -A1 "ANSWER SECTION"
# ;; ANSWER SECTION:
# 254.136.100.10.in-addr.arpa. 15 IN PTR getflag-service.k8s-lan-party.svc.cluster.local
- Or perform reverse lookups for a CIDR via dnscan:
dnscan -subnet 10.100.0.1/16
General
# A) get username and (cluster)roles/(cluster)rolebindings you're in
kubectl auth whoami
# A) List all allowed actions in namespace "foo"
kubectl auth can-i --list --namespace 'foo'
# A) get current config that kubectl uses
kubectl config view
# if it's not successfull try grepping env/set for KUBECONFIG
# A) get all resource types available
kubectl api-resources
# A) get resources of kind
kubectl get configmaps --all-namespaces
# A) get contents of a specific object after you got the name using GET verb
kubectl describe configmap --namespace kube-system coredns
# A) get full secret
kubectl get secret sh.helm.release.v1.fluentd.v1 -o yaml
Initial from within a container
/etc/resolv.conf->searchproperty can give up coreDNS’ root.
# If you have access to /etc/resolv.conf - check if there is 'search ...' - there will be core DNS for K8s
# e.g. if `search ... XXX.YYY`, then
# API might be located at kubernetes.default.svc.XXX.YYY:443
cat /etc/resolv.conf
# search default.svc.cluster.local svc.cluster.local cluster.local kubernetes.org
# nameserver 10.96.0.10
# options ndots:5
# query the apiserver certificate
cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
curl -H "Authorization: Bearer $(cat /run/secrets/kubernetes.io/serviceaccount/token)" --insecure https://kubernetes.default.svc.cluster.local
# {
# "kind": "Status",
# "apiVersion": "v1",
# "metadata": {},
# "status": "Failure",
# "message": "forbidden: User \"system:serviceaccount:default:default\" cannot get path \"/\"",
# "reason": "Forbidden",
# "details": {},
# "code": 403
# }
set->KUBERNETES_XXXenvironment variables will giveup kube-proxy address and let you make requests to kube-apiserver from within a pod
root@nginx-test-574bc578fc-dbsh7:/# set | grep KUBERNETES
# KUBERNETES_PORT=tcp://10.96.0.1:443
# KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
# KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
# KUBERNETES_PORT_443_TCP_PORT=443
# KUBERNETES_PORT_443_TCP_PROTO=tcp
# KUBERNETES_SERVICE_HOST=10.96.0.1
# KUBERNETES_SERVICE_PORT=443
# KUBERNETES_SERVICE_PORT_HTTPS=443
root@nginx-test-574bc578fc-dbsh7:/# curl --insecure https://10.96.0.1
# {
# "kind": "Status",
# "apiVersion": "v1",
# "metadata": {},
# "status": "Failure",
# "message": "forbidden: User \"system:anonymous\" cannot get path \"/\"",
# "reason": "Forbidden",
# "details": {},
# "code": 403
# }
root@nginx-test-574bc578fc-dbsh7:/# curl -H "Authorization: Bearer $(cat /run/secrets/kubernetes.io/serviceaccount/token)" --insecure https://10.96.0.1
# {
# "kind": "Status",
# "apiVersion": "v1",
# "metadata": {},
# "status": "Failure",
# "message": "forbidden: User \"system:serviceaccount:default:default\" cannot get path \"/\"",
# "reason": "Forbidden",
# "details": {},
# "code": 403
# }
/etc/fstabcan giveup volume mount locationsin a container, all pod’s secret should be located under
/run/secrets/kubernetes.io/serviceaccount/directory
root@nginx-test-574bc578fc-dbsh7:/run/secrets/kubernetes.io/serviceaccount# ls -la
# drwxr-xr-x 2 root root 100 Oct 15 17:14 ..2024_10_15_17_14_13.1702215151
# lrwxrwxrwx 1 root root 32 Oct 15 17:14 ..data -> ..2024_10_15_17_14_13.1702215151
# lrwxrwxrwx 1 root root 13 Oct 7 10:02 ca.crt -> ..data/ca.crt
# lrwxrwxrwx 1 root root 16 Oct 7 10:02 namespace -> ..data/namespace
# lrwxrwxrwx 1 root root 12 Oct 7 10:02 token -> ..data/token
access a pod’s webservice using curl
kubectl get services --all-namespaces
# NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
# monitoring grafana ClusterIP 10.99.24.78 <none> 80/TCP
kubectl describe service --namespace monitoring grafana
# Name: grafana
# Namespace: monitoring
# Labels: app.kubernetes.io/instance=grafana
# app.kubernetes.io/managed-by=Helm
# app.kubernetes.io/name=grafana
# app.kubernetes.io/version=11.5.2
# helm.sh/chart=grafana-8.10.3
# Annotations: meta.helm.sh/release-name: grafana
# meta.helm.sh/release-namespace: monitoring
# Selector: app.kubernetes.io/instance=grafana,app.kubernetes.io/name=grafana
# Type: ClusterIP
# IP Family Policy: SingleStack
# IP Families: IPv4
# IP: 10.99.24.78
# IPs: 10.99.24.78
# Port: service 80/TCP
# TargetPort: 3000/TCP
# Endpoints: 10.244.1.186:3000
# Session Affinity: None
# Events: <none>
curl http://10.99.24.78
# <a href="/login">Found</a>
determine what serviceAccount the Pod is using
kubectl get pods/$POD_NAME -o yaml | yq .spec.serviceAccountName
discover current node podCIDR address
# within the container
cat /proc/net/route
# parse the output externally
echo "$OUTPUT" | awk '$2=="00000000"{print $3; exit}' | { read H; printf "%d.%d.%d.%d\n" 0x${H:6:2} 0x${H:4:2} 0x${H:2:2} 0x${H:0:2}; }
# 10.244.2.239
find your permissions
kubectl auth whoami
# ATTRIBUTE VALUE
# Username kubernetes-admin
# Groups [kubeadm:cluster-admins system:authenticated]
# first figure out if anything apart from RBAC (default is "Node,RBAC") is used:
# it can either be defined by using AuthorizationConfiguration resource
# or kube-apiserver command-line parameters defined in it's manifest on master-node
kubectl get authorizationconfigurations
cat /etc/kubernetes/manifests/kube-apiserver.yaml
# the following means that kubeadm:cluster-admins is a ClusterRoleBinding that points to cluster-admin ClusterRole
# OMMIT THE "s"
kubectl get clusterrolebindings -A | grep kubeadm:cluster-admin
# kubeadm:cluster-admins ClusterRole/cluster-admin
# cluster-admin ClusterRole can use ['*'] verbs on ['*'] api-resources, this is boring
# just for the sake of an example (of course it doesn't),
# let's say that cluster-admins binds to ClusterRole/cilium-operator:
# the following means cilium-operator can CREATE/GET/LIST/WATCH customresourcedefinitions and
# UPDATE specific resource named ciliumnodes
# which are contained within apiextensions.k8s.io APIVERSION
kubectl describe clusterrole cilium-operator
# Name: cilium-operator
# Labels: app.kubernetes.io/managed-by=Helm
# Resources Non-Resource URLs Resource Names Verbs
# --------- ----------------- -------------- -----
# customresourcedefinitions.apiextensions.k8s.io [] [] [create get list watch]
# customresourcedefinitions.apiextensions.k8s.io [] [ciliumnodes.cilium.io] [update]
# if the following doesn't give anything it probably (???) means the resource
# was not created in your environment during package installation
kubectl get customresourcedefinitions | grep ciliumnodes
# ciliumnodes.cilium.io 2024-10-06T12:55:42Z
# print object definition
kubectl describe customresourcedefinitions ciliumnodes | less
mount discovery
df
cat /etc/fstab
lsblk # will probably fail
df
# Filesystem 1K-blocks Used Available Use% Mounted on
# overlay 314560492 8837544 305722948 3% /
# fs-0779524599b7d5e7e.efs.us-west-1.amazonaws.com:/ 9007199254739968 0 9007199254739968 0% /efs
# tmpfs 62022172 12 62022160 1% /var/run/secrets/kubernetes.io/serviceaccount
# tmpfs 65536 0 65536 0% /dev/null
dig fs-0779524599b7d5e7e.efs.us-west-1.amazonaws.com | grep -A1 "ANSWER SECTION"
# fs-0779524599b7d5e7e.efs.us-west-1.amazonaws.com. 23 IN A 192.168.124.98
sidecar discovery
- all containers under the Pod share same net namespace
# the following displays /31 subnet, that means that 192.168.28.66 is a neighbor peer
ifconfig
# ns-564e82: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
# inet 192.168.28.67 netmask 255.255.255.254 broadcast 0.0.0.0