1. 개요
본 보고서는 Microsoft Windows PowerShell 5.1에서 발생하는 CVE-2025-54100 MSHTML 파서 취약점에 대해 다룬다.
Windows PowerShell 5.1의 Invoke-WebRequest cmdlet을 통해 원격 웹 서버로부터 악의적인 HTML/JavaScript 콘텐츠를 수신하는 경우, 공격자는 사용자 상호작용 없이 Internet Explorer의 MSHTML 엔진을 통해 임의 코드를 실행할 수 있다.
이 취약점은 PowerShell이 -UseBasicParsing 파라미터가 $false(기본값)일 때, 웹 응답을 처리하기 위해 Internet Explorer의 MSHTML 엔진을 호출하는 설계적 결함에서 비롯된다. 이로 인해 신뢰되지 않은 JavaScript가 로컬 시스템 권한으로 실행될 수 있다.
2. 취약점 근본 원인 분석
항목 | 내용 |
취약점 번호 | CVE-2025-54100 |
취약점 이름 | PowerShell Remote Code Execution Vulnerability |
취약점 종류 | Remote Code Execution |
CVSS | 7.8 (AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H) |
CVE-2025-54100은 단순한 코드 버그가 아닌 아키텍처 설계상의 판단 오류에서 비롯된 취약점이다. PowerShell 5.1이 단순 HTTP 응답을 처리하는 과정에서 텍스트 파서 대신 완전한 웹 브라우저 엔진을 선택한 것이 문제의 핵심이다.
2.1. PowerShell 버전별 동작 비교
기능 | PowerShell 5.1 (기본값) | PowerShell 5.1 (-UseBasicParsing) | PowerShell 7 |
파싱 엔진 | Internet Explorer (MSHTML) | 내장 텍스트 파서 | 내장 텍스트 파서 |
DOM 접근 | 가능 | 불가능 | 불가능 |
JavaScript 실행 | 자동 실행 (취약) | 실행 안 됨 | 실행 안 됨 |
2.2. 관련 함수 호출 구조
PowerShell Command 실행
│
│ curl [공격자주소]/[악성파일]
│ Invoke-WebRequest [공격자주소]/[악성파일]
│
▼
Microsoft.PowerShell.Commands.Utility.dll
│
▼
Microsoft.PowerShell.Commands (namespace)
│
▼
InvokeWebRequestCommand : WebRequestPSCmdlet (Class)
│
▼
WebResponseObjectFactory.GetResponseObject() (Class)
│
├─ UseBasicParsing = false (기본값)
│ │
│ ▼
│ HtmlWebResponseObject() ← IE 엔진 사용 (취약)
│
└─ UseBasicParsing = true
│
▼
BasicHtmlWebResponseObject() ← 텍스트 파서 사용 (안전)
Plain Text
복사
함수명 | 역할 |
InvokeWebRequestCommand | Invoke-WebRequest cmdlet의 진입점 |
WebResponseObjectFactory.GetResponseObject() | 파싱 방식 결정 (IE 엔진 vs 텍스트 파서) |
HtmlWebResponseObject | IE MSHTML 엔진으로 HTML 파싱 (JavaScript 실행됨) |
BasicHtmlWebResponseObject | 단순 텍스트 파서로 HTML 파싱 (JavaScript 실행 안 됨) |
2.3. 취약한 코드 분석
2.3.1 InvokeWebRequestCommand() 호출
namespace Microsoft.PowerShell.Commands
{
// ... 생략 ...
if (response == null)
{
throw new ArgumentNullException("response");
}
// -UseBasicParsing을 사용하지 않으면 IE 엔진 활성화
if (base.ShouldWriteToPipeline && !this.UseBasicParsing)
{
base.VerifyInternetExplorerAvailable(true); // IE 사용 가능 여부 확인
}
Stream stream = StreamHelper.GetResponseStream(response);
if (base.ShouldWriteToPipeline)
{
stream = new WebResponseContentMemoryStream(stream, 10000, this);
// 파싱 방식 결정: UseBasicParsing 값에 따라 분기
WebResponseObject responseObject = WebResponseObjectFactory.GetResponseObject(
response,
stream,
base.Context,
this.UseBasicParsing // false면 IE 엔진, true면 텍스트 파서
);
base.WriteObject(responseObject); // responseObject 실행 ← 여기서 JS 실행
// ... 생략 ...
}
}
C#
복사
[코드 1] InvokeWebRequestCommand() 함수 호출 흐름
UseBasicParsing의 기본값이 false이므로, 사용자가 명시적으로 -UseBasicParsing을 지정하지 않으면 항상 IE 엔진이 사용된다.
2.3.2 VerifyInternetExplorerAvailable() 동작
VerifyInternetExplorerAvailable(true) 호출 시 다음과 같은 검증이 수행된다.
1. "IE를 사용할 수 있는가?" 확인
├─ 레지스트리에서 IE 초기화 상태 확인
├─ RunOnce 설정 확인 (IE 실행 이력)
└─ COM 객체 생성 가능 여부 테스트
2. 사용 가능하면 → IE 엔진 사용 (취약점 발생)
3. 사용 불가능하면 → 예외 발생
Plain Text
복사
2.3.3 WebResponseObjectFactory.GetResponseObject() 함수
internal static class WebResponseObjectFactory
{
internal static WebResponseObject GetResponseObject(
WebResponse response,
Stream responseStream,
ExecutionContext executionContext,
bool useBasicParsing = false // 기본값 false = IE 엔진 사용
)
{
WebResponseObject result;
if (WebResponseHelper.IsText(response))
{
if (!useBasicParsing)
{
// 취약: IE MSHTML 엔진으로 파싱
result = new HtmlWebResponseObject(response, responseStream, executionContext);
}
else
{
// 안전: 단순 텍스트 파서로 파싱
result = new BasicHtmlWebResponseObject(response, responseStream);
}
}
// ... 생략 ...
return result;
}
}
C#
복사
[사진 1] useBasicParsing 기본 값 False 설정
useBasicParsing = false일 때 HtmlWebResponseObject가 생성되며, 이 객체는 내부적으로 IE의 MSHTML 엔진을 사용하여 HTML을 파싱한다. 이 과정에서 <script> 태그 내의 JavaScript 코드가 자동으로 실행된다.
2.4. 파싱 객체 비교
항목 | WebResponseObject | HtmlWebResponseObject | BasicHtmlWebResponseObject |
DOM 파싱 | |||
IE 엔진 | |||
ParsedHtml | |||
-UseBasicParsing | 무관 |
용어 설명:
•
DOM (Document Object Model): HTML/XML 문서를 트리 구조의 객체 모델로 표현하여 프로그래밍적으로 접근 및 조작할 수 있도록 하는 인터페이스
•
IE 엔진 (MSHTML/Trident): mshtml.dll을 사용하여 HTML을 렌더링하고 파싱하는 COM 기반 컴포넌트
2.5. PowerShell 7과의 비교
[사진 2] PowerShell 7 소스코드
PowerShell 7은 오픈소스로 코드가 공개되어 있으며, BasicHtmlWebResponseObject만 사용하도록 설계되어 있다.
2.4. 시연 영상
[동영상 1] PowerShell 5.1 시연 영상
[동영상 2] PowerShell 7 시연 영상
3. 공격 영향
본 취약점이 악용될 경우, 공격자는 PowerShell 5.1 환경에서 실행되는 Invoke-WebRequest를 통해 사용자 개입 없이 원격 스크립트 코드를 실행할 수 있다.
1.
원격 코드 실행
•
공격자가 제어하는 서버에서 전달된 HTML 내 <script> 코드가 자동으로 실행된다. 이는 단순한 웹 요청으로 인식되는 코드가 실제로는 스크립트 실행 환경을 제공하기 때문이다.
2.
브라우저 기반 연쇄 공격
•
IE(MSHTML) 기반 브라우저 실행 환경이 강제로 활성화되면서 다음과 같은 추가 공격이 가능해진다.
공격 유형 | 설명 |
외부 스크립트 로딩 | 외부 .js 파일을 동적으로 로드하여 실행 |
DOM 조작 | 문서 구조를 변경하여 추가 공격 수행 |
URL 리다이렉션 | 특정 웹 페이지로 강제 접속 유도 |
취약점 체이닝 | 추가적인 브라우저 취약점을 연쇄적으로 악용 |
4. 완화 및 권고사항
1.
PowerShell 7 이상(Core) 사용 권장
•
PowerShell 7은 IE/MSHTML 엔진을 제거하고 HTTP 클라이언트 기반으로 Invoke-WebRequest를 재구현하여, HTML 내 <script> 코드가 실행되지 않도록 설계되었다.
2.
PowerShell 5.1 환경에서의 대응
•
부득이하게 PowerShell 5.1을 사용해야 하는 경우, 외부 URL을 직접 호출하는 스크립트에서 Invoke-WebRequest 사용을 지양하고, System.Net.Http.HttpClient 기반의 직접 구현 또는 사전 검증된 내부 저장소만을 대상으로 통신하도록 설계해야 한다.
3.
외부 콘텐츠 신뢰 검증 강화
•
자동화 스크립트에서 호출되는 URL은 반드시 신뢰 가능한 도메인으로 제한하고, 응답 콘텐츠에 대한 무결성 검증(해시 검증 등)을 수행해야 한다.
4.
보안 정책을 통한 IE 엔진 제한
•
AppLocker, WDAC(Windows Defender Application Control) 등의 보안 정책을 활용하여 MSHTML 기반 COM 객체의 비인가 사용을 차단한다.
5. 결론
CVE-2025-54100 취약점은 PowerShell 5.1의 Invoke-WebRequest가 내부적으로 IE(MSHTML) 엔진을 사용함으로써 단순한 HTTP 요청이 스크립트 실행 환경을 제공한다는 구조적 설계 결함에서 기인한다.
공격자는 별도의 사용자 상호작용 없이도 원격에서 전달한 HTML/JavaScript 코드를 실행시킬 수 있으며 자동화 환경에서는 그 위험성이 더욱 증폭된다.
당사의 솔루션인 PurpleHound는 실전 기반 시나리오를 재구성하여 기업이 보유한 보안 장비와 시스템이 실제 공격에 얼마나 효과적으로 대응 가능한지 검증할 수 있도록 지원한다.
본 보고서에서 분석한 취약점 뿐만 아니라 다른 유형의 취약점, 악성코드 활동, 파일 시스템 조작 등 다양한 위협 시나리오를 직접 실행해보며 보안 인프라의 실효성을 객관적으로 확인할 수 있다.




