Skip to content

Commit 20e3a96

Browse files
authored
Merge pull request #2426 from dotnet/dev/arpit/release30_1b
Description Loose xaml can contain executable payload e.g. ObjectDataProvider. This Xaml can be included as part of XpsDocuments or base-64 encoded and then included in StickyNotes' annotation xml files. In WPF, we were allowing XpsDocuments and StickyNotes' annotation xml files to be loaded freely via XamlReader.Load. This exposes an attack vector - when a user downloads an XPS file from the internet for viewing, they could end up executing untrusted code. The fix is to identify known dangerous types and limit them from being deserialized during XAML loading. In order to accomplish this, we add new non-public overloads to the XamlReader.Load method to enable the use of RestrictiveXamlXmlReader. RestrictiveXamlXmlReader restricts known dangerous types from being loaded while deserializing xaml. We then call XamlReader.Load via XamlReaderProxy, which is an adapter for XamlReader type and uses reflection to access XamlReader.Load. Reflection is used to avoid adding additional public surface area to XamlReader in servicing. Small changes are made to TextRange as well since the call-site for the StickyNotes case was through a call to TextRange which in turn calls into XamlReader.Load. Customer Impact Customers would be protected from opening potentially-compromised XPS documents and stickynotes annotation xml files. Regression No. This security issue was reported by an external party. Risk - Low o This change only affects loading XPS documents and loading stickynotes annotation data. o The change has been tested well internally. o We ran regression tests to ensure nothing else is inadvertently broken. o Validated against POC to ensure that the fix works as intended. In .NET Framework, we are introducing a quirk to give developers/cusotmers the option of going back to the old (i.e., unsecure) behavior where deserializing dangerous types like ObjectDataProvider will be allowed. In .NET Core, no quirks are being provided - we do not believe that this is a scenario that should be supported for compatibility in a relatively new platform.
2 parents 6f48cff + 867a524 commit 20e3a96

File tree

6 files changed

+100
-6
lines changed

6 files changed

+100
-6
lines changed

src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Controls/StickyNote/StickyNoteContentControl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ public override void Load(XmlNode node)
234234
RichTextBox richTextBox = (RichTextBox)InnerControl;
235235

236236
FlowDocument document = new FlowDocument();
237-
TextRange rtbRange = new TextRange(document.ContentStart, document.ContentEnd);
237+
TextRange rtbRange = new TextRange(document.ContentStart, document.ContentEnd, useRestrictiveXamlXmlReader:true);
238238
using (MemoryStream buffer = new MemoryStream(Convert.FromBase64String(node.InnerText)))
239239
{
240240
rtbRange.Load(buffer, DataFormats.Xaml);

src/Microsoft.DotNet.Wpf/src/PresentationFramework/PresentationFramework.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
<Compile Include="$(WpfSharedDir)\MS\Utility\ItemMap.cs" />
6767
<Compile Include="$(WpfSharedDir)\System\Windows\Interop\OSVersionHelper.cs" />
6868
<Compile Include="$(WpfSharedDir)\System\Windows\Interop\OperatingSystemVersion.cs" />
69+
<Compile Include="$(WpfSharedDir)\MS\Internal\Markup\XamlReaderProxy.cs " />
6970
<Compile Include="$(WpfSharedDir)\Telemetry\Managed\EventSourceActivity.cs" />
7071
<Compile Include="$(WpfSharedDir)\Telemetry\Managed\TelemetryEventSource.cs" />
7172
<Compile Include="$(WpfSharedDir)\Telemetry\Managed\TraceLoggingProvider.cs" />

src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/TextRange.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using System.Xml;
1414
using System.IO;
1515
using System.Windows.Markup; // Parser
16+
using MS.Internal.PresentationFramework.Markup;
1617

1718
#pragma warning disable 1634, 1691 // suppressing PreSharp warnings
1819

@@ -87,6 +88,14 @@ internal TextRange(ITextPointer position1, ITextPointer position2, bool ignoreTe
8788
ValidationHelper.VerifyPosition(position1.TextContainer, position2, "position2");
8889

8990
TextRangeBase.Select(this, position1, position2);
91+
}
92+
93+
// useRestrictiveXamlXmlReader - false by default
94+
// set to true to disable external xaml loading in specific scenarios like StickyNotes annotation loading
95+
internal TextRange(TextPointer position1, TextPointer position2, bool useRestrictiveXamlXmlReader) :
96+
this((ITextPointer)position1, (ITextPointer)position2)
97+
{
98+
_useRestrictiveXamlXmlReader = useRestrictiveXamlXmlReader;
9099
}
91100

92101
#endregion Constructors
@@ -1364,9 +1373,9 @@ internal string Xml
13641373
// so we use virtual mechanism for extensibility in TextSelection
13651374
TextRangeBase.BeginChange(this);
13661375
try
1367-
{
1368-
// Parse the fragment into a separate subtree
1369-
object xamlObject = XamlReader.Load(new XmlTextReader(new System.IO.StringReader(value)));
1376+
{
1377+
// Parse the fragment into a separate subtree
1378+
object xamlObject = XamlReaderProxy.Load(new XmlTextReader(new System.IO.StringReader(value)), _useRestrictiveXamlXmlReader);
13701379
TextElement fragment = xamlObject as TextElement;
13711380

13721381
if (fragment != null)
@@ -1900,6 +1909,9 @@ private enum Flags
19001909
// Boolean flags, set with Flags enum.
19011910
private Flags _flags;
19021911

1912+
//boolean flag, set to true via constructor when you want to use the restrictive xamlxmlreader
1913+
private bool _useRestrictiveXamlXmlReader;
1914+
19031915
#endregion Private Fields
19041916
}
19051917
}

src/Microsoft.DotNet.Wpf/src/ReachFramework/Packaging/XpsDocument.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ reading and/or writing Xps packages.
3131
using System.Xml;
3232
using System.Security;
3333
using MS.Internal;
34-
using MS.Internal.Security;
3534
using MS.Internal.IO.Packaging;
35+
using MS.Internal.ReachFramework.Markup;
36+
using MS.Internal.Security;
3637

3738
using MS.Internal.IO.Packaging.Extensions;
3839
using Package = System.IO.Packaging.Package;
@@ -629,7 +630,7 @@ XpsImageType imageType
629630

630631
parserContext.BaseUri = PackUriHelper.Create(Uri, CurrentXpsManager.StartingPart.Uri);
631632

632-
object fixedObject = XamlReader.Load(CurrentXpsManager.StartingPart.GetStream(), parserContext);
633+
object fixedObject = XamlReaderProxy.Load(CurrentXpsManager.StartingPart.GetStream(), parserContext, useRestrictiveXamlReader:true);
633634
if (!(fixedObject is FixedDocumentSequence) )
634635
{
635636
throw new XpsPackagingException(SR.Get(SRID.ReachPackaging_NotAFixedDocumentSequence));

src/Microsoft.DotNet.Wpf/src/ReachFramework/ReachFramework.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
<Compile Include="$(WpfSharedDir)\MS\Internal\SecurityHelper.cs" />
5656
<Compile Include="$(WpfSharedDir)\MS\Internal\UriComparer.cs " />
5757
<Compile Include="$(WpfSharedDir)\MS\Utility\BindUriHelper.cs" />
58+
<Compile Include="$(WpfSharedDir)\MS\Internal\Markup\XamlReaderProxy.cs " />
5859
<Compile Include="SR.cs" />
5960
<Compile Include="Serialization\Manager\IXpsSerializationManager.cs" />
6061
<Compile Include="Serialization\Manager\IXpsSerializationManagerAsync.cs" />
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
//
6+
// Description: This helper class reflects into internal overloads of XamlReader.Load to use the RestrictiveXamlXmlReader
7+
// to avoid loading unsafe loose xaml.
8+
//
9+
10+
using System;
11+
using System.IO;
12+
using System.Xml;
13+
using System.Reflection;
14+
using System.Windows.Markup;
15+
16+
#if REACHFRAMEWORK
17+
namespace MS.Internal.ReachFramework
18+
#elif PRESENTATIONFRAMEWORK
19+
namespace MS.Internal.PresentationFramework
20+
#else
21+
namespace MS.Internal
22+
#endif
23+
{
24+
namespace Markup
25+
{
26+
/// <summary>
27+
/// Provides a helper class to create delegates to reflect into XamlReader.Load
28+
/// </summary>
29+
internal class XamlReaderProxy
30+
{
31+
/// <summary>
32+
/// The static constructor creates and stores delegates for overloads of <see cref="XamlReader.Load"/> that need to be reflected into
33+
/// so that we can safeguard entry-points for external xaml loading.
34+
/// </summary>
35+
/// <remark> Doing this in the static constructor guarantees thread safety.</remark>
36+
37+
static XamlReaderProxy()
38+
{
39+
MethodInfo method = _xamlReaderType.GetMethod(XamlLoadMethodName, BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(Stream), typeof(ParserContext), typeof(bool) }, null);
40+
41+
if (method == null)
42+
{
43+
throw new MissingMethodException(XamlLoadMethodName);
44+
}
45+
46+
_xamlLoad3 = (XamlLoadDelegate3)method.CreateDelegate(typeof(XamlLoadDelegate3));
47+
48+
method = _xamlReaderType.GetMethod(XamlLoadMethodName, BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(XmlReader), typeof(bool) }, null);
49+
50+
if (method == null)
51+
{
52+
throw new MissingMethodException(XamlLoadMethodName);
53+
}
54+
55+
_xamlLoad2 = (XamlLoadDelegate2)method.CreateDelegate(typeof(XamlLoadDelegate2));
56+
}
57+
58+
public static object Load(Stream stream, ParserContext parserContext, bool useRestrictiveXamlReader)
59+
{
60+
return _xamlLoad3.Invoke(stream, parserContext, useRestrictiveXamlReader);
61+
}
62+
63+
public static object Load(XmlReader reader, bool useRestrictiveXamlReader)
64+
{
65+
return _xamlLoad2.Invoke(reader, useRestrictiveXamlReader);
66+
}
67+
68+
private delegate object XamlLoadDelegate3(Stream stream, ParserContext parserContext, bool useRestrictiveXamlReader);
69+
private static XamlLoadDelegate3 _xamlLoad3;
70+
71+
private delegate object XamlLoadDelegate2(XmlReader reader, bool useRestrictiveXamlReader);
72+
private static XamlLoadDelegate2 _xamlLoad2;
73+
74+
private static readonly Type _xamlReaderType = typeof(XamlReader);
75+
private const string XamlLoadMethodName = nameof(Load);
76+
77+
}
78+
}
79+
}

0 commit comments

Comments
 (0)