過去 XNUMX 週間にわたって、人気のオープンソース パスワード マネージャー KeePass の「マスター パスワード クラック」と呼ばれるものについて取り上げる一連の記事を目にしました。
このバグは、米国政府の公式識別子 (として知られている) を取得するのに十分重要であると考えられていました。 CVE-2023-32784、追跡したい場合は)、パスワードマネージャーのマスターパスワードがデジタル城全体のほぼ鍵であることを考えると、この話がなぜ多くの興奮を引き起こしたか理解できます。
良いニュースは、このバグを悪用しようとする攻撃者は、ほぼ確実にコンピュータをすでにマルウェアに感染させている必要があり、そのため、とにかくキーストロークや実行中のプログラムを盗み見ることができるということです。
言い換えれば、このバグは、KeePass の作成者がアップデートをリリースするまで、管理が容易なリスクであると考えることができます。アップデートは間もなく (2023 年 XNUMX 月初旬に公開されると思われます) リリースされるはずです。
バグの開示者は次のことに気を配ります。 指摘する:
強力なパスワードを使用してフルディスク暗号化を使用し、システムに [マルウェアが存在しない] 場合は、問題ないはずです。 この発見だけでは、誰もインターネットを介してリモートからパスワードを盗むことはできません。
リスクの説明
このバグを簡単に要約すると、機密データの使用を終了した後にその痕跡をすべてメモリから確実に消去することが難しいということです。
ここでは、たとえ一時的であっても、メモリ内に機密データがまったく存在しないようにする方法の問題は無視します。
この記事では、あらゆるプログラマに、セキュリティ意識の高いレビュー担当者によって「コード自体が正しくクリーンアップされるようです」などのコメント付きで承認されたコードであることを思い出してもらいたいだけです。
…実際にはまったく完全にクリーンアップされていない可能性があり、コード自体を直接調査しても潜在的なデータ漏洩は明らかではない可能性があります。
簡単に言うと、CVE-2023-32784 の脆弱性は、パスワードに関する十分な情報 (実際には生のパスワードそのものではありませんが、ここで焦点を当てます) があるため、KeyPass プログラムが終了した後でも、KeePass マスター パスワードがシステム データから回復できる可能性があることを意味します。すぐにオンになります) がシステム スワップ ファイルまたはスリープ ファイルに残される可能性があり、割り当てられたシステム メモリが後で使用するために保存される可能性があります。
システムの電源がオフになっているときにハード ディスクの暗号化に BitLocker が使用されていない Windows コンピュータでは、ラップトップを盗んだ犯罪者に USB または CD ドライブから起動して、マスター パスワードさえも回復するチャンスが与えられる可能性があります。ただし、KeyPass プログラム自体は、それをディスクに永久に保存しないように注意します。
メモリ内のパスワードが長期にわたって漏洩するということは、理論的には、たとえパスワードを入力してからかなり経ってから、そして KeePass のプログラムが実行されてからずっと後にそのダンプが取得されたとしても、そのパスワードが KeyPass プログラムのメモリ ダンプから回復できる可能性があることを意味します。それ自体はもうそれを保持する必要がありませんでした。
明らかに、システム上にすでに存在するマルウェアは、入力時にアクティブであった限り、さまざまなリアルタイム スヌーピング技術を介して、入力されたほぼすべてのパスワードを回復できると想定する必要があります。 しかし、危険にさらされる時間は入力中の短時間に限定され、その後は数分、数時間、または数日、あるいはコンピュータをシャットダウンした後も含めてさらに長い時間に及ぶことはないと予想することもできます。
何が残ってしまうのでしょうか?
そこで私たちは、コードからは直接明らかではない方法で機密データがメモリ内にどのように残されるのかを概要的に見てみることにしました。
プログラマーでなくても心配する必要はありません。簡単にして、説明を進めていきます。
まず、次の操作を実行してパスワードの入力と一時保存をシミュレートする単純な C プログラムでのメモリの使用とクリーンアップを見ていきます。
- 専用のメモリチャンクの割り当て 特にパスワードを保存するために。
- 既知のテキスト文字列の挿入 そのため、必要に応じてメモリ内で簡単に見つけることができます。
- 16 個の擬似ランダム 8 ビット ASCII 文字を追加 範囲APから。
- プリントアウト シミュレートされたパスワードバッファ。
- メモリを解放する パスワードバッファを消去することを期待しています。
- 終了 プログラム。
大幅に簡略化すると、C コードは次のようになります。エラー チェックは行わず、C ランタイム関数からの低品質の疑似乱数を使用します。 rand()
、バッファ オーバーフロー チェックは無視します (実際のコードでは決してこれを行わないでください)。
// Ask for memory char* buff = malloc(128); // Copy in fixed string we can recognise in RAM strcpy(buff,"unlikelytext"); // Append 16 pseudo-random ASCII characters for (int i = 1; i <= 16; i++) { // Choose a letter from A (65+0) to P (65+15) char ch = 65 + (rand() & 15); // Modify the buff string directly in memory strncat(buff,&ch,1); } // Print it out, so we're done with buff printf("Full string was: %sn",buff); // Return the unwanted buffer and hope that expunges it free(buff);
実際、テストで最終的に使用したコードには、以下に示すいくつかの追加部分が含まれているため、使用時に一時パスワード バッファーの全内容をダンプして、不要なコンテンツや残ったコンテンツを探すことができます。
呼び出し後に意図的にバッファをダンプしていることに注意してください。 free()
これは技術的には use-after-free のバグですが、ここでは、バッファを返した後に何か重要なものが残されていないかどうかを確認するための卑劣な方法としてこれを実行しています。これは、実際には危険なデータ漏洩の穴につながる可能性があります。
こちらもXNUMX本挿入しました Waiting for [Enter]
コードにプロンプトを入力して、プログラムの重要なポイントでメモリ ダンプを作成する機会を与え、プログラムの実行中に何が残ったかを確認するために、後で検索するための生データを提供します。
メモリ ダンプを実行するには、Microsoft の Sysinternals ツール procdump
-ma
オプション(すべてのメモリをダンプする)、Windows を使用するために独自のコードを記述する必要がなくなります。 DbgHelp
システムとそのかなり複雑な MiniDumpXxxx()
機能.
C コードをコンパイルするために、私たちは Fabrice Bellard の無料のオープンソースの小規模でシンプルな独自のビルドを使用しました。 小さな C コンパイラ、 64 ビット Windows で利用可能 ソースとバイナリ形式 GitHub ページから直接アクセスできます。
記事に掲載されているすべてのソース コードのコピー アンド ペースト可能なテキストがページの下部に表示されます。
テスト プログラムをコンパイルして実行すると、次のことが起こりました。
C:UsersduckKEYPASS> petcc64 -stdinc -stdlib unl1.c Tiny C Compiler - Copyright (C) 2001-2023 Fabrice Bellard 学習ツールとして使用するために Paul Ducklin によって削除されました バージョン petcc64-0.9.27 [0006] - 64 ビットを生成しますPE のみ -> unl1.c -> c:/users/duck/tcc/petccinc/stdio.h [. 。 。 .] -> c:/users/duck/tcc/petcclib/libpetcc1_64.a -> C:/Windows/system32/msvcrt.dll -> C:/Windows/system32/kernel32.dll -------- ----------------------- virt ファイル サイズ セクション 1000 200 438 .text 2000 800 2ac .data 3000 c00 24 .pdata -------- ----------------------- <- unl1.exe (3584 バイト) C:UsersduckKEYPASS> unl1.exe 開始時に '新しい' バッファをダンプしています 00F51390: 90 57 F5 00 00 00 00 00 50 01 F5 00 00 00 00 00 .W....P.... 00F513A0: 73 74 65 6D 33 32 5C 63 6D 64 2E 65 78 65 00 44 ステム32cmd。 exe.D 00F513B0: 72 69 76 65 72 44 61 74 61 3D 43 3A 5C 57 69 6E RiverData=C:Win 00F513C0: 64 6F 77 73 5C 53 79 73 74 65 6D 33 32 5C 44 72 32 ダウズシステム00Dr 513F0D69: 76 65 72 73 5 44C 72 69 76 65 72 44 61 74 61 00 iversDriverData 513F0E00: 45 46 43 5 34F 33 37 32 3 31D 00 46 50 53 5 4372F .EFC_1=00.FPS_ 513F0 42F52: 4 57 53F 45 52 5 41 50F 50 5 50 52F 4 46 00F 51400 BROWSER_APP_PROF 49F4: 45 5C 53 54F 52 49 4 47 3E 49 6D 74 65E 72 00 51410 ILE_STRING=Inter 6F65: 74E 20 45 78 70 6 7 56 4C 3A 4 F00 00C AC 00B 51390 75 net ExplzV.< .K.. 完全な文字列は次のとおりでした:ありそうもないtextJHKNEJJCPOMDJHAN 6F6: 69 6E 65C 6 79B 74 65C 78 74 4 48 4 4A 00 513B 0E possibletextJHKN 45F4A4: 43 50A 4A 4 44 4F 48D 41 4A 00 65 00E 44 00 513 0 EJJCPOMDJHAN.eD 72F69B76 : 65 72 44 61 74 61 3 43 3 5D 57 69A 6C 00 513 0E RiverData=C:Win 64F6C77: 73 5F 53 79 73C 74 65 6 33 32 5D 44 72 32C 00 513 dowsSystem0Dr 69F76D65: 72 73 5 44 72 69C 76 65 72 44 61 74 61 00 513 0 iversDriverData 00F45E46: 43 5 34 33 37F 32 3 31 00 46D 50 53 5 4372 1 00F .EFC_513=0.FPS_ 42F52F4: 57 53 45F 52 5 41 50 50F 5 50 52 4F 46 00 51400F 49 BROWSER_APP_PROF 4F45: 5 53C 54 52F 49 4 47 3 49E 6 74D 65 72E 00 51410 6 ILE_STRING=Inter 65F74: 20E 45 78 70 6 7 56 4C 3A 4 F00 00C AC 00B 51390 0 net ExplzV.<.K.. [ENTER] でバッファを解放するのを待っています... free() 後にバッファをダンプしています 67F5: A00 00 F00 00 00 50 01 5 00 00 F00 00 00 00 513 0 .g....P.... .45F4A4: 43 50A 4A 4 44 4F 48D 41 4A 00 65 00E 44 00 513 0 EJJCPOMDJHAN.eD 72F69B76: 65 72 44 61 74 61 3 43 3 5D 57 69A 6C 00 513 0E リバーデータ=C:Win 64F6C77: 73 5F 53 79 73C 74 65 6 33 32 5D 44 72 32C 00 513 dowsSystem0Dr 69F76D65: 72 73 5 44 72 69C 76 65 72 44 61 74 61 00 513 0 iversDriverData 00F45E46: 43 5 34 33 37F 32 3 31 00 46D 50 53 5 4372 1 00F .EFC_513=0.FPS_ 42F52F4: 57 53 45F 52 5 41 50 50F 5 50 52 4F 46 00 51400F 49 BROWSER_APP_PROF 4F45: 5 53C 54 52F 49 4 47 3 49E 6 74D 65 72E 00 51410 6 ILE_STRING=インター 65F74: 20E 45 78 70 6 4 00 00C 4D 4 00 00D AC XNUMXB XNUMX XNUMX net ExplM..MK. [ENTER] で main() を終了するのを待っています... C:UsersduckKEYPASS>
この実行では、このコードがデータを漏洩していることが出力からすぐに分かったので、わざわざプロセス メモリ ダンプを取得する必要はありませんでした。
Windows C ランタイム ライブラリ関数を呼び出した直後 malloc()
返されたバッファには、プログラムの起動コードから残された環境変数データのように見えるものが含まれており、最初の 16 バイトは、ある種の残されたメモリ割り当てヘッダーのように見えるように変更されていることがわかります。
(これらの 16 バイトが 8 つの XNUMX バイトのメモリ アドレスのように見えることに注目してください。 0xF55790
および 0xF50150
、それぞれ独自のメモリ バッファの直後と直前です)。
パスワードがメモリ内にあると想定されている場合、予想どおり、バッファ内で文字列全体がはっきりと確認できます。
でも電話した後 free()
バッファの最初の 16 バイトが、近くのメモリ アドレスのように見えるもので再び書き換えられていることに注目してください。おそらく、メモリ アロケータが再利用できるメモリ内のブロックを追跡できるようにするためです。
…ただし、「消去された」パスワードテキストの残りの部分 (最後のランダムな 12 文字) EJJCPOMDJHAN
)が残されてしまいました。
C で独自のメモリの割り当てと割り当て解除を管理する必要があるだけでなく、データ バッファーを正確に制御したい場合は、データ バッファーに適切なシステム関数を確実に選択する必要もあります。
たとえば、代わりに次のコードに切り替えることで、メモリ内の内容をもう少し制御できるようになります。
から切り替えることにより malloc()
および free()
下位レベルの Windows 割り当て関数を使用する VirtualAlloc()
および VirtualFree()
直接的には、より良い制御が得られます。
ただし、各呼び出しが行われるため、速度の面で代償を払っています。 VirtualAlloc()
呼び出しよりも多くの作業を実行します malloc()
、事前に割り当てられた低レベル メモリのブロックを継続的に分割および再分割することによって機能します。
使い方 VirtualAlloc()
小さなブロックを繰り返し実行すると、各ブロックが分散されるため、全体的により多くのメモリが使用されます。 VirtualAlloc()
通常、4KB の倍数のメモリ (いわゆるメモリを使用している場合は 2MB) を消費します。 大きなメモリページ)、そのため、上記の 128 バイトのバッファーは 4096 バイトに切り上げられ、3968KB メモリ ブロックの終わりの 4 バイトが無駄になります。
しかし、ご覧のとおり、返されるメモリは自動的に空白になる (ゼロに設定される) ため、以前に何があったのかを見ることができなくなり、今度は解放後の使用を実行しようとするとプログラムがクラッシュします。これは、私たちが所有していないメモリを覗き見しようとしていることを Windows が検出するためです。
C:UsersduckKEYPASS> unl2 開始時に「新しい」バッファをダンプしています 0000000000EA0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0000000000EA0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0000000000EA0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .. ................................ 0000000000EA0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0000000000EA0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... 0000000000EA0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................... 0000000000EA0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... 0000000000EA0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... 0000000000EA0080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 ....完全な文字列は次のとおりでした:ありそうもないtextIBIPJPPHEOPOIDLL 0000EA75: 6 6E 69C 6 65B 6 79C 74 65 78 74 49 42 49 50 0000000000らしきものtextIBIP 0010EA4: 50A 50 48 45 4 50 4F 49 44F 4 4 00C 00C 00 00 0000000000 0020 JPPHEOPOIDLL.... 00EA00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 0030 ................ 00EA00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 0040 ... 00EA00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 0050 ................ 00EA00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 0060 ................... .00EA00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 0070 ................ 00EA00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 0080 ................ 00EA00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 0000 ................................ ... [ENTER] でバッファを解放するのを待っています... free() の後にバッファをダンプしています XNUMXEAXNUMX: [Windows が use-after-free をキャッチしたため、プログラムはここで終了しました]
解放したメモリは再割り当てする必要があるため、 VirtualAlloc()
再び使用できるようになる前に、リサイクルされる前にゼロになると想定できます。
ただし、空白になっていることを確認したい場合は、特別な Windows 関数を呼び出すことができます。 RtlSecureZeroMemory()
Windows が最初にバッファにゼロを書き込むことを保証するために、それを解放する直前に。
関連機能 RtlZeroMemory()
疑問に思われた方のために、これは同様のことを行いますが、実際に動作するという保証はありません。コンパイラーは、バッファーが後で再び使用されないことに気付いた場合、理論的に冗長であるとしてそれを削除することが許可されているためです。
ご覧のとおり、メモリに保存されたシークレットが後で残る可能性がある時間を最小限に抑えたい場合は、適切な Windows 機能を使用するようかなりの注意を払う必要があります。
この記事では、シークレットを物理 RAM にロックすることで、シークレットが誤ってスワップ ファイルに保存されるのを防ぐ方法については説明しません。 (ヒント: VirtualLock()
実際には、それだけでは十分ではありません。) 低レベルの Windows メモリ セキュリティについて詳しく知りたい場合は、コメント欄でお知らせください。今後の記事で説明します。
自動メモリ管理の使用
自分でメモリの割り当て、管理、割り当て解除を行わなくて済む優れた方法の XNUMX つは、メモリの割り当てを処理するプログラミング言語を使用することです。 malloc()
および free()
または VirtualAlloc()
および VirtualFree()
自動的に。
次のようなスクリプト言語 パール, Python , Luaの, JavaScriptを 他のものは、バックグラウンドでメモリ使用量を追跡することによって、C および C++ コードを悩ませる最も一般的なメモリ安全性のバグを取り除きます。
前に述べたように、上記の不適切に作成されたサンプル C コードは現在正常に動作していますが、それはそれが固定サイズのデータ構造を備えた非常に単純なプログラムであり、128 個のコードを上書きしないことを検査によって検証できるためです。バイト バッファーであり、次で始まる実行パスは XNUMX つだけであること malloc()
そして対応するもので終わります free()
.
しかし、可変長パスワードの生成を可能にするためにそれを更新したり、生成プロセスに追加の機能を追加したりすると、私たち (または次にコードを保守する人) は簡単にバッファ オーバーフロー、解放後の使用のバグ、またはメモリの使用を終了する可能性があります。決して解放されないため、機密データは必要でなくなった後もずっと放置されたままになります。
Lua のような言語では、Lua ランタイム環境に、専門用語で次のように知られることを実行させることができます。 自動ガベージコレクション、システムからメモリを取得し、メモリの使用が停止されたことが検出されたときにメモリを返す処理を行います。
メモリの割り当てと割り当て解除が自動的に行われると、上にリストした C プログラムが非常に単純になります。
文字列を保持するためにメモリを割り当てます s
文字列を割り当てるだけで 'unlikelytext'
それに。
後で、Lua にもう興味がないことを明示的に示唆することもできます。 s
値を代入することで nil
(すべて nils
本質的に同じ Lua オブジェクトです)、または使用を中止してください s
そして、Lua がそれが不要になったことを検出するまで待ちます。
いずれにしても、使用されるメモリは、 s
最終的には自動的に回復されます。
また、テキスト文字列 (Lua 演算子) に追加する際のバッファ オーバーフローやサイズ管理の誤りを防ぐため、 ..
、発音される 連結、 基本的には次のように XNUMX つの文字列を加算します。 +
Python では)、文字列を拡張または短縮するたびに、Lua は、既存のメモリ位置にある元の文字列を変更したり置き換えたりするのではなく、魔法のように新しい文字列用のスペースを割り当てます。
このアプローチは時間がかかり、テキスト操作中に中間文字列が割り当てられるため、メモリ使用量のピークが C よりも高くなりますが、バッファ オーバーフローに関してははるかに安全です。
しかし、この種の自動文字列管理 (専門用語では 不変性、文字列は決して取得されないため、 突然変異した、 または、作成後にその場で変更されます)、それ自体が新たなサイバーセキュリティの問題を引き起こします。
上記の Lua プログラムを Windows 上で、プログラムが終了する直前の XNUMX 回目の一時停止まで実行しました。
C:UsersduckKEYPASS> lua s1.lua 完全な文字列は次のとおりです: possibletextHLKONBOJILAGLNLN 文字列を解放する前に [ENTER] を待っています... 終了する前に [ENTER] を待っています...
今回は、次のようなプロセス メモリ ダンプを取得しました。
C:UsersduckKEYPASS> procdump -ma lua lua-s1.dmp ProcDump v11.0 - Sysinternals プロセス ダンプ ユーティリティ Copyright (C) 2009-2022 Mark Russinovich および Andrew Richards Sysinternals - www.sysinternals.com [00:00:00] ダンプ 1開始済み: C:UsersduckKEYPASSlua-s1.dmp [00:00:00] ダンプ 1 の書き込み: ダンプ ファイルの推定サイズは 10 MB です。 [00:00:00] ダンプ 1 完了: 10 秒で 0.1 MB が書き込まれました [00:00:01] ダンプ カウントに達しました。
次に、この単純なスクリプトを実行しました。これは、ダンプ ファイルを再度読み取り、既知の文字列が存在するメモリ内のあらゆる場所を見つけます。 unlikelytext
というメッセージが表示され、ダンプファイル内の場所とその直後に続く ASCII 文字とともに出力します。
以前にスクリプト言語を使用したことがある場合や、いわゆるスクリプト言語を備えたプログラミング エコシステムで働いていた場合でも、 マネージド文字列、システムはメモリの割り当てと割り当て解除を追跡し、必要に応じてそれらを処理します…
…このメモリ スキャンによって生成される出力を見て驚くかもしれません。
C:UsersduckKEYPASS> lua findit.lua lua-s1.dmp 006D8AFC: ありそうもないテキストALJBNGOAPLLBDEB 006D8B3C: ありそうもないテキストALJBNGOA 006D8B7C: ありそうもないテキストALJBNGO 006D8BFC: ありそうもないテキストALJBNGOAPLLBDEBJ 006D8CBC: ありそうもないテキストALJBN 006D8D7 C: ありそうもないテキストALJBNGOAP 006D903C: ありそうもないテキストALJBNGOAPL 006D90BC: ありそうもないテキストALJBNGOAPLL 006D90FC: ありそうもないテキストALJBNG 006D913C: ありそうもないテキストALJBNGOAPLLB 006D91BC: ありそうもないテキストALJB 006D91FC: ありそうもないテキストALJBNGOAPLLBD 006D923C : ありそうもないテキストALJBNGOAPLLBDE 006DB70C: ありそうもないテキストALJ 006DBB8C: ありそうもないテキストAL 006DBD0C: ありそうもないテキストA
なんと、文字列の作成は終わっていたにもかかわらず、メモリ ダンプを取得した時点でした。 s
(そして、Lua に次のように言って、もう必要ないことを伝えました。 s = nil
)、コードが途中で作成したすべての文字列はまだRAMに存在しており、まだ回復または削除されていません。
実際、上記の出力を RAM に出現する順序に従うのではなく、文字列自体によってソートすると、一度に XNUMX 文字ずつパスワード文字列に連結するループ中に何が起こったかを想像できるでしょう。
C:UsersduckKEYPASS> lua findit.lua lua-s1.dmp | sort /+10 006DBD0C: ありそうもないテキストA 006DBB8C: ありそうもないテキストAL 006DB70C: ありそうもないテキストALJ 006D91BC: ありそうもないテキストALJB 006D8CBC: ありそうもないテキストALJBN 006D90FC: ありそうもないテキストALJBNG 006D8B7C: ありそうもないテキストALJBNGO 006D8B3C: ありそうもないテキストALJBNGOA 006 8D7D006C: ありそうもないテキストALJBNGOAP 903D006C: ありそうもないテキストALJBNGOAPLL 90D006BC: ありそうもないテキストALJBNGOAPLL 913D006C: ありそうもないテキストALJBNGOAPLLB 91D006FC: ありそうもないテキストALJBNGOAPLLBD 923D006C: ありそうもないテキストALJBNGOAPLLBDE 8D006AFC: ありそうもないテキストALJBNGOAPLL BDEB 8DXNUMXBFC : ありそうもないテキストALJBNGOAPLLBDEBJ
これらの一時的な中間文字列はすべてまだ存在しているため、たとえ最終的な値を完全に消去したとしても、 s
、最後の文字を除くすべてが依然としてリークされてしまいます。
実際、このケースでは、特別な Lua 関数を呼び出してプログラムに不要なデータをすべて強制的に破棄させた場合でも、 collectgarbage()
(ほとんどのスクリプト言語には同様の機能があります)、古き良き使用法を使用して自動メモリ管理を行うように Lua をコンパイルしたため、これらの厄介な一時文字列のデータのほとんどは RAM に残ります。 malloc()
および free()
.
言い換えれば、Lua 自体が一時メモリ ブロックを再利用するために再利用した後でも、それらのメモリ ブロックがいつ、どのように再利用されるかを制御できず、したがってそれらのメモリ ブロックがプロセス内でどれくらいの期間放置されたままになるかは制御できませんでした。盗み出されたり、ダンプされたり、あるいは漏洩されるのを待っているデータが過剰に存在します。
.NETを入力してください
しかし、この記事の出発点となった KeePass についてはどうでしょうか?
KeePass は C# で書かれており、.NET ランタイムを使用するため、C プログラムに伴うメモリ管理ミスの問題を回避できます。
…しかし、C# は Lua と同様に独自のテキスト文字列を管理するため、次のような疑問が生じます。
たとえプログラマが、マスター パスワードを使い終えた後、マスター パスワード全体を XNUMX か所に保存することを避けたとしても、メモリ ダンプにアクセスできる攻撃者は、たとえそれらのデータがあったとしても、マスター パスワードを推測したり回復したりするのに十分な残された一時データを見つけることができるでしょうか。 ? にパスワードを入力してから数分、数時間、または数日後に攻撃者があなたのコンピュータにアクセスしました。
簡単に言うと、消去されたと思われた後でも、RAM 内に残っているマスター パスワードの検出可能な幽霊のような残骸はありますか?
迷惑なことに、Github ユーザーとして ヴドーニー発見、少なくとも 2.54 より前の KeePass バージョンの場合、答えは「はい」です。
明確にしておきますが、実際のマスター パスワードを KeePass メモリ ダンプから単一のテキスト文字列として復元できるとは考えていません。これは、作成者が完全なマスター パスワードの保存を避けるためにわざわざマスター パスワード入力用の特別な関数を作成したためです。パスワードは簡単に見つけられ、盗み見られる場所にあります。
私たちはマスターパスワードを次のように設定することでこれを満足しました。 SIXTEENPASSCHARS
、それを入力し、その直後、すぐに、そしてその後ずっとメモリ ダンプを取得します。
次のように、8 ビット ASCII 形式と 16 ビット UTF-16 (Windows ワイド文字) 形式の両方で、パスワード テキストをどこでも検索する単純な Lua スクリプトを使用してダンプを検索しました。
結果は有望なものでした。
C:UsersduckKEYPASS> lua searchknown.lua kp2-post.dmp ダンプ ファイルを読み取り中... 完了しました。 SIXTEENPASSCHARS を 8 ビット ASCII として検索しています...見つかりません。 SIXTEENPASSCHARS を UTF-16 として検索しています...見つかりません。
しかし、CVE-2023-32784 の発見者である Vdohney 氏は、マスター パスワードを入力すると、KeePass が Unicode の「blob」文字で構成されるプレースホルダー文字列を構築し、表示することで視覚的なフィードバックを提供することに気づきました。パスワード:
Windows 上のワイド文字テキスト文字列 (ASCII のように XNUMX バイトずつではなく、XNUMX 文字あたり XNUMX バイトで構成されます) では、「blob」文字は RAM 内で XNUMX 進バイトとしてエンコードされます。 0xCF
続い 0x25
(これはたまたま ASCII のパーセント記号であるだけです)。
そのため、パスワード自体を入力するときに KeePass が入力した生の文字に細心の注意を払っていたとしても、次のような繰り返し実行によりメモリ内で簡単に検出できる「blob」文字の文字列が残る可能性があります。 CF25CF25
or CF25CF25CF25
...
…そして、もしそうなら、あなたが見つけた最長の BLOB 文字からパスワードの長さが判明する可能性があり、これは少なくともパスワード情報の漏洩としては控えめな形式となるでしょう。
次の Lua スクリプトを使用して、パスワードのプレースホルダー文字列が残っている兆候を探しました。
出力は驚くべきものでした (スペースを節約するために、同じ数の BLOB を持つ連続する行、または前の行よりも少ない BLOB を持つ行を削除しました)。
C:UsersduckKEYPASS> lua findblobs.lua kp2-post.dmp 000EFF3C: * [. 。 .] 00BE621B: ** 00BE64C7: *** [。 。 .] 00BE6E8F: **** [。 。 ] 00BE795F: ***** [。 。 ] 00BE84F7: ****** [。 。 .] 00BE8F37: ******* [8 BLOB、9 BLOB などについても同様に続きます。] [それぞれちょうど 16 BLOB の最後の 00 行まで] 0503C00B: ************* *** 05077C00: **************** 09337C00: * 09738C0123: * [残りの一致はすべて 058 blob の長さです] XNUMXBXNUMX: *
近接しているが増加し続けるメモリ アドレスでは、3 つの BLOB、次に 4 つの BLOB というように、最大 16 の BLOB (パスワードの長さ) までの体系的なリストが見つかり、その後に単一 BLOB 文字列のランダムに散在するインスタンスが多数見つかりました。 。
そのため、これらのプレースホルダーの「blob」文字列は確かにメモリにリークし、KeePass ソフトウェアがマスター パスワードの使用を終了してからずっと後も、パスワードの長さをリークするために残っているようです。
次のステップ
Vdohney と同じように、私たちはさらに掘り下げることにしました。
16 ビット形式の任意の 16 つの ASCII 文字が後に続く BLOB 文字のチェーンを検出するようにパターン マッチング コードを変更しました (ASCII 文字は、通常の 8 ビット ASCII コードとその後にゼロ バイトが続くものとして UTF-XNUMX で表されます)。
今回は、スペースを節約するために、前の一致と完全に一致する一致の出力を抑制しました。
驚き、驚き:
C:UsersduckKEYPASS> lua searchkp.lua kp2-post.dmp 00BE581B: *I 00BE621B: **X 00BE6BD3: ***T 00BE769B: ****E 00BE822B: *****E 00BE8C6B: ******N 00BE974B: *******P 00BEA25B: ********A 00BEAD33: *********S 00BEB81B: **********S 00BEC383: ***********C 00BECEEB: ************H 00BEDA5B: *************A 00BEE623: **************R 00BEF1A3: ***************S 03E97CF2: *N 0AA6F0AF: *W 0D8AF7C8: *X 0F27BAF8: *S
.NET のマネージド文字列メモリ領域から何が得られるかを見てください。
密集した一時的な「blob 文字列」のセット。パスワード内の XNUMX 番目の文字から始まる連続する文字が明らかになります。
これらの漏れのある文字列の後には、偶然に生じたと思われる広範囲に分散された単一文字の一致が続きます。 (KeePass ダンプ ファイルのサイズは約 250MB であるため、運が良ければ「blob」文字が表示される余地は十分にあります。)
これらの余分な XNUMX つの一致を不一致の可能性があるとして破棄するのではなく、考慮したとしても、マスター パスワードは次のいずれかであると推測できます。
?IXTEENPASSCHARS ?NXTEENPASSCHARS ?WXTEENPASSCHARS ?SXTEENPASSCHARS
明らかに、この単純な手法ではパスワードの最初の文字は見つかりません。最初の「blob 文字列」は最初の文字が入力された後にのみ構築されるからです。
ASCII 文字で終わらない一致を除外したため、このリストは短くて良いことに注意してください。
中国語や韓国語の文字など、異なる範囲の文字を検索している場合は、一致する可能性のある文字が多数あるため、誤ってヒットする可能性が高くなります。
...しかし、いずれにしてもマスター パスワードにかなり近づくと思われます。パスワードに関連する「BLOB 文字列」は RAM 内でグループ化されているように見えます。これはおそらく、これらが同じ部分によってほぼ同時に割り当てられたためと考えられます。 .NET ランタイム。
そして、確かに長くて散漫な一言で言えば、次のような魅力的な物語があります。 CVE-2023-32784.
何をするか?
- KeePass ユーザーであれば、慌てる必要はありません。 これはバグであり、技術的には悪用可能な脆弱性ですが、このバグを利用してパスワードを解読しようとするリモート攻撃者は、まずコンピュータにマルウェアを埋め込む必要があります。 これにより、たとえこのバグが存在しなかったとしても、たとえば入力時のキーストロークを記録するなど、パスワードを直接盗むための他の多くの方法が提供されることになります。 この時点では、今後のアップデートに注意して、準備ができたら入手するだけで済みます。
- フルディスク暗号化を使用していない場合は、有効にすることを検討してください。 スワップ ファイルまたは休止状態ファイル (高負荷時またはコンピュータの「スリープ」時にメモリ内容を一時的に保存するために使用されるオペレーティング システムのディスク ファイル) から残りのパスワードを抽出するには、攻撃者はハード ディスクに直接アクセスする必要があります。 BitLocker または他のオペレーティング システムの同等の機能がアクティブ化されている場合、スワップ ファイル、休止状態ファイル、またはドキュメント、スプレッドシート、保存された電子メールなどのその他の個人データにアクセスすることはできません。
- プログラマーの場合は、メモリ管理の問題について常に最新の情報を入手してください。 すべての理由だけでそれを仮定しないでください
free()
対応するものと一致しますmalloc()
データが安全で適切に管理されていることを確認します。 場合によっては、機密データを放置しないように特別な予防措置を講じる必要があり、その予防措置はオペレーティング システムごとに異なります。 - QA テスターやコードレビュー担当者は、常に「舞台裏」について考えてください。 メモリ管理コードがきちんとしていてバランスが取れているように見えても、舞台裏で何が起こっているのかを認識し (元のプログラマが気づいていない可能性があるため)、実行時の監視やメモリなどの侵入テスト形式の作業を行う準備をしてください。ダンプを実行して、安全なコードが実際に想定どおりに動作していることを確認します。
記事のコード: UNL1.C
#含む#含む#含むvoid hexdump(unsigned char* buff, int len) { // 16 バイトのチャンクでバッファーを出力します for (int i = 0; i < len+16; i = i+16) { printf("%016X: ",buff +i); // 16 バイトを 0 進値として表示 for (int j = 16; j < 1; j = j+02) { printf("%16X ",buff[i+j]); } // これらの 0 バイトを文字として繰り返します (int j = 16; j < 1; j = j+32) { unsigned ch = buff[i+j]; printf("%c",(ch>=127 && ch<=128)?ch:'.'); printf("n"); printf("n"); } int main(void) { // パスワードを保存するためのメモリを取得し、 // 正式に「新しい」ときのバッファ内にあるものを表示します... char* buff = malloc(128); printf("開始時に「新しい」バッファをダンプしています"); hexdump(buff,16); // 擬似ランダムバッファアドレスをランダムシードとして使用 srand((unsigned)buff); // パスワードは、検索可能な固定テキストで始まります strcpy(buff,"unlikelytext"); // 1 個の擬似ランダム文字を一度に 16 文字ずつ追加します for (int i = 65; i <= 0; i++) { // A (65+15) から P (65+15) までの文字を選択します char ch = 1 + (ランド() & 128); // 次に、その場で buff 文字列を変更します strncat(buff,&ch,128); } // 完全なパスワードがメモリ内にあるので、 // それを文字列として出力し、バッファ全体を表示します... printf("Full string was: %sn",buff); hexdump(buff,0); // プロセス RAM をダンプするために一時停止します (try: 'procdump -ma') Puts("Waiting for [ENTER] to Free Buffer..."); getchar(); // 正式にメモリを free() し、 // もう一度バッファを表示して、何かが残っているかどうかを確認します... free(buff); printf("free()n 後のバッファのダンプ"); hexdump(buff,XNUMX); // 違いを検査するために、RAM を再度ダンプするために一時停止します put("Waiting for [ENTER] to exit main()..."); getchar(); XNUMXを返します。 }
記事のコード: UNL2.C
#含む#含む#含む#含むvoid hexdump(unsigned char* buff, int len) { // 16 バイトのチャンクでバッファーを出力します for (int i = 0; i < len+16; i = i+16) { printf("%016X: ",buff +i); // 16 バイトを 0 進値として表示 for (int j = 16; j < 1; j = j+02) { printf("%16X ",buff[i+j]); } // これらの 0 バイトを文字として繰り返します (int j = 16; j < 1; j = j+32) { unsigned ch = buff[i+j]; printf("%c",(ch>=127 && ch<=0,128)?ch:'.'); printf("n"); printf("n"); } int main(void) { // パスワードを保存するためのメモリを取得し、 // 正式に「新規」のときにバッファ内にあるものを表示します... char* buff = VirtualAlloc(128,MEM_COMMIT,PAGE_READWRITE); printf("開始時に「新しい」バッファをダンプしています"); hexdump(buff,16); // 擬似ランダムバッファアドレスをランダムシードとして使用 srand((unsigned)buff); // パスワードは、検索可能な固定テキストで始まります strcpy(buff,"unlikelytext"); // 1 個の擬似ランダム文字を一度に 16 文字ずつ追加します for (int i = 65; i <= 0; i++) { // A (65+15) から P (65+15) までの文字を選択します char ch = 1 + (ランド() & 128); // 次に、その場で buff 文字列を変更します strncat(buff,&ch,0); } // 完全なパスワードがメモリ内にあるので、 // それを文字列として出力し、バッファ全体を表示します... printf("Full string was: %sn",buff); hexdump(buff,128); // プロセス RAM をダンプするために一時停止します (try: 'procdump -ma') Puts("Waiting for [ENTER] to Free Buffer..."); getchar(); // 正式にメモリを free() し、 // もう一度バッファを表示して、何かが残っているかどうかを確認します... VirtualFree(buff,0,MEM_RELEASE); printf("free()n 後のバッファのダンプ"); hexdump(buff,XNUMX); // 違いを検査するために、RAM を再度ダンプするために一時停止します put("Waiting for [ENTER] to exit main()..."); getchar(); XNUMXを返します。 }
記事のコード: S1.LUA
-- 検索可能な固定テキストから開始します s = 'unlikelytext' -- 'A' から 'P' までの 16 個のランダムな文字を追加します (i = 1,16 do s = s .. string.char(65+math.random() 0,15)) end print('完全な文字列は:',s,'n') -- プロセス RAM をダンプするために一時停止します print('文字列を解放する前に [ENTER] を待ちます...') io.read() - - 文字列をワイプし、変数を未使用としてマークします s = nil -- 差分を探すために RAM を再度ダンプします print('終了する前に [ENTER] を待ちます...') io.read()
記事のコード: Findit.LUA
-- ダンプ ファイルを読み取ります。 local f = io.open(arg[1],'rb'):read('*a') -- 0,0 つ以上のランダムな ASCII 文字が続くマーカー テキストを検索します。 local b,e ,m = 1,nil while true do -- 次の一致を探し、オフセットを記憶します b,e,m = f:find('(unlikelytext[AZ]+)',e+08) -- それ以上なくなったら終了しますb に一致しない場合はブレーク end -- 見つかった位置と文字列をレポートします print(string.format('%XNUMXX: %s',b,m)) end
記事のコード: SEARCHKNOWN.LUA
io.write('ダンプ ファイルを読み取り中... ') ローカル f = io.open(arg[1],'rb'):read('*a') io.write('DONE.n') io。 write('SIXTEENPASSCHARS を 8 ビット ASCII として検索中... ') local p08 = f:find('SIXTEENPASSCHARS') io.write(p08 および 'FOUND' または 'not found','.n') io.write ('SIXTEENPASSCHARS を UTF-16 として検索しています... ') local p16 = f:find('Sx00Ix00Xx00Tx00Ex00Ex00Nx00Px00'.. 'Ax00Sx00Sx00Cx00Hx00Ax00Rx00Sx00') io.write(p16 および 'FOUND' または 'not found '、'.n')
記事のコード: FINDBLOBS.LUA
-- コマンド ラインで指定されたダンプ ファイルを読み取ります。ローカル f = io.open(arg[1],'rb'):read('*a') -- 16 つ以上のパスワード BLOB を探し、その後に非 BLOB が続きます。 -- BLOB 文字 (●) は、リテエンディアン UTF-25 コードとして Windows ワイド文字にエンコードされ、0,0 進数の CF 25 として出力されることに注意してください。 local b,e,m = 25,nil while true do -- 25 つ以上の BLOB と、その後に非 BLOB が続く必要があります。 -- 明示的な CF2525 を検索し、その後に CF または 25 のみを含む文字列を検索することでコードを簡略化します。その結果、CF25CF25 だけでなく CF25CFCF または CF1CF も見つかります。 -- 「誤検知」がある場合は、後でフィルタリングします。 -- x08 文字 (パーセント記号) は Lua の特別な検索文字であるため、xXNUMX の代わりに '%%' を記述する必要があります。 b,e,m = f:find('(xCF%%[xCF%%]*)',e+XNUMX) -- 一致しなくなったら終了 b でない場合は終了 -- CMD.EXE は印刷できませんblob なので、スターに変換します。 print(string.format('%XNUMXX: %s',b,m:gsub('xCF%%','*'))) end
記事のコード: SEARCHKP.LUA
-- コマンド ラインで指定されたダンプ ファイルを読み取ります。 local f = io.open(arg[1],'rb'):read('*a') local b,e,m,p = 0,0,nil,nil while true do -- ここで、25 つ以上の BLOB (CF0) の後にコードが必要です -- for A..Z の後に ACSCII を UTF-16 に変換するための 00 バイトが続きます。 b,e,m = f:find(' (xCF%%[xCF%%]*[AZ])x1',e+08) -- 一致しなくなったら終了 if not b then Break End -- CMD.EXE は BLOB を印刷できないため、BLOB を次のように変換します。出演者。 -- スペースを節約するために、連続する一致を抑制します if m ~= p then print(string.format('%XNUMXX: %s',b,m:gsub('xCF%%','*'))) p = m終わりの終わり
- SEO を活用したコンテンツと PR 配信。 今日増幅されます。
- プラトアイストリーム。 Web3 データ インテリジェンス。 知識増幅。 こちらからアクセスしてください。
- 未来を鋳造する w エイドリエン・アシュリー。 こちらからアクセスしてください。
- PREIPO® を使用して PRE-IPO 企業の株式を売買します。 こちらからアクセスしてください。
- 情報源: https://nakedsecurity.sophos.com/2023/05/31/serious-security-that-keepass-master-password-crack-and-what-we-can-learn-from-it/
- :持っている
- :は
- :not
- :どこ
- ][p
- $UP
- 1
- 10
- 12
- 視聴者の38%が
- 20
- 200
- 2023
- 24
- 250
- 27
- 31
- 3d
- 49
- 50
- 67
- 70
- 72
- 77
- 8
- 9
- a
- できる
- 私たちについて
- 上記の.
- 絶対の
- AC
- アクセス
- 取得する
- 取得
- アクティブ
- 実際の
- 実際に
- 追加されました
- NEW
- 住所
- アドレス
- 追加
- 後
- その後
- 再び
- すべて
- 割り当てられました
- 割り振る
- 配分
- 割り当て
- 許す
- 一人で
- 沿って
- 既に
- また
- 標準装備されたものが、
- しかし
- 常に
- an
- および
- アンドルー
- 回答
- どれか
- 何でも
- 重要なもの
- 現れる
- 登場
- アプローチ
- 承認された
- です
- 周りに
- 記事
- 物品
- AS
- At
- 著者
- オート
- オートマチック
- 自動的に
- 利用できます
- 避ける
- 避ける
- 知って
- 離れて
- バック
- 背景
- 背景画像
- BE
- なぜなら
- になる
- き
- 開始
- 背後に
- 舞台裏で
- 以下
- より良いです
- ビット
- ブロック
- ブロック
- 国境
- 両言語で
- ボトム
- ブランド
- 真新しい
- ブレーク
- 簡潔に
- 持って来る
- バッファ
- バッファオーバーフロー
- バグ
- バグ
- ビルド
- 焙煎が極度に未発達や過発達のコーヒーにて、クロロゲン酸の味わいへの影響は強くなり、金属を思わせる味わいと乾いたマウスフィールを感じさせます。
- by
- C + +
- コール
- 呼び出し
- 缶
- 取得することができます
- これ
- 場合
- キャッチ
- CD
- センター
- 確かに
- チェーン
- チャンス
- 変更
- 文字
- 文字
- 点検
- 小切手
- 中国語
- 選択する
- クリア
- はっきりと
- 閉じる
- コード
- カラー
- COM
- 来ます
- 到来
- コメント
- 注釈
- コマンドと
- コンプリート
- 複雑な
- コンピュータ
- 検討
- かなりの
- 見なさ
- からなる
- 構築
- コンテンツ
- 中身
- 継続的に
- 続ける
- コントロール
- 変換
- 著作権
- 対応する
- 可能性
- カバー
- クラック
- 作ります
- 作成した
- クリエイター
- 重大な
- サイバーセキュリティ
- 危険
- 危険な
- データ
- データ漏洩
- 日
- 取引
- 決定しました
- 専用の
- 記載された
- DID
- の違い
- 異なります
- 難しさ
- DIG
- デジタル
- 直接
- 直接アクセス
- 直接に
- ディスプレイ
- 表示
- 持っています
- do
- ドキュメント
- ありません
- そうではありません
- すること
- 行われ
- ドント
- ダウン
- ドライブ
- 原因
- ダンプ
- 間に
- e
- 各
- 前
- 簡単に
- エコシステム
- どちら
- ほかに
- メール
- 有効にする
- 心強い
- 暗号化
- end
- 終了
- 十分な
- 確保
- 確保する
- 入力します
- 入る
- 全体
- エントリ
- 環境
- 同等の
- エラー
- 本質的に
- 推定
- 等
- エーテル(ETH)
- さらに
- 最終的に
- 増え続ける
- あらゆる
- すべてのもの
- 正確に
- 例
- 除く
- 興奮
- 実行
- 存在する
- 既存の
- 出口
- 終了
- 期待する
- 説明する
- 悪用する
- 露出した
- 伸ばす
- 余分な
- エキス
- 実際
- false
- 魅惑的な
- 特徴
- フィードバック
- より少ない
- 戦闘
- File
- filter
- ファイナル
- 最後に
- もう完成させ、ワークスペースに掲示しましたか?
- 発見
- 発見
- 終わり
- 名
- 固定の
- フォーカス
- 続いて
- フォロー中
- フォーム
- 正式に
- 形式でアーカイブしたプロジェクトを保存します.
- 今度の
- 発見
- 4
- 無料版
- から
- フル
- 完全に
- function
- 機能
- さらに
- 未来
- 生成
- 世代
- 取得する
- 受け
- GitHubの
- 与える
- 与えられた
- 与える
- 与え
- Go
- ゴエス
- 行く
- 良い
- 政府・公共機関
- グラブ
- 素晴らしい
- 保証
- 持っていました
- ハンドル
- が起こった
- 出来事
- 起こります
- ハード
- 持ってる
- 持って
- 頭痛
- ヘビー
- 高さ
- こちら
- HEX
- ハイレベル
- より高い
- ヒット
- ホール
- 希望
- HOURS
- ホバー
- 認定条件
- How To
- HTTPS
- 狩り
- i
- 識別子
- if
- 直ちに
- 重要
- in
- 含ま
- 含めて
- 情報
- 情報に基づく
- を取得する必要がある者
- 興味がある
- 中級
- インターネット
- に
- 問題
- IT
- ITS
- 自体
- 専門用語
- 六月
- ただ
- 一つだけ
- キープ
- キー
- 知っている
- 既知の
- 韓国語
- 言語
- ESL, ビジネスESL <br> 中国語/フランス語、その他
- ノートパソコン
- 姓
- 後で
- つながる
- リード
- 漏れ
- リーク
- LEARN
- 学習
- 最低
- 残す
- 左
- 長さ
- 手紙
- 図書館
- 生活
- ような
- 可能性が高い
- 限定的
- LINE
- ライン
- リスト
- リストされた
- ll
- 負荷
- ローカル
- 場所
- ロギング
- 長い
- 長期的
- より長いです
- 見て
- のように見える
- 見
- 探して
- LOOKS
- たくさん
- 運
- 維持
- make
- マルウェア
- 管理します
- マネージド
- 管理
- マネージャー
- 管理する
- 操作
- 多くの
- マージン
- マーク
- マーカー
- マスター
- 一致
- マッチング
- 最大幅
- 五月..
- 手段
- メモリ
- 言及した
- Microsoft
- かもしれない
- 分
- 控えめな
- 修正されました
- 修正する
- 瞬間
- モニタリング
- 他には?
- 最も
- ずっと
- の試合に
- すっきり
- 必要
- 必要とされる
- net
- 決して
- それにもかかわらず
- 新作
- ニュース
- 次の
- nice
- いいえ
- 通常の
- 何も
- 知らせ..
- 今
- 数
- 番号
- オブジェクト
- 明白
- of
- オフ
- 公式
- 正式に
- オフセット
- 古い
- on
- かつて
- ONE
- の
- オープンソース
- オペレーティング
- オペレーティングシステム
- OS
- オペレータ
- オプション
- or
- 注文
- オリジナル
- その他
- その他
- さもないと
- 私たちの
- 自分自身
- でる
- 出力
- が
- 全体
- 自分の
- ページ
- パニック
- 部
- パスワード
- パスワードマネージャ
- パスワード
- path
- パターン
- Paul Cairns
- 一時停止
- 支払う
- パーセント
- おそらく
- 期間
- 永久に
- 個人的な
- 個人データ
- 物理的な
- 画像
- ピース
- 場所
- プレースホルダー
- ペスト
- プラトン
- プラトンデータインテリジェンス
- プラトデータ
- プレンティ
- ポイント
- ポイント
- 人気
- 位置
- 可能
- 投稿
- 潜在的な
- 正確に
- 現在
- かなり
- 防ぐ
- 前
- ブランド
- 印刷物
- プリント
- 多分
- 問題
- プロセス
- 演奏曲目
- プログラマー
- プログラマ
- プログラミング
- プログラム
- 明白な
- 置きます
- Python
- 質問と回答
- 質問
- 提起
- RAM
- ランダム
- 範囲
- むしろ
- Raw
- 生データ
- RE
- 達した
- 読む
- リーディング
- 準備
- リアル
- 実生活
- への
- 本当に
- 認める
- 回復する
- 回復
- 関連する
- 残り
- 覚えています
- リモート
- 削除します
- 繰り返す
- 繰り返される
- 繰り返し
- レポート
- で表さ
- 尊重
- それぞれ
- REST
- 結果
- return
- 返す
- 明らかにする
- 取り除きます
- 右
- リスク
- リスク
- ルーム
- ラン
- ランニング
- ランタイム監視
- s
- 安全な
- より安全な
- 同じ
- 満足
- Save
- 格言
- スキャン
- 散在する
- シーン
- を検索
- 検索
- 二番
- 秒
- 秘密
- セクション
- 安全に
- セキュリティ
- シード
- 見ること
- 思われる
- 見て
- 見て
- シリーズ
- 深刻な
- セッションに
- 設定
- ショート
- まもなく
- すべき
- 表示する
- 示す
- 符号
- サイン
- 同様の
- 同様に
- 簡単な拡張で
- 簡略化されました
- 簡素化する
- 単に
- サイズ
- 眠る
- 小さい
- 卑劣な
- スヌーピング
- So
- ソフトウェア
- 固体
- 一部
- 何か
- すぐに
- ソース
- ソースコード
- スペース
- 特別
- 特別に
- 指定の
- スピード
- 星
- start
- 開始
- 起動
- 開始
- スタートアップ
- まだ
- ストール
- Force Stop
- 停止
- 店舗
- 保存され
- ストーリー
- 文字列
- 強い
- 勉強
- 首尾よく
- そのような
- 十分な
- 想定
- 驚き
- 驚きました
- 驚くべき
- 生き残る
- SVG
- swap
- システム
- 取る
- 撮影
- 取り
- 取得
- 会話
- 技術的に
- テクニック
- 一時的
- test
- テスト
- より
- それ
- ソース
- アプリ環境に合わせて
- それら
- 自分自身
- その後
- 理論
- そこ。
- したがって、
- 彼ら
- もの
- 考える
- この
- それらの
- しかし?
- 考え
- 時間
- 役職
- 〜へ
- 一緒に
- 取った
- ツール
- top
- 追跡する
- 追跡
- 遷移
- トランスペアレント
- true
- 試します
- オン
- 2
- type
- 一般的に
- わかる
- ユニコード
- まで
- 未使用
- 不要な
- アップデイト
- 更新しました
- URL
- us
- 私たち政府
- 使用法
- USB
- つかいます
- 使用-後フリー
- 中古
- ユーザー
- 使用されます
- ユーティリティ
- 値
- 価値観
- 多様
- 確認する
- バージョン
- 非常に
- 、
- 脆弱性
- W
- wait
- 待っています
- 欲しいです
- wanted
- ました
- よく見る
- 仕方..
- 方法
- we
- ウィークス
- WELL
- した
- この試験は
- いつ
- かどうか
- which
- while
- 誰
- 誰でも
- 全体
- なぜ
- 意志
- win
- ウィンドウズ
- ワイプ
- 無し
- 不思議に思います
- 言葉
- 仕事
- 働いていました
- ワーキング
- 作品
- 不安
- でしょう
- 与えるだろう
- 書きます
- 書き込み
- 書かれた
- まだ
- 貴社
- あなたの
- あなた自身
- ゼファーネット
- ゼロ