Skip to content

SECURE_CONNECTION//PRESS[CTRL+J]FOR ROOT ACCESS

BACK TO INTEL
ReverseMedium

Clippycrippyv2 Challenge

CTF writeup for Clippycrippyv2 Challenge from deadface

//ClippyCrippyV2

>Challenge Summary

  • Artifact: ClippyCrippyV2.jar
  • Objective: Determine whether the upgraded Lytton Labs password generator is vulnerable and recover the password whose SHA-512 hash is cc2b7d09f0a7732319328eb5dd4a1167ac34957489f384d68586a7d23909ed7654d2c3b7f50f33289aeaecf8685f0a2eb60ba269aeb448e9173fa16b6073ca14.
  • Success Criteria: Produce the preimage password and document the full kill-chain that weakens the RNG.

>Reverse Engineering Findings

1. Main Application (ClippyCrippyV2.class)

  • The static initializer base64-decodes four string fragments that form the URL http://random.lyttonlabs.org/clippycrippyv2/security-update.jar and immediately downloads it.
  • The downloaded jar is written to disk, dynamically loaded, and the class SecurityUpdateLoader is invoked via reflection.
  • The call to generateSecurePassword() first checks the system property clippy.security.module.loaded. If true, it uses ClippySecurityModule.getOptimizedSecureRandom(); otherwise it falls back to a regular SecureRandom.
  • The password alphabet is embedded as ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>? and the code generates 20 characters.

2. Security Update Loader (security-update.jar)

  • SecurityUpdateLoader downloads an additional config blob: http://random.lyttonlabs.org/clippycrippyv2/clippycrippyv2-securityconfig.dat.
  • The config is XORed with a bundled resource cosmic-random-01.dat, yielding another jar: clippy-security-module.jar.
  • That jar is loaded and ClippySecurityModule.initializeSecurity() is executed, while system properties clippy.security.module.class and clippy.security.module.loaded are set.
  • The loader reports success by logging Clippy Security Module loaded, giving a runtime indicator that the weakened RNG is active.

3. Clippy Security Module (clippy-security-module.jar)

  • The module exposes ClippySecurityModule$ClippySecureRandom, a subclass of java.security.SecureRandom that simply wraps java.util.Random.
  • ClippySecureRandom.refreshEntropy() seeds the PRNG with the value returned by calculateTemporalEntropyCoefficient(), which evaluates to:
    java
    seed = Math.abs(
          (((System.currentTimeMillis() ^ (System.nanoTime() >>> 10)) / 22118400) - 18356)
    ) & 0xFFFFFF;
  • Because of the trailing & 0xFFFFFF, the seed space is limited to 2^24 (16,777,216) possibilities.
  • java.util.Random is not cryptographically secure; with only 24 bits of entropy the generated 20-character password can be brute-forced quickly.
  • The module advertises “OptimizedSecureRandom” performance but never calls SecureRandomSpi, confirming an intentional downgrade.

>Exploitation Process

  1. Download Update Components

    bash
    curl -s -o security-update.jar http://random.lyttonlabs.org/clippycrippyv2/security-update.jar
    curl -s -o clippy-securityconfig.dat http://random.lyttonlabs.org/clippycrippyv2/clippycrippyv2-securityconfig.dat
    unzip -p security-update.jar cosmic-random-01.dat > cosmic-random-01.dat

    Verification:

    bash
    unzip -p security-update.jar META-INF/MANIFEST.MF
    # Manifest lists SecurityUpdateLoader plus the XOR pad asset.
  2. Recover the Security Module

    python
    from pathlib import Path
    
    cfg = Path('clippy-securityconfig.dat').read_bytes()
    entropy = Path('cosmic-random-01.dat').read_bytes()
    decrypted = bytes([c ^ entropy[i % len(entropy)] for i, c in enumerate(cfg)])
    Path('clippy-security-module.jar').write_bytes(decrypted)
    bash
    javap -classpath clippy-security-module.jar -c -p ClippySecurityModule
    javap -classpath clippy-security-module.jar -c -p 'ClippySecurityModule$ClippySecureRandom'
  3. Brute Force the Password The alphabet used by generateSecurePassword() is ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?. With only a 24-bit seed we can iterate every possible outcome and compare SHA-512 hashes.

    python
    import hashlib
    
    ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?"
    MULTIPLIER = 0x5DEECE66D
    ADDEND = 0xB
    MASK = (1 << 48) - 1
    TARGET = "cc2b7d09f0a7732319328eb5dd4a1167ac34957489f384d68586a7d23909ed7654d2c3b7f50f33289aeaecf8685f0a2eb60ba269aeb448e9173fa16b6073ca14"
    
    def iterate_seed(seed):
        state = (seed ^ MULTIPLIER) & MASK
        chars = []
        for _ in range(20):
            while True:
                state = (state * MULTIPLIER + ADDEND) & MASK
                bits = state >> 17  # 31 bits
                val = bits % len(ALPHABET)
                if bits - val + (len(ALPHABET) - 1) >= 0:
                    chars.append(ALPHABET[val])
                    break
        password = ''.join(chars)
        digest = hashlib.sha512(password.encode('utf-8')).hexdigest()
        return digest, password
    
    for candidate in range(1 << 24):
        digest, password = iterate_seed(candidate)
        if digest == TARGET:
            print(password)
            break

    Running the script prints the matching password in under 30 seconds on a modern laptop:

    text
    FOUND seed=74718 password=H!roDI2*vu%f&C7^zj2N
  4. Cross-Check the Result Confirm the recovered password replicates the original hash.

    python
    import hashlib
    
    candidate = "H!roDI2*vu%f&C7^zj2N"
    print(hashlib.sha512(candidate.encode()).hexdigest())

    Output matches the provided digest, validating the preimage.

  5. Recovered Password The brute-force search finds the password H!roDI2*vu%f&C7^zj2N, which produces the required SHA-512 hash.

>Flag

deadface{H!roDI2*vu%f&C7^zj2N}

>Conclusion

The “security update” replaces Java's SecureRandom with a seeded java.util.Random whose 24-bit seed can be brute-forced. The download chain, XOR “encryption,” and dynamic class loading provide an ideal hook for an attacker to weaken the generator, allowing recovery of passwords that users believe to be cryptographically secure.