token
, error
_REPORTE DE SEGURIDAD
kubeadm
comando delete
toma como entrada un ID de token de arranque o un token completo. Antes de determinar si la entrada es solo una identificación o un token completo, kubeadm
registra la entrada usando klog
. Si la eliminación falla, el token seguirá siendo válido. Un atacante que tenga acceso a los registros podría usarlo para realizar acciones que requieran un token de arranque, como crear un clúster o unir nodos a un clúster existente.
El código vulnerable está presente en kubernetes 1.19. La línea específica que contiene la llamada a klog
se editó por última vez el 2019-03-24.
El código vulnerable está en el repositorio github.com/kubernetes
, en el archivo kubernetes/cmd/kubeadm/app/cmd/token.go
, en la línea 423. Aquí está la función 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
}
Y aquí está la definición del comando kubeadm que llama a esa función (en el mismo archivo):
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)
},
}
Un atacante que obtenga un token de arranque de los registros podría usarlo para autenticarse con kubeadm
y crear un nuevo clúster o unir nodos a un clúster existente, por ejemplo, para utilizar recursos informáticos. Un atacante también podría realizar otras acciones usando kubeadm
, por ejemplo, enumerar o eliminar otros tokens.
He informado de esta vulnerabilidad a HackerOne y me han informado que, en base a la alta complejidad del ataque y la baja gravedad, creen que esto se puede informar y solucionar públicamente.
Abrí un PR en kubernetes implementando una solución: https://github.com/kubernetes/kubernetes/pull/94727
El comando de eliminación de kubeadm toma como entrada un ID de token de arranque o un token completo. Antes de determinar si la entrada es solo una identificación o un token completo, kubeadm registra la entrada usando klog. Si la eliminación falla, el token seguirá siendo válido. Un atacante que tenga acceso a los registros podría usarlo para realizar acciones que requieran un token de arranque, como crear un clúster o unir nodos a un clúster existente.
hola, y gracias por registrar el problema. para poder leer tales registros, uno necesitaría tener los privilegios correctos y supongo que los registros estarían bajo acceso de root o entregados a un grupo específico que ya tiene un acceso más alto que los tokens de arranque.
además:
--v=>1
debe habilitarse durante la ejecución de kubeadm token delete
si el token está en formato válidoCreo que la mejora en las relaciones públicas es mayormente buena, pero no creo que debamos retroceder a versiones anteriores (<1.20) debido a la complejidad de tal ataque.
Estoy de acuerdo con @ neolit123 en no hacer backport (a menos que haya necesidades realmente específicas)
WRT a la solución, soy +1 para eliminar el TokenID de los registros
cerrando como https://github.com/kubernetes/kubernetes/pull/94727 fusionado.
Gracias