CVE-2026-103261
Tornado before 6.5.9 Denial of Service via Query String
Tornado before 6.5.9 fails to limit the number of query string fields in HTTPServerRequest.init, allowing remote attackers to cause event-loop stalling by sending requests with thousands of query parameters. Attackers can send unauthenticated GET requests with unbounded query-string field counts to degrade response times for all clients sharing the same IOLoop.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
6.9
Tornado before 6.5.9 Denial of Service via Query String
Tornado before 6.5.9 fails to limit the number of query string fields in HTTPServerRequest.init, allowing remote attackers to cause event-loop stalling by sending requests with thousands of query parameters. Attackers can send unauthenticated GET requests with unbounded query-string field counts to degrade response times for all clients sharing the same IOLoop.
- Record
CVE-2026-103261- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N - CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-770
- Updated
- 2026-11-02
EUVDEUVD-2026-90655
6.9
Tornado before 6.5.9 fails to limit the number of query string fields in HTTPServerRequest.init, allowing remote attackers to cause event-loop stalling by sending requests with thousands of query parameters. Attackers can send unauthenticated GET requests with unbounded query-string field counts to degrade response times for all clients sharing the same IOLoop.
- Record
EUVD-2026-90655- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-2v9w-f3h6-74rj
6.9
Tornado before 6.5.9 fails to limit the number of query string fields in HTTPServerRequest...
Tornado before 6.5.9 fails to limit the number of query string fields in HTTPServerRequest.init, allowing remote attackers to cause event-loop stalling by sending requests with thousands of query parameters. Attackers can send unauthenticated GET requests with unbounded query-string field counts to degrade response times for all clients sharing the same IOLoop.
- Record
GHSA-2v9w-f3h6-74rj- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-770
- Updated
- 2026-11-02
GitHub AdvisoriesGHSA-3hv7-mjh2-fv65
5.3
Tornado: Unbounded query-string argument count allows event-loop-stalling DoS
Summary
HTTPServerRequest.__init__ in tornado/httputil.py parses the URL query string via
parse_qs_bytes() with no field-count limit — while the sibling POST-body parsing path
(parse_body_arguments) received a max_num_fields=1000 cap added earlier in this exact
same release (v6.5.8, commit 8d6363ed), explicitly to bound parsing cost for the identical
underlying primitive. This leaves the query-string path with the resource-exhaustion exposure
the body-path fix was meant to close.
File: tornado/httputil.py, line 553 (HTTPServerRequest.__init__)
Root Cause
# tornado/httputil.py:553 (before fix)
self.arguments = parse_qs_bytes(self.query, keep_blank_values=True)
Compare with the POST-body path fixed one commit earlier in the same release:
# tornado/httputil.py:1038-1041
uri_arguments = parse_qs_bytes(
body,
keep_blank_values=True,
max_num_fields=config.urlencoded.max_arguments, # default 1000
)
Both call sites funnel through the same tornado.escape.parse_qs_bytes (a thin wrapper over
urllib.parse.parse_qs), which is exactly why max_num_fields was added to
urllib.parse.parse_qsl upstream — to let frameworks bound field count. The fix was applied
only to the body path; the query-string path was missed.
The request line + headers together are capped at max_header_size (default 65536 bytes), so
this is not literally unbounded, but a single ~64KB request line can carry thousands of short
key=value pairs — far beyond the 1000-field limit the maintainer judged appropriate for the
structurally identical body case.
Attack Scenario
- Attacker sends a
GETrequest whose query string is packed with thousands of short fields (e.g.k0=1&k1=1&...&k7799=1, ~61KB), fitting comfortably undermax_header_size. No authentication, cookies, or prior state required. - Tornado accepts and parses this with no field-count cap, unlike the equivalent POST-body request (which is correctly rejected with 400 once >1000 fields are present).
- Parsing thousands of fields is CPU work performed synchronously inside Tornado's
single-threaded
IOLoop. Several such requests in flight concurrently stall the event loop, delaying processing of all other connections on that loop — not just the attacker's own request.
Verification (dynamic, local reproduction against v6.5.8)
Ran the unmodified v6.5.8 source directly (no external dependencies needed) with a minimal
tornado.web.Application on 127.0.0.1:8888.
- Identical 7800-field/~61KB payload sent as GET query string →
200 OK; sent as POST body (application/x-www-form-urlencoded) →400 Bad Request(correctly rejected by the existingmax_num_fieldsbody-path limit). This confirms the asymmetry directly. - Per-request parse cost: baseline (
/?a=1) averaged 1.86ms; the 7800-field query string averaged 25.1ms (~13x). - Event-loop-blocking amplification (raw-socket test, isolating server-side stall from client overhead): with 10 sequential baseline probe requests fired with no load, average latency was 1.47ms (max 5.9ms). With 5 concurrent 61KB/7800-field requests in flight, the same baseline probes averaged 13.0ms (max 118.1ms) — an 8.9x average slowdown for unrelated clients, produced by ~305KB of unauthenticated attacker traffic.
Impact
All Tornado servers/applications are affected — this triggers on every request with a query
string, independent of application/handler logic. An unauthenticated, unprivileged remote
attacker can measurably degrade response times for all other clients sharing the same
IOLoop, using a small amount of bandwidth and no special conditions. This is an
availability/DoS concern; no confidentiality or integrity impact.
Recommended Fix
# tornado/httputil.py — HTTPServerRequest.__init__
if uri is not None:
self.path, sep, self.query = uri.partition("?")
try:
self.arguments = parse_qs_bytes(
…
- Record
GHSA-3hv7-mjh2-fv65- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-770
- Updated
- 2026-11-02
GitHub AdvisoriesGHSA-c2m8-h5v5-343r
8.2
Tornado: StaticFileHandler follows symlinks outside static root (path traversal)
Summary
StaticFileHandler allows an unauthenticated attacker to read arbitrary files from the server's filesystem by requesting a path that resolves to a symbolic link placed inside the static root directory. Any application that serves user-uploadable content, or whose static directory is populated by a build/deploy pipeline that creates symlinks (e.g. npm link, webpack, Docker volume mounts, CDN sync tools), is affected. An attacker who can trigger the creation of a symlink pointing outside the static root—or exploit one that already exists—can retrieve sensitive files such as /etc/passwd, private keys, configuration files, or application secrets.
Details
The vulnerability is in tornado.web.StaticFileHandler, specifically in the interaction between two methods in tornado/web.py:
- get_absolute_path uses
os.path.abspath()to resolve the requested path: - validate_absolute_path uses the same
os.path.abspath()on the root, then performs a string prefix check:
os.path.abspath() normalises . and .. segments but does not resolve symbolic links. As a result, a path like /var/www/static/link passes the startswith("/var/www/static/") check regardless of where link actually points.
Immediately after, os.path.exists() and os.path.isfile() do follow symlinks, so the file they ultimately open is the symlink's target. The fix would be to replace os.path.abspath() with os.path.realpath() in both methods, so the resolved real path of the symlink target is validated against the root, not just its string representation inside the static directory.
PoC
Complete instructions, including specific configuration details, to reproduce the vulnerability. Prerequisites: Python 3.x, Tornado installed.
- Create the environment
mkdir -p /tmp/static
echo "DB_PASSWORD=s3cr3t" > /tmp/secret.conf
ln -s /tmp/secret.conf /tmp/static/config.conf
- Minimal vulnerable server (server.py):
import tornado.web, tornado.ioloop
app = tornado.web.Application([
(r"/static/(.*)", tornado.web.StaticFileHandler, {"path": "/tmp/static"}),
])
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
- Exploit:
curl http://localhost:8888/static/config.conf
Impact
Any application using StaticFileHandler is potentially affected if:
- the static directory contains symlinks pointing outside it (common with build tooling), or
- the application allows file uploads into the static directory without stripping symlinks.
An unauthenticated remote attacker can read any file readable by the process user: application secrets, private TLS keys, database credentials, /etc/shadow, SSH keys, or source code (depending on the process's filesystem permissions).
- Record
GHSA-c2m8-h5v5-343r- CVSS 4.0
- 8.2 High
CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-59
- Updated
- 2026-11-02
NVD
6.9
Tornado before 6.5.9 fails to limit the number of query string fields in HTTPServerRequest.init, allowing remote attackers to cause event-loop stalling by sending requests with thousands of query parameters. Attackers can send unauthenticated GET requests with unbounded query-string field counts to degrade response times for all clients sharing the same IOLoop.
- Record
CVE-2026-103261- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-770
- Updated
- 2026-11-02
Show 10 moreShow less
OSVBELL-CVE-2026-103261
5.3
- Record
BELL-CVE-2026-103261- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L - Updated
- 2026-11-02
OSV
6.9
Tornado before 6.5.9 Denial of Service via Query String
Tornado before 6.5.9 fails to limit the number of query string fields in HTTPServerRequest.init, allowing remote attackers to cause event-loop stalling by sending requests with thousands of query parameters. Attackers can send unauthenticated GET requests with unbounded query-string field counts to degrade response times for all clients sharing the same IOLoop.
- Record
CVE-2026-103261- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N - Weakness
- CWE-770
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-103261
6.9
Tornado before 6.5.9 fails to limit the number of query string fields in HTTPServerRequest.init, allowing remote attackers to cause event-loop stalling by sending requests with thousands of query parameters. Attackers can send unauthenticated GET requests with unbounded query-string field counts to degrade response times for all clients sharing the same IOLoop.
- Record
DEBIAN-CVE-2026-103261- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Updated
- 2026-11-02
OSVGHSA-3hv7-mjh2-fv65
5.3
Tornado: Unbounded query-string argument count allows event-loop-stalling DoS
Summary
HTTPServerRequest.__init__ in tornado/httputil.py parses the URL query string via
parse_qs_bytes() with no field-count limit — while the sibling POST-body parsing path
(parse_body_arguments) received a max_num_fields=1000 cap added earlier in this exact
same release (v6.5.8, commit 8d6363ed), explicitly to bound parsing cost for the identical
underlying primitive. This leaves the query-string path with the resource-exhaustion exposure
the body-path fix was meant to close.
File: tornado/httputil.py, line 553 (HTTPServerRequest.__init__)
Root Cause
# tornado/httputil.py:553 (before fix)
self.arguments = parse_qs_bytes(self.query, keep_blank_values=True)
Compare with the POST-body path fixed one commit earlier in the same release:
# tornado/httputil.py:1038-1041
uri_arguments = parse_qs_bytes(
body,
keep_blank_values=True,
max_num_fields=config.urlencoded.max_arguments, # default 1000
)
Both call sites funnel through the same tornado.escape.parse_qs_bytes (a thin wrapper over
urllib.parse.parse_qs), which is exactly why max_num_fields was added to
urllib.parse.parse_qsl upstream — to let frameworks bound field count. The fix was applied
only to the body path; the query-string path was missed.
The request line + headers together are capped at max_header_size (default 65536 bytes), so
this is not literally unbounded, but a single ~64KB request line can carry thousands of short
key=value pairs — far beyond the 1000-field limit the maintainer judged appropriate for the
structurally identical body case.
Attack Scenario
- Attacker sends a
GETrequest whose query string is packed with thousands of short fields (e.g.k0=1&k1=1&...&k7799=1, ~61KB), fitting comfortably undermax_header_size. No authentication, cookies, or prior state required. - Tornado accepts and parses this with no field-count cap, unlike the equivalent POST-body request (which is correctly rejected with 400 once >1000 fields are present).
- Parsing thousands of fields is CPU work performed synchronously inside Tornado's
single-threaded
IOLoop. Several such requests in flight concurrently stall the event loop, delaying processing of all other connections on that loop — not just the attacker's own request.
Verification (dynamic, local reproduction against v6.5.8)
Ran the unmodified v6.5.8 source directly (no external dependencies needed) with a minimal
tornado.web.Application on 127.0.0.1:8888.
- Identical 7800-field/~61KB payload sent as GET query string →
200 OK; sent as POST body (application/x-www-form-urlencoded) →400 Bad Request(correctly rejected by the existingmax_num_fieldsbody-path limit). This confirms the asymmetry directly. - Per-request parse cost: baseline (
/?a=1) averaged 1.86ms; the 7800-field query string averaged 25.1ms (~13x). - Event-loop-blocking amplification (raw-socket test, isolating server-side stall from client overhead): with 10 sequential baseline probe requests fired with no load, average latency was 1.47ms (max 5.9ms). With 5 concurrent 61KB/7800-field requests in flight, the same baseline probes averaged 13.0ms (max 118.1ms) — an 8.9x average slowdown for unrelated clients, produced by ~305KB of unauthenticated attacker traffic.
Impact
All Tornado servers/applications are affected — this triggers on every request with a query
string, independent of application/handler logic. An unauthenticated, unprivileged remote
attacker can measurably degrade response times for all other clients sharing the same
IOLoop, using a small amount of bandwidth and no special conditions. This is an
availability/DoS concern; no confidentiality or integrity impact.
Recommended Fix
# tornado/httputil.py — HTTPServerRequest.__init__
if uri is not None:
self.path, sep, self.query = uri.partition("?")
try:
self.arguments = parse_qs_bytes(
…
- Record
GHSA-3hv7-mjh2-fv65- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-770
- Updated
- 2026-11-02
OSVGHSA-c2m8-h5v5-343r
8.2
Tornado: StaticFileHandler follows symlinks outside static root (path traversal)
Summary
StaticFileHandler allows an unauthenticated attacker to read arbitrary files from the server's filesystem by requesting a path that resolves to a symbolic link placed inside the static root directory. Any application that serves user-uploadable content, or whose static directory is populated by a build/deploy pipeline that creates symlinks (e.g. npm link, webpack, Docker volume mounts, CDN sync tools), is affected. An attacker who can trigger the creation of a symlink pointing outside the static root—or exploit one that already exists—can retrieve sensitive files such as /etc/passwd, private keys, configuration files, or application secrets.
Details
The vulnerability is in tornado.web.StaticFileHandler, specifically in the interaction between two methods in tornado/web.py:
- get_absolute_path uses
os.path.abspath()to resolve the requested path: - validate_absolute_path uses the same
os.path.abspath()on the root, then performs a string prefix check:
os.path.abspath() normalises . and .. segments but does not resolve symbolic links. As a result, a path like /var/www/static/link passes the startswith("/var/www/static/") check regardless of where link actually points.
Immediately after, os.path.exists() and os.path.isfile() do follow symlinks, so the file they ultimately open is the symlink's target. The fix would be to replace os.path.abspath() with os.path.realpath() in both methods, so the resolved real path of the symlink target is validated against the root, not just its string representation inside the static directory.
PoC
Complete instructions, including specific configuration details, to reproduce the vulnerability. Prerequisites: Python 3.x, Tornado installed.
- Create the environment
mkdir -p /tmp/static
echo "DB_PASSWORD=s3cr3t" > /tmp/secret.conf
ln -s /tmp/secret.conf /tmp/static/config.conf
- Minimal vulnerable server (server.py):
import tornado.web, tornado.ioloop
app = tornado.web.Application([
(r"/static/(.*)", tornado.web.StaticFileHandler, {"path": "/tmp/static"}),
])
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
- Exploit:
curl http://localhost:8888/static/config.conf
Impact
Any application using StaticFileHandler is potentially affected if:
- the static directory contains symlinks pointing outside it (common with build tooling), or
- the application allows file uploads into the static directory without stripping symlinks.
An unauthenticated remote attacker can read any file readable by the process user: application secrets, private TLS keys, database credentials, /etc/shadow, SSH keys, or source code (depending on the process's filesystem permissions).
- Record
GHSA-c2m8-h5v5-343r- CVSS 4.0
- 8.2 High
CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-59
- Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-103261
6.9
Tornado before 6.5.9 fails to limit the number of query string fields in HTTPServerRequest.init, allowing remote attackers to cause event-loop stalling by sending requests with thousands of query parameters. Attackers can send unauthenticated GET requests with unbounded query-string field counts to degrade response times for all clients sharing the same IOLoop.
- Record
UBUNTU-CVE-2026-103261- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N - CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L - Updated
- 2026-11-06
OSVAZL-105372 No score
CVE-2026-103261 affecting package python-tornado 6.3.3-11
Tornado before 6.5.9 fails to limit the number of query string fields in HTTPServerRequest.init, allowing remote attackers to cause event-loop stalling by sending requests with thousands of query parameters. Attackers can send unauthenticated GET requests with unbounded query-string field counts to degrade response times for all clients sharing the same IOLoop.
- Record
AZL-105372- Updated
- 2026-11-05
OSVCLSA-2026-1790831290 No score
TuxCare security update for tornado (2 CVEs)
TuxCare rebuilt tornado to address 2 CVEs. Fixed in 6.1.0.post9+tuxcare.
- Record
CLSA-2026-1790831290- Updated
- 2026-11-02
OSVCLSA-2026-1790836405 No score
TuxCare security update for tornado (2 CVEs)
TuxCare rebuilt tornado to address 2 CVEs. Fixed in 5.1.1.post5+tuxcare.
- Record
CLSA-2026-1790836405- Updated
- 2026-11-02
OSVROOT-APP-PYPI-GHSA-c2m8-h5v5-343r No score
GHSA-c2m8-h5v5-343r in tornado - Patched by Root
Root has patched GHSA-c2m8-h5v5-343r in the tornado package for Root:PyPI. Multiple fixed versions available.
- Record
ROOT-APP-PYPI-GHSA-c2m8-h5v5-343r- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
tornadoPyPI |
through 6.5.8 | 6.5.9 |
python-tornadoAzure Linux:3 |
through 6.3.3-11 | None known |
py3-tornadoAlpaquita:25 |
6.5.1-r0 up to 6.5.10-r0 | 6.5.10-r0 |
py3-tornadoAlpaquita:stream |
6.3.3-r0 up to 6.5.10-r0 | 6.5.10-r0 |
tornadoTuxCare:PyPI |
6.1 up to 6.1.0.post9+tuxcare, plus listed versions 6.1.0.post1+tuxcare6.1.0.post2+tuxcare6.1.0.post3+tuxcare6.1.0.post4+tuxcare6.1.0.post5+tuxcare6.1.0.post6+tuxcare6.1.0.post7+tuxcare6.1.0.post8+tuxcare |
6.1.0.post9+tuxcare |
tornadoTuxCare:PyPI |
5.1.1 up to 5.1.1.post5+tuxcare, plus listed versions 5.1.1.post1+tuxcare5.1.1.post2+tuxcare5.1.1.post3+tuxcare5.1.1.post4+tuxcare |
5.1.1.post5+tuxcare |
python-tornadoDebian:12 |
All versions, plus listed versions 6.2.0-36.2.0-3+deb12u16.2.0-3+deb12u26.2.0-3+deb12u36.2.0-3+deb12u4 Show 19 moreShow less6.3.2-16.4.0-16.4.0-26.4.1-16.4.1-26.4.1-36.4.2-16.4.2-26.4.2-36.5.2-16.5.2-26.5.2-36.5.4-0.16.5.4-16.5.5-16.5.5-26.5.5-36.5.5-46.5.5-5 |
None known |
python-tornadoDebian:13 |
All versions, plus listed versions 6.4.2-36.4.2-3+deb13u16.4.2-3+deb13u26.5.2-16.5.2-2 Show 8 moreShow less6.5.2-36.5.4-0.16.5.4-16.5.5-16.5.5-26.5.5-36.5.5-46.5.5-5 |
None known |
python-tornadoDebian:14 |
All versions, plus listed versions 6.4.2-36.5.2-16.5.2-26.5.2-36.5.4-0.1 Show 6 moreShow less6.5.4-16.5.5-16.5.5-26.5.5-36.5.5-46.5.5-5 |
None known |
tornadoPyPI |
before 6.5.9, plus listed versions 0.21.01.11.1.11.2 Show 45 moreShow less1.2.12.02.12.1.12.22.2.12.32.42.4.13.03.0.13.0.23.13.1.13.23.2.13.2.24.04.0.14.0.24.14.1b24.24.2.14.2b14.34.3b14.3b24.44.4.14.4.24.4.34.4b14.54.5.14.5.24.5.34.5b14.5b25.05.0.15.0.25.0a15.0b15.1 |
6.5.9 |
Show 8 moreShow less
| Package | Affected | Fixed |
|---|---|---|
tornadoRoot:PyPI |
before 6.5.3+aikido.7 | 6.5.3+aikido.76.2+aikido.126.5.3+aikido.6 |
python-tornadoUbuntu:Pro:16.04:LTS |
All versions, plus listed versions 4.2.1-1ubuntu24.2.1-1ubuntu34.2.1-1ubuntu3.14.2.1-1ubuntu3.1+esm14.2.1-1ubuntu3.1+esm24.2.1-1ubuntu3.1+esm34.2.1-1ubuntu3.1+esm44.2.1-1ubuntu3.1+esm5 |
None known |
python-tornadoUbuntu:Pro:18.04:LTS |
All versions, plus listed versions 4.5.1-2.1~build24.5.2-14.5.3-14.5.3-1ubuntu0.14.5.3-1ubuntu0.2 Show 5 moreShow less4.5.3-1ubuntu0.2+esm14.5.3-1ubuntu0.2+esm24.5.3-1ubuntu0.2+esm34.5.3-1ubuntu0.2+esm44.5.3-1ubuntu0.2+esm5 |
None known |
python-tornadoUbuntu:Pro:20.04:LTS |
All versions, plus listed versions 5.1.1-4ubuntu15.1.1-4ubuntu56.0.3+really5.1.1-26.0.3+really5.1.1-2build16.0.3+really5.1.1-2build2 Show 6 moreShow less6.0.3+really5.1.1-36.0.3+really5.1.1-3ubuntu0.1~esm16.0.3+really5.1.1-3ubuntu0.1~esm36.0.3+really5.1.1-3ubuntu0.1~esm56.0.3+really5.1.1-3ubuntu0.1~esm76.0.3+really5.1.1-3ubuntu0.1~esm8 |
None known |
python-tornadoUbuntu:Pro:22.04:LTS |
All versions, plus listed versions 6.1.0-1build16.1.0-26.1.0-36.1.0-3build16.1.0-3ubuntu0.1~esm1 Show 5 moreShow less6.1.0-3ubuntu0.1~esm26.1.0-3ubuntu0.1~esm46.1.0-3ubuntu0.1~esm56.1.0-3ubuntu0.1~esm66.1.0-3ubuntu0.1~esm7 |
None known |
python-tornadoUbuntu:24.04:LTS |
All versions, plus listed versions 6.3.2-16.4.0-0ubuntu16.4.0-16.4.0-1build16.4.0-1ubuntu0.1 Show 5 moreShow less6.4.0-1ubuntu0.26.4.0-1ubuntu0.46.4.0-1ubuntu0.56.4.0-1ubuntu0.66.4.0-1ubuntu0.7 |
None known |
python-tornadoUbuntu:26.10 |
All versions, plus listed versions 6.5.4-0.16.5.5-26.5.5-46.5.5-56.5.5-5ubuntu1.16.5.5-5ubuntu1.2 |
None known |
python-tornadoUbuntu:26.04:LTS |
All versions, plus listed versions 6.4.2-36.5.2-36.5.4-0.16.5.4-0.1ubuntu0.16.5.4-0.1ubuntu0.26.5.4-0.1ubuntu0.3 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected | OSV | |
| facts | OSV | |
| affected, aliases, references | ||
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-103261 is a medium vulnerability (CVSS 6.9) in Tornadoweb Tornado, published September 30, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.47%, the 40th percentile. Fixed in tornado 6.5.9.
References
github.com/tornadoweb/tornado/security/advisories/GHSA-3hv7-mjh2-fv65
github.com/tornadoweb/tornado/commit/03945136ea9746eccf61caf88edae39642e59c93
vulncheck.com/advisories/tornado-before-6.5.9-denial-of-service-via-query-string
nvd.nist.gov/vuln/detail/CVE-2026-103261
github.com/advisories/GHSA-2v9w-f3h6-74rj
github.com/tornadoweb/tornado/pull/3719
Show 13 moreShow less
github.com/tornadoweb/tornado/commit/8a61dd6005f42733015160f3c23a2bcffe200542
github.com/tornadoweb/tornado/releases/tag/v6.5.9
github.com/advisories/GHSA-3hv7-mjh2-fv65
github.com/tornadoweb/tornado/security/advisories/GHSA-c2m8-h5v5-343r
github.com/tornadoweb/tornado/commit/437ab5f76565403b3403438d2e555d11f8128d32
github.com/tornadoweb/tornado/commit/f9f50b8bab265a2abf9f2415a3d2da6264a5450b
github.com/advisories/GHSA-c2m8-h5v5-343r
docs.bell-sw.com/security/cves/CVE-2026-103261
github.com/CVEProject/cvelistV5/tree/main/cves/2026/103xxx/CVE-2026-103261.json
security-tracker.debian.org/tracker/CVE-2026-103261
github.com/tornadoweb/tornado
ubuntu.com/security/CVE-2026-103261
cve.org/CVERecord?id=CVE-2026-103261
Left out here: 34 versions and the rest of 2 source descriptions. The JSON record has them.