
DLLHijackHunter v2.5.0
자동화된 DLL 하이재킹 발견, 검증 및 확인. 로컬 구성 오류를 무기화되고 확인된 공격 경로로 전환합니다.
DLLHijackHunter
By ProjectMerai
Windows용 자동화된 DLL 하이재킹 탐지, 검증 및 확인
"이 DLL은 하이재킹될 수 있을지도 모른다"에서 "표준 사용자가 SYSTEM이 이 DLL을 로드하도록 만들 수 있다 — 여기에 증거가 있다"까지.
목차
- 개요
- 빠른 시작
- 작동 방식
- 탐지 대상
- 오탐 줄이기
- 검증: 로드 프로브와 카나리
- 점수, 등급 및 공격 체인
- 사용법 참조
- 안전성 및 운영 영향
- 소스에서 빌드하기
- 테스트 랩 (HijackRange)
- 알려진 제한 사항
- 비교
개요
DLLHijackHunter는 Windows 호스트에서 DLL 하이재킹 기회를 찾아내고, 단순히 나열하는 데 그치지 않고 이를 입증하려고 합니다. 이 도구는:
- 바이너리가 자동으로 실행되는 모든 위치 — 서비스, 예약된 작업, Run 키, 시작 폴더, COM 서버, AppInit/AppCert DLL, IFEO 항목, 자동 상승 UAC 바이너리 — 를 탐지하고, 각 가져온 DLL이 어디에서 검색될지 계산합니다.
- 공격자가 실제로 사용할 수 없는 위치 — API 세트, KnownDLL, 표준 사용자가 쓸 수 없는 디렉터리, 보호된 프로세스 등 — 를 필터링합니다.
- (선택 사항) 실제 Windows 로더로 검증하여, 무해한 프로브 DLL을 배치하고 어느 복사본이 검색에서 이기는지 확인합니다.
- (선택 사항) 무해한 카나리 DLL을 배포하고 서비스/작업/COM 개체를 트리거한 뒤 실제로 실행된 ID를 기록하여 확인합니다.
- 각 발견 사항을 신뢰도와 영향도에 따라 점수화하고 보고하며, 발견 사항을 공격 체인(예: 표준 사용자 → SYSTEM)으로 연관시킵니다.
모든 판단은 스캐너 자체가 상승된 권한으로 실행되는지 여부와 관계없이 공격자의 관점 — 권한 없는 표준 사용자 — 에서 이루어집니다. 관리자로 실행하면 도구의 가시성(ETW, 카나리, 서비스 트리거)이 높아지지만, 보호된 디렉터리가 쓰기 가능한 것처럼 보이게 하거나 획득한 권한을 부풀리지는 않습니다.
빠른 시작
DLLHijackHunter.exe를 다운로드하거나 빌드한 뒤(자체 포함; .NET 설치 불필요), 상승된 프롬프트에서:
# Passive, read-only triage: no file drops, no service/task starts
.\DLLHijackHunter.exe --profile safe
# Full static analysis without touching the system, JSON report
.\DLLHijackHunter.exe --profile aggressive --no-canary --no-etw -o report.json
# Static analysis + real-loader verification (writes probe DLLs transiently)
.\DLLHijackHunter.exe --profile aggressive --no-canary --no-etw --verify-load -o report.html
# Everything, including ETW and canary confirmation — lab / authorised targets only
.\DLLHijackHunter.exe --profile aggressive -o report.html
# Focus on one application
.\DLLHijackHunter.exe --target "C:\Program Files\MyApp\app.exe" --no-canary
안티바이러스: 카나리 및 프로브 DLL은 서명되지 않았고 하이재킹 페이로드처럼 동작하므로 Microsoft Defender가 이를 격리할 수 있습니다. 이 도구는 이를 감지하여 해당 결과를 "하이재킹 불가"가 아닌 불확정으로 보고합니다. 테스트 머신에서는 확정적인 결과를 얻으려면 대상 디렉터리와
%ProgramData%\DLLHijackHunter를 제외하십시오.
작동 방식
flowchart TB
subgraph S1["1 · Discovery"]
direction LR
EN["Launch points<br/>services · tasks · Run keys<br/>startup · COM · AutoElevate"] --> PE["PE analysis<br/>imports · delay loads<br/>manifest"] --> SO["Search order<br/>identity-aware PATH"]
ETW["ETW runtime trace<br/>optional, elevated"]
end
subgraph S2["2 · Filter pipeline"]
direction LR
HG["Hard gates<br/>API sets · KnownDLLs<br/>writability"] --> SG["Soft gates<br/>manifest · privilege<br/>PPL · LoadLibraryEx"] --> DD["Dedup and<br/>profile filters"]
end
subgraph S3["3 · Verification (optional)"]
direction LR
LP["Load probe<br/>real Windows loader"] --> CN["Canary<br/>deploy, trigger, confirm"]
end
subgraph S4["4 · Output"]
direction LR
SC["Scoring and tiers"] --> AC["Attack chains"] --> RP["Console · JSON · HTML"]
end
S1 --> S2 --> S3 --> S4
| 단계 | 관리자 권한 필요 | 시스템 수정 |
|---|---|---|
| 정적 탐색 + 필터 | 아니요 (관리자는 더 많이 확인) | 아니요 |
| ETW 런타임 탐색 | 예 | DLL 로드를 유도하기 위해 자동 시작 서비스(최대 100개)를 시작하고 예약된 작업(최대 30개)을 실행 |
로드 프로브 (--verify-load) | 아니요 | 각 후보 위치에 무해한 프로브 DLL을 잠시 기록한 후 제거하고 원본이 있으면 복원 |
| 카나리 확인 | 예 | 카나리 DLL을 기록하고, 서비스를 중지/시작하고, 작업을 실행하고, COM 개체를 활성화한 후 정리 |
탐지 대상
하이재킹 유형
| 유형 | 의미 | 참고 |
|---|---|---|
| 팬텀(Phantom) | DLL이 검색 경로 어디에도 존재하지 않음 | 바이너리가 검색하는 어느 위치에나 배치 가능 |
| 검색 순서 | 쓰기 가능한 디렉터리가 정당한 복사본보다 먼저 검색됨 | 쓰기 가능한 디렉터리의 DLL 덮어쓰기 포함 |
| ENV PATH | PATH의 쓰기 가능한 디렉터리에서 누락된 DLL을 검색 | 서비스는 머신 PATH에 대해서만 평가됨 — 사용자 자신의 PATH 항목은 서비스가 절대 검색하지 않음 |
| .local 리디렉션 | <app>.exe.local\<dll> 리디렉션 | 매니페스트가 없는 실행 파일에 대해서만 보고됨 (또는 IFEO DevOverrideEnable=1인 경우); 그 외의 경우 로더는 .local을 무시하며, KnownDLLs는 절대 리디렉션하지 않음 |
| 사이드로드 (UAC) | 자동 상승 EXE를 배치된 DLL 옆에 복사 | 매니페스트에 <autoElevate>true</autoElevate>가 있는 EXE에만 해당. 자동 상승은 신뢰할 수 있는 위치를 요구하므로 복사본이 해당 검사를 통과해야 함 (예: 모의 C:\Windows \System32\ 폴더) |
| AppInit DLLs | AppInit_DLLs가 쓰기 가능한 DLL을 가리킴 | user32.dll을 로드하는 모든 프로세스에 로드됨 |
| AppCert DLLs | AppCertDLLs가 쓰기 가능한 DLL을 가리킴 | 모든 CreateProcess/WinExec 호출자에 로드됨 |
IFEO Debugger 및 SilentProcessExit\MonitorProcess 바이너리는 다른 실행 지점과 마찬가지로 열거되고 분석됩니다. CWD 기반 하이재킹은 현재 생성되지 않습니다.
대상이 실행되는 계정
올바른 ID는 권한 상승 판정과 영향 점수 모두를 좌우합니다:
- 서비스 —
ObjectName(LocalSystem, Local Service, Network Service 또는 계정); svchost 서비스는 해당ServiceDll도 분석합니다. - 예약된 작업 — 작업 주체.
S-1-5-18과 같은 SID 형식은NT AUTHORITY\SYSTEM으로 매핑됨; 그룹 주체는 로그온한 구성원으로 실행됨 (Administrators + 최고 권한만 상승된 것으로 간주).rundll32,cmd /c또는powershell -File로 감싼 페이로드도 분석됩니다. - Run 키 / 시작 폴더 — 스캐너를 실행하는 계정뿐만 아니라 로드된 모든 사용자 프로필(
HKEY_USERS)과 각 프로필의 시작 폴더에 대해 열거되며, 해당 사용자에게 귀속됨. - COM — 아웃오브프로세스 서버는
AppID→RunAs/LocalService를 따름; 인프로세스 서버는 이를 로드하는 클라이언트로 실행됨.
지식 기반 및 PATH 무기화
- HijackLibs 데이터셋의 번들 스냅샷(약 700개 실행 파일에 걸친 약 590개 DLL,
Resources/hijacklibs.json)이 교차 참조됩니다. 일치하면 신뢰도가 높아지고 참조가 연결됨; 일치하지 않으면 아무것도 아님. 파일을https://hijacklibs.net/api/hijacklibs.json으로 교체하여 갱신할 수 있음. 데이터셋 © HijackLibs 프로젝트 및 기여자. - 머신 PATH의 각 쓰기 가능한 디렉터리에 대해, 누락된 DLL을 PATH에서 검색하는 잘 알려진 SYSTEM 서비스(
IKEEXT → wlbsctrl.dll,Spooler → ualapi.dll,Schedule → WptsExtensions.dll,SessionEnv → tsmsisrv.dll,WlanSvc → wlanhlp.dll)에 대한 후보가 생성됩니다.
오탐 줄이기
하드 게이트 (후보가 제외됨):
- API 세트 — 모든
api-ms-*/ext-ms-*계약 이름은 검색 순서가 아니라 API set 스키마를 통해 확인됩니다. - KnownDLLs — 레지스트리 시드 목록뿐 아니라 활성
\KnownDlls및\KnownDlls32객체 디렉터리에서 읽으며, 항상 가장 먼저 매핑되는ntdll.dll도 포함합니다. - 공격자 기준 쓰기 가능성 —
Users,Authenticated Users,Everyone, 또는INTERACTIVE(및LOCAL SERVICE와 같은 비권한 서비스 계정)가 중요한 권한을 보유한 경우에만 해당 위치가 인정됩니다. DLL 드롭의 경우 파일 생성,.local디렉터리의 경우 폴더 생성이 해당됩니다. 이는 스캐너 자체의 토큰과 무관하게 ACL에서 계산되므로, 권한 상승된 스캔에서도System32,Program Files, 또는 드라이브 루트(C:\)를 쓰기 가능한 것으로 보지 않습니다.
소프트 게이트 (신뢰도가 조정되며, 그 이유는 발견 항목의 노트에 기록됩니다):
- DLL의 매니페스트 / WinSxS 바인딩.
- 권한 차이 — 표준 사용자 공격자를 기준으로 측정합니다. SYSTEM, LOCAL/NETWORK SERVICE, 그리고 UAC로 권한 상승된 대상은 권한 상승으로 간주하며, 동일 권한 대상은 지속성 / 방어 회피 발견 항목으로 유지합니다.
- LoadLibraryEx 완화책 —
SetDefaultDllDirectories,AddDllDirectory,SetDllDirectory임포트. - 보호된 프로세스 — 알려진 PPL 바이너리, SCM 구성에서
LaunchProtected가 설정된 서비스(최신 Windows 및 타사 AV/EDR 서비스 포함), 그리고FORCE_INTEGRITY이미지. - 오류 처리된 선택적 로드.
게이트 평가에 실패하더라도 후보에 불이익은 주어지지 않지만, 실패 사실은 숨기지 않고 해당 노트와 로그에 기록됩니다.
검증: 로드 프로브 및 카나리
로드 프로브 (--verify-load)
Phantom, 검색 순서, 및 사이드로드 후보의 경우, 무해한 프로브 DLL이 쓰기 가능한 위치에 복사되고 단기 실행 자식 프로세스가 실제 Windows 로더에게 이름 확인을 요청합니다: