Skip to content

Commit

Permalink
Initial implementation
Browse files Browse the repository at this point in the history
Fixed casing
  • Loading branch information
daniel-romano-DD committed Sep 27, 2024
1 parent e2d7112 commit fec2738
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 50 deletions.
74 changes: 34 additions & 40 deletions tracer/src/Datadog.Trace/Iast/IastModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ internal static partial class IastModule
private static readonly Func<TaintedObject, bool> Always = (x) => true;
private static readonly DbRecordManager DbRecords = new DbRecordManager(IastSettings);
private static bool _showTimeoutExceptionError = true;
private static SourceType[] _dbSources = [SourceType.SqlRowValue];

internal static void LogTimeoutError(RegexMatchTimeoutException err)
{
Expand All @@ -74,29 +75,6 @@ internal static void LogTimeoutError(RegexMatchTimeoutException err)
}
}

internal static bool NoDbSource(TaintedObject tainted)
{
// Reject any vuln with all ranges coming from a db source
try
{
foreach (var range in tainted.Ranges)
{
if (range.Source?.Origin != SourceType.SqlRowValue)
{
return true;
}
}

return false;
}
catch (Exception err)
{
Log.Error(err, "Error while checking for non Database tainted origins.");
}

return true;
}

internal static string? OnUnvalidatedRedirect(string? evidence)
{
if (Iast.Instance.Settings.Enabled && evidence != null && OnUnvalidatedRedirect(evidence, IntegrationId.UnvalidatedRedirect).VulnerabilityAdded)
Expand Down Expand Up @@ -176,7 +154,7 @@ internal static IastModuleResponse OnTrustBoundaryViolation(string name)
}

OnExecutedSinkTelemetry(IastInstrumentedSinks.TrustBoundaryViolation);
return GetScope(name, IntegrationId.TrustBoundaryViolation, VulnerabilityTypeName.TrustBoundaryViolation, OperationNameTrustBoundaryViolation, NoDbSource);
return GetScope(name, IntegrationId.TrustBoundaryViolation, VulnerabilityTypeName.TrustBoundaryViolation, OperationNameTrustBoundaryViolation, taintValidator: Always, safeSources: _dbSources);
}
catch (Exception ex)
{
Expand All @@ -195,7 +173,7 @@ internal static IastModuleResponse OnLdapInjection(string evidence)
}

OnExecutedSinkTelemetry(IastInstrumentedSinks.LdapInjection);
return GetScope(evidence, IntegrationId.Ldap, VulnerabilityTypeName.LdapInjection, OperationNameLdapInjection, NoDbSource);
return GetScope(evidence, IntegrationId.Ldap, VulnerabilityTypeName.LdapInjection, OperationNameLdapInjection, taintValidator: Always, safeSources: _dbSources);
}
catch (Exception ex)
{
Expand All @@ -214,7 +192,7 @@ internal static IastModuleResponse OnSSRF(string evidence)
try
{
OnExecutedSinkTelemetry(IastInstrumentedSinks.Ssrf);
return GetScope(evidence, IntegrationId.Ssrf, VulnerabilityTypeName.Ssrf, OperationNameSsrf, NoDbSource, exclusionSecureMarks: SecureMarks.Ssrf);
return GetScope(evidence, IntegrationId.Ssrf, VulnerabilityTypeName.Ssrf, OperationNameSsrf, taintValidator: Always, safeSources: _dbSources, exclusionSecureMarks: SecureMarks.Ssrf);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -252,7 +230,7 @@ public static IastModuleResponse OnPathTraversal(string evidence)
try
{
OnExecutedSinkTelemetry(IastInstrumentedSinks.PathTraversal);
return GetScope(evidence, IntegrationId.PathTraversal, VulnerabilityTypeName.PathTraversal, OperationNamePathTraversal, NoDbSource);
return GetScope(evidence, IntegrationId.PathTraversal, VulnerabilityTypeName.PathTraversal, OperationNamePathTraversal, taintValidator: Always, safeSources: _dbSources);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -310,7 +288,7 @@ public static IastModuleResponse OnCommandInjection(string file, string argument
{
OnExecutedSinkTelemetry(IastInstrumentedSinks.CommandInjection);
var evidence = BuildCommandInjectionEvidence(file, argumentLine, argumentList);
return string.IsNullOrEmpty(evidence) ? IastModuleResponse.Empty : GetScope(evidence, integrationId, VulnerabilityTypeName.CommandInjection, OperationNameCommandInjection, NoDbSource);
return string.IsNullOrEmpty(evidence) ? IastModuleResponse.Empty : GetScope(evidence, integrationId, VulnerabilityTypeName.CommandInjection, OperationNameCommandInjection, taintValidator: Always, safeSources: _dbSources);
}
catch (Exception ex)
{
Expand All @@ -324,7 +302,7 @@ public static IastModuleResponse OnReflectionInjection(string param, Integration
try
{
OnExecutedSinkTelemetry(IastInstrumentedSinks.ReflectionInjection);
return GetScope(param, integrationId, VulnerabilityTypeName.ReflectionInjection, OperationNameReflectionInjection, NoDbSource);
return GetScope(param, integrationId, VulnerabilityTypeName.ReflectionInjection, OperationNameReflectionInjection, taintValidator: Always, safeSources: _dbSources);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -616,7 +594,17 @@ public static bool AddRequestVulnerabilitiesAllowed()
return isRequest && traceContext?.IastRequestContext?.AddVulnerabilitiesAllowed() == true;
}

private static IastModuleResponse GetScope(string evidenceValue, IntegrationId integrationId, string vulnerabilityType, string operationName, Func<TaintedObject, bool>? taintValidator = null, bool addLocation = true, int? hash = null, StackTrace? externalStack = null, SecureMarks exclusionSecureMarks = SecureMarks.None)
private static IastModuleResponse GetScope(
string evidenceValue,
IntegrationId integrationId,
string vulnerabilityType,
string operationName,
Func<TaintedObject, bool>? taintValidator = null,
bool addLocation = true,
int? hash = null,
StackTrace? externalStack = null,
SecureMarks exclusionSecureMarks = SecureMarks.None,
SourceType[]? safeSources = null)
{
var tracer = Tracer.Instance;
if (!IastSettings.Enabled || !tracer.Settings.IsIntegrationEnabled(integrationId))
Expand Down Expand Up @@ -653,10 +641,17 @@ private static IastModuleResponse GetScope(string evidenceValue, IntegrationId i
}
}

// Contains at least one range that is not safe (when analyzing a vulnerability that can have secure marks)
if (exclusionSecureMarks != SecureMarks.None && !Ranges.ContainsUnsafeRange(tainted?.Ranges))
var ranges = tainted?.Ranges;
if (ranges is not null)
{
return IastModuleResponse.Empty;
var unsafeRanges = Ranges.GetUnsafeRanges(ranges, exclusionSecureMarks, safeSources);
if (unsafeRanges is null || unsafeRanges.Length == 0)
{
return IastModuleResponse.Empty;
}

// Contains at least one range that is not safe (when analyzing a vulnerability that can have secure marks)
ranges = unsafeRanges;
}

var location = addLocation ? GetLocation(externalStack, currentSpan) : null;
Expand All @@ -665,10 +660,9 @@ private static IastModuleResponse GetScope(string evidenceValue, IntegrationId i
return IastModuleResponse.Empty;
}

var unsafeRanges = Ranges.UnsafeRanges(tainted?.Ranges, exclusionSecureMarks);
var vulnerability = (hash is null) ?
new Vulnerability(vulnerabilityType, location, new Evidence(evidenceValue, unsafeRanges), integrationId) :
new Vulnerability(vulnerabilityType, (int)hash, location, new Evidence(evidenceValue, unsafeRanges), integrationId);
new Vulnerability(vulnerabilityType, location, new Evidence(evidenceValue, ranges), integrationId) :
new Vulnerability(vulnerabilityType, (int)hash, location, new Evidence(evidenceValue, ranges), integrationId);

if (!IastSettings.DeduplicationEnabled || HashBasedDeduplication.Instance.Add(vulnerability))
{
Expand Down Expand Up @@ -801,7 +795,7 @@ internal static void OnHeaderInjection(IntegrationId integrationId, string heade

var evidence = StringAspects.Concat(headerName, HeaderInjectionEvidenceSeparator, headerValue);
var hash = ("HEADER_INJECTION:" + headerName).GetStaticHashCode();
GetScope(evidence, integrationId, VulnerabilityTypeName.HeaderInjection, OperationNameHeaderInjection, NoDbSource, false, hash);
GetScope(evidence, integrationId, VulnerabilityTypeName.HeaderInjection, OperationNameHeaderInjection, taintValidator: Always, safeSources: _dbSources, addLocation: false, hash: hash);
}

internal static IastModuleResponse OnXpathInjection(string xpath)
Expand All @@ -814,7 +808,7 @@ internal static IastModuleResponse OnXpathInjection(string xpath)
try
{
OnExecutedSinkTelemetry(IastInstrumentedSinks.XPathInjection);
return GetScope(xpath, IntegrationId.XpathInjection, VulnerabilityTypeName.XPathInjection, OperationNameXPathInjection, NoDbSource);
return GetScope(xpath, IntegrationId.XpathInjection, VulnerabilityTypeName.XPathInjection, OperationNameXPathInjection, taintValidator: Always, safeSources: _dbSources);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -844,8 +838,8 @@ internal static void OnEmailHtmlInjection(object? message)
return;
}

// We use the same secure marks as XSS
GetScope(messageDuck.Body, IntegrationId.EmailHtmlInjection, VulnerabilityTypeName.EmailHtmlInjection, OperationNameEmailHtmlInjection, taintValidator: NoDbSource, exclusionSecureMarks: SecureMarks.Xss);
// We use the same secure marks as XSS, but excluding db sources
GetScope(messageDuck.Body, IntegrationId.EmailHtmlInjection, VulnerabilityTypeName.EmailHtmlInjection, OperationNameEmailHtmlInjection, taintValidator: Always, safeSources: _dbSources, exclusionSecureMarks: SecureMarks.Xss);
}

internal static void RegisterDbRecord(object instance)
Expand Down
11 changes: 11 additions & 0 deletions tracer/src/Datadog.Trace/Iast/Range.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;

namespace Datadog.Trace.Iast;

Expand Down Expand Up @@ -61,6 +62,16 @@ public bool IsMarked(SecureMarks marks)
return (SecureMarks & marks) != NotMarked;
}

public bool IsSafeSource(SourceType[]? safeSources)
{
if (safeSources is null || Source is null)
{
return false;
}

return safeSources.Contains(Source.Origin);
}

internal bool IsBefore(Range? range)
{
if (range == null)
Expand Down
40 changes: 40 additions & 0 deletions tracer/src/Datadog.Trace/Iast/Ranges.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

using System;
using System.Collections.Generic;
using Datadog.Trace.Vendors.Newtonsoft.Json.Utilities;

namespace Datadog.Trace.Iast;

Expand Down Expand Up @@ -206,6 +207,45 @@ internal static Range[] CopyWithMark(Range[] ranges, SecureMarks secureMarks)
return newRanges.ToArray();
}

internal static Range[]? GetUnsafeRanges(Range[] ranges, SecureMarks safeMarks, SourceType[]? safeSources)
{
if (safeMarks == SecureMarks.None && safeSources is null)
{
return ranges;
}

bool existsSecureRanges = false;
for (int x = 0; x < ranges.Length; x++)
{
var range = ranges[x];
if (range.IsMarked(safeMarks) || range.IsSafeSource(safeSources))
{
existsSecureRanges = true;
break;
}
}

if (!existsSecureRanges)
{
// This is made in order to avoid unnecesary allocations (most common situation)
return ranges;
}

List<Range> insecureRanges = new List<Range>(ranges.Length);
for (int x = 0; x < ranges.Length; x++)
{
var range = ranges[x];
if (range.IsMarked(safeMarks) || range.IsSafeSource(safeSources))
{
continue;
}

insecureRanges.Add(range);
}

return insecureRanges.ToArray();
}

internal static bool ContainsUnsafeRange(IEnumerable<Range>? ranges)
{
if (ranges is null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,7 @@
"source": 0
},
{
"value": "<script language='javascript' type='text/javascript'>alert('Stored XSS attack');</script>",
"source": 1
},
{
"value": ":443/api/v1/test/123/?param1=pone&param2=ptwo#fragment1=fone&fragment2=ftwo"
"value": "<script language='javascript' type='text/javascript'>alert('Stored XSS attack');</script>:443/api/v1/test/123/?param1=pone&param2=ptwo#fragment1=fone&fragment2=ftwo"
}
]
}
Expand All @@ -57,11 +53,6 @@
"origin": "http.request.parameter",
"name": "host",
"value": "localhost"
},
{
"origin": "sql.row.value",
"name": "Details",
"value": "<script language='javascript' type='text/javascript'>alert('Stored XSS attack');</script>"
}
]
}
Expand Down

0 comments on commit fec2738

Please sign in to comment.