token
, error
_RELATÓRIO DE SEGURANÇA_
kubeadm
comando delete
recebe como entrada um ID de token de bootstrap ou um token completo. Antes de determinar se a entrada é apenas um id ou um token completo, kubeadm
registra a entrada usando klog
. Se a exclusão falhar, o token permanecerá válido. Um invasor com acesso aos logs pode usá-los para executar ações que exigem um token de bootstrap, como criar um cluster ou juntar nós a um cluster existente.
O código vulnerável está presente no kubernetes 1.19. A linha específica que contém a chamada para klog
foi editada pela última vez em 24/03/2019.
O código vulnerável está no repositório github.com/kubernetes
, no arquivo kubernetes/cmd/kubeadm/app/cmd/token.go
, na linha 423. Aqui está a função completa:
// RunDeleteTokens removes a bootstrap tokens from the server.
func RunDeleteTokens(out io.Writer, client clientset.Interface, tokenIDsOrTokens []string) error {
for _, tokenIDOrToken := range tokenIDsOrTokens {
// Assume this is a token id and try to parse it
tokenID := tokenIDOrToken
klog.V(1).Infof("[token] parsing token %q", tokenIDOrToken) // POTENTIAL LEAK HERE
if !bootstraputil.IsValidBootstrapTokenID(tokenIDOrToken) {
// Okay, the full token with both id and secret was probably passed. Parse it and extract the ID only
bts, err := kubeadmapiv1beta2.NewBootstrapTokenString(tokenIDOrToken)
if err != nil {
return errors.Errorf("given token %q didn't match pattern %q or %q",
tokenIDOrToken, bootstrapapi.BootstrapTokenIDPattern, bootstrapapi.BootstrapTokenIDPattern)
}
tokenID = bts.ID
}
tokenSecretName := bootstraputil.BootstrapTokenSecretName(tokenID)
klog.V(1).Infof("[token] deleting token %q", tokenID)
if err := client.CoreV1().Secrets(metav1.NamespaceSystem).Delete(context.TODO(), tokenSecretName, metav1.DeleteOptions{}); err != nil {
return errors.Wrapf(err, "failed to delete bootstrap token %q", tokenID)
}
fmt.Fprintf(out, "bootstrap token %q deleted\n", tokenID)
}
return nil
}
E aqui está a definição do comando kubeadm que chama essa função (no mesmo arquivo):
deleteCmd := &cobra.Command{
Use: "delete [token-value] ...",
DisableFlagsInUseLine: true,
Short: "Delete bootstrap tokens on the server",
Long: dedent.Dedent(`
This command will delete a list of bootstrap tokens for you.
The [token-value] is the full Token of the form "[a-z0-9]{6}.[a-z0-9]{16}" or the
Token ID of the form "[a-z0-9]{6}" to delete.
`),
RunE: func(tokenCmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.Errorf("missing subcommand; 'token delete' is missing token of form %q", bootstrapapi.BootstrapTokenIDPattern)
}
kubeConfigFile = cmdutil.GetKubeConfigPath(kubeConfigFile)
client, err := getClientset(kubeConfigFile, dryRun)
if err != nil {
return err
}
return RunDeleteTokens(out, client, args)
},
}
Um invasor que obtém um token de bootstrap dos logs pode usá-lo para autenticar com kubeadm
e criar um novo cluster ou juntar nós a um cluster existente, por exemplo, para usar recursos de computação. Um invasor também pode executar outras ações usando kubeadm
, por exemplo, listar ou excluir outros tokens.
Eu relatei essa vulnerabilidade ao HackerOne e eles me informaram que, com base na alta complexidade do ataque e na baixa gravidade, eles acham que isso pode ser relatado e corrigido publicamente.
Abri um PR sobre kubernetes implementando uma correção: https://github.com/kubernetes/kubernetes/pull/94727
O comando delete do kubeadm leva como entrada um ID de token de bootstrap ou um token completo. Antes de determinar se a entrada é apenas um id ou um token completo, o kubeadm registra a entrada usando klog. Se a exclusão falhar, o token permanecerá válido. Um invasor com acesso aos logs pode usá-los para executar ações que exigem um token de bootstrap, como criar um cluster ou juntar nós a um cluster existente.
oi e obrigado por registrar o problema. para ser capaz de ler tais logs, seria necessário ter os privilégios corretos e eu presumiria que os logs estariam sob acesso root ou fornecidos a um grupo específico que já tem acesso mais alto do que os tokens de bootstrap.
tb:
--v=>1
deve ser habilitado durante a execução de kubeadm token delete
se o token estiver em um formato válidoAcho que a melhoria no PR está quase tudo bem, mas não acho que devemos fazer backport para versões mais antigas (<1.20) devido à complexidade de tal ataque.
Eu concordo com @ neolit123 em não
WRT para a correção, eu sou +1 para remover o TokenID dos registros
fechando como https://github.com/kubernetes/kubernetes/pull/94727 mesclado.
obrigado