Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
my-CVE-2021-1675 — 악성 프린터 드라이버 설치를 통한 RPC 기반 권한 상승 및 원격 코드 실행이 포함된 Windows PrintNightmare(CVE-2021-1675/34527)에 대한 상세 분석 및 익스플로잇 구현. | Kitploit
도구/GitHubGitHub/hahaleyile/my-cve-2021-1675
Privilege EscalationVulnerability AnalysisExploitationPapers & ResearchLearning & EducationRemote Access ToolBinary Exploitation
GitHubhahaleyile/my-cve-2021-1675

my-CVE-2021-1675

악성 프린터 드라이버 설치를 통한 RPC 기반 권한 상승 및 원격 코드 실행이 포함된 Windows PrintNightmare(CVE-2021-1675/34527)에 대한 상세 분석 및 익스플로잇 구현.

저장소 보기
3315년 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

= Print Nightmare 분석 보고서 :imagesdir: Figures :toc: :icons: font :figure-caption: 그림 :xrefstyle: short :pdf-theme: basic-theme.yml

2021년 6월 29일, 매우 심각한 Windows 인쇄 서비스 취약점이 0day로 공개되었으며, 기본 점수 8.8점으로 GitHub에 게시되었습니다(현재 삭제됨). 이 취약점은 유명한 PrintNightmare: CVE-2021-34527로, 그 위험성은 EternalBlue보다도 높습니다.

== 취약점 기본 정보

34527 취약점은 Windows 7, Windows Server 2008 이후의 거의 모든 버전에 영향을 미치며, 자세한 내용은 <>를 참조하십시오.

피해 측면에서 볼 때, 공격자는 일반 사용자 인증을 사용하여 관리자 권한으로 원격에서 임의 코드를 실행할 수 있습니다. 취약점 악용 난이도 측면에서 이 취약점은 매우 쉽게 악용될 수 있으므로 위험성이 큽니다.

취약점 특성 측면에서 34527 취약점은 CVE-2021-1675 취약점을 기반으로 합니다. 1675 취약점은 로컬 권한 상승 및 원격 코드 실행 취약점으로, 34527 취약점과 매우 유사한 부분이 있습니다.

취약점의 작동 원리를 이해하기 전에 Windows 인쇄 스풀러 아키텍처를 대략적으로 이해해야 하며, 이를 통해 취약점 관련 각 모듈 간의 관계를 명확히 할 수 있습니다.

== CVE-2021-1675 호출 흐름

=== Windows 인쇄 스풀러 아키텍처

스풀러 아키텍처는 <<spooler_arch>>로 나타낼 수 있습니다:

[[spooler_arch]] .Print Spooler 아키텍처 image::Print Spooler Architecture.png[]

구체적으로 스풀러는 인쇄 작업을 관리하며, 다음 구성 요소로 이루어져 있습니다:

winspool.drv:: 사용자에게 제공되는 동적 링크 라이브러리 파일입니다. 이 파일은 스풀러 관련 Win32 API를 정의하여 사용자가 호출할 수 있도록 합니다. 내부의 API는 모두 원격 프로시저 호출 형태로 서비스를 제공합니다.

spoolsv.exe:: spoolsv.exe는 시스템에서 서버 역할을 하며, API 호출을 최초로 처리하는 프로그램입니다. 이러한 설계는 인쇄 스풀러가 로컬 인쇄 작업뿐만 아니라 원격 인쇄 작업도 차이 없이 처리할 수 있도록 하기 위함입니다.

spoolsv.dll:: 라우팅 프로그램입니다. spoolsv.exe가 수신한 인쇄 요청을 각 인쇄 공급자에게 전달하고, 최종적으로 어떤 인쇄 공급자가 요청을 처리할지 결정합니다. 그 역할은 인쇄 작업이 원격 작업인지 로컬 작업인지 구분하는 것입니다. 원격 머신에서는 작업을 로컬 인쇄 공급자에게 고정적으로 할당합니다.

localspl.dll:: 로컬 인쇄 공급자입니다. 인쇄 공급자의 주요 작업은 인쇄 작업 관리 요구를 해결하는 것이며, 대부분의 API는 이 모듈 내에서 구현됩니다.

위 이론에 따라 계속 연구하면, 예를 들어 AddPrinterDriverEx 함수(CVE-2021-1675)를 호출할 때 다음 흐름을 거칩니다:

=== 함수 버전 선택

먼저 이 함수는 실제로 매크로로, 로컬 컴파일 환경에 따라 유니코드 버전(W) 또는 Ansi 버전(A)을 선택합니다(<> 참조):

[[AddPrinterDriverEx]] .AddPrinterDriverEx image::AddPrinterDriverEx.png[]

하지만 와이드 문자 버전이든 좁은 문자 버전이든 결과는 동일합니다. Windows 커널 문자열은 유니코드 인코딩을 사용하기 때문에, Ansi 버전의 호출은 결국 유니코드 버전으로 변환됩니다(<> 참조):

[[AnsiToUnicode]] .AnsiToUnicode image::AnsiToUnicode.png[]

Ansi 함수의 매개변수가 유니코드 버전으로 변환되면 함수가 호출됩니다(<>):

[[AnsiCallUnicode]] .AnsiCallUnicode image::AnsiCallUnicode.png[]

이 함수는 실제로 유니코드 버전의 AddPrinterDriverEx입니다(<>):

[[GetUnicodeProcAddress]] .GetUnicodeProcAddress image::GetUnicodeProcAddress.png[]

=== API 함수가 RPC 요청을 스풀러 서버로 전송

유니코드 버전의 함수 내부에 들어가면 먼저 Level 값에 따라 함수 매개변수 유형을 선택합니다:

image::pDriverInfo.png[]

이 취약점에서는 Level을 2로 설정하여 pDriverInfo 매개변수의 유형을 DRIVER_INFO_2 구조체로 선택합니다. 그런 다음 Windows는 함수 매개변수를 처리하고, 처리가 완료되면 원격 프로시저 호출을 통해 API를 계속 처리합니다:

image::set arguments.png[]

image::NdrClientCall3.png[]

=== MSRPC 메커니즘

Microsoft의 원격 프로시저 호출 메커니즘은 DCE 표준을 기반으로 구축되었습니다. 쉽게 설명하면, 원격 프로시저 호출은 원격 시스템에서 프로세스를 실행하는 것이며, 이러한 프로세스는 프로그래머 또는 시스템에 의해 미리 정의됩니다.

RPC의 구체적인 방법은 원격으로 호출하려는 함수를 직렬화하여 네트워크를 통해 원격 시스템으로 전송하고, 원격 시스템에서 이를 역직렬화하여 실행하는 것입니다. Microsoft가 구축한 시스템에서 TCP/IP와 SMB는 일반적으로 RPC 호출을 전송하는 데 선택되는 프로토콜입니다.

MSRPC를 사용하려면 먼저 호출할 함수의 IDL 인터페이스 설명을 정의하고, MIDL 도구를 사용하여 클라이언트와 서버에 해당하는 직렬화 프로그램 스텁을 생성해야 합니다. 일부 Win32 API의 경우 서버 스텁이 이미 정의되어 있으므로 클라이언트 스텁만 생성하여 사용하면 됩니다.

MSRPC는 UUID를 사용하여 특정 유형의 프로토콜을 식별합니다. 예를 들어 MS-RPRN은 원격 인쇄 프로토콜을 설명하는 데 사용되며, 원격 인쇄와 관련된 모든 함수는 이 프로토콜의 일부입니다. MSPRC는 UUID 12345678-1234-ABCD-EF00-0123456789AB를 사용하여 이 프로토콜을 식별합니다(<<rprn_uuid>>):

[[rprn_uuid]] .MS-RPRN UUID image::spoolss uuid.png[]

그런 다음 이 연결을 기반으로 연산자 번호를 사용하여 프로토콜 내 함수를 식별하여 원격으로 호출할 수 있습니다. 예를 들어 AddPrinterDriverEx는 89를 사용하여 자신을 식별합니다(<<addPrinterDriverEx_opnum>>):

[[addPrinterDriverEx_opnum]] .AddPrinterDriverEx Opnum image::AddPrinterDriverEx Opnum.png[]

MSRPC 사용 과정에서 두 가지 사항에 주의해야 합니다:

[IMPORTANT]

  • TCP/IP 연결은 동적 포트를 사용하며, 135번 포트에서 수신 대기하는 엔드포인트 매퍼를 통해 포트 값을 가져와야 합니다.

"As you can see in your output, the scripts are trying to connect to port 135 (endpoint mapper) in order to get the TCP/IP port where the DCOM endpoint is listening (that is a dynamic port)." -- SecureAuthCorp/impacket issue #412

  • 또한 일부 RPC 함수는 호출 시 인증이 필요하므로, 이 취약점은 일반 사용자 권한이 필요합니다. ====

=== spoolsv.exe가 API 요청 처리

[[call_flow]] .RpcAddPrinterDriverEx 호출 흐름 image::Function Calls.png[]

<<call_flow>>에서 볼 수 있듯이, spoolsv.exe는 이러한 함수들을 호출하며, 함수 내부 분석 결과 이 모듈은 초기화 외에는 완료하는 작업이 없습니다. 마지막으로, 이 모듈은 pLocalProvidor가 가리키는 함수, 즉 localspl.dll 모듈 내의 LocalAddPrinterDriverEx 함수를 호출합니다. localspl은 로컬 인쇄 공급자로서 실제로 API 기능을 구현하는 모듈입니다.

=== 로컬 인쇄 공급자의 함수 구현 로직

[[LocalAddPrinterDriverEx]] .LocalAddPrinterDriverEx image::LocalAddPrinterDriverEx.png[]

먼저 <>는 이 모듈이 스풀러가 정상 작동하는지 확인한 후 함수 SplAddPrinterDriverEx로 이동함을 보여줍니다.

[[SplAddPrinterDriverEx]] .SplAddPrinterDriverEx image::SplAddPrinterDriverEx.png[]

<> 함수 내부는 AddPrinterDriverEx 함수가 성공적으로 실행될 수 있는지 판단하는 중요한 위치입니다. 전반부는 볼 필요가 없습니다. WPP는 로그 관련 기술이므로 일단 건너뜁니다.

후반부에서 Microsoft는 변수 v12를 정의했으며, 이는 함수가 계속 실행될지 아니면 직접 종료될지를 판단하는 플래그입니다.

[[bittest]] .bittest dwFileCopyFlags image::bittest in spl.png[]

<>에서 볼 수 있듯이, 계속 실행 조건은 두 가지입니다: 하나는 v12가 0인 경우(bittest 성공), 다른 하나는 Validate 성공입니다. Validate는 권한 검증으로, 쉽게 우회할 수 없습니다. 내부에 OpenProcessToken API가 있어 다음 프로세스에서 권한을 상승시켜야 하며, 관리자 계정이 아니면 이를 수행할 수 없습니다.

따라서 계속 실행하려면 bittest 판단을 우회해야 하며, 판단 대상인 a4는 함수의 네 번째 매개변수로, 공식 문서에 설명된 매개변수 <>입니다:

|=== |이름/값 |설명

|APD_STRICT_UPGRADE

0x00000001

|현재 설치된 드라이버의 모든 파일보다 교체 드라이버의 파일이 더 오래되지 않은 경우에만 교체 프린터 드라이버를 추가합니다.

|APD_STRICT_DOWNGRADE

0x00000002

|현재 설치된 드라이버의 모든 파일이 교체 드라이버의 해당 파일보다 더 오래되지 않은 경우에만 교체 프린터 드라이버를 추가합니다.

|APD_COPY_ALL_FILES

0x00000004

|프린터 드라이버를 추가하고 드라이버 디렉터리의 모든 파일을 복사합니다. 파일 타임스탬프는 무시해야 합니다.

|APD_COPY_NEW_FILES

0x00000008

|프린터 드라이버를 추가하고 드라이버 디렉터리에서 현재 사용 중인 해당 파일보다 최신인 파일을 복사합니다.

|APD_COPY_FROM_DIRECTORY

0x00000010

|_DRIVER_INFO_6 구조체에 지정된 정규화된 파일 이름을 사용하여 프린터 드라이버를 추가합니다. 이 플래그가 지정된 경우 이 비트 필드의 다른 복사 플래그 중 하나를 지정해야 합니다.

|APD_DONT_COPY_FILES_TO_CLUSTER

0x00001000

|프린트 서버 클러스터에 프린터 드라이버를 추가할 때 드라이버 파일을 공유 클러스터 디스크에 복사하지 않습니다.

|APD_COPY_TO_ALL_SPOOLERS

0x00002000

|클러스터 스풀러 서버에 프린터 드라이버를 추가합니다.

|APD_INSTALL_WARNED_DRIVER

0x00008000

|서버의 경고 프린터 드라이버 목록에 있더라도 프린터 드라이버를 추가합니다.

|APD_RETURN_BLOCKING_STATUS_CODE

0x00010000

|서버 정책에 의해 프린터 드라이버 설치가 차단된 경우 반환할 구현별 오류 코드를 지정합니다.

|===

bittest 16은 변수의 16번째 비트가 1인지 확인하며, 해당 매개변수 값은 0x8000(APD_INSTALL_WARNED_DRIVER)입니다. 정의에 따르면, 이 매개변수는 프린터 드라이버를 검증 없이 서버에 추가하는 것을 의미합니다.

1675가 수정되기 전까지 이 매개변수는 공식 문서에 나타나지 않았다고 합니다. 취약점의 위치를 쉽게 알 수 있습니다.

=== 취약점 악용 방법

프린터 드라이버 추가 메서드가 실제로 실행될 때 DRIVER_INFO_2 구조체를 선택하면 다음 작업이 발생합니다:

. DriverFile, ConfigFile 및 DataFile을 각각 열어 세 파일이 존재하는지 확인합니다. 그중 DataFile만 UNC 경로가 허용됩니다.

. 세 파일이 모두 존재하면 C:\Windows\System32\spool\drivers\x64\3\New 디렉터리로 복사합니다(<<cp_conf_file>>, <<cp_data_file>> 참조): + [[cp_conf_file]] .Config 파일 복사 image::copy config file.png[] + [[cp_data_file]] .Data 파일 복사 image::copy data file.png[]

. 이 디렉터리로 복사하는 이유는 해당 파일을 실행하기 위함이며, 3은 프린터 드라이버가 v3 유형임을 나타냅니다. 먼저 새 파일을 New 디렉터리에 복사하여 3 디렉터리의 파일이 덮어쓰이는 것을 방지합니다. 3 디렉터리에 동일한 이름의 파일이 있으면 해당 파일을 Old 디렉터리로 백업한 후 New의 파일을 3 디렉터리로 복사하여 덮어씁니다. 이는 RpcAddPrinterDriverEx 메서드를 두 번째로 실행할 때 관찰할 수 있습니다: + .두 번째 RPC 호출 image::second time call.png[] + .Old 디렉터리로 파일 백업 image::backup file.png[] + .대상으로 새 파일 복사 image::copy file.png[] + 이 메커니즘에 따라 원격 경로의 파일을 로컬 경로의 파일로 저장할 수 있습니다. 함수 매개변수에서 드라이버 파일과 구성 파일 매개변수는 로컬 경로만 허용되며, 데이터 파일 매개변수만 원격 경로가 허용되기 때문입니다.

. <>에 따르면 pConfigFile은 장치 드라이버의 구성 동적 링크 라이브러리이므로 초기화를 위해 한 번 로드해야 합니다. 이는 실제 실행 상황에서도 확인되었습니다. + .pConfigFile 로드 image::Load Image.png[] + 따라서 악성 DLL을 작성하여 DLL 진입점에 악성 코드를 배치하면 관리자 권한으로 임의 코드를 실행할 수 있습니다. + .spoolsv.exe는 관리자 권한으로 실행됨 image::spoolsv user.png[]

. CreateInternalDriverFileArray() 함수는 파일 작업 플래그에 따라 스풀 드라이버 디렉터리를 검사할지 결정합니다. a5 플래그가 False로 설정된 경우 드라이버 로드 함수는 사용자 디렉터리에 복사할 드라이버 파일이 포함되어 있는지만 확인합니다. 그렇지 않으면 함수는 스풀 드라이버 디렉터리에서 대상 드라이버를 찾으려고 시도합니다. 이를 위해서는 dwFileCopyFlags에 APD_COPY_FROM_DIRECTORY 매개변수를 함께 설정해야 합니다. + image::APD.png[] + image::APD_1.png[]

ifdef::backend-pdf[] 악성 프로그램 실행 결과는 https://github.com/hahaleyile/my-CVE-2021-1675[내 저장소]를 참조하십시오. 데모 gif는 Figures 디렉터리의 gif 파일입니다. endif::[]

== Microsoft의 1675 취약점 패치

2021년 6월 8일, Microsoft는 CVE-2021-1675 취약점에 대한 패치를 적용했습니다. 구체적인 수정 사항은 다음과 같습니다:

[[path_1675]] .CVE-2021-1675 패치 image::IsElevated.png[]

[[YIsElevationRequired]] .YIsElevationRequired image::YIsElevationRequired.png[]

[[YIsElevated]] .YIsElevated image::YIsElevated.png[]

[[unset_1675]] .APD_INSTALL_WARNED_DRIVER 해제 image::JudgeIsElevated.png[]

Microsoft는 RpcAddPrinterDriverEx 함수에 사용자 권한 상승 확인을 추가했으며, 사용자는 레지스트리에서 이 제한을 제거할 수 있습니다(<<path_1675>>). 사용자는 레지스트리의 지정된 위치에 NoWaringNoElevationOnInstall이라는 키를 생성하거나(<>), RPC 계정이 TOKEN_QUERY 프로세스 토큰을 얻을 수 있으면(<>) 이 패치를 우회할 수 있습니다. 패치가 적용되면 dwFileCopyFlags 매개변수의 16번째 비트가 AND 연산으로 0이 되어 APD_INSTALL_WARNED_DRIVER 매개변수 값이 무효화됩니다(<<unset_1675>>).

== 1675 패치 우회

Microsoft가 RpcAddPrinterDriverEx 함수에 패치를 적용했지만, 여전히 RpcAsyncAddPrinterDriver를 통한 원격 호출로 우회할 수 있습니다. <<async_send>>와 같이 클라이언트의 이 함수는 서버에 대한 직접적인 원격 호출입니다:

[[async_send]] .RpcAsyncAddPrinterDriver 전송 image::RpcAsyncAddPrinterDriver Send.png[]

서버는 먼저 스레드에 공간을 할당한 다음 계속 호출합니다(<<async_receive>> 참조):

[[async_receive]] .RpcAsyncAddPrinterDriver 수신 image::RpcAsyncAddPrinterDriver Receive.png[]

계속 호출하는 함수는 모든 매개변수를 스택에 푸시하고, 스레드 방식으로 YAddPrinterDriverEx를 실행합니다(<<async_yadd>>):

[[async_yadd]] .스레드 시작 YAddPrinterDriverEx image::thread start YAddPrinterDriverEx.png[]

이렇게 하면 Microsoft의 RpcAddPrinterDriverEx 패치를 성공적으로 우회할 수 있습니다.

즉, RpcAsyncAddPrinterDriver 함수를 원격으로 호출하면 계속 관리자 권한으로 임의 코드를 실행할 수 있습니다.

또한 <>에 따르면 이 패치는 Token 검증에 문제가 있으며, UAC가 완전히 비활성화된 머신에서는 패치가 무효화된다고 합니다. 그러나 저는 이 메커니즘에 대해 잘 알지 못하므로 더 이상 언급하지 않겠습니다.

== Microsoft의 34527 취약점 수정

2021년 7월 6일, Microsoft는 새로운 패치를 통해 이번 백그라운드 인쇄 취약점 문제를 임시로 해결했습니다.

공식 성명에 따르면 이번 패치는 인쇄 서버에 프린터 드라이버를 설치할 때 관리자만 수행할 수 있도록 합니다. 또한 Microsoft는 사용자가 이 정책을 사용자 정의할 수 있도록 하나의 그룹 정책과 두 개의 레지스트리 키를 추가했습니다.

<<restrict_async>>의 IDA 역컴파일을 통해 Microsoft가 Async 함수에 Token 및 레지스트리 항목 검증을 새로 추가한 것을 볼 수 있습니다.

[[restrict_async]] .async 함수의 제한 image::restrict in async.png[]

<<restrict_rpcadd>>를 통해 Microsoft가 RpcAddPrinterDriverEx 함수에 사용자 그룹 및 해당 레지스트리 키에 대한 검증을 추가한 것을 알 수 있습니다.

[[restrict_rpcadd]] .RpcAddPrinterDriverEx 함수의 제한 image::restrict in rpcadd.png[]

따라서 1675 취약점 수정에는 Token 검증 문제가 여전히 존재하는 것으로 보입니다.

== 프로젝트 계획

. RpcAsyncAddPrinterDriver 원격 호출 지원을 추가하여 Microsoft의 6월 8일 패치를 우회하고 CVE-2021-34527 exp를 구현합니다.

. UAC 메커니즘을 이해하고 이 취약점 분석을 보완합니다.

[bibliography] == 참고 문헌

도구 다운로드

[TIP]

요약하면, 악성 코드의 악용 방안은 다음과 같습니다: 악성 DLL을 configfile로 초기화하면 관리자 권한으로 임의 코드를 실행할 수 있습니다. 공격자 호스트에서 삼바 공유를 열고 피해자가 datafile 형식으로 해당 악성 프로그램을 로컬로 다운로드하여 실행하도록 하면 됩니다.

== 악용 프로그램 사용 방법

이 프로그램은 docker 기반으로 구축되어 환경 의존성 문제를 효과적으로 해결할 수 있습니다.

먼저 사용자는 compose 파일을 자신의 디렉터리에 다운로드한 후, 해당 디렉터리에 share 폴더를 생성하여 마운트 디렉터리로 사용합니다. 사용자는 악성 프로그램을 share 폴더에 넣을 수 있으며, 이 폴더는 삼바 경로 smb로 공유됩니다.

그런 다음 사용자는 터미널에 다음 명령어를 입력합니다

[source,docker]

docker-compose up -d

그러면 클러스터(하나의 컨테이너)가 시작됩니다. 사용자는 컨테이너 내부에 직접 들어가서 작업할 수 있으며, 컨테이너 내의 환경은 모두 설정되어 있습니다.

또는 터미널에 직접 다음 명령어를 입력하여 명령을 실행할 수도 있습니다

[source,docker]

docker-compose run my_cve python main.py -h

== 악용 프로그램 실행 결과

참고를 위해 <>가 오픈소스로 제공한 코드에 감사드립니다!

ifndef::backend-pdf[] .프로그램 실행 결과 image::exploit.gif[] endif::[]

  • [[[a,공식 웹사이트]]] Windows Print Spooler Remote Code Execution Vulnerability https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2021-34527

  • [[[b,dwFileCopyFlags]]] https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/b96cc497-59e5-4510-ab04-5484993b259b

  • [[[c,Windows PrintNightmare 취약점(CVE-2021-34527) 및 패치 분석]]] https://www.freebuf.com/vuls/279876.html

  • [[[d,gentilkiwi/mimikatz]]] https://github.com/gentilkiwi/mimikatz

  • [[[e,공식 문서]]] DRIVER_INFO_2 structure https://docs.microsoft.com/en-us/windows/win32/printdocs/driver-info-2

  • [[[f,cube0x0]]] https://github.com/cube0x0

  • [[[g,James Forshaw]]] https://twitter.com/tiraniddo/status/1410726790994169857

  • [[[h,공식 성명]]] KB5005010: Restricting installation of new printer drivers after applying the July 6, 2021 updates https://support.microsoft.com/en-us/topic/kb5005010-restricting-installation-of-new-printer-drivers-after-applying-the-july-6-2021-updates-31b91c02-05bc-4ada-a7ea-183b129578a7