Skip to content

Commit

Permalink
[ASM] IAST: Add web form tests (#6276)
Browse files Browse the repository at this point in the history
## Summary of changes

An issue in an IAST customer was detected involving query parameter name
and web forms in a vulnerability.

In order to reproduce the issue, some tests were created for web forms
using vulnerabilities with a query parameter name in the evidence. Even
though the test passed and no code change was required, the new tests
will be added to our integration tests since, currently, we have no
tests covering these cases.

## Reason for change

## Implementation details

## Test coverage

## Other details
<!-- Fixes #{issue} -->

<!-- ⚠️ Note: where possible, please obtain 2 approvals prior to
merging. Unless CODEOWNERS specifies otherwise, for external teams it is
typically best to have one review from a team member, and one review
from apm-dotnet. Trivial changes do not require 2 reviews. -->
  • Loading branch information
NachoEchevarria authored Nov 15, 2024
1 parent 1527d43 commit bd8f5d5
Show file tree
Hide file tree
Showing 14 changed files with 537 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
// </copyright>

#if NETFRAMEWORK
using System.Net;
using System.Security.Policy;
using System.Threading.Tasks;
using Datadog.Trace.TestHelpers;
using Xunit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,26 @@ await VerifyHelper.VerifySpans(spansFiltered, settings)
newFixture.Dispose();
newFixture.SetOutput(null);
}

[Fact]
[Trait("Category", "ArmUnsupported")]
[Trait("RunOnWindows", "True")]
public async Task TestQueryParameterNameVulnerability()
{
var filename = "Iast.QueryParameterName.AspNetCore5";
var url = "/Iast/Print?Encrypt=True&ClientDatabase=774E4D65564946426A53694E48756B592B444A6C43673D3D&p=413&ID=2376&EntityType=114&Print=True&OutputType=WORDOPENXML&SSRSReportID=1";
IncludeAllHttpSpans = true;
await TryStartApp();
var agent = Fixture.Agent;
var spans = await SendRequestsAsync(agent, [url]);
var spansFiltered = spans.Where(x => x.Type == SpanTypes.Web).ToList();

var settings = VerifyHelper.GetSpanVerifierSettings();
settings.AddIastScrubbing();
await VerifyHelper.VerifySpans(spansFiltered, settings)
.UseFileName(filename)
.DisableRequireUniquePrefix();
}
}

// Classes to test particular features
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@ public async Task TestStackTraceLeak(string test, string url)
{
await TestStrictTransportSecurityHeaderMissingVulnerability(test, url);
}

[Trait("Category", "EndToEnd")]
[Trait("RunOnWindows", "True")]
[Trait("LoadFromGAC", "True")]
[SkippableTheory]
[InlineData(AddressesConstants.RequestQuery, "/Iast/Print?Encrypt=True&ClientDatabase=774E4D65564946426A53694E48756B592B444A6C43673D3D&p=413&ID=2376&EntityType=114&Print=True&OutputType=WORDOPENXML&SSRSReportID=1")]
public async Task TestQueryParameterNameVulnerability(string test, string url)
{
await TestQueryParameterName(test, url);
}
}

[Collection("IisTests")]
Expand Down Expand Up @@ -650,6 +660,19 @@ await VerifyHelper.VerifySpans(spansFiltered, settings)
.DisableRequireUniquePrefix();
}

protected async Task TestQueryParameterName(string test, string url)
{
var sanitisedUrl = VerifyHelper.SanitisePathsForVerify(url);
var settings = VerifyHelper.GetSpanVerifierSettings(test, sanitisedUrl, null);
var spans = await SendRequestsAsync(_iisFixture.Agent, [url]);
var filename = GetFileName("QueryParameterName");
var spansFiltered = spans.Where(x => x.Type == SpanTypes.Web).ToList();
settings.AddIastScrubbing();
await VerifyHelper.VerifySpans(spansFiltered, settings)
.UseFileName(filename)
.DisableRequireUniquePrefix();
}

protected override string GetTestName() => _testName;

private string GetFileName(string testName)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// <copyright file="AspNetWebFormsWithIast.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 NETFRAMEWORK
using System.Threading.Tasks;
using Datadog.Trace.Iast.Telemetry;
using Datadog.Trace.TestHelpers;
using Xunit;
using Xunit.Abstractions;

#pragma warning disable SA1402 // File may only contain a single class
#pragma warning disable SA1649 // File name must match first type name
namespace Datadog.Trace.Security.IntegrationTests.IAST;

[Collection("IisTests")]
public class AspNetWebFormsIntegratedWithIast : AspNetWebFormsWithIast
{
public AspNetWebFormsIntegratedWithIast(IisFixture iisFixture, ITestOutputHelper output)
: base(iisFixture, output, classicMode: false, enableSecurity: true)
{
}
}

[Collection("IisTests")]
public class AspNetWebFormsClassicIntegratedWithIast : AspNetWebFormsWithIast
{
public AspNetWebFormsClassicIntegratedWithIast(IisFixture iisFixture, ITestOutputHelper output)
: base(iisFixture, output, classicMode: true, enableSecurity: true)
{
}
}

public abstract class AspNetWebFormsWithIast : AspNetBase, IClassFixture<IisFixture>, IAsyncLifetime
{
private readonly IisFixture _iisFixture;
private readonly bool _classicMode;

public AspNetWebFormsWithIast(IisFixture iisFixture, ITestOutputHelper output, bool classicMode, bool enableSecurity)
: base("WebForms", output, "/home/shutdown", @"test\test-applications\security\aspnet")
{
EnableIast(true);
EnableEvidenceRedaction(false);
EnableIastTelemetry((int)IastMetricsVerbosityLevel.Off);
SetEnvironmentVariable("DD_IAST_DEDUPLICATION_ENABLED", "false");
SetEnvironmentVariable("DD_IAST_REQUEST_SAMPLING", "100");
SetEnvironmentVariable("DD_IAST_MAX_CONCURRENT_REQUESTS", "100");
SetEnvironmentVariable("DD_IAST_VULNERABILITIES_PER_REQUEST", "100");
SetEnvironmentVariable(Configuration.ConfigurationKeys.AppSec.StackTraceEnabled, "false");

_iisFixture = iisFixture;
_classicMode = classicMode;
_testName = "Security." + nameof(AspNetWebForms)
+ (classicMode ? ".Classic" : ".Integrated")
+ ".enableSecurity=" + enableSecurity;
}

[Trait("Category", "EndToEnd")]
[Trait("RunOnWindows", "True")]
[Trait("LoadFromGAC", "True")]
[SkippableTheory]
[InlineData("TestQueryParameterNameVulnerability")]
public async Task TestQueryParameterNameVulnerability(string test)
{
var url = "/print?Encrypt=True&ClientDatabase=774E4D65564946426A53694E48756B592B444A6C43673D3D&p=413&ID=2376&EntityType=114&Print=True&OutputType=WORDOPENXML&SSRSReportID=1";

var settings = VerifyHelper.GetSpanVerifierSettings(test);
settings.AddIastScrubbing();

await TestAppSecRequestWithVerifyAsync(_iisFixture.Agent, url, null, 1, 1, settings, userAgent: "Hello/V");
}

public async Task InitializeAsync()
{
await _iisFixture.TryStartIis(this, _classicMode ? IisAppType.AspNetClassic : IisAppType.AspNetIntegrated);
SetHttpPort(_iisFixture.HttpPort);
}

public Task DisposeAsync() => Task.CompletedTask;

protected override string GetTestName() => _testName;
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
[
{
TraceId: Id_1,
SpanId: Id_2,
Name: aspnet_core.request,
Resource: GET /iast/print,
Service: Samples.Security.AspNetCore5,
Type: web,
Tags: {
aspnet_core.endpoint: Samples.Security.AspNetCore5.Controllers.IastController.PrintReport (Samples.Security.AspNetCore5),
aspnet_core.route: iast/print,
component: aspnet_core,
env: integration_tests,
http.method: GET,
http.request.headers.host: localhost:00000,
http.route: iast/print,
http.status_code: 200,
http.url: http://localhost:00000/Iast/Print?Encrypt=True&ClientDatabase=774E4D65564946426A53694E48756B592B444A6C43673D3D&p=413&ID=2376&EntityType=114&Print=True&OutputType=WORDOPENXML&SSRSReportID=1,
http.useragent: Mistake Not...,
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.iast.enabled: 1,
_dd.iast.json:
{
"vulnerabilities": [
{
"type": "SQL_INJECTION",
"hash": -209503571,
"location": {
"spanId": XXX,
"path": "Samples.Security.AspNetCore5.Controllers.IastController",
"method": "ExecuteQuery"
},
"evidence": {
"valueParts": [
{
"value": "\r\nDECLARE @ClientDatabaseID INT = (SELECT ClientDatabaseID FROM[GetClientDatabase]('",
"source": 0
},
{
"value": "'))\r\n\r\nSELECT SSRSReports FROM [ClientCentral].[dbo].[ClientDatabases] WHERE ClientDatabaseID = @ClientDatabaseID)",
"source": 0
}
]
}
}
],
"sources": [
{
"origin": "http.request.parameter.name",
"name": "ClientDatabase"
}
]
}
},
Metrics: {
process_id: 0,
_dd.top_level: 1.0,
_dd.tracer_kr: 1.0,
_sampling_priority_v1: 2.0
}
},
{
TraceId: Id_1,
SpanId: Id_3,
Name: aspnet_core_mvc.request,
Resource: GET /iast/print,
Service: Samples.Security.AspNetCore5,
Type: web,
ParentId: Id_2,
Tags: {
aspnet_core.action: printreport,
aspnet_core.controller: iast,
aspnet_core.route: iast/print,
component: aspnet_core,
env: integration_tests,
language: dotnet,
span.kind: server
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
[
{
TraceId: Id_1,
SpanId: Id_2,
Name: aspnet.request,
Resource: GET /iast/print,
Service: sample,
Type: web,
Tags: {
env: integration_tests,
http.method: GET,
http.request.headers.host: localhost:00000,
http.route: {controller}/{action}/{id},
http.status_code: 200,
http.url: http://localhost:00000/Iast/Print?Encrypt=True&ClientDatabase=774E4D65564946426A53694E48756B592B444A6C43673D3D&p=413&ID=2376&EntityType=114&Print=True&OutputType=WORDOPENXML&SSRSReportID=1,
http.useragent: Mistake Not...,
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.iast.enabled: 1,
_dd.iast.json:
{
"vulnerabilities": [
{
"type": "SQL_INJECTION",
"hash": -209503571,
"location": {
"spanId": XXX,
"path": "Samples.Security.AspNetCore5.Controllers.IastController",
"method": "ExecuteQuery"
},
"evidence": {
"valueParts": [
{
"value": "\r\nDECLARE @ClientDatabaseID INT = (SELECT ClientDatabaseID FROM[GetClientDatabase]('",
"source": 0
},
{
"value": "'))\r\n\r\nSELECT SSRSReports FROM [ClientCentral].[dbo].[ClientDatabases] WHERE ClientDatabaseID = @ClientDatabaseID)",
"source": 0
}
]
}
}
],
"sources": [
{
"origin": "http.request.parameter.name",
"name": "ClientDatabase"
}
]
}
},
Metrics: {
process_id: 0,
_dd.top_level: 1.0,
_dd.tracer_kr: 1.0,
_sampling_priority_v1: 2.0
}
},
{
TraceId: Id_1,
SpanId: Id_3,
Name: aspnet-mvc.request,
Resource: GET /iast/print,
Service: sample,
Type: web,
ParentId: Id_2,
Tags: {
aspnet.action: print,
aspnet.controller: iast,
aspnet.route: {controller}/{action}/{id},
env: integration_tests,
http.method: GET,
http.request.headers.host: localhost:00000,
http.status_code: 200,
http.url: http://localhost:00000/Iast/Print?Encrypt=True&ClientDatabase=774E4D65564946426A53694E48756B592B444A6C43673D3D&p=413&ID=2376&EntityType=114&Print=True&OutputType=WORDOPENXML&SSRSReportID=1,
http.useragent: Mistake Not...,
language: dotnet,
span.kind: server
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
[
{
TraceId: Id_1,
SpanId: Id_2,
Name: aspnet.request,
Resource: GET /print,
Service: sample,
Type: web,
Tags: {
env: integration_tests,
http.method: GET,
http.request.headers.host: localhost:00000,
http.status_code: 200,
http.url: http://localhost:00000/print?Encrypt=True&ClientDatabase=774E4D65564946426A53694E48756B592B444A6C43673D3D&p=413&ID=2376&EntityType=114&Print=True&OutputType=WORDOPENXML&SSRSReportID=1,
http.useragent: Mistake Not... Hello/V,
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.iast.enabled: 1,
_dd.iast.json:
{
"vulnerabilities": [
{
"type": "PATH_TRAVERSAL",
"hash": -1368908679,
"location": {
"spanId": XXX,
"path": "Iast_Print",
"method": "Page_Load",
"line": XXX
},
"evidence": {
"valueParts": [
{
"value": "ClientDatabase",
"source": 0
}
]
}
}
],
"sources": [
{
"origin": "http.request.parameter.name",
"name": "ClientDatabase"
}
]
}
},
Metrics: {
process_id: 0,
_dd.top_level: 1.0,
_dd.tracer_kr: 1.0,
_sampling_priority_v1: 2.0
}
}
]
Loading

0 comments on commit bd8f5d5

Please sign in to comment.