Skip to content

Latest commit

 

History

History
145 lines (115 loc) · 12.1 KB

File metadata and controls

145 lines (115 loc) · 12.1 KB

Combined Vulnerability List

1. Uncontrolled Resource Consumption via Deeply Nested Expressions

  • Description: An attacker can craft a deeply nested JMESPath expression that, when parsed and interpreted, consumes excessive CPU resources, potentially leading to performance degradation. The vulnerability arises from the recursive nature of the parser and interpreter, which can be exploited by deeply nested structures. To trigger this vulnerability, an attacker would need to send a specially crafted JMESPath query to the application. This query would contain an expression with an extremely deep level of nesting, for example, by repeatedly chaining subexpressions like a.a.a.a.a.... When the application attempts to parse and execute this expression using the go-jmespath library, the recursive parsing and interpretation functions will consume significant CPU resources.

  • Impact: High CPU consumption on the server processing the JMESPath expression. This can lead to several negative consequences:

    • Performance degradation for all users of the application due to resource exhaustion.
    • In shared hosting environments or applications with usage quotas, this could lead to exceeding resource limits, potentially resulting in service suspension or additional costs.
    • Denial of Service (DoS) in extreme cases, where the server becomes unresponsive due to overwhelming CPU load.
    • Increased latency and slower response times for legitimate requests.
  • Vulnerability Rank: High

  • Currently Implemented Mitigations: None. A review of the codebase indicates that there are no explicit measures in place to limit the complexity or depth of JMESPath expressions processed by the application. The application currently parses and executes any JMESPath expression provided without any validation or restrictions on its structure.

  • Missing Mitigations: To mitigate this vulnerability, the following mitigations should be implemented:

    • Implement Expression Depth Limits: Introduce a maximum allowable depth for JMESPath expressions. This limit would restrict the level of nesting permitted in the expression's Abstract Syntax Tree (AST). Expressions exceeding this depth should be rejected during parsing.
    • Implement Parsing/Interpretation Timeouts: Set timeouts for both the parsing and interpretation stages of JMESPath expression processing. If either stage exceeds the timeout duration, the process should be terminated to prevent excessive resource consumption.
    • Complexity Analysis and Rejection: Develop a mechanism to analyze the complexity of a JMESPath expression before parsing or interpretation. This could involve analyzing the structure of the expression and estimating its computational cost. Overly complex expressions should be rejected early in the processing pipeline.
  • Preconditions: The following preconditions must be met for this vulnerability to be exploitable:

    • The application must utilize the go-jmespath library for processing JMESPath expressions.
    • The application must allow external users to provide JMESPath expressions as input, either directly or indirectly, that are then processed by the application. This is typical in applications that offer filtering, querying, or data transformation capabilities based on user-defined expressions.
    • There must be no existing limits or controls on the complexity or processing time of JMESPath expressions.
  • Source Code Analysis:

    The vulnerability arises from the recursive nature of both the parsing and interpretation stages within the go-jmespath library.

    Parser (parser.go):

    The parseExpression function in parser.go is the primary entry point for parsing expressions and is inherently recursive.

    func (p *Parser) parseExpression(bindingPower int) (ASTNode, error) {
        // ... (initial parsing logic) ...
        leftNode, err := p.nud(leftToken) // nud: Null Denotation - handles prefix tokens
        if err != nil {
            return ASTNode{}, err
        }
        currentToken := p.current()
        // Loop continues as long as the current token has higher binding power
        for bindingPower < bindingPowers[currentToken] {
            p.advance() // Move to the next token
            leftNode, err = p.led(currentToken, leftNode) // led: Left Denotation - handles infix tokens (RECURSIVE CALL)
            if err != nil {
                return ASTNode{}, err
            }
            currentToken = p.current()
        }
        return leftNode, nil
    }

    The led functions, responsible for handling infix operators like ., [, (, also contribute to recursion. For example, the led function for the dot operator (tDot):

    func (p *Parser) led(tokenType tokType, node ASTNode) (ASTNode, error) {
    	switch tokenType {
    	case tDot:
    		if p.current() != tStar {
    			right, err := p.parseDotRHS(bindingPowers[tDot]) // RECURSIVE CALL for right-hand side after the dot
    			return ASTNode{
    				nodeType: ASTSubexpression,
    				children: []ASTNode{node, right},
    			}, err
    		}
            // ... (handling for wildcard `*.`) ...
        }
        // ... (other led cases) ...
        return ASTNode{}, fmt.Errorf("unexpected token: %s", tokenType) // Should not reach here in valid expressions
    }

    parseDotRHS, parseProjectionRHS, and other parsing functions similarly involve recursive calls to handle nested expressions, creating a deeply nested Abstract Syntax Tree (AST) for deeply nested input expressions.

    Interpreter (interpreter.go):

    The Execute function in interpreter.go is responsible for traversing and interpreting the AST. It is also recursive, mirroring the structure of the AST.

    func (intr *treeInterpreter) Execute(node ASTNode, value interface{}) (interface{}, error) {
    	switch node.nodeType {
        // ... (handling for various AST node types) ...
        case ASTSubexpression, ASTIndexExpression:
    		left, err := intr.Execute(node.children[0], value) // RECURSIVE CALL to evaluate the left child
    		if err != nil {
    			return nil, err
    		}
    		return intr.Execute(node.children[1], left) // RECURSIVE CALL to evaluate the right child with the result of the left child
        // ... (handling for projection and filter projection) ...
        case ASTFilterProjection:
            // ...
            for _, element := range sliceType {
    			result, err := intr.Execute(compareNode, element) // RECURSIVE CALL to evaluate the filter condition
                // ...
                current, err := intr.Execute(node.children[1], element) // RECURSIVE CALL to evaluate the projection expression
                // ...
            }
        // ...
        case ASTProjection:
            // ...
            for _, element := range sliceType {
    			current, err = intr.Execute(node.children[1], element) // RECURSIVE CALL to evaluate the projection expression
                // ...
            }
        // ...
        }
        // ... (other node type handling) ...
    }

    The Execute function's recursive calls directly correspond to the nested structure of the AST. A deeply nested AST, resulting from a deeply nested JMESPath expression, will lead to deep recursion in the Execute function, consuming significant stack space and CPU time as it traverses the tree.

    Visualization:

    Imagine the AST as a tree. For a deeply nested expression like a.a.a.a..., the AST becomes a long chain of nodes, each representing a subexpression. Both parsing and interpretation involve traversing this tree from the root to the deepest leaves recursively. The deeper the tree (more nesting), the more recursive calls are made, leading to increased resource consumption.

  • Security Test Case: To validate this vulnerability, perform the following steps from an external attacker's perspective, assuming access to a publicly available instance of the application:

    1. Identify the JMESPath Input Point: Locate where the application accepts JMESPath expressions as input. This could be through API parameters, URL query parameters, form fields, or other input mechanisms.

    2. Craft a Deeply Nested JMESPath Expression: Create a JMESPath expression with a very deep level of nesting. A simple way to do this is to repeatedly chain the subexpression operator (.). For example:

      a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.        ```
      Generate this expression programmatically to create expressions of varying depths (e.g., 100, 1000, 10000 levels of nesting).
      
      
    3. Send the Malicious Query: Submit the crafted JMESPath query to the application through the identified input point.

    4. Monitor Server Resources: Observe the server's CPU usage. Use system monitoring tools (e.g., top, htop, cloud provider monitoring dashboards) to track the CPU utilization of the server processing the request.

    5. Analyze CPU Usage: Compare the CPU usage when processing the malicious query with the CPU usage during normal application operation or when processing benign JMESPath queries.

    6. Confirm Vulnerability: If the CPU usage spikes significantly and remains elevated while processing the deeply nested JMESPath expression, and if this is disproportionate to the resource usage of normal operations, then the vulnerability is confirmed. Increased response times or application unresponsiveness during the test also indicate successful exploitation.

    7. Repeat with Varying Depths: Repeat steps 2-6 with JMESPath expressions of increasing nesting depth to determine the threshold at which resource exhaustion becomes significant and to assess the severity of the vulnerability at different depths.

    By following these steps, you can effectively test and demonstrate the uncontrolled resource consumption vulnerability caused by deeply nested JMESPath expressions.