Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rasp command injection tests #3524

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions manifests/cpp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ tests/:
test_path_parameter.py: irrelevant (ASM is not implemented in C++)
test_uri.py: irrelevant (ASM is not implemented in C++)
rasp/:
test_cmdi.py: irrelevant (ASM is not implemented in C++)
test_lfi.py: irrelevant (ASM is not implemented in C++)
test_libddwaf.py: irrelevant (ASM is not implemented in C++)
test_shi.py: irrelevant (ASM is not implemented in C++)
Expand Down
13 changes: 13 additions & 0 deletions manifests/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,17 @@ tests/:
test_uri.py:
TestURI: irrelevant
rasp/:
test_cmdi.py:
Test_Cmdi_BodyJson: missing_feature
Test_Cmdi_BodyUrlEncoded: missing_feature
Test_Cmdi_BodyXml: missing_feature
Test_Cmdi_Capability: missing_feature
Test_Cmdi_Mandatory_SpanTags: missing_feature
Test_Cmdi_Optional_SpanTags: missing_feature
Test_Cmdi_Rules_Version: missing_feature
Test_Cmdi_StackTrace: missing_feature
Test_Cmdi_Telemetry: missing_feature
Test_Cmdi_UrlQuery: missing_feature
test_lfi.py:
Test_Lfi_BodyJson: v2.51.0
Test_Lfi_BodyUrlEncoded: v2.51.0
Expand All @@ -162,6 +173,7 @@ tests/:
Test_Lfi_UrlQuery: v2.51.0
test_libddwaf.py:
Test_Libddwaf_Version: v3.4.1
Test_Libddwaf_Version_CmdI: missing_feature
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we are going to have multiple libddwaf min versions, it will worth to move the test to each test file, like we are doing with Rules_Version

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I have updated the code addressing your comment.

test_shi.py:
Test_Shi_BodyJson: v3.2.0
Test_Shi_BodyUrlEncoded: v3.2.0
Expand All @@ -172,6 +184,7 @@ tests/:
Test_Shi_Rules_Version: v3.5.0
Test_Shi_StackTrace: v3.2.0
Test_Shi_Telemetry: v3.3.0
Test_Shi_Telemetry_Variant_Tag: missing_feature
Test_Shi_UrlQuery: v3.2.0
test_sqli.py:
Test_Sqli_BodyJson: v2.54.0
Expand Down
1 change: 1 addition & 0 deletions manifests/golang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ tests/:
test_uri.py:
TestURI: missing_feature
rasp/:
test_cmdi.py: missing_feature
test_lfi.py: missing_feature
test_libddwaf.py: missing_feature
test_shi.py: irrelevant (there is no equivalent to system(3) in go)
Expand Down
2 changes: 2 additions & 0 deletions manifests/java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,7 @@ tests/:
vertx3: missing_feature
vertx4: missing_feature
rasp/:
test_cmdi.py: missing_feature
test_lfi.py:
Test_Lfi_BodyJson:
'*': v1.40.0
Expand Down Expand Up @@ -609,6 +610,7 @@ tests/:
Test_Libddwaf_Version:
'*': v1.40.0
spring-boot-3-native: missing_feature (GraalVM. Tracing support only)
Test_Libddwaf_Version_CmdI: missing_feature
test_shi.py: irrelevant (Not support in Java)
# SQLi was introduced in v1.38.0 (with RASP disabled by default, but was flaky)
test_sqli.py:
Expand Down
3 changes: 3 additions & 0 deletions manifests/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ tests/:
test_uri.py:
TestURI: missing_feature
rasp/:
test_cmdi.py: missing_feature
test_lfi.py:
Test_Lfi_BodyJson:
'*': *ref_5_24_0
Expand All @@ -297,6 +298,7 @@ tests/:
nextjs: missing_feature
test_libddwaf.py:
Test_Libddwaf_Version: *ref_5_25_0
Test_Libddwaf_Version_CmdI: missing_feature
test_shi.py:
Test_Shi_BodyJson:
'*': *ref_5_25_0
Expand All @@ -315,6 +317,7 @@ tests/:
Test_Shi_Telemetry:
'*': *ref_5_25_0
nextjs: missing_feature
Test_Shi_Telemetry_Variant_Tag: missing_feature
Test_Shi_UrlQuery:
'*': *ref_5_25_0
nextjs: missing_feature
Expand Down
1 change: 1 addition & 0 deletions manifests/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ tests/:
test_uri.py:
TestURI: missing_feature
rasp/:
test_cmdi.py: missing_feature
test_lfi.py: missing_feature
test_libddwaf.py: missing_feature
test_shi.py: missing_feature
Expand Down
3 changes: 3 additions & 0 deletions manifests/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ tests/:
test_uri.py:
TestURI: missing_feature
rasp/:
test_cmdi.py: missing_feature
test_lfi.py:
Test_Lfi_BodyJson: v2.10.0
Test_Lfi_BodyUrlEncoded: v2.10.0
Expand All @@ -250,6 +251,7 @@ tests/:
Test_Lfi_UrlQuery: v2.10.0
test_libddwaf.py:
Test_Libddwaf_Version: v2.15.0
Test_Libddwaf_Version_CmdI: missing_feature
test_shi.py:
Test_Shi_BodyJson: v2.11.0-rc2
Test_Shi_BodyUrlEncoded: v2.11.0-rc2
Expand All @@ -260,6 +262,7 @@ tests/:
Test_Shi_Rules_Version: v2.15.0
Test_Shi_StackTrace: v2.11.0-rc2
Test_Shi_Telemetry: v2.11.0-rc2
Test_Shi_Telemetry_Variant_Tag: missing_feature
Test_Shi_UrlQuery: v2.11.0-rc2
test_sqli.py:
Test_Sqli_BodyJson: v2.10.0
Expand Down
1 change: 1 addition & 0 deletions manifests/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ tests/:
test_uri.py:
TestURI: missing_feature
rasp/:
test_cmdi.py: missing_feature
test_lfi.py: missing_feature
test_libddwaf.py: missing_feature
test_shi.py: missing_feature
Expand Down
51 changes: 50 additions & 1 deletion tests/appsec/rasp/rasp_ruleset.json
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,55 @@
"on_match": [
"stack_trace", "block"
]
}
},
{
"id": "rasp-932-110",
"name": "OS command injection exploit",
"enabled": true,
"tags": {
"type": "command_injection",
"category": "vulnerability_trigger",
"cwe": "77",
"capec": "1000/152/248/88",
"confidence": "0",
"module": "rasp"
},
"conditions": [
{
"parameters": {
"resource": [
{
"address": "server.sys.exec.cmd"
}
],
"params": [
{
"address": "server.request.query"
},
{
"address": "server.request.body"
},
{
"address": "server.request.path_params"
},
{
"address": "grpc.server.request.message"
},
{
"address": "graphql.server.all_resolvers"
},
{
"address": "graphql.server.resolver"
}
]
},
"operator": "cmdi_detector"
}
],
"transformers": [],
"on_match": [
"stack_trace", "block"
]
}
]
}
190 changes: 190 additions & 0 deletions tests/appsec/rasp/test_cmdi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# Unless explicitly stated otherwise all files in this repository are licensed under the the Apache License Version 2.0.
# This product includes software developed at Datadog (https://www.datadoghq.com/).
# Copyright 2021 Datadog, Inc.

from utils import features, weblog, interfaces, scenarios, rfc
from utils.dd_constants import Capabilities
from tests.appsec.rasp.utils import (
validate_span_tags,
validate_stack_traces,
find_series,
validate_metric_variant,
Base_Rules_Version,
)


@rfc("https://docs.google.com/document/d/1DDWy3frMXDTAbk-BfnZ1FdRwuPx6Pl7AWyR4zjqRFZw")
@features.rasp_command_injection
@scenarios.appsec_rasp
class Test_Cmdi_UrlQuery:
"""Shell Injection through query parameters"""

def setup_cmdi_get(self):
self.r = weblog.get("/rasp/cmdi", params={"command": "/bin/evilCommand"})

def test_cmdi_get(self):
assert self.r.status_code == 403

interfaces.library.assert_rasp_attack(
self.r,
"rasp-932-110",
{
"resource": {"address": "server.sys.exec.cmd", "value": "/bin/evilCommand",},
"params": {"address": "server.request.query", "value": "/bin/evilCommand",},
},
)


@rfc("https://docs.google.com/document/d/1DDWy3frMXDTAbk-BfnZ1FdRwuPx6Pl7AWyR4zjqRFZw")
@features.rasp_command_injection
@scenarios.appsec_rasp
class Test_Cmdi_BodyUrlEncoded:
"""Shell Injection through a url-encoded body parameter"""

def setup_cmdi_post_urlencoded(self):
self.r = weblog.post("/rasp/cmdi", data={"command": "/bin/evilCommand"})

def test_cmdi_post_urlencoded(self):
assert self.r.status_code == 403

interfaces.library.assert_rasp_attack(
self.r,
"rasp-932-110",
{
"resource": {"address": "server.sys.exec.cmd", "value": "/bin/evilCommand",},
"params": {"address": "server.request.body", "value": "/bin/evilCommand",},
},
)


@rfc("https://docs.google.com/document/d/1DDWy3frMXDTAbk-BfnZ1FdRwuPx6Pl7AWyR4zjqRFZw")
@features.rasp_command_injection
@scenarios.appsec_rasp
class Test_Cmdi_BodyXml:
"""Shell Injection through an xml body parameter"""

def setup_cmdi_post_xml(self):
data = "<?xml version='1.0' encoding='utf-8'?><command>/bin/evilCommand</command>"
self.r = weblog.post("/rasp/cmdi", data=data, headers={"Content-Type": "application/xml"})

def test_cmdi_post_xml(self):
assert self.r.status_code == 403

interfaces.library.assert_rasp_attack(
self.r,
"rasp-932-110",
{
"resource": {"address": "server.sys.exec.cmd", "value": "/bin/evilCommand",},
"params": {"address": "server.request.body", "value": "/bin/evilCommand",},
},
)


@rfc("https://docs.google.com/document/d/1DDWy3frMXDTAbk-BfnZ1FdRwuPx6Pl7AWyR4zjqRFZw")
@features.rasp_command_injection
@scenarios.appsec_rasp
class Test_Cmdi_BodyJson:
"""Shell Injection through a json body parameter"""

def setup_cmdi_post_json(self):
"""AppSec detects attacks in JSON body values"""
self.r = weblog.post("/rasp/cmdi", json={"command": "/bin/evilCommand"})

def test_cmdi_post_json(self):
assert self.r.status_code == 403

interfaces.library.assert_rasp_attack(
self.r,
"rasp-932-110",
{
"resource": {"address": "server.sys.exec.cmd", "value": "/bin/evilCommand",},
"params": {"address": "server.request.body", "value": "/bin/evilCommand",},
},
)


@rfc("https://docs.google.com/document/d/1DDWy3frMXDTAbk-BfnZ1FdRwuPx6Pl7AWyR4zjqRFZw")
@features.rasp_span_tags
@features.rasp_command_injection
@scenarios.appsec_rasp
class Test_Cmdi_Mandatory_SpanTags:
"""Validate span tag generation on exploit attempts"""

def setup_cmdi_span_tags(self):
self.r = weblog.get("/rasp/cmdi", params={"command": "/bin/evilCommand"})

def test_cmdi_span_tags(self):
validate_span_tags(self.r, expected_metrics=["_dd.appsec.rasp.duration"])


@rfc("https://docs.google.com/document/d/1DDWy3frMXDTAbk-BfnZ1FdRwuPx6Pl7AWyR4zjqRFZw")
@features.rasp_span_tags
@features.rasp_command_injection
@scenarios.appsec_rasp
class Test_Cmdi_Optional_SpanTags:
"""Validate span tag generation on exploit attempts"""

def setup_cmdi_span_tags(self):
self.r = weblog.get("/rasp/cmdi", params={"command": "/bin/evilCommand"})

def test_cmdi_span_tags(self):
validate_span_tags(
self.r, expected_metrics=["_dd.appsec.rasp.duration_ext", "_dd.appsec.rasp.rule.eval",],
)


@rfc("https://docs.google.com/document/d/1DDWy3frMXDTAbk-BfnZ1FdRwuPx6Pl7AWyR4zjqRFZw")
@features.rasp_stack_trace
@features.rasp_command_injection
@scenarios.appsec_rasp
class Test_Cmdi_StackTrace:
"""Validate stack trace generation on exploit attempts"""

def setup_cmdi_stack_trace(self):
self.r = weblog.get("/rasp/cmdi", params={"command": "/bin/evilCommand"})

def test_cmdi_stack_trace(self):
assert self.r.status_code == 403
validate_stack_traces(self.r)


@rfc("https://docs.google.com/document/d/1DDWy3frMXDTAbk-BfnZ1FdRwuPx6Pl7AWyR4zjqRFZw")
@features.rasp_command_injection
@scenarios.appsec_rasp
class Test_Cmdi_Telemetry:
"""Validate Telemetry data on exploit attempts"""

def setup_cmdi_telemetry(self):
self.r = weblog.get("/rasp/cmdi", params={"command": "/bin/evilCommand"})

def test_cmdi_telemetry(self):
assert self.r.status_code == 403

series_eval = find_series(True, "appsec", "rasp.rule.eval")
assert series_eval
assert any(validate_metric_variant("rasp.rule.eval", "command_injection", "exec", s) for s in series_eval), [
s.get("tags") for s in series_eval
]

series_match = find_series(True, "appsec", "rasp.rule.match")
assert series_match
assert any(validate_metric_variant("rasp.rule.match", "command_injection", "exec", s) for s in series_match), [
s.get("tags") for s in series_match
]


@rfc("https://docs.google.com/document/d/1DDWy3frMXDTAbk-BfnZ1FdRwuPx6Pl7AWyR4zjqRFZw")
@features.rasp_command_injection
@scenarios.remote_config_mocked_backend_asm_dd
class Test_Cmdi_Capability:
"""Validate that ASM_RASP_CMDI (37) capability is sent"""

def test_cmdi_capability(self):
interfaces.library.assert_rc_capability(Capabilities.ASM_RASP_CMDI)


@features.rasp_command_injection
class Test_Cmdi_Rules_Version(Base_Rules_Version):
"""Test cmdi min rules version"""

min_version = "1.13.5"
Loading