Bug 2474712 (CVE-2026-73196) - CVE-2026-73196 ipa: FreeIPA: Authenticated DoS in `otptoken-add` via unbounded OTP key decoding/re-encoding
Summary: CVE-2026-73196 ipa: FreeIPA: Authenticated DoS in `otptoken-add` via unbounde...
Keywords:
Status: NEW
Alias: CVE-2026-73196
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
medium
medium
Target Milestone: ---
Assignee: Product Security
QA Contact:
URL:
Whiteboard:
Depends On: 2520169
Blocks:
TreeView+ depends on / blocked
 
Reported: 2026-05-11 21:57 UTC by OSIDB Bzimport
Modified: 2026-08-20 10:00 UTC (History)
6 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed:
Embargoed:


Attachments (Terms of Use)

Description OSIDB Bzimport 2026-05-11 21:57:08 UTC
AI_ONLY_REPORT
package: ipa-4.13.1-3.el10
------
Summary: Authenticated DoS in `otptoken-add` via unbounded OTP key  
decoding/re-encoding: a low-privilege authenticated user can submit an  
oversized `ipatokenotpkey` value that is Base32-decoded, re-encoded, and  
embedded into an enrollment URI without an effective size bound, causing  
excessive CPU and memory use in the IPA API worker handling the request.
Requirements to exploit: Authenticated access to the IPA RPC interface as a  
user allowed to create self-managed OTP tokens, plus the ability to submit  
an oversized request body that is not rejected earlier by  
deployment-specific HTTP request-size controls.
Component affected: `ipa-4.13.1-3.el10`, `ipaserver/plugins/otptoken.py`,  
`OTPTokenKey._convert_scalar()`, `otptoken_add.pre_callback()`
Version affected: `ipa-4.13.1-3.el10`
Patch available: no released package fix established; proposed patch  
included below
Version fixed: unknown
Upstream coordination: Not notified.
CVSS: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L - 5.3 (MEDIUM)
AV:N - The issue is reachable over the network through the authenticated  
IPA RPC interface.
AC:L - Exploitation requires only an oversized valid Base32  
`ipatokenotpkey` value.
PR:L - A regular authenticated user with self-service token creation  
rights is sufficient.
UI:N - No victim interaction is required.
S:U - The impact remains within the vulnerable IPA service.
C:N - No confidentiality impact is established by the available evidence.
I:N - No integrity impact is established by the available evidence.
A:L - The request can consume worker CPU and memory and degrade service  
availability, but the available evidence does not establish consistent  
full-service outage across all deployments and some installations may  
reduce exposure with request-size limits.
Impact: Important. Red Hat classifies flaws that allow remote users to  
cause denial of service as Important. This issue is reachable through a  
network-exposed authenticated endpoint, and the supplied materials show  
default self-managed token creation permissions for ordinary authenticated  
users. The demonstrated impact is availability degradation rather than  
confidentiality or integrity loss, and some deployments may reduce exposure  
with front-end request-size limits, but the established behavior still fits  
Important more closely than Moderate.
Embargo: no
Reason: The issue requires authenticated access, is limited to  
availability impact, and can often be reduced operationally with  
request-size controls or tighter token-management permissions, so embargo  
handling does not appear necessary.
Acknowledgement: Aisle Research
Vulnerability Details: Observed facts: `ipatokenotpkey` is declared without  
a size bound, `OTPTokenKey._convert_scalar()` decodes attacker-controlled  
Base32 input before returning to the normal bytes conversion path, and  
`otptoken_add.pre_callback()` then Base32-encodes the decoded bytes again  
and URL-encodes them into an `otpauth://` URI stored in request context.
```python
OTPTokenKey('ipatokenotpkey?',
cli_name='key',
label=_('Key'),
doc=_('Token secret (Base32; default: random)'),
default_from=lambda: os.urandom(KEY_LENGTH),
autofill=True,
force server-side conversion
     normalizer=lambda x: x,
     flags=('no_display', 'no_update', 'no_search'),
),
...
class OTPTokenKey(Bytes):
     """A binary password type specified in base32."""


password = True
def _convert_scalar(self, value, index=None):
if isinstance(value, (tuple, list)) and len(value) == 2:
(p1, p2) = value
if p1 != p2:
raise PasswordMismatch(name=self.name)
value = p1
if isinstance(value, unicode):
try:
value = base64.b32decode(value, True)
except TypeError as e:
raise ConversionError(name=self.name, error=str(e))
return super(OTPTokenKey, self)._convert_scalar(value)
...
Build the URI parameters
         args = {}
         args['issuer'] = issuer
         args['secret'] = base64.b32encode(entry_attrs['ipatokenotpkey'])
         args['digits'] = entry_attrs['ipatokenotpdigits']
         args['algorithm'] = entry_attrs['ipatokenotpalgorithm'].upper()
         if options['type'] == 'totp':
             args['period'] = entry_attrs['ipatokentotptimestep']
         elif options['type'] == 'hotp':
             args['counter'] = entry_attrs['ipatokenhotpcounter']


Build the URI
         label = urllib.parse.quote(entry_attrs['ipatokenuniqueid'])
         parameters = urllib.parse.urlencode(args)
         uri = u'otpauth://%s/%s:%s?%s' % (options['type'], issuer, label,  
parameters)
         setattr(context, 'uri', uri)
```


The supplied materials also show a default ACI named `Users can create  
self-managed tokens`, so low-privilege authenticated users can reach this  
code path when token self-management is available as packaged.
Reasonable inference: oversized valid Base32 input can force substantial  
CPU and memory work before the request completes, because the server  
decodes the supplied key, re-encodes it, URL-encodes the resulting  
parameters, and constructs a large enrollment URI in the same request path.  
Repeated or parallel requests can therefore degrade service availability.
Open uncertainty: the available materials do not establish a universal  
crash threshold or prove that every supported deployment accepts  
arbitrarily large HTTP request bodies. Installations with strict front-end  
request-size limits may reduce or prevent exploitation before the  
vulnerable code path is reached.
Steps to reproduce:
1. Authenticate to the IPA RPC interface as a normal user.
2. Create a JSON-RPC `otptoken_add` request with `type` set to `totp` and  
`ipatokenotpkey` set to a very large valid Base32 string, for example  
roughly 32 MiB of repeated `A`.
3. POST the request to `/ipa/session/json` with `Content-Type:  
application/json` using the authenticated session.
4. Observe CPU and memory spikes in the IPA API worker while the request is  
processed, specifically during Base32 decode, Base32 re-encode, parameter  
URL encoding, and `otpauth://` URI construction.
5. Repeat or parallelize the request to amplify the availability impact.
Mitigation: Until a fix is available, enforce conservative HTTP  
request-body limits in front of `/ipa/session/json` so oversized payloads  
are rejected before IPA parameter conversion. If operationally acceptable,  
restrict self-managed token creation to trusted users and monitor or  
rate-limit repeated large authenticated requests.
Proposed Fix: Reject oversized Base32 input before decode and enforce a  
decoded-size cap on `ipatokenotpkey`.
```diff
diff --git a/ipaserver/plugins/otptoken.py b/ipaserver/plugins/otptoken.py
— a/ipaserver/plugins/otptoken.py
+++ b/ipaserver/plugins/otptoken.py
@@
KEY_LENGTH = 35
+MAX_OTPKEY_BYTES = 1024
+# Base32 expansion: 5 bytes -> 8 chars
+MAX_OTPKEY_B32_CHARS = ((MAX_OTPKEY_BYTES + 4) // 5) * 8
@@
class OTPTokenKey(Bytes):
@@
def _convert_scalar(self, value, index=None):
@@
if isinstance(value, unicode):
+            if len(value) > MAX_OTPKEY_B32_CHARS:
+                raise ConversionError(name=self.name, error='OTP key is  
too large')
try:
value = base64.b32decode(value, True)
except TypeError as e:
raise ConversionError(name=self.name, error=str(e))
+            if len(value) > MAX_OTPKEY_BYTES:
+                raise ConversionError(name=self.name, error='OTP key is  
too large')
@@
OTPTokenKey('ipatokenotpkey?',
cli_name='key',
label=_('Key'),
doc=_('Token secret (Base32; default: random)'),
+            maxlength=MAX_OTPKEY_BYTES,
default_from=lambda: os.urandom(KEY_LENGTH),
autofill=True,
normalizer=lambda x: x,
flags=('no_display', 'no_update', 'no_search'),
),
```
------
This report was generated using AI technology. Always review AI-generated  
content prior to use

Comment 1 Christopher Lusk 2026-06-26 17:05:56 UTC
Tracker filed for rhel-10.3: https://issues.redhat.com/browse/RHEL-188997


Note You need to log in before you can comment on or make changes to this bug.