From dd90e55cc1689d69c486c523993640be2efc4646 Mon Sep 17 00:00:00 2001 From: Drun1baby <2717763591@qq.com> Date: Thu, 12 Dec 2024 11:35:34 +0800 Subject: [PATCH 1/5] modify k8s service account functions --- pkg/evaluate/evaluate.go | 14 ++++++- pkg/evaluate/k8s_service_account.go | 59 +++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/pkg/evaluate/evaluate.go b/pkg/evaluate/evaluate.go index b2dd022..2cbb88f 100644 --- a/pkg/evaluate/evaluate.go +++ b/pkg/evaluate/evaluate.go @@ -17,8 +17,8 @@ limitations under the License. package evaluate import ( + "fmt" "github.com/cdk-team/CDK/pkg/util" - "github.com/cdk-team/CDK/conf" ) // CallBasics is a function to call basic functions @@ -51,7 +51,17 @@ func CallBasics() { CheckK8sAnonymousLogin() util.PrintH2("Discovery - K8s Service Account") - CheckPrivilegedK8sServiceAccount(conf.K8sSATokenDefaultPath) + + path := GetDefaultK8SAccountInfo() + address, err := GetKubernetesAddress() + + if err != nil { + fmt.Println("Error:", err) + } else { + fmt.Println("KUBERNETES_PORT_443_TCP_ADDR:", address) + } + + CheckPrivilegedK8sServiceAccount(path, address) util.PrintH2("Discovery - Cloud Provider Metadata API") CheckCloudMetadataAPI() diff --git a/pkg/evaluate/k8s_service_account.go b/pkg/evaluate/k8s_service_account.go index b8b68cf..d236130 100644 --- a/pkg/evaluate/k8s_service_account.go +++ b/pkg/evaluate/k8s_service_account.go @@ -1,4 +1,3 @@ - /* Copyright 2022 The Authors of https://github.com/CDK-TEAM/CDK . @@ -18,17 +17,19 @@ limitations under the License. package evaluate import ( + "bytes" "fmt" "github.com/cdk-team/CDK/pkg/tool/kubectl" "log" + "os/exec" "strings" ) -func CheckPrivilegedK8sServiceAccount(tokenPath string) bool { +func CheckPrivilegedK8sServiceAccount(tokenPath string, address string) bool { resp, err := kubectl.ServerAccountRequest( kubectl.K8sRequestOption{ - TokenPath: "", - Server: "", + TokenPath: tokenPath + "/token", + Server: address, Api: "/apis", Method: "get", PostData: "", @@ -71,3 +72,53 @@ func CheckPrivilegedK8sServiceAccount(tokenPath string) bool { return false } } + +func GetDefaultK8SAccountInfo() string { + // 执行 df -T 命令来看 serviceaccount 保存路径 + cmd := exec.Command("df", "-T") + var out bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &out + + err := cmd.Run() + if err != nil { + fmt.Printf("Command execution failed: %s\n", err) + return "" + } + + output := out.String() + lines := strings.Split(output, "\n") + + var serviceAccountLines []string + + for _, line := range lines { + if strings.Contains(line, "serviceaccount") { + fmt.Println("\tk8s account service path fetch success" + line) + serviceAccountLines = append(serviceAccountLines, line) + } + } + + return strings.Join(serviceAccountLines, "\n") +} + +func GetKubernetesAddress() (string, error) { + cmd := exec.Command("env") + var out bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &out + err := cmd.Run() + if err != nil { + return "", fmt.Errorf("command execution failed: %s", err) + } + + output := out.String() + lines := strings.Split(output, "\n") + + for _, line := range lines { + if strings.HasPrefix(line, "KUBERNETES_PORT_443_TCP_ADDR=") { + return strings.TrimPrefix(line, "KUBERNETES_PORT_443_TCP_ADDR="), nil + } + } + + return "", fmt.Errorf("KUBERNETES_PORT_443_TCP_ADDR not found") +} From 18a45fd0a7fd50f3dbdc47e6df9e73a645b5cc3c Mon Sep 17 00:00:00 2001 From: Drun1baby <2717763591@qq.com> Date: Thu, 12 Dec 2024 21:10:00 +0800 Subject: [PATCH 2/5] modify some codes --- pkg/evaluate/evaluate.go | 12 +----- pkg/evaluate/k8s_service_account.go | 58 +++++++++++------------------ 2 files changed, 23 insertions(+), 47 deletions(-) diff --git a/pkg/evaluate/evaluate.go b/pkg/evaluate/evaluate.go index 2cbb88f..ca4b26d 100644 --- a/pkg/evaluate/evaluate.go +++ b/pkg/evaluate/evaluate.go @@ -17,7 +17,6 @@ limitations under the License. package evaluate import ( - "fmt" "github.com/cdk-team/CDK/pkg/util" ) @@ -52,16 +51,7 @@ func CallBasics() { util.PrintH2("Discovery - K8s Service Account") - path := GetDefaultK8SAccountInfo() - address, err := GetKubernetesAddress() - - if err != nil { - fmt.Println("Error:", err) - } else { - fmt.Println("KUBERNETES_PORT_443_TCP_ADDR:", address) - } - - CheckPrivilegedK8sServiceAccount(path, address) + CheckPrivilegedK8sServiceAccount(GetDefaultK8SAccountInfo(), GetKubernetesAddress()) util.PrintH2("Discovery - Cloud Provider Metadata API") CheckCloudMetadataAPI() diff --git a/pkg/evaluate/k8s_service_account.go b/pkg/evaluate/k8s_service_account.go index d236130..e068b97 100644 --- a/pkg/evaluate/k8s_service_account.go +++ b/pkg/evaluate/k8s_service_account.go @@ -17,14 +17,16 @@ limitations under the License. package evaluate import ( - "bytes" + "bufio" "fmt" "github.com/cdk-team/CDK/pkg/tool/kubectl" "log" - "os/exec" + "os" "strings" ) +const mountInfoPath string = "/proc/self/mountinfo" + func CheckPrivilegedK8sServiceAccount(tokenPath string, address string) bool { resp, err := kubectl.ServerAccountRequest( kubectl.K8sRequestOption{ @@ -74,51 +76,35 @@ func CheckPrivilegedK8sServiceAccount(tokenPath string, address string) bool { } func GetDefaultK8SAccountInfo() string { - // 执行 df -T 命令来看 serviceaccount 保存路径 - cmd := exec.Command("df", "-T") - var out bytes.Buffer - cmd.Stdout = &out - cmd.Stderr = &out - - err := cmd.Run() + file, err := os.Open("/proc/self/mountinfo") if err != nil { - fmt.Printf("Command execution failed: %s\n", err) - return "" + fmt.Println("error opening /proc/self/mountinfo: %w", err) } + defer file.Close() - output := out.String() - lines := strings.Split(output, "\n") + scanner := bufio.NewScanner(file) - var serviceAccountLines []string - - for _, line := range lines { + for scanner.Scan() { + line := scanner.Text() if strings.Contains(line, "serviceaccount") { - fmt.Println("\tk8s account service path fetch success" + line) - serviceAccountLines = append(serviceAccountLines, line) + fmt.Println("find serviceaccount successfully") + return line } } - return strings.Join(serviceAccountLines, "\n") -} - -func GetKubernetesAddress() (string, error) { - cmd := exec.Command("env") - var out bytes.Buffer - cmd.Stdout = &out - cmd.Stderr = &out - err := cmd.Run() - if err != nil { - return "", fmt.Errorf("command execution failed: %s", err) + if err := scanner.Err(); err != nil { + fmt.Println("error reading /proc/self/mountinfo: %w", err) } - output := out.String() - lines := strings.Split(output, "\n") + return "" +} - for _, line := range lines { - if strings.HasPrefix(line, "KUBERNETES_PORT_443_TCP_ADDR=") { - return strings.TrimPrefix(line, "KUBERNETES_PORT_443_TCP_ADDR="), nil +func GetKubernetesAddress() string { + env := os.Environ() + for _, e := range env { + if strings.HasPrefix(e, "KUBERNETES_PORT_443_TCP_ADDR=") { + return strings.TrimPrefix(e, "KUBERNETES_PORT_443_TCP_ADDR=") } } - - return "", fmt.Errorf("KUBERNETES_PORT_443_TCP_ADDR not found") + return "KUBERNETES_PORT_443_TCP_ADDR not found" } From 6ecc0210c5261208dcea134761b01a156acdadec Mon Sep 17 00:00:00 2001 From: Drun1baby <2717763591@qq.com> Date: Thu, 12 Dec 2024 21:32:57 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20auto=5Fescape=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/task/auto_escape.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/task/auto_escape.go b/pkg/task/auto_escape.go index 947c7a8..4cc72a8 100644 --- a/pkg/task/auto_escape.go +++ b/pkg/task/auto_escape.go @@ -25,7 +25,6 @@ import ( "github.com/cdk-team/CDK/pkg/exploit/persistence" "log" - "github.com/cdk-team/CDK/conf" "github.com/cdk-team/CDK/pkg/cli" "github.com/cdk-team/CDK/pkg/evaluate" "github.com/cdk-team/CDK/pkg/plugin" @@ -109,7 +108,13 @@ func autoEscape(shellCommand string) bool { // 4. check k8s anonymous login fmt.Printf("\n[Auto Escape - K8s API Server]\n") anonymousLogin := evaluate.CheckK8sAnonymousLogin() - privServiceAccount := evaluate.CheckPrivilegedK8sServiceAccount(conf.K8sSATokenDefaultPath) + defaultAccountInfo := GetDefaultK8SAccountInfo() + kubernetesAddress := GetKubernetesAddress() + + privServiceAccount := evaluate.CheckPrivilegedK8sServiceAccount( + CheckPrivilegedK8sServiceAccount(defaultAccountInfo, kubernetesAddress), + ) + k8sExploit = privServiceAccount || anonymousLogin if !k8sExploit { From e61cb20cc93e785abedcd9038eb59c5d2395a3b3 Mon Sep 17 00:00:00 2001 From: Drun1baby <2717763591@qq.com> Date: Tue, 24 Dec 2024 17:11:22 +0800 Subject: [PATCH 4/5] modify codes error --- cmd/cdk/build.sh | 1 + pkg/evaluate/evaluate.go | 3 +- pkg/evaluate/k8s_service_account.go | 67 +++++++++++++---------------- 3 files changed, 32 insertions(+), 39 deletions(-) create mode 100644 cmd/cdk/build.sh diff --git a/cmd/cdk/build.sh b/cmd/cdk/build.sh new file mode 100644 index 0000000..3c6e9cc --- /dev/null +++ b/cmd/cdk/build.sh @@ -0,0 +1 @@ +GOOS=linux GOARCH=amd64 go build -o cdk \ No newline at end of file diff --git a/pkg/evaluate/evaluate.go b/pkg/evaluate/evaluate.go index ca4b26d..f33b5d4 100644 --- a/pkg/evaluate/evaluate.go +++ b/pkg/evaluate/evaluate.go @@ -17,6 +17,7 @@ limitations under the License. package evaluate import ( + "github.com/cdk-team/CDK/conf" "github.com/cdk-team/CDK/pkg/util" ) @@ -51,7 +52,7 @@ func CallBasics() { util.PrintH2("Discovery - K8s Service Account") - CheckPrivilegedK8sServiceAccount(GetDefaultK8SAccountInfo(), GetKubernetesAddress()) + CheckPrivilegedK8sServiceAccount(conf.K8sSATokenDefaultPath) util.PrintH2("Discovery - Cloud Provider Metadata API") CheckCloudMetadataAPI() diff --git a/pkg/evaluate/k8s_service_account.go b/pkg/evaluate/k8s_service_account.go index e068b97..3880499 100644 --- a/pkg/evaluate/k8s_service_account.go +++ b/pkg/evaluate/k8s_service_account.go @@ -25,13 +25,38 @@ import ( "strings" ) -const mountInfoPath string = "/proc/self/mountinfo" +var ( + k8sAccountInfoPath string + kubernetesAddress string +) + +func CheckPrivilegedK8sServiceAccount(tokenPath string) bool { + + // fetch mount info + file, err := os.Open("/proc/self/mountinfo") + if err != nil { + fmt.Printf("error opening /proc/self/mountinfo: %v\n", err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + + for scanner.Scan() { + line := scanner.Text() + if strings.Contains(line, "serviceaccount") { + fmt.Println("find serviceaccount successfully") + k8sAccountInfoPath = line + } + } + + if err := scanner.Err(); err != nil { + fmt.Printf("error reading /proc/self/mountinfo: %v\n", err) + } -func CheckPrivilegedK8sServiceAccount(tokenPath string, address string) bool { resp, err := kubectl.ServerAccountRequest( kubectl.K8sRequestOption{ - TokenPath: tokenPath + "/token", - Server: address, + TokenPath: k8sAccountInfoPath, + Server: "", Api: "/apis", Method: "get", PostData: "", @@ -74,37 +99,3 @@ func CheckPrivilegedK8sServiceAccount(tokenPath string, address string) bool { return false } } - -func GetDefaultK8SAccountInfo() string { - file, err := os.Open("/proc/self/mountinfo") - if err != nil { - fmt.Println("error opening /proc/self/mountinfo: %w", err) - } - defer file.Close() - - scanner := bufio.NewScanner(file) - - for scanner.Scan() { - line := scanner.Text() - if strings.Contains(line, "serviceaccount") { - fmt.Println("find serviceaccount successfully") - return line - } - } - - if err := scanner.Err(); err != nil { - fmt.Println("error reading /proc/self/mountinfo: %w", err) - } - - return "" -} - -func GetKubernetesAddress() string { - env := os.Environ() - for _, e := range env { - if strings.HasPrefix(e, "KUBERNETES_PORT_443_TCP_ADDR=") { - return strings.TrimPrefix(e, "KUBERNETES_PORT_443_TCP_ADDR=") - } - } - return "KUBERNETES_PORT_443_TCP_ADDR not found" -} From 7be99443b59700d2a53eaf8361d3d6f4c12c77a6 Mon Sep 17 00:00:00 2001 From: Drun1baby <2717763591@qq.com> Date: Wed, 25 Dec 2024 10:49:57 +0800 Subject: [PATCH 5/5] update auto_escape module --- cmd/cdk/build.sh | 1 - pkg/evaluate/k8s_anonymous_login.go | 26 ++++++++++++++++++++++++-- pkg/evaluate/k8s_service_account.go | 2 +- pkg/task/auto_escape.go | 7 +------ pkg/tool/kubectl/common.go | 2 +- 5 files changed, 27 insertions(+), 11 deletions(-) delete mode 100644 cmd/cdk/build.sh diff --git a/cmd/cdk/build.sh b/cmd/cdk/build.sh deleted file mode 100644 index 3c6e9cc..0000000 --- a/cmd/cdk/build.sh +++ /dev/null @@ -1 +0,0 @@ -GOOS=linux GOARCH=amd64 go build -o cdk \ No newline at end of file diff --git a/pkg/evaluate/k8s_anonymous_login.go b/pkg/evaluate/k8s_anonymous_login.go index 9c19839..e8c6d94 100644 --- a/pkg/evaluate/k8s_anonymous_login.go +++ b/pkg/evaluate/k8s_anonymous_login.go @@ -1,4 +1,3 @@ - /* Copyright 2022 The Authors of https://github.com/CDK-TEAM/CDK . @@ -18,8 +17,10 @@ limitations under the License. package evaluate import ( + "bufio" "fmt" "log" + "os" "strings" "github.com/cdk-team/CDK/pkg/tool/kubectl" @@ -30,9 +31,30 @@ func CheckK8sAnonymousLogin() bool { // check if api-server allows system:anonymous request log.Println("checking if api-server allows system:anonymous request.") + // fetch mount info + file, err := os.Open("/proc/self/mountinfo") + if err != nil { + fmt.Printf("error opening /proc/self/mountinfo: %v\n", err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + + for scanner.Scan() { + line := scanner.Text() + if strings.Contains(line, "serviceaccount") { + fmt.Println("find serviceaccount successfully") + k8sAccountInfoPath = line + } + } + + if err := scanner.Err(); err != nil { + fmt.Printf("error reading /proc/self/mountinfo: %v\n", err) + } + resp, err := kubectl.ServerAccountRequest( kubectl.K8sRequestOption{ - TokenPath: "", + TokenPath: k8sAccountInfoPath, Server: "", // default Api: "/", Method: "get", diff --git a/pkg/evaluate/k8s_service_account.go b/pkg/evaluate/k8s_service_account.go index 3880499..49bb88b 100644 --- a/pkg/evaluate/k8s_service_account.go +++ b/pkg/evaluate/k8s_service_account.go @@ -73,7 +73,7 @@ func CheckPrivilegedK8sServiceAccount(tokenPath string) bool { log.Println("trying to list namespaces") resp, err := kubectl.ServerAccountRequest( kubectl.K8sRequestOption{ - TokenPath: "", + TokenPath: k8sAccountInfoPath, Server: "", Api: "/api/v1/namespaces", Method: "get", diff --git a/pkg/task/auto_escape.go b/pkg/task/auto_escape.go index 4cc72a8..016c077 100644 --- a/pkg/task/auto_escape.go +++ b/pkg/task/auto_escape.go @@ -108,12 +108,7 @@ func autoEscape(shellCommand string) bool { // 4. check k8s anonymous login fmt.Printf("\n[Auto Escape - K8s API Server]\n") anonymousLogin := evaluate.CheckK8sAnonymousLogin() - defaultAccountInfo := GetDefaultK8SAccountInfo() - kubernetesAddress := GetKubernetesAddress() - - privServiceAccount := evaluate.CheckPrivilegedK8sServiceAccount( - CheckPrivilegedK8sServiceAccount(defaultAccountInfo, kubernetesAddress), - ) + privServiceAccount := evaluate.CheckPrivilegedK8sServiceAccount(conf.K8sSATokenDefaultPath) k8sExploit = privServiceAccount || anonymousLogin diff --git a/pkg/tool/kubectl/common.go b/pkg/tool/kubectl/common.go index e694cf1..7d7b5d6 100644 --- a/pkg/tool/kubectl/common.go +++ b/pkg/tool/kubectl/common.go @@ -92,7 +92,7 @@ func SecretToken(tokenPath string) (string, error) { var token string if tokenPath != "" { - token, tokenErr = GetServiceAccountToken(tokenPath) + token, tokenErr = GetServiceAccountToken(tokenPath + "/token") } else if token == "" { token, tokenErr = GetServiceAccountToken(conf.K8sSATokenDefaultPath) }