2 Punkte von GN⁺ 2024-09-20 | 1 Kommentare | Auf WhatsApp teilen

Einführung in die Cloud-Funktionen von Arc

  • Für die Nutzung von Arc ist ein Konto erforderlich
  • Für die Authentifizierung wird Firebase verwendet
  • Es gibt eine whiteboard-ähnliche Funktion namens „Easels“
  • Selbst beim Klick auf den Freigabe-Button erscheint in mitmproxy keine Anfrage

Hacking einer Firebase-App auf Objective-C-Basis

  • Mit Firestore wird die Datenbank abgesichert, ohne ein Backend zu schreiben, indem nur Sicherheitsregeln für die Datenbank definiert werden
  • Firestore folgt im Swift SDK nicht den System-Proxy-Einstellungen
  • Es wurde ein Frida-Skript geschrieben, um die relevanten Aufrufe zu dumpen
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 speichert in Firestore Benutzerpräferenzen, Benutzerobjekte, Empfehlungen und Boosts

Was sind Arc Boosts?

  • Arc Boosts sind eine Möglichkeit für Nutzer, Websites anzupassen
  • Elemente blockieren, Schriftarten ändern, Farben ändern sowie benutzerdefiniertes CSS und JS verwenden ist möglich
  • Es ist möglich, einen Boost zu erstellen und ihn mit der Benutzer-ID einer anderen Person zu aktualisieren

So erhält man die ID anderer Nutzer

  • Benutzerempfehlungen: Aus der Empfehlungstabelle lässt sich eine Benutzer-ID auslesen
  • Öffentliche Boosts: Ein Boost-Snapshot enthält die Benutzer-ID des Erstellers
  • Benutzer-Easels: Durch das Teilen eines Easels lässt sich eine Benutzer-ID erhalten

Die finale Angriffskette

  • Die Benutzer-ID des Opfers wird erlangt
  • Ein bösartiger Boost wird erstellt und im eigenen Konto gespeichert
  • Das Feld creatorID des Boosts wird auf die ID des Ziels aktualisiert
  • Wenn das Opfer die Ziel-Website besucht, wird es infiziert

RCE auf privilegierten Seiten

  • Boosts werden auch auf anderen Protokollen ausgeführt
  • Auf der Seite chrome://settings ist eine Privilegieneskalation möglich

Datenschutzprobleme

  • Daten über die besuchten Websites werden an den Server gesendet
  • Das verstößt gegen die Datenschutzrichtlinie von Arc

Zusammenfassung von GN⁺

  • Dieser Artikel analysiert die Cloud-Funktionen von Arc und die Sicherheitslücken darin
  • Behandelt werden Sicherheitsprobleme eines Firestore-basierten Backends
  • Es wird erläutert, wie Arc Boosts zur Nutzeranpassung funktionieren und welche Sicherheitslücken sie aufweisen
  • Es wird gezeigt, wie sich über die ID anderer Nutzer bösartige Boosts ausführen lassen
  • Zudem werden Bedenken zu Datenschutzproblemen und möglicher Privilegieneskalation geäußert

1 Kommentare

 
GN⁺ 2024-09-20
Hacker-News-Kommentare
  • Die Sicherheitslücke im Arc-Browser ist auf einem unverzeihlichen Niveau, deshalb werde ich Arc nie wieder benutzen.
  • Die Pixel-Art-Katze, die bei jedem Klick angelaufen kommt, ist lustig und erinnert daran, dass das Internet ein unterhaltsamer Ort sein kann.
  • Um Menschen zu warnen, die den Arc-Browser verwenden, sollte im Titel des Beitrags „Arc“ ergänzt werden.
  • Arc verlangt ein Konto und sendet den Hostnamen jeder vom Nutzer besuchten Seite sowie die Benutzer-ID an Googles Firebase. Das bedeutet, dass Arc derzeit der am wenigsten datenschutzfreundliche Webbrowser ist, den ich benutze.
  • Die Standardeinstellungen der Firebase-Sicherheitsregeln sind merkwürdig, und erfahrene Entwickler lassen den Client nicht seine eigene Benutzer-ID an einen geschützten API-Pfad übermitteln.
  • Der OP spricht über den Arc-Browser und sollte nicht mit der Arc-Sprache oder anderen Projekten verwechselt werden.
  • Der Arc-Browser wird sich vermutlich nicht lange halten, und Chrome ist der sicherste Browser. Bei der Wahl neuer Software sollte man vorsichtig sein.
  • Eine Prämie von 2000 $ ist angesichts einer so großen Schwachstelle eine beleidigend niedrige Summe.
  • Manche fragen sich, was mit „arc“ im Blogbeitrag gemeint ist. Es scheint der Arc-Browser zu sein.
  • Der Artikel ist schwer zu lesen, weil die Großschreibung nicht richtig verwendet wird.