他人の空似

2019 年 10 月 14 日

OneCore.lib/MinCore.libの紹介

Filed under: 未分類 — 中の人 @ 5:27 PM

Windows SDKが提供する.libファイルには関数群を提供するもの、単一.dllへの参照を提供するものがありますが、複数.dllへの参照を提供するものがあることは意外と知られていません。

今日はそういった複数の.dllへの参照を提供する.libの中でも代表的なOneCoreシリーズとMinCoreシリーズについて私の持つ知見を紹介しようと思います。

※これらは私が独自に調査蒐集した情報であり、公式の動作を保証するものではありません。

忙しい人向け要約

  • OneCoreは1つリンクすれば全てOKなオールインワン.lib
    • OneCore_downlevel.lib
      • version.dllなどのオールドスタイル.dllへリンク
    • OneCore_apiset.lib
      • api-ms-win-core-version-l1-1-0.dllなどAPI Setsへのリンク
    • OneCore.lib
      • 可能な限りオールドスタイルだがAPI SetsにしかないものはAPI Setsでリンク
  • MinCoreは限定環境向けAPI Sets提供.lib
    • MinCore_downlevel.lib
      • Windows7など極限制限下向け
    • MinCore.lib
      • Windows8.1やWIndowsPEなどの環境向け

前提知識:API Setsとは

Windows7から入った「OSのAPI群を厳格な名前空間に再配置したもの」及び「それらの名前から実体へリンクするライブラリローダー」です。

例えばGetFileVersionInfoExWはAPI Sets上はapi-ms-win-core-version-l1-1-0.dllにより提供されており、実行時に適切な.dll(環境によりversion.dllだったりKernelBase.dllなど)にリンクされ実行されます。

Windows10においては旧来のWin32APIと呼ばれていたものは全てカバーされており、旧来の.dllにリンクせずともほぼすべてのアプリを実装することが出来ます。

とはいえそれはWIndows10の話であり、Windows8.1やWindows7ではAPI Setsのカバー範囲が狭いため、それらのOSでの動作も必要な場合はまだ安易に利用できる状態ではありません。

またライブラリローダー自体が別系統であるため、DLLプリロード攻撃などに対しても耐性があります。

公式には旧来の方式に比べメモリー使用量が削減されることもあると説明されています。

OneCoreとは

API関係で数ある.libをすべてまとめたオールインワンな.libです。

具体的にはOneCore系の.libにさえリンクしておけばOS提供のAPI群は全て使用することが出来ます。

もちろんリンク時に必要なAPIのみ参照する方式なのでexeサイズの肥大化なども特にありません。

ただしAPI Setsの使用有無で若干のバリエーションがあります。

OneCore_downlevel.lib

旧来の.dllで提供できるAPIだけに絞ったOneCoreで、リンクされる.dllも全て旧来の.dllになります。

主にWindows7/8.1などの古い環境でも動作するアプリを作る開発者向けです。

当然ですが API Setsでのみ提供される 最新のAPI群は使用できません(リンクエラーになります)

OneCore_apiset.lib

全てのAPIをAPI Setsで提供するOneCoreです。

基本的にWindows10でしか動作しないようになりますが、全てのAPIを使用できDLLプリロード攻撃などに対しても耐性があります。

今はまだ遠い未来の話ですが、Windows10未満のOSがすべてサポート切れした後、MSはこれを使うことを主流としたいのだと予想されます。

WindowsSDKのバージョンが古い場合は提供されていないことがあります。

OneCore.lib

可能な限り旧来の.dllで提供し、それ以外はAPI Setsで提供するOneCoreです。

コード中で使用するAPIによって対象OSが変化してしまう問題があるため、私個人は使用を回避すべきだと考えます。

Windows10未満で動作する必要があればOneCore_downlevel.lib、Windows10でしか動作しなくてよいならOneCore_apiset.libの使用をお勧めします。

使用する場合は各APIのMSDNを良く参照の上、OSごとの動作確認をしっかり行うように注意してください。

また、WindowsSDKのバージョンが古いとOneCore_apiset.libと同じように動作することがあります。

MinCoreとは

旧来の.dllは環境(モバイルなど)により提供されないことがあります。

そういった環境でもAPI Setsは提供されるため、特殊環境向けにAPI Setsへリンクする.libが必要となりました、それがMinCoreです。

一般的な開発内の用途としてはWindows10未満向けでもAPI Setsを利用したい場合などがあげられます。

必要が無ければ基本的にはOneCoreを使うべきです。

MinCore_downlevel.lib

Windows7相当のAPI Setsを提供するMinCoreです。

これのみを使用してリンクエラーになっていないexeはWindows7上でも動作することが確約出来ます。

ただし提供されるAPI Setsはごく少量であるため(MSDN)あまり多くのことはできないでしょう。

具体的にこのMinCoreで動作が保証される特殊環境に何があるかは私は把握していません。

MinCore.lib

Windows8.1相当のAPI Setsを提供するMinCoreです。

ただ、実態調査をしたところWindows8.1では提供されていないAPI Setsへのリンクも提供されており実態が不明確です、可能なら使用は避けた方がよいでしょう。

提供されるAPI Setsはdownlevelに比べれば多彩ですがまだかなりの制限があると思うべきです(MSDN)

最低でもWindowsPEでMinCore.libの動作が保証されています(MSDN)

使い方

では実際に使ってみましょう。

要点は二つ、NODEFAULTLIBでkernel32.libとole32.libを外す、必要なOneCoreかMinCoreへのリンクを追加する、だけです。

もちろん手動で何かしらシステム.libへのリンクを追加しているなら撤去する必要があります。

では実際にやっていきましょう。

改造元として適当なC++プロジェクトを用意しました。

これはGetFileVersionInfoExWを使用していますがOneCoreもMinCoreも利用していないので、ビルドしてdumpbinすると以下のようにversion.dllへの参照がついています。

では改造していきましょう。

まず従来のlibと同時に使用すると正しく適用できないため外します。

ここを

こう消します。

さらに一部は暗黙でリンクされるので追加で除外設定を入れます。

そしてリンクする.libを変更します(今回はわかりやすくOneCore_apiset.lib

無事api-ms-win-core-version-l1-1-0.dllを参照する形に変わりました。

※OneCore_downlevel.libかOneCore.libの場合version.dllのままとなります。

今回はコード中からOneCore_apiset.libのリンク指示を入れましたが、プロジェクトプロパティの追加の依存ファイルでももちろん問題はありません。

終わりに

今回はOneCore/MinCoreについてある程度知見を広めることを目的とした記事でした。

巷では不親切なドキュメントのせいで誤ってリンクしてしまい問題になるケースも多く、そういった悲劇を少しでも減らしたいという思いもあります。

自分自身OneCoreやMinCoreに思うところがないではないですが、OneCoreの煩雑なリンク作業の手間を軽減しようとする試み自体は良いことだと思います。

ではまた次の記事で。

2019 年 9 月 25 日

非公式東方鬼形獣バグ修正パッチリリース

Filed under: software,東方 — 中の人 @ 3:59 PM

非公式ですが東方鬼形獣のバグを直して回るパッチ作ったので公開します。

th17patch.zip

直るバグは以下の通り

  • オオワシ妖夢の攻撃力上限が他より100低い
  • 実績から再生できるEDが違う
  • カワウソ妖夢1面の誤字
  • 実績38の誤字
  • replayフォルダ以下にもなぜかsnapshotフォルダがある
  • リプレイでクリア系実績が解放される

2019 年 9 月 17 日

エクスプロイトを書きつつ学ぶWindowsセキュリティー機能 ~CFG export suppression~

Filed under: 未分類 — 中の人 @ 4:58 PM

今回はControl Flow Guard(解説記事)の派生技術をご紹介。

忙しい人向けCFG export suppressionまとめ:

  • dllのexport関数のアドレスを正規の手段以外で得てcallするとクラッシュする機能
  • 正規の手段は以下の通り
    • 静的リンク(Import Address Tableなど)経由
    • GetProcAddress関数
    • dllにexport以外で関数ポインタなどで受け渡す機能が備わっていた
  • 基本的な機構はControl Flow Guardと一緒
    • 具体的にはコンパイラが動的なcallやjmpの直前にチェック関数を挿入している
  • 有効化するにはexeとdllのすべてで/guard:cfを指定し、exeのリンカに /guard:exportsuppress を指定する

前置き

Windowsの32bitアプリケーションはアドレスランダマイズが不足しており危険だという話はPCセキュリティ界隈では比較的よく聞く話です。

その中でも特に脆弱なのがdllのアドレスランダマイズで、過去の記事でも触れたとおり比較的軽微なメモリ漏洩でも関数のアドレスが推測可能になるのが現状です。

そういった現状を踏まえ任意コード実行の脆弱性を少しでも難しくするために生み出された技術、それが今回紹介するCFG export suppression(以降CFG ES)です。

以降はサンプルコード前提で進みます。

cfg_es.zip

注意事項

本機能を有効にする/guard:exportsuppressは2019/09/17現在、MSDN上では非公開なオプションであり正式に提供されている機能ではない可能性があります。

今後正式に提供されるとしても挙動など変更になっている可能性を念頭においてご利用ください。

攻撃:情報漏洩と任意コード実行

話をシンプルにするためにexport関数のアドレスを漏洩する関数を持つdllを用意します。

// ただのexport関数
extern "C" __declspec(dllexport) void proc() {
   std::cout << "Arbitrary code execution\n";
}

// procのアドレスを漏洩する脆弱な関数
extern "C" __declspec(dllexport) __declspec(guard(ignore)) unsigned int leak() {
   return reinterpret_cast<unsigned int>(proc);
}

leak関数はprocの関数を漏洩しています。

関数ポインタの取得ではない旨を示すために__declspec(guard(ignore))するのを忘れずに。
※関数ポインタの取得とみなされるとCFG ESの対象外となるため

攻撃対象のproc関数はただ標準出力にメッセージを出すだけの関数です。

攻撃側は既に脆弱性を突かれている前提でleak関数の返り値を実行するのを直に実装します。

int main(const int argc, const char * const * const argv) {
   const HMODULE dll = ::LoadLibraryA("dll.dll");
   unsigned int addr = reinterpret_cast<unsigned int (*)()>(::GetProcAddress(dll, "leak"))();
   reinterpret_cast<void (*)()>(addr)();
   std::cin.get();
   return 0;
}

実際に上記のソースコードと実行ファイルがフォルダ1に入れてあるので実行してみましょう、特に問題もなくproc関数が実行できてしまいます。

ではCFG ESで対抗しましょう。

防御:CFG ES

/guard:cf が有効でないなら有効にします。
※本アプリでは最初から有効になっています。

次にexe側のリンカオプションで /guard:exportsuppressを設定するだけです。

今回も設定済みの物をフォルダ2に入れてあるので実行すると、今回はクラッシュしました。

「proc関数はGetProcAddressされていないのにアドレスを知っているのはおかしい、攻撃だな!」

との判断により強制終了された結果です。

このチェックによりエラーとされない方法は以下の通りです。

  • GetProcAddressによりアドレスを取得する
  • 静的リンクして関数を呼び出す
  • dll内部で関数ポインタとして取得されたものを受け取る
  • /guard:cfを解除する(セキュリティが低下します)

一般的な使い方をしていれば回避する方が難しいのがよくわかるかと思います。

上記以外で不正に取得したアドレスでcallやjmpすることが難しくなるというわけですね。

このチェックは/guard:cfの機能により行われるために/guard:cfも有効でないといけません。

まとめ

  • CFG ESは/guard:cfと/guard:exportsuppressをつければ有効になる
  • 有効になると不正に入手したexport関数アドレスは実行できない

実用性

GetProcAddressを呼び出してしまえば許可されてしまうため、100%の防御というよりは攻撃者の手札を減らす類のもの、場合により気休め程度の効果にしかならないことも。

リンクするdllすべてでControl Flow Guardが有効でなければ効果が薄いのと、一定より古いOSではそもそも無視される問題があり頼りづらい。

Buffer Overflowなど任意コード実行を目指す攻撃は動的アドレスへのcallやjmpの利用を目指すことがあるため、そういった攻撃を防げるのは水際防御としては有効そう。

とはいえreturnアドレスなど他にも利用できるものは多いため、やはり単独で使用できるものではなくSafeSEHBuffer Security Checkなどの技術との併用は必須。

構造上Control Flow Guardとの併用が必須であり、Control Flow GuardはJITエンジンなど食い合わせが悪い機能が多く採用が難しいという問題がある、採用する際は十分テストを行い問題ないことを確認したい。

メモリや計算量にオーバーヘッドが存在するため速度がダイレクトに響く分野での採用も難しい。

総じてメリットは小さくデメリットは大きい傾向がある技術、採用はほかの技術を優先したい。

終わりに

Control Flow Guardの派生技術ということで取り上げましたがいかがだったでしょうか。

CFG ESのかかったバイナリは手元ではEdgeしか発見できていないレア物だったりもします。

無条件で使えるものではないのは確かですが、ここまで誰も使わないほど有用性が低い技術でもないと思うので、少しでも採用アプリが増えてくれたらうれしいんですがどうでしょうね?

ではまた。

2019 年 9 月 15 日

エクスプロイトを書きつつ学ぶWindowsセキュリティー機能 ~Arbitrary Code Guard~

Filed under: 未分類 — 中の人 @ 4:25 PM

今回は被害を防ぐのではなく被害を抑える系技術のご紹介。

忙しい人向けArbitrary Code Guardまとめ

  • 自身からメモリアクセス権限を変更する権限を取り上げる技術
  • 具体的にはEXECUTEの新規付与禁止と既存EXECUTEメモリにWRITE付与禁止
  • 自身がマルウェアに乗っ取られた場合に他プロセスへの攻撃を難しくする
    • 具体的には他プロセスへの悪意あるコードの注入
  • 一度設定したら解除不能
  • 不特定多数の相手と通信するようなマルウェア被害が強く懸念されるアプリで被害最小化を図る場合向けの技術

前置き

攻撃を防ぐ技術は数えきれないほど存在しますが、それでも100%完全に攻撃を防ぐことは現代社会においては非現実的とされています。

そのため攻撃を防ぐことだけ考え、攻撃成功した後のことを考えなかった場合、あっという間に全てを乗っ取られ取り返しのつかない事態になってしまいます。

そこで攻撃が成功しても被害の拡大を防ぐ技術についても日々研究されています。

今回紹介するのもそういった方向の技術の1つで、Arbitrary Code Guard(以降ACG)です。

以降はサンプルコード前提で進みます。

ArbitraryCodeGuard.zip

例その1:メモリ書き換えによるコード注入

フォルダ1内のTarget.exeを攻撃します。

Target.exeの実装は以下の通り。

#include <iostream>

int main() {
   std::cin.get();
}

ただ入力待ちするだけのいたって善良な子です。

こいつにdllを注入します。

今回は注入に成功したことが分かればいいので特に悪事を働かせたりはしません。

#include <iostream>

#include <Windows.h>

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:
       std::cout << "Injection!" << std::endl;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

ただロード時に「Injection!」と出力するだけです。

次に攻撃者ですが、既に侵入に成功している想定で普通に攻撃を実装します。

   unsigned char code[] = {
      0x68, 0x00, 0x00, 0x00, 0x00, // PUSH 0x00000000(PUSH &"dll.dll")
      0x68, 0x00, 0x00, 0x00, 0x00, // PUSH 0x00000000(PUSH &::LoadLibraryA)
      0x58, // POP EAX
      0xFF, 0xD0, // JMP EAX
      0xC3, // RETN
   };

長くなる上ACGとは関係がないので端折りますが、要するに上記のコードを相手プロセス内に配置して実行するコードです。

内容的には::LoadLibraryA(“dll.dll”);に相当します。

ではTarget.exe>Attacker.exeの順に起動してみましょう。

上記のようにTarget.exe側にInjection!と出力されたはずです。

※普通に攻撃処理なのでアンチウイルスなどに止められる可能性があります。

次はこの攻撃をACGで失敗させます。

例その2:ACG実演

ACGとはメモリアクセス権(読み書き実行)の変更権限を制限する機能です。

攻撃者に侵入される前のアプリでACGを有効にしていたと仮定します。

void enableACG() {
   PROCESS_MITIGATION_DYNAMIC_CODE_POLICY policy;
   policy.Flags = 0;
   policy.ProhibitDynamicCode = 0x01;
   policy.ReservedFlags = 0;
   ::SetProcessMitigationPolicy(ProcessDynamicCodePolicy, &policy, sizeof(policy));
}

上記でACGが有効になるのでmainの頭で呼んでおきます。

上記の変更が入ったものがフォルダ2に入っているので実行します。

失敗しました。

これは実行権限付きメモリー確保に失敗したためです。

実行権限が付与できなければいくらコードを書き込めても実行させることが出来ず任意コード実行まで到達できません。

このようにACGを有効にしておくことで、マルウェアに制御を奪われても被害拡大を防ぐことが出来ます。

例その3:拡大阻止ではなく防御には使えないのか

結論から言うと防御には使えません、以下で実演します。

Target.exe内でACGを有効にします。

入れた物がフォルダ3に用意してあるので実行します。

攻撃成功しました。

このように、あくまで自身が他者を攻撃しないようにするだけで、他者からの攻撃を防ぐ用途では使えません。

例その4:攻撃者にACGは外されないのか

結論から言うと外せません。

void disableACG() {
   PROCESS_MITIGATION_DYNAMIC_CODE_POLICY policy;
   policy.Flags = 0;
   policy.ProhibitDynamicCode = 0x00;
   policy.ReservedFlags = 0;
   ::SetProcessMitigationPolicy(ProcessDynamicCodePolicy, &policy, sizeof(policy));
}

フォルダ4に上記修正を入れました。

しかし攻撃は失敗します。

普通の方法では後から外すことはできません。

まとめ

  • ACGは自身のメモリアクセス権変更権限を取り上げる技術
  • 有効にすると他プロセスに任意コードを書き込んで実行させるのは難しい
  • 自身に制約をかけ被害拡大阻止するだけで防御には使えない
  • 一度設定したら解除不能

その他詳細

  • 厳密には以下の権限が失われる
    • EXECUTE属性の新規発行
    • EXECUTE属性持ちにWRITE属性付与
    • EXECUTE_READWRITEを指定すること自体
      • EXECUTE_READWRITE =>EXECUTE_READWRITE(権限変更なし)もなぜか問答無用で失敗するように
  • 自分自身への付与も該当
    • ただしLoadLibraryでのDLL割り当てなどカーネル(他者)を経由する場合は問題ない

実用性

一般的なプログラマー視点であれば実用性はほぼないとみてよい。

ACGを導入してもファイルシステム経由でのdllインジェクションなどがありうるため、基本的に単独では意味が薄いのも痛い。

ユーザーがとても多く、防御策も軒並み全部採用している状態でなおセキュリティを高める場合にようやく選択肢に上る程度。

上記に該当するのは例えばブラウザなど。

とはいえそれほどデメリットもきつくないので、食い合わせの悪い技術(JITコンパイラなど)を使わないならば採用してもよい。

終わりに

今回は啓蒙というよりは紹介で、特に採用必要性のない技術でした。

とはいえブラウザ保護などには役立っているので、世の中はこういうものまで含めた積み重ねで安全が担保されているのだなと思っていただければ。

本記事でACGとは何かがちょっとでも伝わっていれば幸い。

ではまた。

2019 年 5 月 6 日

Windows開発はclang+lldでも実用レベルか?セキュリティ面から調べてみた

Filed under: 解析 — 中の人 @ 8:12 PM

「clang(コンパイラ)+lld(リンカ)でWindows開発をしたい」というのはC++でWindows開発しているなら誰もが1度ぐらいは考えるのではないでしょうか?

そのためには環境構築難度、可搬性、デバッグ難度、権利問題、いろいろあるとは思います。

今回はそのうちの1つ「VisualStudioに比べてセキュリティは低下しないの?」を調べてみました。

調査範囲

VisualStudioのコンパイラ及びリンカ提供の機能のうちセキュリティ的に主要機能と思われるものを独断と偏見で選びだし調査しました。

またこれらについては過去に本ブログで個別記事を書いています

結論

結論から先に書いておきます。

ある程度は問題ないですが、節々が怪しいので未実装の機能を使わないとしてもセンシティブなプロダクトでは危ないと感じました。

以下まとめ

  • コンパイラ
  • リンカ
    • /DYNAMICBASE
      • 問題なし
    • /SAFESEH
      • オプションの処理に不具合、おそらく動いている?
    • /GUARD:CF
      • おそらく問題はないがコンパイラ側が未実装なため不明

検証環境

公式の手順に従いVisualStudioでビルドしたclang8.0.0の32bit版を使用。

ただしVisualStudioは2019で、clangビルドでコンパイルエラーした箇所のみパッチを当てた物。

また、正しい動作が得られなかったものについてのみ検証時のorigin/masterのHEADである9fa56f7829aa5f5cca911c400bb43d854b46dc15をビルドしたものも使用した。

/GS(BufferSecurityCheck)

コンパイラオプション

  • VisualStudio
    • cl.exe /GS a.cpp
  • clang
    • clang++.exe -fuse-ld=lld -fstack-protector-strong -v a.cpp

検証方法

#include <cstdio>

int main() {
   char buf[8];
   ::fgets(buf, 256, stdin);
   return 0;
}

上記をオプションありとなしでコンパイルして大量の入力を行い、オプションなしはデバッガにてreturnまで到達してから異常終了すること、ありはreturnより前に検知され強制終了するかを検証した。

結果

両者共に正しく強制終了した、逆アセンブラ結果もカナリアの値も問題なし。

/guard:cf(ControlFlowGuard)

本項はコンパイラとリンカ両方合わせての検証となる。

コンパイラオプション

  • VisualStudio
    • cl.exe /guard:cf a.cpp
  • clang
    • clang++.exe -fuse-ld=lld -cfguard -Wl,-guard:cf -v a.cpp

-cfguardオプションはそのままだと無視されるバグが存在しており、パッチを当てて有効化して使用した。

diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index cbaf5cb..773e69e 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -3470,6 +3470,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
   // FIXME: Implement custom jobs for internal actions.
   CmdArgs.push_back("-cc1");

+  if (Args.hasArg(options::OPT_cfguard)) {
+    CmdArgs.push_back("-cfguard");
+  }
+
   // Add the "effective" target triple.
   CmdArgs.push_back("-triple");
   CmdArgs.push_back(Args.MakeArgString(TripleStr));

パッチを当てない場合-vオプションでいったん-cc1ありの長いコマンド例を出した後、そこに-cfguardを加えて実行すると認識する。

検証方法

#include <cstdio>

class Hoge {
   public:
   virtual int hoge() {
      return 128;
   }
};

class Fuga : public Hoge {
   public:
   virtual int hoge() {
      return 256;
   }
};

Hoge * ptr = new Hoge();
Hoge * ptr2 = new Fuga();

int Haga() {
   return 512;
}

typedef int (*HagaPtr)();
volatile HagaPtr ptr3 = Haga;

void Hege(Hoge *ptr, Hoge *ptr2, HagaPtr ptr3) {
   ::printf("%d:%d:%d\n", ptr->hoge(), ptr2->hoge(), ptr3());
}

int main() {
   Hege(ptr, ptr2, ptr3);
   return 0;
}

上記をオプションありとなしでコンパイルしてそれぞれの仮想関数テーブルや関数ポインターなどをデバッガで別関数へ書き換え実行、オプションなしは別関数が実行されること、ありは強制終了するかを検証した。

結果

clang+lldでは強制終了しなかった。

>clang/lib/Driver/ToolChains/Clang.cpp:5873
>Currently there’s no support emitting CFG instrumentation; the flag only emits the table of address-taken functions.

とある通りチェック処理は未実装の模様。

また、解析した範囲ではチェック関数用の関数アドレスリストの生成や有効化フラグ自体は問題なく発行されているように見えた。

/DYNAMICBASE

コンパイラオプション

  • VisualStudio
    • cl.exe a.cpp /link/DYNAMICBASE
  • clang
    • clang++.exe -fuse-ld=lld -Wl,-dynamicbase -v a.cpp

検証方法

int main() {
   return 0;
}

上記をオプションありとなしでコンパイルしてデバッガ越しに実行、a.exeがロードされるアドレスを確認しtouchした後再度同様の作業をする、オプションなしはアドレスが変化していないこと、オプションありは変化していることを検証した。

結果

問題なし、ロードされるアドレスも自身が知っているランダマイズの範疇であった。

/SAFESEH

コンパイラオプション

  • VisualStudio
    • cl.exe a.cpp /link/SafeSEH
  • clang
    • clang++.exe -fuse-ld=lld -Wl,-safeseh -v a.cpp

検証方法

#include <cstdio>

int main() {
   char buf[8];
   ::fgets(buf, 256, stdin);
   return 0;
}

上記をコンパイルオプションありとなしでコンパイルしてデバッガ越しに実行、大量の入力によりseh連鎖を上書きした後デバッガにより例外を発生させた、オプションなしは異常終了すること、オプションありは強制終了することと検証した。

結果

clang+lldでは常に強制終了した。

どうやら-safeseh:noを指定してもsafeseh有効のバイナリを吐くようで(おそらくバグ)、無効にする方法が見つからなかった。

そのため、SafeSEHが正しく作用した結果強制終了しているのか、なんらかの別要員でたまたま強制終了したのかの区別がつかなかった。

一応解析した範囲ではSafeSEHのフラグが立っており正常に適用されているように見えるので動いている可能性が高いとは思える。

終わりに

数にして4つのみの検証となったが未実装1、不具合により検証不能1という結果になった。

実際のところこれが信頼に値するかはわからないが、自分の感性においてはまだ不安が残る数値だと考える。

とはいえchromeでの採用実績があるとのことなので使用を志す人は検討してみるのもいいだろう。

ちなみに自分が調べた範囲だとchromeは/guard:cfを有効でコンパイルしているようだが、当然チェック関数は未実装であり、ControlFlowGuardは機能していなかったことをここに記録しておく。

2019 年 3 月 30 日

/DYNAMICBASE一問一答

Filed under: 解析 — 中の人 @ 5:15 PM

/DYNAMICBASEオプションによるベースアドレスのランダマイズ(以下ASLR)について、わかっているようでわかっていない人も多いかと思ったので細かい挙動についてまとめておきます。

なお以下はすべてWindows10 1809におけるもので、それ以降やそれ以前では異なることがあります。

Q:ASLRってなに?
A:この場で言うASLRは.exeや.dllがメモリ上にマップされるときのアドレスをランダムにしようぜって機能のこと

Q:どういう恩恵があるの?
A:攻撃されたときにアドレスがばれているかどうかは、攻撃者がLv1魔王とLv100魔王ぐらいには変わる、当然隠していた方がよい

Q:.exeはどれぐらいランダムなの?
A:実測したら254通り

Q:.exeのランダムの範囲は?
A:NULLページ(0x00000000のこと)や旧来のデフォルトベースアドレス(0x00400000-0x007F0000)を除いて0x00010000ごとに254か所の中からランダム
/BASEオプションが無指定なら0x00010000から、指定されていた場合そのアドレスから0x00FE0000引いたアドレスからになる

Q:.exeの再ランダマイズされる条件は?
A:OS再起動、またはtouchやコピーで属性が更新されたとき(アンロードや時間経過やリネームなどは含まれず)

Q:.dllはどれぐらいランダムなの?
A:仕様によれば256通りらしいが再起動必須であるため労力の関係から実測は叶わなかった

Q:.dllのランダムの範囲は?
A:以下推測も多く混ざる点に注意
OS起動時に0x7F000000-0x7FFF0000の間を0x00010000ごとに256か所の中からランダムで選ぶ
そこから後ろ詰めで1つでもロードされているDLLのアドレスを埋めていき、全プロセスでアンロードされたDLL部分を開放するを延々繰り返していると思われる

Q:.dllの再ランダマイズされる条件は?
A:OS再起動のみ
アドレス変化自体は全プロセスからアンロードしてから元アドレス付近が埋まるまで他.dllをロードし保持したまま改めてロードしなおせばよい
またはtouchやコピーで属性を更新した別dllを用意して別々に同時ロードするなどでもよい

Q:結局どういうこと?
A:.exeは254通りと少ないがランダマイズされているので比較的安心していい
.dllは.dll全体で1回のランダマイズなのでアドレスが1個でもばれたら全部ばれ得るので注意が必要


2019 年 3 月 26 日

DLLプリロード攻撃のチェッカーをリリース

Filed under: software — 中の人 @ 2:27 PM

前回の記事に関連して、DLLプリロード攻撃が可能なexeかのチェッカーを作りました。

DllPreloadingAttackChecker.zip

使い方はchecker.exeにチェックしたいexeをD&Dするだけ。

といっても所詮はDLLプリロード攻撃なので、NGとか出ても特におびえたりする必要はないです。
(というか誰の環境でも雑にexeなめたら山ほどNG出ると思う)

ただしインストーラーで出たら即開発元に連絡しよう、約束だぞ!

※2019/03/26 18:50頃追記

setup.exeだけ特別扱いしている問題に対応

※2019/03/28 21:10頃追記

DEPENDENTLOADFLAGに対応

2019 年 3 月 24 日

エクスプロイトを書きつつ学ぶWindowsセキュリティー機能 ~DLLプリロード攻撃~

Filed under: 未分類 — 中の人 @ 6:48 PM

前回までとは雰囲気を変えて防御手法ではなく攻撃手法に着目する回。

題材は「DLLプリロード攻撃」、それも特に静的リンクの物に絞ります。

簡単に説明すると、.exeと同じフォルダに細工した.dllファイルを配置するだけで.exe起動時に実行されてしまうよっていう攻撃のことです。

今回はこの攻撃の原理と防御法について一通り解説していきます。

いつものようにサンプルコード DllPreloadingAttack.zip

実演

百聞は一見に如かずということでまず実演してみましょう。

PrintVersion内のPrintVersion.exeを起動してみてください。

これは単にバージョンリソース内のバージョンを標準出力に出すだけの極々一般的なアプリです。

では次にexploit内のversion.dllをPrintVersion.exeと同じディレクトリに配置してからPrintVersion.exeを実行してみてください。

表示が変わりましたね 。

これがDLLプリロード攻撃です。

何が起きたのか

WindowsOSが提供するAPIは.dll形式でアプリに提供されています。

その機構に乗っかってアプリの一部を.dllに分離することもできます。

しかし、どの.dllを使うかはファイル名1つで決められているため、OS提供の物かどうかは.exe視点だと区別がついていません。

※LoadLibrary関数による動的ロードの場合はその限りではない

なのでOS提供の.dllの”一部”は、同名の.dllを特定ディレクトリに配置することで勘違いさせ無理やりロードさせることが可能なのです。

DLLの検索順序は公式に説明されています

この順序で検索して、本物の.dllより前に偽物の.dllを配置することが出来ると攻撃が成立するわけですね。

防御その1、書き込み不能ディレクトリに配置する

検索順序は要するに「.exeと同じディレクトリに同名の.dllがあるとそちらを優先して読んでしまう」です。

※カレントディレクトリの.dllを読んでしまうケースもありますが、そちらはOSの脆弱性として対処される流れのようです。

逆に言えば攻撃者がそこに書き込むことが出来ないならば、いくら脆弱でも問題はないわけです。

例えばProgram Filesがユーザー権限では書き込み不能なのはこのためです。

逆に言うと、Program Files以外に.exeを配置する場合(例:インストーラー)には別の対策が求められます。

防御その2、出来るだけ動的ロードを行う

LoadLibraryのロードでフルパスを指定することでDLLプリロード攻撃はおおよそ防ぐことが出来ます。

※後述しますがLoadLibraryが実装された.dll自体はDLLプリロード攻撃される恐れはありません。

そこでLoadLibraryとGetProcAddressを駆使してフルパスによる動的ロードを徹底することでDLLプリロード攻撃からは堅牢にすることが出来ます。

問題は、その作業がかなりの手間であり、実装コストが跳ね上がることでしょうか。

実際の修正例はPrintVersion_sono2として入れてあります。

実行例:

修正部(※一部):

typedef DWORD (WINAPI *GetFileVersionInfoSizeW_T)(
   _In_ LPCWSTR lptstrFilename,
   _Out_opt_ LPDWORD lpdwHandle
);
...
int main() {
   wchar_t versionPath[MAX_PATH] = { 0 };
   ::GetSystemDirectoryW(versionPath, _countof(versionPath));
   ::wcscat_s(versionPath, L"\\version.dll");
   const HMODULE module = ::LoadLibraryW(versionPath);
   const GetFileVersionInfoSizeW_T GetFileVersionInfoSizeW = reinterpret_cast<GetFileVersionInfoSizeW_T>(::GetProcAddress(module, "GetFileVersionInfoSizeW"));

防御その3、安全なDLLのみを使用する

検索順序で少し説明されていますが、Windowsにおいては絶対にシステムディレクトリから読まれる.dll名というのが存在します。

具体的には

  • NT Object Manager領域に公開されているKnownDLLsに含まれる.dll(レジストリの方じゃないので注意)
  • API Setに含まれる.dll
  • twain_32.dll

の3種類です。

KnownDLLsは以下

advapi32.dllbcrypt.dllbcryptPrimitives.dllcfgmgr32.dll
clbcatq.dllcombase.dllCOMCTL32.dllCOMDLG32.dll
coml2.dllCRYPT32.dllcryptsp.dlldifxapi.dll
gdi32.dllgdi32full.dllgdiplus.dllIMAGEHLP.dll
IMM32.dllkernel.appcore.dllkernel32.dllkernelbase.dll
MSASN1.dllMSCTF.dllmsvcp_win.dllMSVCRT.dll
NORMALIZ.dllNSI.dllntdll.dllole32.dll
OLEAUT32.dllpowrprof.dllprofapi.dllPSAPI.DLL
rpcrt4.dllsechost.dllSetupapi.dllSHCORE.dll
SHELL32.dllSHLWAPI.dllucrtbase.dlluser32.dll
win32u.dllwindows.storage.dllWINTRUST.dllWLDAP32.dll
wow64.dllwow64cpu.dllwow64win.dllWS2_32.dll

※Windows10 Version1809のもの

WinObjを使えばだれでも見れます、
各バージョン別の内容はhttps://windowssucks.wordpress.com/knowndlls/さんとかが追っているようです。

API Setはapi-ms-win-*あるいはext-ms-win-*で始まるdllのこと。

※関連してVirtualDLLの仕組みという記事を過去に書いたこともあります。

twain_32.dllは特例(あるいは過去の遺物)で、プリロードとLoadLibraryAに限って.dllまで省略せずに指定した場合のみシステムディレクトリからロードされる実装になっています。

おそらくは下位互換の都合でしょう。

これは例えばwindows互換OSを目指しているreactOSでも再現されています。

https://doxygen.reactos.org/de/de3/dll_2win32_2kernel32_2client_2loader_8c.html#ab6c0d4229db6159ce0cc96228f87b811

上記までに含まれる.dllのみを静的リンクする分にはDLLプリロード攻撃される心配はないので、その範囲でのみ実装することで堅牢にすることが出来ます。

実際にAPI Setを使用するようOneCore.libでリンクしたPrintVersion_sono3.exeが入れてあります。

※古いWindowsだとリンクエラーで動きません。

※API Set的にはapi-ms-win-core-version-l1-1-0.dllとapi-ms-win-core-version-l1-1-1.dllでリンクされていますが、version.dllをそちらの名前にリネームしても同様に正常動作します

組んだアプリが実際に大丈夫かはMSDNなど公式のドキュメントから得られるdll名と別途突き合わせてください。

防御その4、setup.exeにする

※2019/03/26 18:30頃追記

なんとsetup.exeというファイル名だと常に全.dllがシステムディレクトリから優先してロードされる挙動になる。

なんでもインストーラーがDLLプリロード攻撃される事例が相次いだために例外的対処として入ったとかどうとか……。

本当に最後の手段だが、setup.exeというファイル名を使うという手があることは覚えておいてもよいかもしれない。

防御その5、DEPENDENTLOADFLAGを設定する(Windows10限定)

※2019/03/28 21:00頃追記

VisualStudio C++のリンカオプションに/DEPENDENTLOADFLAG:0x00000800 を指定することでシステムディレクトリ以外の.dllとのリンクを抑止可能。

このオプションはLoadLibraryおよび起動時のリンク処理のデフォルト挙動を変更するオプションで、0x00000800はSystem32からのみ.dllを探索するように変更するもの。

当然ながらアプリケーションディレクトリに.dllを並べるタイプのアプリでは使用できない。

そういう場合は防御その1に書いた通りProgram Filesへ配置してください。

主にインストーラーや.exe単独で動作するアプリ向け。

詳しくはMSDNのDEPENDENTLOADFLAGおよびLoadLibraryExの仕様を参照してください。

ちなみにWindows10 RS1未満だとただ指定無視されて従来と同じ動きになります。

結論

Program Filesに置く以外の対策は非常に面倒かつ難しく、普通のプログラマーではミスなく終えるのはまず無理というのはわかってもらえたかと思います。

のでProgram Filesに置く運用大事。

とはいえMSは公式にアプリケーションディレクトリを介する攻撃について

アプリケーション ディレクトリにおける DLL の植え付けのカテゴリに該当する DLL の植え付けの問題は、多層防御の問題として扱われ、更新プログラムは将来のバージョンについてのみ検討されます。 ~中略~ その攻撃の本質はソーシャル エンジニアリングです。

https://blogs.technet.microsoft.com/jpsecurity/2018/04/10/triaging-a-dll-planting-vulnerability/

と述べており、基本的には脆弱性ではないとする方針のようです。

ちなみに一番問題になるのがインストーラーのケース(ダウンロードフォルダから実行されやすい)、次が特権実行されるアプリのケース(特権昇格に繋がる)です。

これらに該当しない場合、対応しなくても社会通念上許されることがほとんどです。

終わりに

私の作ったものにはこのdllプリロード攻撃を善用しているものが多数含まれています。

そういった関係から溜まった知識をダンプする目的で記述したため、他に比べると有用性は大分低い記事になったかもしれません。

ではまた、次を書く機会があればその時に。

2019/03/26 14:30追記:

脆弱かのチェッカー作ったのでよかったら見て
「DLLプリロード攻撃のチェッカーをリリース

2019 年 3 月 12 日

Mozillaに脆弱性を報告したら3年前から既知だと突っ返された話

Filed under: 未分類 — 中の人 @ 1:38 PM

さすがに3年放置しているとは思わんわ、あと検索機能馬鹿すぎて見つけられなかったのなんとかして。

概略

  • FirefoxとThunderbirdのインストーラーに特権昇格の脆弱性を見つける
  • 報告する
  • https://bugzilla.mozilla.org/show_bug.cgi?id=1269142 と重複と突っ返される
  • 些末にしろ3年放置はやめろ←今ここ

脆弱性詳細

インストーラーはまずユーザー権限で一時フォルダにファイルを展開し、特権を得てからそのファイルを実行したりProgram Filesにコピーしたりする。

当然一時フォルダはユーザー権限で書き換え可能なので、途中で実行される.exeを書きかえれば特権昇格、その他ファイルを書き換えればProgram Filesへの侵入(を経ての特権昇格)が可能。

またこれらへの対策も一切なく、実行.exeの署名検証やProgram Filesへコピーするファイルのホワイトリストなども存在しない。

よって特権昇格が可能である。

https://www.youtube.com/watch?v=DuhyTylF0T0

※C:\直下は特権なしではファイルを書き込めないのに書き込めている

PoC本体は https://resemblances.click3.org/product_list/index.cgi/detail/91 を参照のこと。

回答

2016-04-30 12:38投稿の https://bugzilla.mozilla.org/show_bug.cgi?id=1269142 と内容が重複しているとの回答があった。

見間違えじゃないぞ、2016年のとだ。

比較的些末とはいえ3年近くもの間特権昇格の脆弱性を放置するブラウザ……。

結論

Mozillaのインストーラーはこの調子だと何年でも脆弱なまま放置されそうなので、インストールするときは細心の注意を払おうな!

必要になったときだけインストールして即アンインストール運用は特に危険だぞ!

2018 年 5 月 20 日

cv2converterリリース

Filed under: 未分類 — 中の人 @ 1:18 PM

緋想天/非想天則内部形式の.cv2と.pngの相互変換ツールリリース。
http://wordpress.click3.org/garakuta/cv2converter.zip

単独では何の役にも立たないので適当に何かと組み合わせて下さい。
パレット型pngとか8bitカラーや16bitカラーも対応しようと思ったけど、手元の画像編集ソフトがことごとく非対応で作れなかったからもういいかなって。

Older Posts »

Powered by WordPress