FlawOpen

flawopen.com/Incidents/solarwinds-sunburst-build-compromise-december-2020

● SolarWinds · CWE-506 / CWE-829 · September 2019 – December 2020 · Critique
Recherche · FlawOpen

SUNBURST : attaque de la chaîne de compilation

SUNSPOT a modifié le code source d’Orion au build et intégré SUNBURST aux mises à jour signées. La signature prouvait l’origine, pas l’intégrité.

💡 Explication en Langage Simple (ELI5)

Imaginez un serrurier de confiance qui envoie des kits d'entretien scellés et tamponnés à des milliers d'immeubles. Quelqu'un s'introduit dans l'usine où l'on assemble les kits et ajoute une petite pièce à chacun avant la pose du scellé et du tampon. Chaque immeuble reçoit un kit qui vient réellement du serrurier et l'installe. Le scellé n'a jamais été brisé, car la falsification a eu lieu avant.

Concepts Clés et Termes

SUNSPOT
L'implant du serveur de build analysé par CrowdStrike. Il guettait MSBuild.exe compilant Orion et remplaçait un fichier source par la version piégée le temps de la compilation.
SUNBURST
La porte dérobée compilée dans SolarWinds.Orion.Core.BusinessLayer.dll. Elle restait dormante jusqu'à deux semaines, puis contactait l'infrastructure de l'attaquant en HTTP.
Signature de code
Une signature cryptographique montrant qu'un binaire vient du détenteur de la clé. Elle ne dit rien de l'éventuelle falsification du build qui l'a produit.
Build reproductible
Un build qui produit une sortie identique au bit près à partir des mêmes sources, pour que des builders indépendants puissent vérifier les résultats des autres.
CWE-506
Code malveillant intégré. Ici, il a été inséré pendant le build de l'éditeur lui-même, pas dans le dépôt de sources.

Correspondances de référentiels

Chronologie de l'Incident

2019-09-04

Les attaquants accèdent à l'environnement de SolarWinds ; le 12 septembre, ils injectent du code de test dans un build d'Orion pour vérifier que la méthode fonctionne.

2020-02-20

SUNBURST est compilé dans les builds d'Orion via SUNSPOT.

2020-03-26

SolarWinds commence à diffuser des correctifs d'Orion qui contiennent SUNBURST à son insu ; parmi les versions touchées figurent 2019.4 HF5 et 2020.2.

2020-06-04

Les attaquants retirent SUNSPOT de l'environnement de build.

2020-12-13

FireEye publie son analyse de SUNBURST et la CISA émet l'Emergency Directive 21-01, qui ordonne aux agences fédérales de déconnecter Orion.

Déroulement de l'Attaque Étape par Étape

Étape 1

Le serveur de build est compromis

Les attaquants ont obtenu un accès à l'environnement de build de SolarWinds et installé SUNSPOT sur le serveur qui compilait Orion.

Étape 2

Un fichier source est remplacé en pleine compilation

Quand SUNSPOT voyait MSBuild.exe compiler la solution Orion, il remplaçait InventoryManager.cs par une version piégée, puis restaurait l'original après la compilation.

Étape 3

La vraie clé signe le résultat

Le SolarWinds.Orion.Core.BusinessLayer.dll compilé passait par l'étape de signature habituelle : la DLL piégée portait donc une signature SolarWinds valide.

Étape 4

Les clients installent une mise à jour de confiance

Moins de 18 000 clients ont installé des mises à jour touchées. Les attaquants ont ensuite choisi un groupe de victimes bien plus restreint pour des intrusions ultérieures.

Code Source : Vulnérable vs Sécurisé

C# illustratif, pas la chaîne de SolarWinds. Le job vulnérable fait confiance à un unique serveur de build de longue durée et signe tout ce qu'il produit, précisément ce qu'a exploité SUNSPOT. La barrière durcie exige deux builds du même commit approuvé sur des builders fraîchement provisionnés et administrés séparément, compare leurs empreintes SHA-256 en temps constant et refuse de signer à la moindre différence. Un implant sur un builder produit alors un écart au lieu d'une version signée ; cela n'échoue que si l'attaquant contrôle les deux builders, d'où l'interdiction de partager identifiants ou images.

IMPLÉMENTATION VULNÉRABLE
using System;
using System.Diagnostics;

public static class ReleasePipeline
{
    // Les compilations utilisent la copie de travail partagée et modifiable d’un serveur, puis signent
    // tout résultat. Toute modification de l’arborescence pendant la compilation est aussi signée.
    public static void BuildAndSign(string project, string outputDll)
    {
        using var build = Process.Start("msbuild.exe", $"\"{project}\" /p:Configuration=Release");
        build!.WaitForExit();
        if (build.ExitCode != 0)
            throw new InvalidOperationException("build failed");

        Signer.Sign(outputDll);   // HSM-backed signing service client
    }
}
PATCH SÉCURISÉ ET ROBUSTE
using System;
using System.IO;
using System.Security.Cryptography;

public static class ReleaseGate
{
    // Deux compilateurs isolés compilent le même commit épinglé depuis des checkouts neufs en lecture seule,
    // avec /p:Deterministic=true. Le contrôle ne signe que des résultats identiques.
    public static void VerifyAndSign(string builderAOutput, string builderBOutput,
                                     string builtCommit, string approvedCommit)
    {
        if (!string.Equals(builtCommit, approvedCommit, StringComparison.Ordinal))
            throw new InvalidOperationException("build is not from the approved commit");

        byte[] a = SHA256.HashData(File.ReadAllBytes(builderAOutput));
        byte[] b = SHA256.HashData(File.ReadAllBytes(builderBOutput));
        if (!CryptographicOperations.FixedTimeEquals(a, b))
            throw new InvalidOperationException("independent builds differ; refusing to sign");

        Signer.Sign(builderAOutput, Convert.ToHexString(a));   // digest is logged with the signature
    }
}

Liste de Contrôle de Sécurité pour l'Ingénierie

Sources