CVE-2020-10594

9.1 CRITICAL

📋 TL;DR

This vulnerability in drf-jwt 1.15.x allows attackers with access to a blacklisted or invalidated JWT token to obtain a new, valid token through the refresh endpoint. The blacklist protection mechanism fails to properly validate tokens during refresh operations, enabling authentication bypass. This affects any system using the vulnerable drf-jwt library for Django REST Framework authentication.

💻 Affected Systems

Products:
  • drf-jwt (Styria-Digital fork of django-rest-framework-jwt)
Versions: 1.15.x before 1.15.1
Operating Systems: All operating systems running Python/Django
Default Config Vulnerable: ⚠️ Yes
Notes: Only affects systems using token refresh functionality with blacklist protection enabled. The original jpadilla/django-rest-framework-jwt is unmaintained but may have similar issues.

📦 What is this software?

⚠️ Risk & Real-World Impact

🔴

Worst Case

Complete authentication bypass allowing attackers to impersonate any user, access sensitive data, and perform unauthorized actions as authenticated users.

🟠

Likely Case

Attackers with stolen or previously valid tokens can maintain persistent access to user accounts even after token revocation.

🟢

If Mitigated

Limited impact with proper token expiration policies and additional authentication layers, though refresh functionality remains compromised.

🌐 Internet-Facing: HIGH - Any internet-facing API using vulnerable drf-jwt is exposed to token refresh attacks.
🏢 Internal Only: MEDIUM - Internal systems are still vulnerable but attack surface is reduced compared to internet-facing systems.

🎯 Exploit Status

Public PoC: ⚠️ Yes
Weaponized: LIKELY
Unauthenticated Exploit: ✅ No
Complexity: LOW

Exploitation requires access to a previously valid token (stolen, leaked, or otherwise obtained). The vulnerability is well-documented in GitHub issues with technical details.

🛠️ Fix & Mitigation

✅ Official Fix

Patch Version: 1.15.1

Vendor Advisory: https://github.com/Styria-Digital/django-rest-framework-jwt/issues/36

Restart Required: Yes

Instructions:

1. Update drf-jwt package: pip install drf-jwt==1.15.1
2. Restart Django application
3. Verify blacklist protection works with token refresh

🔧 Temporary Workarounds

Disable token refresh endpoint

all

Temporarily disable JWT token refresh functionality until patching is possible

# In Django settings.py or JWT configuration
JWT_AUTH = {
    'JWT_ALLOW_REFRESH': False,
    # Other JWT settings...
}

Implement custom token refresh validation

all

Add middleware or custom view to validate blacklisted tokens before refresh

# Custom refresh view example
from rest_framework_jwt.views import refresh_jwt_token
from rest_framework_jwt.blacklist.models import BlacklistedToken

class SecureRefreshView(refresh_jwt_token):
    def post(self, request, *args, **kwargs):
        token = request.data.get('token')
        if BlacklistedToken.objects.filter(token=token).exists():
            return Response({'error': 'Token blacklisted'}, status=400)
        return super().post(request, *args, **kwargs)

🧯 If You Can't Patch

  • Disable JWT token refresh functionality entirely in application configuration
  • Implement additional authentication checks (MFA, IP restrictions) for sensitive operations

🔍 How to Verify

Check if Vulnerable:

Check installed drf-jwt version: pip show drf-jwt | grep Version. If version is 1.15.0 or earlier 1.15.x, system is vulnerable.

Check Version:

pip show drf-jwt | grep Version

Verify Fix Applied:

1. Verify version is 1.15.1 or higher
2. Test that blacklisted tokens cannot be refreshed
3. Validate refresh endpoint returns error for invalidated tokens

📡 Detection & Monitoring

Log Indicators:

  • Multiple refresh attempts with same token
  • Refresh requests succeeding after token invalidation
  • Unusual refresh patterns from single user

Network Indicators:

  • HTTP POST requests to /api-token-refresh/ endpoint with previously used tokens
  • Refresh token reuse patterns

SIEM Query:

source="django" AND (url_path="/api-token-refresh/" OR message="token refresh") AND status=200 | stats count by src_ip, user

🔗 References

📤 Share & Export