Skip to content

Instantly share code, notes, and snippets.

@conikeec
Last active August 6, 2020 21:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save conikeec/11463d2004d212cf91e1331b94a01920 to your computer and use it in GitHub Desktop.
Save conikeec/11463d2004d212cf91e1331b94a01920 to your computer and use it in GitHub Desktop.

CVE-2020–15957 Hunting using ShiftLeft Ocular (powering ShiftLeft NG-SAST)

Startup Ocular Shell

sl ocular

Ocular shell starts up and looks like this:

 ██████╗  ██████╗██╗   ██╗██╗      █████╗ ██████╗
██╔═══██╗██╔════╝██║   ██║██║     ██╔══██╗██╔══██╗
██║   ██║██║     ██║   ██║██║     ███████║██████╔╝
██║   ██║██║     ██║   ██║██║     ██╔══██║██╔══██╗
╚██████╔╝╚██████╗╚██████╔╝███████╗██║  ██║██║  ██║
 ╚═════╝  ╚═════╝ ╚═════╝ ╚══════╝╚═╝  ╚═╝╚═╝  ╚═╝ 
Version: 0.3.114
Type `help` or `browse(help)` to begin
ocular>

ℹ️ Note: Following commands are now run on the Ocular Shell

Download the vulnerable JAR

https://github.com/DP-3T/dp3t-sdk-backend/releases/download/v1.1.0/dpppt-backend-sdk-ws.jar

Import Code and create a CPG

val JARFILE = "/XXXX/dpppt-backend-sdk-ws.jar"
importCode(JARFILE)

Apply Policies to graph

run.securityprofile

SCA (Package Dependencies)

//get dependencies (name, version) pair from manifest
def getDependencies(packageJsonFile : String) : Map[String,String] = {
    val packageString = os.read(os.Path(packageJsonFile))
    val packageData = ujson.read(packageString)
    packageData.obj("dependencies").obj.toMap.map { case(k,v)=> k->v.toString.replaceAll("\"","") }
}

getDependencies(PACKAGE_FILE)

Get Exposed Sources in Application (this represents all exposed API endpoints in code)

val source = cpg.method.filter(_.tag.name("EXPOSED_METHOD")).parameter
val api = cpg.method.name(".*=>.*").parameter

All Findings (based on automated security profile)

Table of Findings in text format:

cpg.finding.p

Title: JWT Unsafe Parsing: Unsafe parsing of JWT token via `token` to log in `DPPTJwtDecoder.decode`
Score: 5.0
Categories: [a3-sensitive-data-exposure]
Flow ids: [9219088073995584368]
Description: When `alg:none` is used, it becomes possible to skip signature check which may lead to authentication bypass

## Countermeasures

This vulnerability can be prevented by using `parseClaimsJws` method instead of `parse` method

## Additional information

**[CWE-347](https://cwe.mitre.org/data/definitions/347.html)**

**[OWASP-A3](https://owasp.org/www-project-top-ten/OWASP_Top_Ten_2017/Top_10-2017_A3-Sensitive_Data_Exposure)**
-------------------------------------
 ____________________________________________________________________________________
 | tracked| lineNumber| method| file                                                 |
 |===================================================================================|
 | token  | 38        | decode| org/dpppt/backend/sdk/ws/security/DPPTJwtDecoder.java|
 | token  | 39        | decode| org/dpppt/backend/sdk/ws/security/DPPTJwtDecoder.java|
 | param0 | N/A       | parse | io/jsonwebtoken/JwtParser.java                       |

Consumable JSON Format:

cpg.finding.toJsonPretty

Hunting CVE-2020–15957

What happends under-the hood of automated finding

Verify the name of the class than extends/implements org.springframework.security.oauth2.jwt.JwtDecoder interface
cpg.typeDecl.l.filter(_.inheritsFromTypeFullName.contains("org.springframework.security.oauth2.jwt.JwtDecoder"))
res3: List[TypeDecl] = List(
  TypeDecl(
    id -> 2150L,
    name -> "DPPTJwtDecoder",
    fullName -> "org.dpppt.backend.sdk.ws.security.DPPTJwtDecoder",
    isExternal -> false,
    inheritsFromTypeFullName -> List(
      "java.lang.Object",
      "org.springframework.security.oauth2.jwt.JwtDecoder"
    ),
    astParentType -> "NAMESPACE_BLOCK",
    astParentFullName -> "org/dpppt/backend/sdk/ws/security/DPPTJwtDecoder.java:org.dpppt.backend.sdk.ws.security",
    aliasTypeFullName -> None,
    order -> null,
    filename -> "org/dpppt/backend/sdk/ws/security/DPPTJwtDecoder.java"
  )
)
Get name of the implementing class
val jwtImplClasszName = cpg.typeDecl.l.filter(_.inheritsFromTypeFullName.contains("org.springframework.security.oauth2.jwt.JwtDecoder")).head.fullName

jwtImplClasszName: String = "org.dpppt.backend.sdk.ws.security.DPPTJwtDecoder"
Get methods implemented by jwtImplClasszName
cpg.method.fullName(".*"+jwtImplClasszName+".*").l

res9: List[Method] = List(
  Method(
    id -> 5976128189582169760L,
    name -> "setJwtValidator",
    fullName -> "org.dpppt.backend.sdk.ws.security.DPPTJwtDecoder.setJwtValidator:void(org.springframework.security.oauth2.core.OAuth2TokenValidator)",
    isExternal -> false,
    signature -> "void(org.springframework.security.oauth2.core.OAuth2TokenValidator)",
    astParentType -> "TYPE_DECL",
    astParentFullName -> "org.dpppt.backend.sdk.ws.security.DPPTJwtDecoder",
    lineNumber -> Some(32),
    columnNumber -> None,
    lineNumberEnd -> None,
    columnNumberEnd -> None,
    order -> null,
    filename -> "org/dpppt/backend/sdk/ws/security/DPPTJwtDecoder.java",
    hasMapping -> None,
    depthFirstOrder -> Some(-8),
    internalFlags -> Some(4),
    binarySignature -> Some("(Lorg/springframework/security/oauth2/core/OAuth2TokenValidator;)V")
  ),
  Method(
    id -> 6387388624065243808L,
    name -> "<init>",
    fullName -> "org.dpppt.backend.sdk.ws.security.DPPTJwtDecoder.<init>:void(java.security.PublicKey)",
    isExternal -> false,
    signature -> "void(java.security.PublicKey)",
    astParentType -> "TYPE_DECL",
    astParentFullName -> "org.dpppt.backend.sdk.ws.security.DPPTJwtDecoder",
    lineNumber -> Some(27),
    columnNumber -> None,
    lineNumberEnd -> None,
    columnNumberEnd -> None,
    order -> null,
    filename -> "org/dpppt/backend/sdk/ws/security/DPPTJwtDecoder.java",
    hasMapping -> None,
    depthFirstOrder -> Some(-22),
    internalFlags -> Some(4),
    binarySignature -> Some("(Ljava/security/PublicKey;)V")
  ),
  Method(
    id -> 2733168305168762944L,
    name -> "decode",
    fullName -> "org.dpppt.backend.sdk.ws.security.DPPTJwtDecoder.decode:org.springframework.security.oauth2.jwt.Jwt(java.lang.String)",
    isExternal -> false,
    signature -> "org.springframework.security.oauth2.jwt.Jwt(java.lang.String)",
    astParentType -> "TYPE_DECL",
    astParentFullName -> "org.dpppt.backend.sdk.ws.security.DPPTJwtDecoder",
    lineNumber -> Some(38),
    columnNumber -> None,
    lineNumberEnd -> None,
    columnNumberEnd -> None,
    order -> null,
    filename -> "org/dpppt/backend/sdk/ws/security/DPPTJwtDecoder.java",
    hasMapping -> None,
    depthFirstOrder -> Some(-139),
    internalFlags -> Some(4),
    binarySignature -> Some("(Ljava/lang/String;)Lorg/springframework/security/oauth2/jwt/Jwt;")
  )
)
Conduct dataflow analysis from attacker controlled source (decode) to security sensitive sink (parse)
var source = cpg.method.fullName(".*org.dpppt.backend.sdk.ws.security.DPPTJwtDecoder.decode.*").parameter 

var sink = cpg.method.fullName(".*parse(.*").parameter 

sink.reachableBy(source).flows.p 
res7: List[String] = List(
  """ _______________________________________________________________________________________________________
 | tracked    | lineNumber| method               | file                                                 |
 |======================================================================================================|
 | this       | 38        | decode               | org/dpppt/backend/sdk/ws/security/DPPTJwtDecoder.java|
 | this.parser| 39        | decode               | org/dpppt/backend/sdk/ws/security/DPPTJwtDecoder.java|
 | param1     | N/A       | <operator>.assignment| N/A                                                  |
 | param0     | N/A       | <operator>.assignment| N/A                                                  |
 | $r0        | 39        | decode               | org/dpppt/backend/sdk/ws/security/DPPTJwtDecoder.java|
 | $r0        | 39        | decode               | org/dpppt/backend/sdk/ws/security/DPPTJwtDecoder.java|
 | this       | N/A       | parse                | io/jsonwebtoken/JwtParser.java                       |
""",
  """ ____________________________________________________________________________________________
 | tracked| lineNumber| method        | file                                                 |
 |===========================================================================================|
 | token  | 38        | decode        | org/dpppt/backend/sdk/ws/security/DPPTJwtDecoder.java|
 | token  | 39        | decode        | org/dpppt/backend/sdk/ws/security/DPPTJwtDecoder.java|
 | param0 | N/A       | parse         | io/jsonwebtoken/JwtParser.java                       |
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment