python.django.security.injection.ssrf.ssrf-injection-urllib.ssrf-injection-urllib

profile photo of semgrepsemgrep
Author
6,591
Download Count*

Data from request object is passed to a new server-side request. This could lead to a server-side request forgery (SSRF), which could result in attackers gaining access to private organization data. To mitigate, ensure that schemes and hosts are validated against an allowlist, do not forward the response to the user, and ensure proper authentication and transport-layer security in the proxied request.

Run Locally

Run in CI

Defintion

rules:
  - id: ssrf-injection-urllib
    message: Data from request object is passed to a new server-side request. This
      could lead to a server-side request forgery (SSRF), which could result in
      attackers gaining access to private organization data. To mitigate, ensure
      that schemes and hosts are validated against an allowlist, do not forward
      the response to the user, and ensure proper authentication and
      transport-layer security in the proxied request.
    metadata:
      cwe:
        - "CWE-918: Server-Side Request Forgery (SSRF)"
      owasp:
        - A10:2021 - Server-Side Request Forgery (SSRF)
      references:
        - https://owasp.org/www-community/attacks/Server_Side_Request_Forgery
      category: security
      technology:
        - django
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - vuln
      likelihood: MEDIUM
      impact: HIGH
      confidence: MEDIUM
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - Server-Side Request Forgery (SSRF)
    languages:
      - python
    severity: ERROR
    patterns:
      - pattern-inside: |
          def $FUNC(...):
            ...
      - pattern-either:
          - pattern: urllib.request.urlopen(..., $S.format(..., request.$W.get(...), ...),
              ...)
          - pattern: urllib.request.urlopen(..., $S % request.$W.get(...), ...)
          - pattern: urllib.request.urlopen(..., f"...{request.$W.get(...)}...", ...)
          - pattern: urllib.request.urlopen(..., request.$W.get(...), ...)
          - pattern: |
              $DATA = request.$W.get(...)
              ...
              urllib.request.urlopen(..., $DATA, ...)
          - pattern: |
              $DATA = request.$W.get(...)
              ...
              $INTERM = $DATA
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: |
              $DATA = request.$W.get(...)
              ...
              urllib.request.urlopen(..., $STR.format(..., $DATA, ...), ...)
          - pattern: |
              $DATA = request.$W.get(...)
              ...
              $INTERM = $STR.format(..., $DATA, ...)
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: |
              $DATA = request.$W.get(...)
              ...
              urllib.request.urlopen(..., $STR % $DATA, ...)
          - pattern: |
              $DATA = request.$W.get(...)
              ...
              $INTERM = $STR % $DATA
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: |
              $DATA = request.$W.get(...)
              ...
              urllib.request.urlopen(..., f"...{$DATA}...", ...)
          - pattern: |
              $DATA = request.$W.get(...)
              ...
              $INTERM = f"...{$DATA}..."
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: |
              $DATA = request.$W.get(...)
              ...
              urllib.request.urlopen(..., $STR + $DATA, ...)
          - pattern: |
              $DATA = request.$W.get(...)
              ...
              $INTERM = $STR + $DATA
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: $A = urllib.request.urlopen(..., request.$W.get(...), ...)
          - pattern: return urllib.request.urlopen(..., request.$W.get(...), ...)
          - pattern: urllib.request.urlopen(..., $S.format(..., request.$W(...), ...), ...)
          - pattern: urllib.request.urlopen(..., $S % request.$W(...), ...)
          - pattern: urllib.request.urlopen(..., f"...{request.$W(...)}...", ...)
          - pattern: urllib.request.urlopen(..., request.$W(...), ...)
          - pattern: |
              $DATA = request.$W(...)
              ...
              urllib.request.urlopen(..., $DATA, ...)
          - pattern: |
              $DATA = request.$W(...)
              ...
              $INTERM = $DATA
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: |
              $DATA = request.$W(...)
              ...
              urllib.request.urlopen(..., $STR.format(..., $DATA, ...), ...)
          - pattern: |
              $DATA = request.$W(...)
              ...
              $INTERM = $STR.format(..., $DATA, ...)
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: |
              $DATA = request.$W(...)
              ...
              urllib.request.urlopen(..., $STR % $DATA, ...)
          - pattern: |
              $DATA = request.$W(...)
              ...
              $INTERM = $STR % $DATA
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: |
              $DATA = request.$W(...)
              ...
              urllib.request.urlopen(..., f"...{$DATA}...", ...)
          - pattern: |
              $DATA = request.$W(...)
              ...
              $INTERM = f"...{$DATA}..."
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: |
              $DATA = request.$W(...)
              ...
              urllib.request.urlopen(..., $STR + $DATA, ...)
          - pattern: |
              $DATA = request.$W(...)
              ...
              $INTERM = $STR + $DATA
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: $A = urllib.request.urlopen(..., request.$W(...), ...)
          - pattern: return urllib.request.urlopen(..., request.$W(...), ...)
          - pattern: urllib.request.urlopen(..., $S.format(..., request.$W[...], ...), ...)
          - pattern: urllib.request.urlopen(..., $S % request.$W[...], ...)
          - pattern: urllib.request.urlopen(..., f"...{request.$W[...]}...", ...)
          - pattern: urllib.request.urlopen(..., request.$W[...], ...)
          - pattern: |
              $DATA = request.$W[...]
              ...
              urllib.request.urlopen(..., $DATA, ...)
          - pattern: |
              $DATA = request.$W[...]
              ...
              $INTERM = $DATA
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: |
              $DATA = request.$W[...]
              ...
              urllib.request.urlopen(..., $STR.format(..., $DATA, ...), ...)
          - pattern: |
              $DATA = request.$W[...]
              ...
              $INTERM = $STR.format(..., $DATA, ...)
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: |
              $DATA = request.$W[...]
              ...
              urllib.request.urlopen(..., $STR % $DATA, ...)
          - pattern: |
              $DATA = request.$W[...]
              ...
              $INTERM = $STR % $DATA
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: |
              $DATA = request.$W[...]
              ...
              urllib.request.urlopen(..., f"...{$DATA}...", ...)
          - pattern: |
              $DATA = request.$W[...]
              ...
              $INTERM = f"...{$DATA}..."
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: |
              $DATA = request.$W[...]
              ...
              urllib.request.urlopen(..., $STR + $DATA, ...)
          - pattern: |
              $DATA = request.$W[...]
              ...
              $INTERM = $STR + $DATA
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: $A = urllib.request.urlopen(..., request.$W[...], ...)
          - pattern: return urllib.request.urlopen(..., request.$W[...], ...)
          - pattern: urllib.request.urlopen(..., $S.format(..., request.$W, ...), ...)
          - pattern: urllib.request.urlopen(..., $S % request.$W, ...)
          - pattern: urllib.request.urlopen(..., f"...{request.$W}...", ...)
          - pattern: urllib.request.urlopen(..., request.$W, ...)
          - pattern: |
              $DATA = request.$W
              ...
              urllib.request.urlopen(..., $DATA, ...)
          - pattern: |
              $DATA = request.$W
              ...
              $INTERM = $DATA
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: |
              $DATA = request.$W
              ...
              urllib.request.urlopen(..., $STR.format(..., $DATA, ...), ...)
          - pattern: |
              $DATA = request.$W
              ...
              $INTERM = $STR.format(..., $DATA, ...)
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: |
              $DATA = request.$W
              ...
              urllib.request.urlopen(..., $STR % $DATA, ...)
          - pattern: |
              $DATA = request.$W
              ...
              $INTERM = $STR % $DATA
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: |
              $DATA = request.$W
              ...
              urllib.request.urlopen(..., f"...{$DATA}...", ...)
          - pattern: |
              $DATA = request.$W
              ...
              $INTERM = f"...{$DATA}..."
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: |
              $DATA = request.$W
              ...
              urllib.request.urlopen(..., $STR + $DATA, ...)
          - pattern: |
              $DATA = request.$W
              ...
              $INTERM = $STR + $DATA
              ...
              urllib.request.urlopen(..., $INTERM, ...)
          - pattern: $A = urllib.request.urlopen(..., request.$W, ...)
          - pattern: return urllib.request.urlopen(..., request.$W, ...)

Examples

ssrf-injection-urllib.py

def test1():
    from urllib.request import urlopen
    from django.shortcuts import render

    def send_to_redis(request):
        # ruleid: ssrf-injection-urllib
        bucket = request.GET.get("bucket")
        inner_response = urlopen("http://my.redis.foo/{}".format(bucket), data=3)
        return render({"response_code": inner_response.status_code})

def test2():
    from urllib.request import urlopen
    from django.http import HttpResponse

    def send_to_redis(request):
        # ruleid: ssrf-injection-urllib
        bucket = request.GET.get("bucket")
        inner_response = urlopen("http://my.redis.foo/{}".format(bucket), data=3)
        return HttpResponse(body = {"response_code": inner_response.status_code})