java.spring.security.injection.tainted-url-host.tainted-url-host

profile photo of semgrepsemgrep
Author
unknown
Download Count*

User data flows into the host portion of this manually-constructed URL. This could allow an attacker to send data to their own server, potentially exposing sensitive data such as cookies or authorization information sent with this request. They could also probe internal servers or other resources that the server runnig this code can access. (This is called server-side request forgery, or SSRF.) Do not allow arbitrary hosts. Instead, create an allowlist for approved hosts hardcode the correct host, or ensure that the user data can only affect the path or parameters.

Run Locally

Run in CI

Defintion

rules:
  - id: tainted-url-host
    languages:
      - java
    severity: ERROR
    message: User data flows into the host portion of this manually-constructed URL.
      This could allow an attacker to send data to their own server, potentially
      exposing sensitive data such as cookies or authorization information sent
      with this request. They could also probe internal servers or other
      resources that the server runnig this code can access. (This is called
      server-side request forgery, or SSRF.) Do not allow arbitrary hosts.
      Instead, create an allowlist for approved hosts hardcode the correct host,
      or ensure that the user data can only affect the path or parameters.
    options:
      interfile: true
    metadata:
      cwe:
        - "CWE-918: Server-Side Request Forgery (SSRF)"
      owasp:
        - A10:2021 - Server-Side Request Forgery (SSRF)
      references:
        - https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
      category: security
      technology:
        - java
        - spring
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - vuln
      impact: MEDIUM
      likelihood: MEDIUM
      confidence: MEDIUM
      interfile: true
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - Server-Side Request Forgery (SSRF)
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  $METHODNAME(..., @$REQ(...) $TYPE $SOURCE,...) {
                    ...
                  }
              - pattern-inside: |
                  $METHODNAME(..., @$REQ $TYPE $SOURCE,...) {
                    ...
                  }
          - metavariable-regex:
              metavariable: $TYPE
              regex: ^(?!(Integer|Long|Float|Double|Char|Boolean|int|long|float|double|char|boolean))
          - metavariable-regex:
              metavariable: $REQ
              regex: (RequestBody|PathVariable|RequestParam|RequestHeader|CookieValue|ModelAttribute)
          - focus-metavariable: $SOURCE
    pattern-sinks:
      - pattern-either:
          - pattern: new URL($ONEARG)
          - patterns:
              - pattern-either:
                  - pattern: |
                      "$URLSTR" + ...
                  - pattern: |
                      "$URLSTR".concat(...)
                  - patterns:
                      - pattern-inside: |
                          StringBuilder $SB = new StringBuilder("$URLSTR");
                          ...
                      - pattern: $SB.append(...)
                  - patterns:
                      - pattern-inside: |
                          $VAR = "$URLSTR";
                          ...
                      - pattern: $VAR += ...
                  - patterns:
                      - pattern: String.format("$URLSTR", ...)
                      - pattern-not: String.format("$URLSTR", "...", ...)
                  - patterns:
                      - pattern-inside: |
                          String $VAR = "$URLSTR";
                          ...
                      - pattern: String.format($VAR, ...)
              - metavariable-regex:
                  metavariable: $URLSTR
                  regex: http(s?)://%(v|s|q).*

Examples

tainted-url-host.java

package org.sasanlabs.service.vulnerability.ssrf;

import com.nimbusds.jose.util.StandardCharset;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.sasanlabs.internal.utility.LevelConstants;
import org.sasanlabs.internal.utility.annotations.AttackVector;
import org.sasanlabs.internal.utility.annotations.VulnerableAppRequestMapping;
import org.sasanlabs.internal.utility.annotations.VulnerableAppRestController;
import org.sasanlabs.service.vulnerability.bean.GenericVulnerabilityResponseBean;
import org.sasanlabs.vulnerability.types.VulnerabilityType;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.RequestParam;

@VulnerableAppRestController(descriptionLabel = "SSRF_VULNERABILITY", value = "SSRFVulnerability")
public class SSRFVulnerability {

    private static final String IMAGE_URL = "imageurl";
    private static final transient Logger LOGGER = LogManager.getLogger(SSRFVulnerability.class);

    @AttackVector(
            vulnerabilityExposed = VulnerabilityType.SIMPLE_SSRF,
            description = "IMAGE_URL_PASSED_TO_REQUEST")
    @VulnerableAppRequestMapping(value = LevelConstants.LEVEL_1, htmlTemplate = "LEVEL_1/SSRF")
    public ResponseEntity<GenericVulnerabilityResponseBean<byte[]>> getVulnerablePayloadLevel1(
            @RequestParam(IMAGE_URL) String urlImage) {
        try {
            // ruleid: tainted-url-host
            URL u = new URL(urlImage);
            URLConnection urlConnection = u.openConnection();
            byte[] bytes;
            try (InputStream in = urlConnection.getInputStream()) {
                bytes = StreamUtils.copyToByteArray(urlConnection.getInputStream());
            }
            return new ResponseEntity<>(
                    new GenericVulnerabilityResponseBean<>(bytes, true), HttpStatus.OK);
        } catch (Exception e) {
            LOGGER.error(
                    "Following exception occurred while opening the connection to {}", urlImage, e);
        }
        return new ResponseEntity<>(
                new GenericVulnerabilityResponseBean<>(
                        ("Failed to fetch image from URL " + urlImage)
                                .getBytes(StandardCharset.UTF_8),
                        false),
                HttpStatus.BAD_REQUEST);
    }
}


@RestController
@RequestMapping("/user03")
public class User03Controller {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/get")
    public UserDTO get(@RequestParam("id") Integer id) {
        // ok: tainted-url-host
        String url = String.format("http://%s/user/get?id=%d", "demo-provider", id);
        return restTemplate.getForObject(url, UserDTO.class);
    }

    @PostMapping("/add")
    public Integer add(UserAddDTO addDTO) {
        // 请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        // 请求体
        String body = JSON.toJSONString(addDTO);
        // 创建 HttpEntity 对象
        HttpEntity<String> entity = new HttpEntity<>(body, headers);
        // 执行请求
        // ok: tainted-url-host
        String url = String.format("http://%s/user/add", "demo-provider");
        return restTemplate.postForObject(url, entity, Integer.class);
    }

}