| 标题 | CTFd https://github.com/CTFd/CTFd v3.8.4 Open Redirect |
|---|
| 描述 | # Open Redirect in CTFd v3.8.4 via `/register?next=` and `/login?next=`
## Severity
- CVSS v3.1 score: 4.3
- Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
- Suggested rating: Medium
## Setup
This exploit was tested on the official published Docker image for CTFd, version `3.8.4`.
This is the hash of the image used for reproducability:
`sha256:da25249c41d19556573f11cf3c9fd887d47830310ae8d725ad443644a35b3455`
## Requirements
You must be able to control the `next=` parameter in the `login` or `register` flow.
## Issue Description
This is a trivial open redirect. By convincing a user to login or register using a link with an attacker controlled `next` query parameter, the attacker can redirect a user to an arbitrary host.
## Proof of Concept
**Environment:** MacOS, CTFd 3.8.4, Docker.
**Reliability:** Deterministically reproducible.
The POC is simple, but the reason why it works is a bit more nuanced.
```
http://localhost:8000/login?next=//%09/example.com
```
The `%09` is a URL encoded tab character. This is important later. For now, notice the validation logic applied to the URL:
https://github.com/CTFd/CTFd/blob/7727f4aadaaedaea1b69155b0528d3a4d2256a1c/CTFd/utils/validators/__init__.py#L28
```
def _is_safe_url(target):
# TODO: CTFd 4.0 In Django this was renamed to `url_has_allowed_host_and_scheme`. Consider similar.
if target.startswith("///") or len(target) > MAX_URL_LENGTH:
# urlsplit does not perform validation of inputs. Unicode normalization
# is very slow on Windows and can be a DoS attack vector.
# https://docs.python.org/3/library/urllib.parse.html#url-parsing-security
return False
try:
url_info = urlsplit(target)
except ValueError: # e.g. invalid IPv6 addresses
return False
# Forbid URLs like http:///example.com - with a scheme, but without a
# hostname. In that URL, example.com is not the hostname but, a path
# component. However, Chrome will still consider example.com to be the
# hostname, so we must not allow this syntax.
if not url_info.netloc and url_info.scheme:
return False
# Forbid URLs that start with control characters. Some browsers (like
# Chrome) ignore quite a few control characters at the start of a
# URL and might consider the URL as scheme relative.
if unicodedata.category(target[0])[0] == "C":
return False
# The below came from Pallets snippets however it is insufficient
ref_url = urlparse(request.host_url)
test_url = urlparse(urljoin(request.host_url, target))
return test_url.scheme in ("http", "https") and ref_url.netloc == test_url.netloc
```
The URL encoded tab is decoded, and processing begins.
The check for `///` is passed because of the tab, so we continue...
When `urlsplit` sees the given input url, it parses it as the following:
```
scheme='' netloc='' path='/example.com'
```
A relative path is allowed, so this causes no problems with the validator. This URL is marked as safe.
The exploitable differential occurs in how the browser (and by extension WHATWG) parse URLs.
In the scheme, host, port, path, query, and fragment, tabs/newlines (\x09, \x0a, \x0d) are stripped during parsing.
Ex: `//\t/example.com` → `///example.com`.
When a base URL is specified and `///example.com` is received in a `3xx`, the browser will actually treat example.com as a hostname!
## Recommended Fix
I recommend output encoding unexpected or unusual characters. Maybe take another look at how Django does their redirection protections, they seem to have done a good job in the same language.
## Disclosure
- Discovered: 05/13/26 22:48 UTC
- Vendor notified: 05/13/26 23:15 UTC
- Response received: TBD
- Public disclosure: 08/11/26 (90 days)
## References
- CWE: CWE-601 - Open Redirect
- CVE: Requested
## Notes
- Credit: Michael Blunt
This research was conducted in coordination with a US based academic research lab.
We practice responsible disclosure and aim to support the open source community.
We request a CVE identifier be assigned for this vulnerability and will delay
public disclosure to allow adequate time for a patch to be developed and released.
This vulnerability has been validated and patched in the 3.8.5 release of CTFd. Permission for disclosure was given by the CTFd team. |
|---|
| 来源 | ⚠️ https://mblunt.dev/writeups/ctfd-open-redirect/ |
|---|
| 用户 | mblunt (UID 99615) |
|---|
| 提交 | 2026-07-07 23時42分 (2 月前) |
|---|
| 管理 | 2026-08-23 08時28分 (2 months later) |
|---|
| 状态 | 已接受 |
|---|
| VulDB条目 | 394533 [CTFd 直到 3.8.4 __init__.py _is_safe_url 下一步 Redirect] |
|---|
| 积分 | 20 |
|---|