2 pontos por GN⁺ 2024-09-20 | 1 comentários | Compartilhar no WhatsApp

Introdução aos recursos em nuvem do Arc

  • É necessária uma conta para usar o Arc
  • Usa o Firebase para autenticação
  • Há um recurso parecido com quadro branco chamado 'Easels'
  • Mesmo ao clicar no botão de compartilhamento, nenhuma requisição aparece no mitmproxy

Hackeando um app Firebase baseado em Objective-C

  • Usa o Firestore para implementar o backend sem escrever um backend, apenas definindo regras de segurança do banco de dados
  • O Firestore não segue as configurações de proxy do sistema no SDK Swift
  • Foi escrito um script Frida para fazer dump das chamadas 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");
  }
});
  • O Arc armazena no Firestore as preferências padrão do usuário, objetos de usuário, recomendações e boosts

O que são os boosts do Arc

  • Os boosts do Arc são uma forma de o usuário personalizar websites
  • É possível bloquear elementos, mudar fontes, mudar cores e usar CSS e JS personalizados
  • É possível criar um boost e atualizá-lo com o ID de outro usuário

Obtendo o ID de outros usuários

  • Recomendações de usuários: é possível obter IDs de usuário na tabela de recomendações
  • Boosts públicos: snapshots de boosts incluem o ID de usuário do criador
  • Easels de usuários: é possível obter o ID de usuário ao compartilhar um easel

Cadeia final do ataque

  • Obter o ID de usuário da vítima
  • Criar um boost malicioso e armazená-lo na própria conta
  • Atualizar o campo creatorID do boost com o ID do alvo
  • Quando a vítima visita o website alvo, ela é infectada

RCE em páginas privilegiadas

  • Os boosts também são executados em outros protocolos
  • É possível escalar privilégios na página chrome://settings

Problemas de privacidade

  • Dados sobre os sites visitados são enviados ao servidor
  • Isso viola a política de privacidade do Arc

Resumo do GN⁺

  • Artigo que analisa os recursos em nuvem do Arc e suas vulnerabilidades de segurança
  • Aborda problemas de segurança no backend com uso do Firestore
  • Explica a personalização de usuários com os boosts do Arc e suas vulnerabilidades de segurança
  • Mostra como obter o ID de outros usuários e executar boosts maliciosos
  • Levanta preocupações sobre problemas de privacidade e possibilidade de escalada de privilégios

1 comentários

 
GN⁺ 2024-09-20
Comentários do Hacker News
  • A vulnerabilidade de segurança do navegador Arc é imperdoável, e por causa disso não vou usar o Arc novamente
  • O gato em pixel art que aparece correndo a cada clique é divertido e lembra que a internet pode ser um lugar agradável
  • É preciso adicionar Arc ao título da postagem para alertar as pessoas que usam o navegador Arc
  • O Arc exige uma conta e envia para o Firebase do Google o nome do host de todas as páginas que o usuário visita e o ID do usuário. Isso significa que o Arc é atualmente o navegador web menos protetor de privacidade em uso
  • A configuração padrão das regras de segurança do Firebase é estranha, e um desenvolvedor experiente não faria o cliente transmitir seu próprio ID de usuário para um caminho de API protegido
  • O OP está falando sobre o navegador Arc, e isso não deve ser confundido com a linguagem Arc ou outros projetos
  • O navegador Arc não deve durar muito, e o Chrome é o navegador mais seguro. É preciso ter cuidado ao escolher um software novo
  • A recompensa de US$ 2000 é um valor insultante para uma vulnerabilidade tão grande
  • Há quem esteja se perguntando o que é o 'arc' mencionado na postagem do blog. Parece ser o navegador Arc
  • É um artigo difícil de ler porque não usa letras maiúsculas corretamente