2 points par GN⁺ 2024-09-20 | 1 commentaires | Partager sur WhatsApp

Présentation des fonctionnalités cloud d’Arc

  • Un compte est nécessaire pour utiliser Arc
  • Firebase est utilisé pour l’authentification
  • Il existe une fonctionnalité de type tableau blanc appelée « Easels »
  • Même en cliquant sur le bouton de partage, aucune requête n’apparaît dans mitmproxy

Piratage d’une application Firebase basée sur Objective-C

  • Firestore est utilisé, avec uniquement des règles de sécurité de base de données écrites, sans backend dédié
  • Firestore ne suit pas les paramètres du proxy système dans le SDK Swift
  • Un script Frida a été écrit pour dumper les appels concernés
var documentWithPath = ObjC.classes.FIRCollectionReference["- documentWithPath:"];
var queryWhereFieldIsEqualTo = ObjC.classes.FIRQuery["- queryWhereField:isEqualTo:"];
var collectionWithPath = ObjC.classes.FIRFirestore["- collectionWithPath:"];

function getFullPath(obj) {
  if (obj.path && typeof obj.path === "function") {
    return obj.path().toString();
  }
  return obj.toString();
}

var queryStack = [];

function logQuery(query) {
  var queryString = `firebase.${query.type}("${query.path}")`;
  query.whereClauses.forEach((clause) => {
    queryString += `.where("${clause.fieldName}", "==", "${clause.value}")`;
  });
  console.log(queryString);
}

Interceptor.attach(documentWithPath.implementation, {
  onEnter: function (args) {
    var parent = ObjC.Object(args[0]);
    var docPath = ObjC.Object(args[2]).toString();
    var fullPath = getFullPath(parent) + "/" + docPath;
    var query = { type: "doc", path: fullPath, whereClauses: [] };
    queryStack.push(query);
    logQuery(query);
  },
});

Interceptor.attach(collectionWithPath.implementation, {
  onEnter: function (args) {
    var collectionPath = ObjC.Object(args[2]).toString();
    var query = { type: "collection", path: collectionPath, whereClauses: [] };
    queryStack.push(query);
  },
});

Interceptor.attach(queryWhereFieldIsEqualTo.implementation, {
  onEnter: function (args) {
    var fieldName = ObjC.Object(args[2]).toString();
    var value = ObjC.Object(args[3]).toString();

    if (queryStack.length > 0) {
      var currentQuery = queryStack[queryStack.length - 1];
      currentQuery.whereClauses.push({ fieldName: fieldName, value: value });
    }
  },
  onLeave: function (retval) {},
});

var executionMethods = [
  "- getDocuments",
  "- addSnapshotListener:",
  "- getDocument",
  "- addDocumentSnapshotListener:",
  "- getDocumentsWithCompletion:",
  "- getDocumentWithCompletion:",
];

executionMethods.forEach(function (methodName) {
  if (ObjC.classes.FIRQuery[methodName]) {
    Interceptor.attach(ObjC.classes.FIRQuery[methodName].implementation, {
      onEnter: function (args) {
        if (queryStack.length > 0) {
          var query = queryStack.pop();
          logQuery(query);
        }
      },
    });
  }
});

function formatFirestoreData(data) {
  if (data.isKindOfClass_(ObjC.classes.NSDictionary)) {
    let result = {};
    data.enumerateKeysAndObjectsUsingBlock_(
      ObjC.implement(function (key, value) {
        result[key.toString()] = value.toString();
      })
    );
    return JSON.stringify(result);
  }
  return data.toString();
}

var documentMethods = [
  { name: "- updateData:completion:", type: "update" },
  { name: "- updateData:", type: "update" },
  { name: "- setData:completion:", type: "set" },
  { name: "- setData:", type: "set" },
];

documentMethods.forEach(function (method) {
  if (ObjC.classes.FIRDocumentReference[method.name]) {
    Interceptor.attach(
      ObjC.classes.FIRDocumentReference[method.name].implementation,
      {
        onEnter: function (args) {
          var docRef = ObjC.Object(args[0]);
          var data = ObjC.Object(args[2]);
          var fullPath = getFullPath(docRef);
          var formattedData = formatFirestoreData(data);
          console.log(
            `firebase.doc("${fullPath}").${method.type}(${formattedData})`
          );
        },
      }
    );
  } else {
    console.log("Warning: " + method.name + " not found");
  }
});
  • Arc stocke dans Firestore les préférences utilisateur, les objets utilisateur, les recommandations et les boosts

Que sont les boosts d’Arc ?

  • Les boosts d’Arc permettent aux utilisateurs de personnaliser les sites web
  • Ils peuvent bloquer des éléments, changer les polices, modifier les couleurs et utiliser du CSS et du JS personnalisés
  • Il est possible de créer un boost puis de le mettre à jour avec l’ID d’un autre utilisateur

Obtenir l’ID d’un autre utilisateur

  • Recommandations utilisateur : il est possible d’obtenir un ID utilisateur depuis la table des recommandations
  • Boosts publics : les instantanés de boosts incluent l’ID utilisateur du créateur
  • Easels utilisateur : partager un Easel permet d’obtenir l’ID utilisateur

Chaîne d’attaque finale

  • Obtenir l’ID utilisateur de la victime
  • Créer un boost malveillant et l’enregistrer sur son propre compte
  • Mettre à jour le champ creatorID du boost avec l’ID de la cible
  • La victime est infectée lorsqu’elle visite le site web ciblé

RCE sur des pages privilégiées

  • Les boosts s’exécutent aussi sur d’autres protocoles
  • Une élévation de privilèges est possible sur la page chrome://settings

Problèmes de confidentialité

  • Des données sur les sites visités sont envoyées au serveur
  • Cela contrevient à la politique de confidentialité d’Arc

Résumé de GN⁺

  • Article analysant les fonctionnalités cloud d’Arc et ses vulnérabilités de sécurité
  • Il traite des problèmes de sécurité backend liés à l’utilisation de Firestore
  • Il explique la personnalisation utilisateur via les boosts d’Arc et les failles de sécurité associées
  • Il montre comment obtenir l’ID d’un autre utilisateur pour exécuter un boost malveillant
  • Il soulève des inquiétudes sur les problèmes de confidentialité et la possibilité d’une élévation de privilèges

1 commentaires

 
GN⁺ 2024-09-20
Avis sur Hacker News
  • La faille de sécurité du navigateur Arc est d’un niveau impardonnable, et à cause de cela je n’utiliserai plus jamais Arc
  • Le chat en pixel art qui accourt à chaque clic est amusant et rappelle qu’Internet peut être un espace réjouissant
  • Il faut ajouter Arc au titre du post afin d’avertir les personnes qui utilisent le navigateur Arc
  • Arc exige un compte et envoie à Firebase de Google le nom d’hôte de chaque page visitée par l’utilisateur ainsi que son identifiant utilisateur. Cela signifie qu’Arc est actuellement le navigateur web le moins respectueux de la vie privée que j’utilise
  • La configuration par défaut des règles de sécurité Firebase est étrange, et un développeur expérimenté ne laisserait pas le client transmettre son propre identifiant utilisateur à une route d’API protégée
  • L’OP parle du navigateur Arc, et il ne faut pas le confondre avec le langage Arc ou d’autres projets
  • Le navigateur Arc ne semble pas parti pour durer, et Chrome est le navigateur le plus sûr. Il faut être prudent dans le choix de nouveaux logiciels
  • Une récompense de 2000 $ est un montant insultant au regard de l’ampleur de la vulnérabilité
  • Certaines personnes se demandent ce que désigne « arc » dans le billet de blog. Il semble s’agir du navigateur Arc
  • Article difficile à lire car l’usage des majuscules n’est pas correct