Skip to content

Commit

Permalink
[ASM] iast: Tainting of DefaultInterpolatedStringHandler (#6340)
Browse files Browse the repository at this point in the history
## Summary of changes

This PR introduce the support of `DefaultInterpolatedStringHandler` for
IAST.
The resulting strings of `DefaultInterpolatedStringHandler` will now be
tainted.

## Reason for change

Since the release of .NET Core 6, interpolated strings got a performance
optimisation and now use
[DefaultInterpolatedStringHandler](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.defaultinterpolatedstringhandler?view=net-6.0)
to build strings.

Some vulnerabilities couldn't be detected because strings built with
`DefaultInterpolatedStringHandler` weren't tainted.

## Implementation details

As `DefaultInterpolatedStringHandler` is a ref struct, we call some IL
to get it's own stack pointer value and taint it.
We need to get that pointer value to track it and its tainted sources.

## Test coverage

- New unit tests were added:
- testing all aspects with an explicit call to the
`DefaultInterpolatedStringHandler` ref struct
- testing implicit interpolated strings (`$""`) in various complex cases
- Aspects tests added for `IastInstrumentationUnitTests`

## Other details

This PR don't handle the correct values for `start` and `length` of
tainted Sources.

---------

Co-authored-by: Daniel Romano <108014683+daniel-romano-DD@users.noreply.github.com>
  • Loading branch information
e-n-0 and daniel-romano-DD authored Nov 28, 2024
1 parent 57b69bb commit b677deb
Show file tree
Hide file tree
Showing 10 changed files with 821 additions and 5 deletions.
14 changes: 13 additions & 1 deletion tracer/build/_build/CodeGenerators/CallSitesGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,26 @@ internal static void RetrieveCallSites(Dictionary<string, AspectClass> aspectCla

static string GetMethodName(MethodDefinition method)
{
var fullName = method.FullName;
var parameters = string.Join(",", method.Parameters.Select(GetParameter));
var fullName = $"{method.FullName.Substring(0, method.FullName.IndexOf('('))}({parameters})";

var methodNameStart = fullName.IndexOf("::");
if (methodNameStart < 0)
{
throw new InvalidOperationException("Could not find '::' in method name " + fullName);
}

return fullName.Substring(methodNameStart + 2).Replace("<T>", "<!!0>").Replace("&", "");

static string GetParameter(ParameterDefinition parameter)
{
var paramType = parameter.ParameterType.FullName;
return paramType switch
{
"T" => "!!0",
_ => paramType.Replace("<T>", "<!!0>"),
};
}
}

static bool IsAspectClass(Mono.Cecil.CustomAttribute attribute)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
// <copyright file="DefaultInterpolatedStringHandlerAspect.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

#if NET6_0_OR_GREATER

#nullable enable

using System;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using Datadog.Trace.Iast.Dataflow;
using Datadog.Trace.Iast.Propagation;
using InlineIL;
using static InlineIL.IL.Emit;

namespace Datadog.Trace.Iast.Aspects.System.Runtime;

/// <summary> DefaultInterpolatedString class aspect </summary>
[AspectClass("System.Runtime")]
[global::System.ComponentModel.Browsable(false)]
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public class DefaultInterpolatedStringHandlerAspect
{
/// <summary>
/// System.Runtime DefaultInterpolatedStringHandler.AppendFormatted(String) aspect
/// </summary>
/// <param name="target"> the ref DefaultInterpolatedStringHandler </param>
/// <param name="value"> the string value </param>
[AspectMethodReplace("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted(System.String)")]
public static void AppendFormatted1(ref DefaultInterpolatedStringHandler target, string value)
{
target.AppendFormatted(value);
try
{
DefaultInterpolatedStringHandlerModuleImpl.Append(ToPointer(ref target), value);
}
catch (Exception ex)
{
IastModule.Log.Warning(ex, $"Error invoking {nameof(DefaultInterpolatedStringHandlerAspect)}.{nameof(AppendFormatted1)}");
}
}

/// <summary>
/// System.Runtime DefaultInterpolatedStringHandler.AppendFormatted(String, Int32, String) aspect
/// </summary>
/// <param name="target"> the ref DefaultInterpolatedStringHandler </param>
/// <param name="value"> the string value </param>
/// <param name="alignment"> the alignment value </param>
/// <param name="format"> the format value </param>
[AspectMethodReplace("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted(System.String,System.Int32,System.String)")]
public static void AppendFormatted2(ref DefaultInterpolatedStringHandler target, string? value, int alignment, string? format)
{
target.AppendFormatted(value, alignment, format);
try
{
DefaultInterpolatedStringHandlerModuleImpl.Append(ToPointer(ref target), value);
}
catch (Exception ex)
{
IastModule.Log.Warning(ex, $"Error invoking {nameof(DefaultInterpolatedStringHandlerAspect)}.{nameof(AppendFormatted2)}");
}
}

/// <summary>
/// System.Runtime DefaultInterpolatedStringHandler.AppendFormatted(Object, Int32, String) aspect
/// </summary>
/// <param name="target"> the ref DefaultInterpolatedStringHandler </param>
/// <param name="value"> the object value </param>
/// <param name="alignment"> the alignment value </param>
/// <param name="format"> the format value </param>
[AspectMethodReplace("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted(System.Object,System.Int32,System.String)")]
public static void AppendFormatted3(ref DefaultInterpolatedStringHandler target, object? value, int alignment, string? format)
{
target.AppendFormatted(value, alignment, format);
try
{
if (value is string str)
{
DefaultInterpolatedStringHandlerModuleImpl.Append(ToPointer(ref target), str);
}
}
catch (Exception ex)
{
IastModule.Log.Warning(ex, $"Error invoking {nameof(DefaultInterpolatedStringHandlerAspect)}.{nameof(AppendFormatted3)}");
}
}

/// <summary>
/// System.Runtime DefaultInterpolatedStringHandler.AppendFormatted(T) aspect
/// </summary>
/// <typeparam name="T">The first generic type parameter.</typeparam>
/// <param name="target"> the ref DefaultInterpolatedStringHandler </param>
/// <param name="value"> the string value </param>
[AspectMethodReplace("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted(!!0)")]
public static void AppendFormatted4<T>(ref DefaultInterpolatedStringHandler target, T value)
{
target.AppendFormatted<T>(value);
try
{
if (value is string str)
{
DefaultInterpolatedStringHandlerModuleImpl.Append(ToPointer(ref target), str);
}
}
catch (Exception ex)
{
IastModule.Log.Warning(ex, $"Error invoking {nameof(DefaultInterpolatedStringHandlerAspect)}.{nameof(AppendFormatted4)}");
}
}

/// <summary>
/// System.Runtime DefaultInterpolatedStringHandler.AppendFormatted(T, String) aspect
/// </summary>
/// <typeparam name="T">The first generic type parameter.</typeparam>
/// <param name="target"> the ref DefaultInterpolatedStringHandler </param>
/// <param name="value"> the string value </param>
/// <param name="alignment"> the alignment value </param>
[AspectMethodReplace("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted(!!0,System.Int32)")]
public static void AppendFormatted5<T>(ref DefaultInterpolatedStringHandler target, T value, int alignment)
{
target.AppendFormatted(value, alignment);
try
{
if (value is string str)
{
DefaultInterpolatedStringHandlerModuleImpl.Append(ToPointer(ref target), str);
}
}
catch (Exception ex)
{
IastModule.Log.Warning(ex, $"Error invoking {nameof(DefaultInterpolatedStringHandlerAspect)}.{nameof(AppendFormatted5)}");
}
}

/// <summary>
/// System.Runtime DefaultInterpolatedStringHandler.AppendFormatted(T, String) aspect
/// </summary>
/// <typeparam name="T">The first generic type parameter.</typeparam>
/// <param name="target"> the ref DefaultInterpolatedStringHandler </param>
/// <param name="value"> the string value </param>
/// <param name="format"> the format value </param>
[AspectMethodReplace("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted(!!0,System.String)")]
public static void AppendFormatted6<T>(ref DefaultInterpolatedStringHandler target, T value, string? format)
{
target.AppendFormatted(value, format);
try
{
if (value is string str)
{
DefaultInterpolatedStringHandlerModuleImpl.Append(ToPointer(ref target), str);
}
}
catch (Exception ex)
{
IastModule.Log.Warning(ex, $"Error invoking {nameof(DefaultInterpolatedStringHandlerAspect)}.{nameof(AppendFormatted6)}");
}
}

/// <summary>
/// System.Runtime DefaultInterpolatedStringHandler.AppendFormatted(T, String) aspect
/// </summary>
/// <typeparam name="T">The first generic type parameter.</typeparam>
/// <param name="target"> the ref DefaultInterpolatedStringHandler </param>
/// <param name="value"> the string value </param>
/// <param name="alignment"> the alignment value </param>
/// <param name="format"> the format value </param>
[AspectMethodReplace("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted(!!0,System.Int32,System.String)")]
public static void AppendFormatted7<T>(ref DefaultInterpolatedStringHandler target, T value, int alignment, string? format)
{
target.AppendFormatted(value, alignment, format);
try
{
if (value is string str)
{
DefaultInterpolatedStringHandlerModuleImpl.Append(ToPointer(ref target), str);
}
}
catch (Exception ex)
{
IastModule.Log.Warning(ex, $"Error invoking {nameof(DefaultInterpolatedStringHandlerAspect)}.{nameof(AppendFormatted7)}");
}
}

/// <summary>
/// System.Runtime DefaultInterpolatedStringHandler.AppendLiteral(String) aspect
/// </summary>
/// <param name="target"> the ref DefaultInterpolatedStringHandler </param>
/// <param name="value"> the string value </param>
[AspectMethodReplace("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendLiteral(System.String)")]
public static void AppendLiteral(ref DefaultInterpolatedStringHandler target, string value)
{
target.AppendLiteral(value);
try
{
DefaultInterpolatedStringHandlerModuleImpl.Append(ToPointer(ref target), value);
}
catch (Exception ex)
{
IastModule.Log.Warning(ex, $"Error invoking {nameof(DefaultInterpolatedStringHandlerAspect)}.{nameof(AppendLiteral)}");
}
}

/// <summary>
/// System.Runtime DefaultInterpolatedStringHandler.ToStringAndClear aspect
/// </summary>
/// <param name="target"> the ref DefaultInterpolatedStringHandler </param>
/// <returns> the string value </returns>
[AspectMethodReplace("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::ToStringAndClear()")]
public static string ToStringAndClear(ref DefaultInterpolatedStringHandler target)
{
var result = target.ToStringAndClear();
try
{
DefaultInterpolatedStringHandlerModuleImpl.PropagateTaint(ToPointer(ref target), result);
}
catch (Exception ex)
{
IastModule.Log.Warning(ex, $"Error invoking {nameof(DefaultInterpolatedStringHandlerAspect)}.{nameof(ToStringAndClear)}");
}

return result;
}

private static IntPtr ToPointer(ref DefaultInterpolatedStringHandler ts)
{
Ldarg(nameof(ts));
return IL.Return<IntPtr>();
}
}

#endif
15 changes: 13 additions & 2 deletions tracer/src/Datadog.Trace/Iast/DefaultTaintedMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,23 @@ public DefaultTaintedMap()
}

_map.TryGetValue(IndexObject(objectToFind), out var entry);
bool isString = objectToFind is string;

while (entry != null)
{
if (objectToFind == entry.Value)
if (isString)
{
return entry;
if (objectToFind == entry.Value)
{
return entry;
}
}
else
{
if (objectToFind.Equals(entry.Value))
{
return entry;
}
}

entry = entry.Next;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// <copyright file="DefaultInterpolatedStringHandlerModuleImpl.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

#if NET6_0_OR_GREATER

#nullable enable

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using Datadog.Trace.VendoredMicrosoftCode.System.Runtime.InteropServices;

namespace Datadog.Trace.Iast.Propagation;

internal static class DefaultInterpolatedStringHandlerModuleImpl
{
public static unsafe void Append(IntPtr target, string? value)
{
FullTaintIfAnyTainted(target, value);
}

public static unsafe void FullTaintIfAnyTainted(IntPtr target, string? input)
{
try
{
IastModule.OnExecutedPropagationTelemetry();

if (input is null)
{
return;
}

var iastContext = IastModule.GetIastContext();
if (iastContext is null)
{
return;
}

var taintedObjects = iastContext.GetTaintedObjects();
var tainted = PropagationModuleImpl.GetTainted(taintedObjects, target);
var targetIsTainted = tainted is not null;

if (!targetIsTainted && (tainted = GetTaintedWithRanges(taintedObjects, input)) is null)
{
return;
}

var rangesResult = new[] { new Range(0, 0, tainted!.Ranges[0].Source, tainted.Ranges[0].SecureMarks) };
if (!targetIsTainted)
{
taintedObjects.Taint(target, rangesResult);
}
else
{
tainted.Ranges = rangesResult;
}
}
catch (Exception error)
{
IastModule.Log.Error(error, $"{nameof(DefaultInterpolatedStringHandlerModuleImpl)}.{nameof(FullTaintIfAnyTainted)} exception");
}
}

public static object? PropagateTaint(object? input, string? result)
{
try
{
IastModule.OnExecutedPropagationTelemetry();

if (result is null || input is null)
{
return result;
}

var iastContext = IastModule.GetIastContext();
if (iastContext == null)
{
return result;
}

var taintedObjects = iastContext.GetTaintedObjects();
var taintedSelf = taintedObjects.Get(input);

if (taintedSelf == null)
{
return result;
}

var range = new Range(0, result.Length, taintedSelf.Ranges[0].Source, taintedSelf.Ranges[0].SecureMarks);
taintedObjects.Taint(result, [range]);
}
catch (Exception err)
{
IastModule.Log.Error(err, $"{nameof(DefaultInterpolatedStringHandlerModuleImpl)}.{nameof(PropagateTaint)} exception");
}

return result;
}

private static TaintedObject? GetTaintedWithRanges(TaintedObjects taintedObjects, object? value)
{
var tainted = PropagationModuleImpl.GetTainted(taintedObjects, value);
return tainted is not null && tainted?.Ranges.Length > 0 ? tainted : null;
}
}

#endif
Loading

0 comments on commit b677deb

Please sign in to comment.