2
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Organization

Windows Kernelを理解する(PPL編)

はじめに

Windows8.1およびWindows Server 2012 R2で導入されたPPLによるプロセス保護をカーネル構造体の観点から学び、保護を無効化する方法を通じてその理解を深めることが出来たので以下にまとめます。

目次

Windowsがプロセスを保護する機能PPL

  • 従来のProtected Processと同じように、管理者権限であってもPPLのプロセスに対する要求は制限される
    • プロセスの終了
    • スレッドの挿入
    • スタックの中身の確認 等
  • Protected ProcessまたはPPLがImportできるDLLは、当該プロセスよりも上位の署名があるもののみに制限される
  • Protected ProcessはPPL(Protected Process Light)にアクセス可能

PPL実例:WindowsDefenderのプロセス

Process Exprolerで確認すると、PsProtectedSigner Antimalware-LightのPPLとして保護されていることが分かる。
2019-12-14-16-49-21.png

管理者権限のProcessExprolerにてWindowsDefenderのサービスを停止しようとするとアクセス拒否のエラーが出る。
2019-12-14-17-06-12.png

同様にWindowsDefenderプロセスのThread内のStackにアクセスしようとして拒否される。
このようにしてPPLはマルウェアによるAnti virusプロセスの改ざん/停止を防ぐ手段としても用いられている。
2019-12-14-16-53-55.png

PPLをバイパスしてプロセスを不正操作するには

ここまで、PPLで保護されたプロセスは管理者権限を持ったプロセスあってもアクセスが制限されることを示してきた。

しかし、PPLはカーネルプロセスに対する保護を提供していないため、マルウェアまたは一般ユーザによってPPLをバイパスする方法が存在している。
ここではPPLをバイパスする方法としてカーネルモードドライバーを利用する2つの方法について触れる。

PPLKiller

PPLをバイパスする方法の1つ目はPPLKillerの利用である。
PPLKillerはPPLの保護を無効化する機能を持つカーネルドライバーであり、Github上にてGPL-3.0でOSSとして公開されている。
ただしMicrosoftの署名を持たないため、Windows 10 Aniversary update以降では、テストモードにてドライバー署名の強制を無効化しなければ利用することが出来ない。
ドライバーに対する署名の要求については、インサイドWindows 第7版(上)の記載が参考になる。

以下にPPLKillerのソースコードを抜粋する。

PPLKiller/main.cpp抜粋
if (PsProtectionOffset != 0) // Do we have any PPLs tounprotect?
{
    const PPS_PROTECTION PsProtection = reinterpret_cast<PPS_PROTECTION>(
    reinterpret_cast<PUCHAR>(Process) + PsProtectionOffset);
    // Skip non-light protected processes (i.e. System).
    // You could also discriminate by signer, e.g. to leave LSASS or antimalware protection enabled
    if (PsProtection->Level != 0 &&
        PsProtection->s.Type == PsProtectedTypeProtectedLight)
    {
        Log("PID %u (%wZ) at 0x%p is a PPL: { type: %u, audit: %u, signer: %u }.\n", Pid, &Entry->ImageName,
            Process, PsProtection->s.Type, PsProtection->s.Audit, PsProtection->s.Signer);

        // Goodnight sweet prince
        PsProtection->Level = 0;
        (*NumProcessesUnprotected)++;
        Log("Protection removed.\n\n");
    }
}
if (Pid != 0 && !PsIsSystemProcess(Process) &&                  // Not a system process?
    SignatureLevelOffset != 0 && SectionSignatureLevelOffset != 0)  // >= Windows 10 RS2, and offsets known?
{
    const PUCHAR SignatureLevelByte = reinterpret_cast<PUCHAR>(Process) + SignatureLevelOffset;
    const PUCHAR SectionSignatureLevelByte = reinterpret_cast<PUCHAR>(Process) + SectionSignatureLevelOffset;
    const UCHAR SignatureLevel = *SignatureLevelByte & 0xF;
    const UCHAR ImageSignatureType = (*SignatureLevelByte >> 4) & 0x7;
    const UCHAR SectionSignatureLevel = *SectionSignatureLevelByte & 0xF;
    if ((SignatureLevel == SE_SIGNING_LEVEL_MICROSOFT ||
        SignatureLevel == SE_SIGNING_LEVEL_WINDOWS ||
        SignatureLevel == SE_SIGNING_LEVEL_ANTIMALWARE ||
        SignatureLevel == SE_SIGNING_LEVEL_WINDOWS_TCB)
        &&
        (SectionSignatureLevel == SE_SIGNING_LEVEL_MICROSOFT ||
        SectionSignatureLevel == SE_SIGNING_LEVEL_WINDOWS))
    {
        Log("PID %u (%wZ) at 0x%p has a Microsoft code signing requirement:\n", Pid, &Entry->ImageName, Process);
        // NB: the SE_IMAGE_SIGNATURE_TYPE can be 'none' while still having an MS code signing policy, so this isn't a reliable indicator.
        // Normally though it will either be SeImageSignatureEmbedded (system process) or SeImageSignatureCatalogCached (other processes).
        Log("Image signature level:\t0x%02X [%s], type: 0x%02X [%s]\n",
            SignatureLevel, SeSigningLevelNames[SignatureLevel],
            ImageSignatureType, SeSigningTypeNames[ImageSignatureType]);
        Log("Section signature level:\t0x%02X [%s]\n",
            SectionSignatureLevel, SeSigningLevelNames[SectionSignatureLevel]);
        // Hasta la vista baby
        *SignatureLevelByte = 0;
        *SectionSignatureLevelByte = 0;
        (*NumSignatureRequirementsRemoved)++;
        Log("Requirements removed.\n\n");
    }
}

PsProtection->Levelと*SignatureLevelByte、*SectionSignatureLevelByteに0を代入することでのPPLを無効化している。
ちなみに、それら変数はEPROESS構造体のメンバ変数であることをWinDBGにてdt -r nt!_EPROCESSコマンドを実行することで確認できる。
2019-12-14-23-39-11.png

EPROCESS構造体はその大部分がシステム空間に存在する。

*SignatureLevelByte、*SectionSignatureLevelByteに0を代入する効果については、Aalto Universityの学位論文を参照した。

PsProtection->Levelへ0を代入する効果については現在参考情報を収集中。1byteの内、上位4bitがプロセス?権限?を表しており下位4bitの0/1で保護の有無を表していると現時点では推測。

CVE-2018-6593を利用したExploit

MalwareFoxが無料で提供するAnti virusのカーネルドライバにCVE-2018-6593の脆弱性があり、DeviceIoControl()を利用して保護されたプロセスへのフルアクセス権限を得ることが出来る。
以下、参考URL
- https://www.unknowncheats.me/forum/anti-cheat-bypass/262766-bypass-ppl-protected-process-light-user-mode-using-vulnerable-driver.html
- https://www.nmmapper.com/st/exploitdetails/43987/9647/malwarefox-antimalware-2740150-privilege-escalation/
```

最後に

PPLで保護されたプロセスであってもカーネル権限で保護を無効化することが出来ることを確認しました。
カーネル権限を持たないユーザプロセスがPPLを無効化する為には、カーネルドライバの脆弱性を突く必要があることも分かりました。
今後もWindows Kernelについて理解を深める上で興味を惹かれる部分があれば、こうして何らかの形でアウトプットしていきます。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
2
Help us understand the problem. What are the problem?