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

chore(asm): add SSRF support for urllib.request #9224

Merged
merged 53 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
43f8468
Add SSRF support for http.client
juanjux May 9, 2024
72d798d
Merge branch 'main' into juanjux/httpclient-ssrf
juanjux May 9, 2024
612b656
Add SSRF support for webbrowser
juanjux May 9, 2024
03bc4fe
checkpoint
juanjux May 9, 2024
9a6d747
tests
juanjux May 9, 2024
4fb5ea1
Merge branch 'main' into juanjux/webbrowser-ssrf
juanjux May 9, 2024
275df80
fmt
juanjux May 9, 2024
5233265
Merge branch 'main' into juanjux/webbrowser-ssrf
avara1986 May 9, 2024
baf5768
fmt
juanjux May 9, 2024
994c49f
Merge branch 'juanjux/webbrowser-ssrf' of github.com:DataDog/dd-trace…
juanjux May 9, 2024
6368d18
Ignore false mypy messsage
juanjux May 9, 2024
d8603dd
Merge branch 'main' into juanjux/webbrowser-ssrf
juanjux May 9, 2024
4a2fcb3
Merge branch 'main' into juanjux/webbrowser-ssrf
avara1986 May 9, 2024
5ffeb37
Merge branch 'main' into juanjux/webbrowser-ssrf
juanjux May 9, 2024
f0b1462
Fix mypy ignore
juanjux May 9, 2024
56278b1
Merge branch 'juanjux/webbrowser-ssrf' of github.com:DataDog/dd-trace…
juanjux May 9, 2024
9a82296
Update .suitespec.json to add the webbrowser contrib
juanjux May 10, 2024
89087cc
Merge branch 'main' into juanjux/webbrowser-ssrf
juanjux May 10, 2024
8ab080d
Merge branch 'main' into juanjux/webbrowser-ssrf
avara1986 May 10, 2024
d3b5982
Fix conflicts
juanjux May 10, 2024
979569e
fix
juanjux May 10, 2024
07f573d
fix
juanjux May 10, 2024
a72a96e
Add SSRF support for urlib.request
juanjux May 10, 2024
d329e9b
fix
juanjux May 10, 2024
f31214b
Merge branch 'juanjux/webbrowser-ssrf' into juanjux/stdlib-urllib-ssrf
juanjux May 10, 2024
d92c375
changelog
juanjux May 10, 2024
6c023c1
Add telemetry metric instrumented
juanjux May 10, 2024
c12beaa
Add telemetry metric instrumented
juanjux May 10, 2024
6f53add
Update .suitespec.json to add the urllib contrib
juanjux May 10, 2024
8ea93d0
fix
juanjux May 10, 2024
d8feea8
Update .suitespec.json to add the urllib contrib
juanjux May 10, 2024
7094e47
fmt
juanjux May 10, 2024
bd515ed
fix
juanjux May 10, 2024
b38c65b
Merge branch 'main' into juanjux/webbrowser-ssrf
juanjux May 10, 2024
c55dcd1
Merge branch 'main' into juanjux/stdlib-urllib-ssrf
juanjux May 10, 2024
a9c3bf1
Update CODEOWNERS
juanjux May 10, 2024
821cdf7
Merge branch 'juanjux/stdlib-urllib-ssrf' of github.com:DataDog/dd-tr…
juanjux May 10, 2024
bdfa75b
Merge branch 'main' into juanjux/webbrowser-ssrf
juanjux May 10, 2024
e9d8dd4
Merge branch 'main' into juanjux/stdlib-urllib-ssrf
juanjux May 10, 2024
694256e
Use internal utils instead of reinventing the wheel
juanjux May 10, 2024
4e01714
Merge branch 'juanjux/webbrowser-ssrf' of github.com:DataDog/dd-trace…
juanjux May 10, 2024
77c0e11
safer
juanjux May 10, 2024
06507cc
Merge branch 'juanjux/webbrowser-ssrf' into juanjux/stdlib-urllib-ssrf
juanjux May 10, 2024
a040bd3
Merge branch 'juanjux/stdlib-urllib-ssrf' of github.com:DataDog/dd-tr…
juanjux May 10, 2024
4aba25d
fmt
juanjux May 10, 2024
86783fd
Merge branch 'main' into juanjux/stdlib-urllib-ssrf
avara1986 May 13, 2024
4cb26ec
mypy
juanjux May 13, 2024
43542f1
Merge branch 'main' into juanjux/stdlib-urllib-ssrf
avara1986 May 13, 2024
34dee00
Merge branch 'main' into juanjux/stdlib-urllib-ssrf
juanjux May 13, 2024
c056289
doc fix
juanjux May 13, 2024
a90c291
fmt
juanjux May 13, 2024
2edc164
Merge branch 'main' into juanjux/stdlib-urllib-ssrf
juanjux May 13, 2024
5aa62f0
Merge branch 'main' into juanjux/stdlib-urllib-ssrf
juanjux May 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ ddtrace/settings/asm.py @DataDog/asm-python
ddtrace/contrib/subprocess/ @DataDog/asm-python
ddtrace/contrib/flask_login/ @DataDog/asm-python
ddtrace/contrib/webbrowser @DataDog/asm-python
ddtrace/contrib/urllib @DataDog/asm-python
ddtrace/internal/_exceptions.py @DataDog/asm-python
tests/appsec/ @DataDog/asm-python
tests/contrib/dbapi/test_dbapi_appsec.py @DataDog/asm-python
Expand Down
1 change: 1 addition & 0 deletions ddtrace/appsec/_iast/taint_sinks/ssrf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class SSRF(VulnerabilityBase):
_FUNC_TO_URL_ARGUMENT = {
"http.client.request": (1, "url"),
"requests.sessions.request": (1, "url"),
"urllib.request.urlopen": (0, "url"),
"urllib3._request_methods.request": (1, "url"),
"urllib3.request.request": (1, "url"),
"webbrowser.open": (0, "url"),
Expand Down
12 changes: 12 additions & 0 deletions ddtrace/contrib/urllib/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""
Trace the standard library ``urllib.request`` library to trace
HTTP requests and detect SSRF vulnerabilities. It is enabled by default
if ``DD_IAST_ENABLED`` is set to ``True`` (for detecting sink points) and/or
``DD_ASM_ENABLED`` is set to ``True`` (for exploit prevention).
"""
from .patch import get_version
from .patch import patch
from .patch import unpatch


__all__ = ["patch", "unpatch", "get_version"]
34 changes: 34 additions & 0 deletions ddtrace/contrib/urllib/patch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import urllib.request

from ddtrace.appsec._common_module_patches import wrapped_request_D8CB81E472AF98A2 as _wrap_open
from ddtrace.appsec._iast._metrics import _set_metric_iast_instrumented_sink
from ddtrace.appsec._iast.constants import VULN_SSRF
from ddtrace.settings.asm import config as asm_config
from ddtrace.vendor.wrapt import wrap_function_wrapper as _w

from ..trace_utils import unwrap as _u


def get_version():
# type: () -> str
return ""


def patch():
"""patch the built-in urllib.request methods for tracing"""
if getattr(urllib.request, "__datadog_patch", False):
return
urllib.request.__datadog_patch = True

_w("urllib.request", "urlopen", _wrap_open)
if asm_config._iast_enabled:
_set_metric_iast_instrumented_sink(VULN_SSRF)


def unpatch():
"""unpatch any previously patched modules"""
if not getattr(urllib.request, "__datadog_patch", False):
return
urllib.request.__datadog_patch = False

_u(urllib.request, "urlopen")
5 changes: 5 additions & 0 deletions releasenotes/notes/asm-ssrf-expanded-cc7d8abaa3f9c7dd.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
features:
- |
Expand SSRF vulnerability support for Code Security and Exploit Prevention for the modules ``urllib3``, ``http.client``,
``webbrowser`` and ``urllib.request``.
11 changes: 11 additions & 0 deletions tests/.suitespec.json
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,9 @@
"webbrowser": [
"ddtrace/contrib/webbrowser/*"
],
"urllib": [
"ddtrace/contrib/urllib/*"
],
"rq": [
"ddtrace/contrib/rq/*"
],
Expand Down Expand Up @@ -1298,6 +1301,14 @@
"@webbrowser",
"tests/appsec/iast/taint_sinks/test_ssrf.py"
],
"urllib": [
"@bootstrap",
"@core",
"@contrib",
"@tracing",
"@urllib",
"tests/appsec/iast/taint_sinks/test_ssrf.py"
],
"vertica": [
"@bootstrap",
"@core",
Expand Down
42 changes: 42 additions & 0 deletions tests/appsec/iast/taint_sinks/test_ssrf.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from ddtrace.contrib.httplib.patch import unpatch as httplib_unpatch
from ddtrace.contrib.requests.patch import patch as requests_patch
from ddtrace.contrib.requests.patch import unpatch as requests_unpatch
from ddtrace.contrib.urllib.patch import patch as urllib_patch
from ddtrace.contrib.urllib.patch import unpatch as urllib_unpatch
from ddtrace.contrib.urllib3.patch import patch as urllib3_patch
from ddtrace.contrib.urllib3.patch import unpatch as urllib3_unpatch
from ddtrace.contrib.webbrowser.patch import patch as webbrowser_patch
Expand Down Expand Up @@ -142,6 +144,26 @@ def test_ssrf_webbrowser(tracer, iast_span_defaults):
webbrowser_unpatch()


def test_urllib_request(tracer, iast_span_defaults):
with override_global_config(dict(_iast_enabled=True)):
urllib_patch()
try:
import urllib.request

tainted_url, tainted_path = _get_tainted_url()
try:
# label test_urllib_request
urllib.request.urlopen(tainted_url)
except urllib.error.URLError:
pass

span_report = core.get_item(IAST.CONTEXT_KEY, span=iast_span_defaults)
assert span_report
_check_report(span_report, tainted_path, "test_urllib_request")
finally:
urllib_unpatch()


def _check_no_report_if_deduplicated(span_report, num_vuln_expected):
if num_vuln_expected == 0:
assert span_report is None
Expand Down Expand Up @@ -232,3 +254,23 @@ def test_ssrf_webbrowser_deduplication(num_vuln_expected, tracer, iast_span_dedu
_check_no_report_if_deduplicated(span_report, num_vuln_expected)
finally:
webbrowser_unpatch()


@pytest.mark.parametrize("num_vuln_expected", [1, 0, 0])
def test_ssrf_urllib_deduplication(num_vuln_expected, tracer, iast_span_deduplication_enabled):
urllib_patch()
try:
import urllib.request

tainted_url, tainted_path = _get_tainted_url()
for _ in range(0, 5):
try:
# label test_urllib_request_deduplication
urllib.request.urlopen(tainted_url)
except urllib.error.URLError:
pass

span_report = core.get_item(IAST.CONTEXT_KEY, span=iast_span_deduplication_enabled)
_check_no_report_if_deduplicated(span_report, num_vuln_expected)
finally:
urllib_unpatch()