CVE-2023-33201: Improper Certificate Validation (CWE-295)
value: Bouncy Castle For Java before 1.74 is affected by an LDAP injection vulnerability. The vulnerability only affects applications that use an LDAP CertStore from Bouncy Castle to validate X.509 certificates. During the certificate validation process, Bouncy Castle inserts the certificate's Subject Name into an LDAP search filter without any escaping, which leads to an LDAP injection vulnerability.
CWE description: The product does not validate, or incorrectly validates, a certificate.
CVSS v3 base score: 5.3
Published: 2023-07-05T03:15Z
Repository: https://github.com/bcgit/bc-java
Language: Java
Files changed: prov/src/main/java/org/bouncycastle/jce/provider/X509LDAPCertStoreSpi.java
Commit date: 2023-04-25 23:11:52 -0400
Commit message: added filter encode to search

Diff: diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509LDAPCertStoreSpi.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509LDAPCertStoreSpi.java
index 874444267..ba1ac2452 100644
--- a/prov/src/main/java/org/bouncycastle/jce/provider/X509LDAPCertStoreSpi.java
+++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509LDAPCertStoreSpi.java
@@ -373,10 +373,63 @@ public Collection engineGetCRLs(CRLSelector selector)
                 "CRL cannot be constructed from LDAP result " + e);
         }
 
         return crlSet;
     }
+    private static String[] FILTER_ESCAPE_TABLE = new String['\\' + 1];
+
+
+    static {
+
+        // Filter encoding table -------------------------------------
+
+        // fill with char itself
+        for (char c = 0; c < FILTER_ESCAPE_TABLE.length; c++) {
+            FILTER_ESCAPE_TABLE[c] = String.valueOf(c);
+        }
+
+        // escapes (RFC2254)
+        FILTER_ESCAPE_TABLE['*'] = "\\2a";
+        FILTER_ESCAPE_TABLE['('] = "\\28";
+        FILTER_ESCAPE_TABLE[')'] = "\\29";
+        FILTER_ESCAPE_TABLE['\\'] = "\\5c";
+        FILTER_ESCAPE_TABLE[0] = "\\00";
+
+    }
+
+    /**
+     * Escape a value for use in a filter.
+     * @param value the value to escape.
+     * @return a properly escaped representation of the supplied value.
+     */
+    private String filterEncode(String value)
+    {
+        if (value == null)
+        {
+            return null;
+        }
+
+        // make buffer roomy
+        StringBuilder encodedValue = new StringBuilder(value.length() * 2);
+
+        int length = value.length();
+
+        for (int i = 0; i < length; i++) {
+
+            char c = value.charAt(i);
+
+            if (c < FILTER_ESCAPE_TABLE.length) {
+                encodedValue.append(FILTER_ESCAPE_TABLE[c]);
+            }
+            else {
+                // default: add the char
+                encodedValue.append(c);
+            }
+        }
+
+        return encodedValue.toString();
+    }
 
     /**
      * Returns a Set of byte arrays with the certificate or CRL encodings.
      *
      * @param attributeName  The attribute name to look for in the LDAP.
@@ -386,11 +439,12 @@ public Collection engineGetCRLs(CRLSelector selector)
      * @return Set of byte arrays with the certificate encodings.
      */
     private Set search(String attributeName, String attributeValue,
                        String[] attrs) throws CertStoreException
     {
-        String filter = attributeName + "=" + attributeValue;
+        String filter = attributeName + "=" + filterEncode(attributeValue);
+        System.out.println(filter);
         if (attributeName == null)
         {
             filter = null;
         }
         DirContext ctx = null;

Vulnerable code:         String filter = attributeName + "=" + attributeValue;
Fixed code:     private static String[] FILTER_ESCAPE_TABLE = new String['\\' + 1];


    static {

        // Filter encoding table -------------------------------------

        // fill with char itself
        for (char c = 0; c < FILTER_ESCAPE_TABLE.length; c++) {
            FILTER_ESCAPE_TABLE[c] = String.valueOf(c);
        }

        // escapes (RFC2254)
        FILTER_ESCAPE_TABLE['*'] = "\\2a";
        FILTER_ESCAPE_TABLE['('] = "\\28";
        FILTER_ESCAPE_TABLE[')'] = "\\29";
        FILTER_ESCAPE_TABLE['\\'] = "\\5c";
        FILTER_ESCAPE_TABLE[0] = "\\00";

    }

    /**
     * Escape a value for use in a filter.
     * @param value the value to escape.
     * @return a properly escaped representation of the supplied value.
     */
    private String filterEncode(String value)
    {
        if (value == null)
        {
            return null;
        }

        // make buffer roomy
        StringBuilder encodedValue = new StringBuilder(value.length() * 2);

        int length = value.length();

        for (int i = 0; i < length; i++) {

            char c = value.charAt(i);

            if (c < FILTER_ESCAPE_TABLE.length) {
                encodedValue.append(FILTER_ESCAPE_TABLE[c]);
            }
            else {
                // default: add the char
                encodedValue.append(c);
            }
        }

        return encodedValue.toString();
    }
        String filter = attributeName + "=" + filterEncode(attributeValue);
        System.out.println(filter);