2 puntos por GN⁺ 2024-09-20 | 1 comentarios | Compartir por WhatsApp

Introducción a las funciones en la nube de Arc

  • Para usar Arc se necesita una cuenta
  • Usa Firebase para la autenticación
  • Tiene una función similar a una pizarra llamada 'Easels'
  • Incluso al hacer clic en el botón de compartir, no aparece ninguna solicitud en mitmproxy

Hackeo de una app de Firebase basada en Objective-C

  • Usa Firestore para construir la base de datos sin escribir un backend, solo reglas de seguridad de la base de datos
  • Firestore no sigue la configuración del proxy del sistema en el SDK de Swift
  • Se escribió un script de Frida para volcar las llamadas relacionadas
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 guarda en Firestore las preferencias del usuario, objetos de usuario, recomendaciones y boosts

Qué es Arc Boost

  • Arc Boost es una forma en que los usuarios pueden personalizar sitios web
  • Permite bloquear elementos, cambiar fuentes, cambiar colores y usar CSS y JS personalizados
  • Se puede crear un boost y actualizarlo con el ID de otro usuario

Cómo obtener el ID de otro usuario

  • Recomendaciones de usuarios: se puede obtener el ID en la tabla de recomendaciones
  • Boosts públicos: la instantánea del boost incluye el ID del usuario creador
  • Easels de usuario: al compartir un easel se puede obtener el ID del usuario

Cadena de ataque final

  • Obtener el ID de usuario de la víctima
  • Crear un boost malicioso y guardarlo en la propia cuenta
  • Actualizar el campo creatorID del boost con el ID del objetivo
  • Cuando la víctima visita el sitio web objetivo, queda infectada

RCE en páginas privilegiadas

  • El boost también se ejecuta en otros protocolos
  • Es posible elevar privilegios en la página chrome://settings

Problemas de privacidad

  • Los datos sobre los sitios visitados se envían al servidor
  • Esto contradice la política de privacidad de Arc

Resumen de GN⁺

  • Es un artículo que analiza las funciones en la nube de Arc y sus vulnerabilidades de seguridad
  • Aborda problemas de seguridad del backend basados en Firestore
  • Explica la personalización de usuarios mediante Arc Boost y sus vulnerabilidades de seguridad
  • Presenta un método para obtener el ID de otro usuario y ejecutar un boost malicioso
  • Plantea preocupaciones sobre privacidad y la posibilidad de elevación de privilegios

1 comentarios

 
GN⁺ 2024-09-20
Opiniones en Hacker News
  • La vulnerabilidad de seguridad del navegador Arc es imperdonable, y por esto no volveré a usar Arc nunca más
  • El gato de pixel art que aparece cada vez que haces clic es divertido y recuerda que internet puede ser un lugar agradable
  • Hace falta agregar Arc al título de la publicación para advertir a quienes usan el navegador Arc
  • Arc exige una cuenta y envía a Firebase de Google el nombre de host de cada página que visita el usuario y su ID de usuario. Esto significa que Arc es actualmente el navegador web menos respetuoso con la privacidad que uso
  • La configuración predeterminada de las reglas de seguridad de Firebase es extraña, y un desarrollador con experiencia no haría que el cliente enviara su propio ID de usuario a una ruta de API protegida
  • El OP está hablando del navegador Arc, y no debe confundirse con el lenguaje Arc ni con otros proyectos
  • Parece que el navegador Arc no durará mucho, y Chrome es el navegador más seguro. Hay que ser cuidadosos al elegir software nuevo
  • La recompensa de $2000 es una cantidad insultante para una vulnerabilidad tan grande
  • Hay gente preguntándose qué es el "arc" mencionado en la publicación del blog. Parece ser el navegador Arc
  • Es un artículo difícil de leer porque no se usan bien las mayúsculas