2 ポイント 投稿者 GN⁺ 2024-09-20 | 1件のコメント | WhatsAppで共有

Arcのクラウド機能の紹介

  • Arcを使うにはアカウントが必要
  • 認証にFirebaseを使用
  • Easels というホワイトボードのような機能がある
  • 共有ボタンをクリックしても、mitmproxyにはリクエストが表示されない

Objective-CベースのFirebaseアプリのハッキング

  • Firestoreを使ってバックエンドを書かず、データベースのセキュリティルールだけを記述
  • FirestoreはSwift SDKでシステムプロキシ設定に従わない
  • Fridaスクリプトを書いて関連する呼び出しをダンプ
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はFirestoreにユーザー設定、ユーザーオブジェクト、推薦、Boostを保存している

Arc Boostとは何か

  • Arc Boostは、ユーザーがWebサイトをカスタマイズするための方法
  • 要素のブロック、フォント変更、色変更、カスタムCSSとJSの使用が可能
  • Boostを作成し、別のユーザーIDで更新できる

他ユーザーのIDを取得する

  • ユーザー推薦: 推薦テーブルからユーザーIDを取得できる
  • 公開Boost: Boostスナップショットに作成者のユーザーIDが含まれる
  • ユーザーEasel: Easelを共有してユーザーIDを取得できる

最終的な攻撃チェーン

  • 被害者のユーザーIDを取得する
  • 悪意のあるBoostを作成し、自分のアカウントに保存する
  • Boostの creatorID フィールドを標的のIDに更新する
  • 被害者が標的のWebサイトを訪問すると感染する

特権ページでのRCE

  • Boostは別のプロトコルでも実行される
  • chrome://settings ページで権限昇格が可能

プライバシーの問題

  • 訪問しているサイトに関するデータがサーバーへ送信される
  • Arcのプライバシーポリシーに反する

GN⁺のまとめ

  • Arcのクラウド機能とセキュリティ脆弱性を分析した記事
  • Firestoreを活用したバックエンドのセキュリティ問題を扱う
  • Arc Boostによるユーザーカスタマイズとセキュリティ脆弱性を説明
  • 他ユーザーのIDを取得して悪意のあるBoostを実行する方法を示す
  • プライバシー問題と権限昇格の可能性への懸念を提起する

1件のコメント

 
GN⁺ 2024-09-20
Hacker Newsの意見
  • Arcブラウザのセキュリティ脆弱性は許しがたいレベルであり、この件でArcは二度と使わない
  • クリックするたびに現れるピクセルアートの猫が面白く、インターネットが楽しい場所になり得ることを思い出させてくれる
  • Arcブラウザを使っている人たちに警告するため、投稿タイトルにArcを追加する必要がある
  • Arcはアカウントを要求し、ユーザーが訪問するすべてのページのホスト名とユーザーIDをGoogleのFirebaseに送信する。これは、Arcが現在使われている中で最もプライバシー保護が不十分なウェブブラウザであることを意味する
  • Firebaseのセキュリティルールのデフォルト設定はおかしく、経験のある開発者ならクライアントに自分のユーザーIDを保護されたAPIパスへ渡させたりしない
  • OPはArcブラウザについて話しており、Arc言語や他のプロジェクトと混同すべきではない
  • Arcブラウザは長くは続かない気がするし、Chromeが最も安全なブラウザだ。新しいソフトウェアの選択には慎重であるべき
  • 2000ドルの報奨金は、この大きな脆弱性に比べると侮辱的な金額だ
  • ブログ投稿で言及されている「arc」が何なのか気になっている人がいる。Arcブラウザのことらしい
  • 大文字の使い方が適切でなく、読みにくい記事だ