CreateProcess時、PROC_THREAD_ATTRIBUTE_MITIGATION_POLICYでプロセスに対する一部の挙動を変更することが可能です。その中にWindows 10のいつからか、DLLの検索パスについての定数が増えています。前々から気になっていたので、試してみました。
その定数は、UpdateProcThreadAttributeに記載のある以下です。
- PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_DEFER
- PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON
- PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_OFF
まず、題材となるプログラムを作ります。これをコンパイルして、app.exeを作ります。iphlpapi.dllを何も考えずにLoadLibrayしているのは、もちろんわざとです。効果を確認するため、セキュリティ上問題のある書き方にしています。
// cl /MT /EHsc app.cpp /link /subsystem:console,5.01 #define UNICODE #define _UNICODE #include <iostream> #include <windows.h> #include <iphlpapi.h> int main() { auto hmod = LoadLibrary(L"iphlpapi.dll"); if (auto pfn = reinterpret_cast<decltype(GetTcpTable2)*>( GetProcAddress(hmod, "GetTcpTable2"))) { ULONG size = 0; pfn(nullptr, &size, 0); std::cout << size << std::endl; } } |
では、偽のiphlpapi.dllを作りましょう。次のソースコードをコンパイルします。
// cl iphlpapi.cpp user32.lib /LD #define UNICODE #define _UNICODE #include <windows.h> #pragma comment(linker, "/export:GetTcpTable2=_GetTcpTable2@12") extern "C" __declspec(dllexport) ULONG WINAPI GetTcpTable2(void*, ULONG*, BOOL) { MessageBox(nullptr, L"やっほー", L"evil bit=1", MB_OK); return ERROR_INVALID_PARAMETER; } |
さっきのapp.exeと同じフォルダに、偽iphlpapi.dllを置き、app.exeを実行します。すると、メッセージボックスが表示されます。
さて、例のフラグ…_ALWAYS_ONを試します。CreateProcessを呼び出すlauncher.exeを作ります。
// cl /MT launcher.cpp #define UNICODE #define _UNICODE #include <iostream> #include <memory> #include <cstdint> #include <cstdlib> #include <windows.h> int main() { WCHAR cmdLine[] = L"app.exe"; SIZE_T bufSize = 0; if (!InitializeProcThreadAttributeList(nullptr, 1, 0, &bufSize) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { std::quick_exit(1); } auto buf = std::make_unique<BYTE[]>(bufSize); auto attrList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(buf.get()); if (!InitializeProcThreadAttributeList(attrList, 1, 0, &bufSize)) { std::quick_exit(1); } std::uint64_t value = PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON; if (!UpdateProcThreadAttribute( attrList, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &value, sizeof value, nullptr, nullptr)) { std::quick_exit(1); } STARTUPINFOEX si{}; si.StartupInfo.cb = sizeof si; si.lpAttributeList = attrList; PROCESS_INFORMATION pi{}; if (CreateProcess(nullptr, cmdLine, nullptr, nullptr, FALSE, EXTENDED_STARTUPINFO_PRESENT, nullptr, nullptr, &si.StartupInfo, &pi)) { WaitForSingleObject(pi.hProcess, INFINITE); } // DeleteProcThreadAttributeListなどは省略 } |
これをコンパイルして実行すると、見事メッセージボックスは表示されませんでした。
なお、定数…_DEFERを指定して、app.exe内でPreferSystem32Images = 1を指定してSetProcessMitigationPolicyを呼び出せば同様にsystem32優先になると予想していたのですが、これはうまくいきませんでした。偽iphlpapi.dllが読み込まれる結果となりました。やり方にミスがあったのでしょうか。
この方法、ご覧のとおりCreateProcessを呼び出す側での対処です。アプリ側に手を入れずに効果を発揮させることが可能で、しかもアプリ(app.exe)側のプロセス内で無効化できないはずという点で強力です。逆に、JVNTA#91240916: Windows アプリケーションによる DLL 読み込みやコマンド実行に関する問題(UNLHA32.DLLで作成された自己解凍書庫における任意のDLL読み込みに関する脆弱性)への対処に使うのは大変手間がかかるでしょう。
というわけで、DLL検索パスの指定方法がまた1つ増えました。
スポンサード リンク |