Skip to content

Latest commit

 

History

History
147 lines (125 loc) · 10.8 KB

vulnerabilities-workflow-1.md

File metadata and controls

147 lines (125 loc) · 10.8 KB

Combined Vulnerability List

This document consolidates identified vulnerabilities, removing duplicates and providing a comprehensive description for each.

Predictable ULIDs generated by default Make() function and -q flag in command line tool

  • Description:

    1. The ulid.Make() function in the ulid library is designed to generate ULIDs using the current time and random entropy. By default, it utilizes defaultEntropy as the entropy source.
    2. defaultEntropy is initialized to use math/rand.Rand, a pseudo-random number generator that is not cryptographically secure, seeded with the current Unix nano timestamp.
    3. Consequently, ULIDs generated using ulid.Make() by default are predictable, especially if an attacker can observe a sequence of generated ULIDs or estimate the time of generation.
    4. Similarly, the command-line tool ulid provides a -q or --quick flag. When used, this flag also employs math/rand for entropy generation, leading to the creation of predictable ULIDs in the command-line tool as well.
    5. An attacker who can observe or infer the approximate time at which ULIDs are generated might narrow down the seed value for math/rand. With knowledge (or estimation) of the seed, the attacker can reproduce the pseudo-random stream and predict future ULIDs.
    6. This predictability issue affects both the library's default Make() function and the command-line tool when the -q flag is used.
  • Impact:

    • If ULIDs generated by ulid.Make() or ulid -q are used as security-sensitive identifiers (e.g., session IDs, API keys, CSRF tokens, or in URL parameters), this vulnerability can have a high impact.
    • Predictable ULIDs can enable attackers to bypass security measures, gain unauthorized access, perform session hijacking, or execute CSRF attacks by predicting or enumerating valid identifiers.
    • The vulnerability rank is high because predictable identifiers in security contexts can directly lead to breaches of confidentiality and integrity.
  • Vulnerability Rank: high

  • Currently Implemented Mitigations:

    • Documentation: The README.md file explicitly warns that math/rand.Rand is not cryptographically secure and recommends using crypto/rand for security-sensitive use cases. It also notes that Make() uses a "pseudo-random" source of entropy.
    • Command Line Help: The command-line tool help text for the -q flag states "when generating, use non-crypto-grade entropy", indicating that the quick mode is not intended for secure applications.
  • Missing Mitigations:

    • Default Entropy Source: The default entropy source for ulid.Make() should be changed to a cryptographically secure random number generator, such as crypto/rand.Reader.
    • Command Line Tool Default: The command line tool ulid should use crypto/rand.Reader as the default entropy source when generating ULIDs without the -q flag to promote secure-by-default behavior.
    • Security Warning: Add a prominent security warning in the documentation, specifically highlighting the risks of using ulid.Make() and ulid -q in security-sensitive contexts due to the use of pseudo-random entropy. Recommend using ulid.New with crypto/rand.Reader for secure applications.
    • Runtime Check (Optional): Consider adding an optional runtime check or build flag that would enforce the use of crypto/rand in production environments or for security-sensitive operations, although this might add complexity for users.
  • Preconditions:

    • The application or system must be using the ulid.Make() function or the ulid -q command-line tool to generate ULIDs.
    • These generated ULIDs are employed as security-sensitive identifiers where unpredictability is a requirement for maintaining security (e.g., authentication tokens, access control identifiers).
    • An attacker needs to be able to observe or intercept a sufficient number of generated ULIDs, or have knowledge about the system's time, to potentially analyze and predict future values if math/rand is used.
  • Source Code Analysis:

    • /code/ulid.go:

      var defaultEntropy = func() io.Reader {
          rng := rand.New(rand.NewSource(time.Now().UnixNano())) // math/rand is used here
          return &LockedMonotonicReader{MonotonicReader: Monotonic(rng, 0)}
      }()
      
      func Make() (id ULID) {
          return MustNew(Now(), defaultEntropy) // defaultEntropy is used by Make()
      }

      The Make() function, intended for general use, defaults to defaultEntropy, which utilizes math/rand. This makes the generated entropy source predictable.

    • /code/cmd/ulid/main.go:

      func generate(quick, zero bool) {
          entropy := cryptorand.Reader // Default entropy is crypto/rand
          if quick {
              seed := time.Now().UnixNano()
              source := mathrand.NewSource(seed)
              entropy = mathrand.New(source) // math/rand is used when -q is used
          }
          // ...
          id, err := ulid.New(ulid.Timestamp(time.Now()), entropy)
          // ...
      }

      In the generate function of the command-line tool, the default entropy source is crypto/rand.Reader, which is secure. However, when the -q flag is used, it switches to math/rand, making the generated ULIDs predictable.

  • Security Test Case:

    1. Deploy a service or application that uses ulid.Make() to generate identifiers in a security-sensitive context (e.g., session IDs).
    2. Collect a sequence of ULIDs generated by this service, along with timestamps of their generation if possible.
    3. Alternatively, use the command line tool with -q flag:
      ulid -q
      ulid -q
      ulid -q
      Collect several ULIDs generated using ulid -q.
    4. Analyze the entropy portion of the collected ULIDs. Observe if there are discernible patterns or a lack of expected randomness, especially when compared to ULIDs generated without the -q flag or using crypto/rand directly.
    5. Attempt to predict future ULIDs. Based on the observed patterns or by trying to estimate the seed used for math/rand (if timestamps are available), attempt to predict subsequently generated ULIDs. If successful, this confirms the predictability of ULIDs generated using math/rand and thus the vulnerability.
    6. For command line -q test case, generate a few ULIDs and observe the entropy part for repetition or lack of randomness compared to default ulid command output.

Zeroed Entropy Mode via “-z” Flag – Predictable ULIDs

  • Description:

    1. The command-line tool ulid supports a -z (or --zero) flag intended to “fix entropy to all-zeroes.”
    2. When this flag is used, the generate() function in cmd/ulid/main.go sets the entropy source to zeroReader{}.
    3. The zeroReader's Read method fills every byte of the provided slice with zero.
    4. As a result, when the -z flag is used, the ULID's entropy component becomes entirely predictable (all bytes are zero), and only the timestamp portion varies.
  • Impact:

    • If ULIDs generated with zero entropy (using the -z flag) are inadvertently or intentionally used as unique identifiers in a security-sensitive application (e.g., in URL parameters, session IDs, or API access tokens), they become trivially predictable.
    • An attacker aware that the -z flag is in use can easily enumerate ULID values (since the entropy portion is fixed) and potentially attempt unauthorized actions such as resource enumeration or impersonation.
    • This vulnerability has a high rank because it directly leads to predictable identifiers in security contexts, severely compromising confidentiality and integrity.
  • Vulnerability Rank: high

  • Currently Implemented Mitigations:

    • Explicit Option: The -z flag is provided explicitly as a command-line option with accompanying help text, suggesting it is intended for testing or demonstration purposes.
    • No Runtime Warning: There is no additional guard or runtime warning when the -z flag is used.
  • Missing Mitigations:

    • Usage Prevention/Warning: The tool does not prevent or warn against using the -z flag in production or any environment where ULID unpredictability is critical for security.
    • Confirmation Prompt: A safer implementation might prompt for confirmation or refuse to operate in security-sensitive contexts when an all-zero entropy source is selected, to prevent accidental misuse in production.
  • Preconditions:

    • An operator uses the command-line tool with the -z flag (either inadvertently or due to misconfiguration) in a production or publicly accessible service environment.
    • ULIDs generated with all-zero entropy are employed for purposes beyond mere testing, specifically as security-sensitive identifiers (e.g., authentication tokens or resource identifiers).
  • Source Code Analysis:

    • /code/cmd/ulid/main.go:
      func generate(quick, zero bool) {
          // ...
          if zero {
            entropy = zeroReader{}
          }
          // ...
          id, err := ulid.New(ulid.Timestamp(time.Now()), entropy)
          // ...
      }
    • /code/cmd/ulid/main.go:
      type zeroReader struct{}
      
      func (zeroReader) Read(p []byte) (int, error) {
          for i := range p {
              p[i] = 0
          }
          return len(p), nil
      }
      The generate() function checks for the zero flag. If set, it uses zeroReader as the entropy source. zeroReader's Read method always fills the buffer with zeros. This means every generated ULID with -z will have its lower 10 bytes (entropy) fixed to 0, leaving only the time-based 6 bytes variable.
  • Security Test Case:

    1. Run the ULID command-line tool with the -z flag:
      ulid -z
    2. Observe the generated ULID output. Note that the last 16 characters (encoding the 80-bit entropy) correspond to a fixed value (all zeroes). For example: 01AN4Z07BY77EZK1CQ0XDGZY0Z. The entropy part 77EZK1CQ0XDGZY0Z when decoded from base32 will be all zeros.
    3. Generate multiple ULIDs in quick succession using the -z flag:
      ulid -z
      ulid -z
      ulid -z
      Confirm that only the timestamp portion changes while the entropy portion remains unchanged (all zeroes).
    4. Demonstrate Predictability: Explain that knowing the timestamp is sufficient to almost completely determine the ULID, as the entropy is fixed. In a real-world scenario, if an attacker knows that -z flag is used and can estimate the time of ULID generation, they can effectively predict the entire identifier, confirming the vulnerability.