CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-103022

CVE-2026-103022: Denial of Service via Unhandled Unicode Exception and File Descriptor Leak in Urwid Web Display

Alon Barad
Alon Barad
Software Engineer

Nov 10, 2026·10 min read·1 visit

Executive Summary (TL;DR)

Urwid versions prior to 4.0.7 contain unhandled Unicode encoding/decoding exceptions and resource leaks in urwid/display/web.py. A remote attacker with a valid session ID can send non-ASCII POST payloads to crash CGI request handlers and exhaust system file descriptors, resulting in a Denial of Service.

A Denial of Service (DoS) vulnerability exists in the web display module (urwid/display/web.py) of the Urwid console user interface library for Python in versions prior to 4.0.7. The handle_short_request() function unconditionally encodes HTTP POST input as ASCII without exception handling and opens a FIFO pipe file descriptor without a guaranteed cleanup block. Submitting non-ASCII characters triggers an unhandled UnicodeEncodeError that terminates CGI request processing and leaks the open file descriptor. Additionally, the screen update polling routine decodes Unix domain socket streams iteratively as UTF-8 without exception handling or deterministic socket closure, enabling uncaught UnicodeDecodeError exceptions and socket leaks.

Vulnerability Overview

Urwid is a widely used Python library for building console and terminal user interfaces. In addition to standard terminal rendering backends such as curses and raw terminal modes, Urwid includes an optional web display module (urwid/display/web.py). This module allows terminal applications to be rendered interactively within a web browser by acting as a Common Gateway Interface (CGI) bridge between HTTP clients and a backend Python application process.

The web display architecture relies on inter-process communication (IPC) mechanisms stored in a temporary directory (_prefs.pipe_dir). Specifically, the CGI script processes short-lived HTTP requests via the handle_short_request() function to forward user keystrokes into a named pipe (FIFO) and poll screen updates from a Unix domain socket. Because this handler directly parses network-supplied HTTP request bodies and environment variables, it serves as the primary network attack surface for deployments utilizing Urwid's web display feature.

In Urwid versions prior to 4.0.7, handle_short_request() contains three related defects mapped to CWE-116 (Improper Encoding or Escaping of Output), CWE-248 (Uncaught Exception), and CWE-775 (Missing Release of File Descriptor or Handle after Effective Lifetime). When processing user input or polling updates, the function fails to handle character encoding mismatches and omits resource cleanup blocks.

An attacker who obtains a valid X-Urwid-ID session identifier can send non-ASCII characters in HTTP POST requests to trigger an uncaught UnicodeEncodeError. In persistent execution environments such as FastCGI, SCGI, or custom WSGI wrappers, each unhandled exception bypasses the os.close() call for the underlying FIFO pipe. Repeated requests exhaust available operating system file descriptors, causing the application to fail with EMFILE (Too many open files) and denying service to legitimate users.

Root Cause Analysis

The root cause of CVE-2026-103022 lies in improper exception handling and deterministic resource management across two distinct execution paths inside urwid/display/web.py: the keyboard input path and the screen update polling path.

In the keyboard input path, the handler opens a write-only file descriptor (fd) pointing to /tmp/urwid_<id>.input using os.open(). Immediately following this operation, the function reads up to MAX_READ characters from sys.stdin and calls keydata.encode("ascii") directly inside the os.write() argument list. Because Python 3 defaults sys.stdin to UTF-8 in most server environments, reading multibyte UTF-8 characters succeeds, producing a Python str containing non-ASCII code points. Passing this string to .encode("ascii") raises a UnicodeEncodeError whenever any character falls outside the 7-bit ASCII range (0x00–0x7F).

Because the os.open() call and the subsequent os.close(fd) call are not enclosed within a try...finally block, the raised UnicodeEncodeError unwinds the call stack immediately. The os.close(fd) statement becomes unreachable. While standard single-shot CGI scripts terminate the OS process upon completion—allowing the kernel to reclaim the leaked descriptor—persistent CGI runners, FastCGI daemons, or embedded HTTP servers retain the leaked descriptor inside the worker process table indefinitely.

A secondary defect exists in the screen update polling path, triggered when the HTTP_X_URWID_METHOD environment variable is set to "polling". The function instantiates an AF_UNIX stream socket and enters a while loop calling s.recv(BUF_SZ).decode("utf-8"). This implementation contains two flaws: first, s.recv(BUF_SZ) reads arbitrary byte chunks from the stream, meaning a multibyte UTF-8 character split across two BUF_SZ boundaries will fail to decode and raise UnicodeDecodeError. Second, the surrounding try...except block only catches OSError, leaving UnicodeDecodeError uncaught and failing to close the socket s via a context manager or finally clause.

Code Analysis

The vulnerability was remediated in commit b79d9a96843a6f5950bb2bfab5d059bdc61c538b (Pull Request #1201). Comparing the vulnerable implementation with the patched implementation demonstrates how both the encoding assumptions and resource leaks were resolved.

In the vulnerable version of urwid/display/web.py, the polling and keyboard input paths were implemented as follows:

    if os.environ.get("HTTP_X_URWID_METHOD", None) == "polling":
        # Vulnerable: Socket is not opened in a context manager
        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        try:
            s.connect(os.path.join(_prefs.pipe_dir, f"urwid_{urwid_id}.update"))
            # Vulnerable: Iterative UTF-8 decoding fails on split multibyte sequences
            data = f"Content-type: text/plain\r\n\r\n{s.recv(BUF_SZ).decode('utf-8')}"
            while data:
                sys.stdout.write(data)
                data = s.recv(BUF_SZ).decode("utf-8")
        except OSError:
            # Vulnerable: Does not catch UnicodeDecodeError or close socket 's'
            sys.stdout.write("Status: 404 Not Found\r\n\r\n")
            return True
 
    # Keyboard input request
    fd = os.open(os.path.join(_prefs.pipe_dir, f"urwid_{urwid_id}.input"), os.O_WRONLY)
    if fd < 0:
        sys.stdout.write("Status: 404 Not Found\r\n\r\n")
        return True
 
    # FIXME: use the correct encoding based on the request
    keydata = sys.stdin.read(MAX_READ)
    # Vulnerable: Hardcoded ASCII encoding without exception handling or finally block
    os.write(fd, keydata.encode("ascii"))
    os.close(fd)

The patch introduces a helper function _request_charset() utilizing Python's email.message.Message parser to extract the charset parameter from the CONTENT_TYPE environment variable, defaulting to utf-8 if unspecified. It also restructures both the socket and file descriptor lifecycles:

def _request_charset() -> str:
    content_type = Message()
    content_type["content-type"] = os.environ.get("CONTENT_TYPE", "")
    return content_type.get_content_charset() or "utf-8"

Within handle_short_request(), the patch modifies the polling and input logic to enforce deterministic cleanup and catch encoding exceptions:

    if os.environ.get("HTTP_X_URWID_METHOD", None) == "polling":
        try:
            # Fix: Context manager guarantees socket closure on exit
            with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
                s.connect(os.path.join(_prefs.pipe_dir, f"urwid_{urwid_id}.update"))
                chunks = []
                # Fix: Accumulate raw bytes before decoding to prevent split multibyte errors
                while data := s.recv(BUF_SZ):
                    chunks.append(data)
        except OSError:
            sys.stdout.write("Status: 404 Not Found\r\n\r\n")
            return True
 
        try:
            decoded = b"".join(chunks).decode("utf-8")
        except UnicodeDecodeError:
            # Fix: Catch invalid UTF-8 and return 502 Bad Gateway
            sys.stdout.write("Status: 502 Bad Gateway\r\n\r\n")
            return True
 
        sys.stdout.write(f"Content-type: text/plain; charset=utf-8\r\n\r\n{decoded}")
        return True
 
    # Keyboard input request
    fd = os.open(os.path.join(_prefs.pipe_dir, f"urwid_{urwid_id}.input"), os.O_WRONLY)
    if fd < 0:
        sys.stdout.write("Status: 404 Not Found\r\n\r\n")
        return True
 
    try:
        try:
            keydata = sys.stdin.read(MAX_READ)
            # Fix: Encode using client-specified charset or UTF-8 fallback
            encoded_keydata = keydata.encode(_request_charset())
        except (LookupError, UnicodeError):
            # Fix: Catch unknown charsets (LookupError) and encoding failures (UnicodeError)
            sys.stdout.write("Status: 400 Bad Request\r\n\r\n")
            return True
        os.write(fd, encoded_keydata)
    finally:
        # Fix: Guarantee file descriptor release even when returning early or raising
        with suppress(OSError):
            os.close(fd)

The fix is complete with respect to both exception handling and descriptor lifecycle management. By catching (LookupError, UnicodeError), the code prevents attacker-controlled Content-Type headers (e.g., Content-Type: text/plain; charset=invalid-codec) from raising an uncaught LookupError during the .encode() call. Furthermore, wrapping os.close(fd) inside finally: with contextlib.suppress(OSError) guarantees that the file descriptor is closed regardless of whether the inner block returns early on a 400 Bad Request or raises an unexpected OSError during os.write().

Exploitation

Exploiting CVE-2026-103022 requires network access to an HTTP server exposing an Urwid application via urwid/display/web.py, along with a valid X-Urwid-ID session token. Before reaching the vulnerable code path, handle_short_request() validates that the HTTP_X_URWID_ID environment variable consists strictly of alphanumeric characters (urwid_id.isalnum()) and corresponds to an existing session pipe in _prefs.pipe_dir. If the web display interface allows unauthenticated users to initialize a session, an attacker can obtain a valid X-Urwid-ID directly by loading the initial web page.

Once a valid session ID is acquired, the attacker sends an HTTP POST request containing non-ASCII bytes in the request body without setting the X-Urwid-Method: polling header. For example, transmitting the two-byte UTF-8 sequence \xc3\xa9 (representing é) triggers the flaw:

POST /cgi-bin/urwid_web.cgi HTTP/1.1
Host: target.example.com
X-Urwid-ID: 482910492819
Content-Type: text/plain; charset=utf-8
Content-Length: 2
 
ƒ)

When the target server processes this request, handle_short_request() opens /tmp/urwid_482910492819.input and reads the body from sys.stdin. The call to keydata.encode("ascii") immediately fails with:

UnicodeEncodeError: 'ascii' codec can't encode character '\xe9' in position 0: ordinal not in range(128)

To achieve a persistent Denial of Service in a multi-request worker process (such as FastCGI, uWSGI, or mod_python), the attacker repeats this POST request in a loop. Each iteration leaks one file descriptor pointing to the FIFO pipe. Once the worker process reaches the operating system's per-process file descriptor limit (RLIMIT_NOFILE, commonly 1024 on Linux systems), all subsequent calls to os.open() or socket.socket() within that worker process fail with OSError: [Errno 24] Too many open files. At this point, the web display interface cannot process further keystrokes or screen updates for any active user.

Impact Assessment

CVE-2026-103022 is rated at a CVSS v3.1 base score of 5.3 (Medium) with the vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L. The vulnerability is exploitable over the network (AV:N) with low attack complexity (AC:L) and requires no user interaction (UI:N). Because session initialization in standard Urwid web deployments does not enforce authentication by default, privileges required is rated as None (PR:N).

The security impact is strictly confined to availability (C:N/I:N/A:L). The flaw does not permit memory corruption, arbitrary file read/write, or remote code execution. The availability impact is classified as Low (A:L) because applications using standard CGI execution models spawn a fresh OS process per HTTP request; in those configurations, the operating system kernel automatically closes leaked file descriptors when the crashed CGI process exits, limiting the impact to individual failed requests (HTTP 500 responses) rather than full system exhaustion.

However, in deployments where handle_short_request() is invoked repeatedly within a long-lived Python interpreter process, the availability impact escalates to a complete service outage for the affected worker pool. Furthermore, in the polling code path, reading buffered terminal output in fixed chunks (BUF_SZ) without reassembling byte streams prior to UTF-8 decoding causes intermittent crashes during normal usage whenever multibyte box-drawing characters or Unicode symbols straddle buffer boundaries.

According to threat intelligence metrics, CVE-2026-103022 is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog. The Exploit Prediction Scoring System (EPSS) assigns this vulnerability a score of 0.00472 (0.472%), placing it in the 41.15th percentile. No public weaponized exploit modules or active exploitation campaigns have been recorded.

Remediation

The primary remediation for CVE-2026-103022 is to upgrade the urwid package to version 4.0.7 or later. Version 4.0.7 incorporates commit b79d9a96843a6f5950bb2bfab5d059bdc61c538b, which replaces hardcoded ASCII encoding with dynamic charset resolution, buffers raw socket bytes prior to UTF-8 decoding, and wraps both file descriptors and Unix domain sockets in deterministic cleanup blocks.

Administrators and developers can apply the update using pip:

pip install --upgrade "urwid>=4.0.7"

> [!NOTE] > Applications that only use Urwid's standard console display modules (such as raw_display or curses_display) and do not expose urwid.display.web over an HTTP server are not reachable by this attack vector.

If an immediate upgrade to Urwid 4.0.7 is not possible, operators running urwid.display.web should implement compensating controls at the reverse proxy or Web Application Firewall (WAF) layer:

  1. Input Filtering: Configure the reverse proxy or WAF to inspect POST request bodies sent to the CGI endpoint and reject requests containing byte values outside the 7-bit ASCII range (0x00–0x7F) with an HTTP 400 Bad Request status before they reach the Python handler.
  2. Process Lifecycle Management: Ensure the CGI server executes urwid/display/web.py as a strict one-process-per-request CGI script rather than inside a persistent FastCGI or WSGI worker process, or configure aggressive worker recycling (max-requests) to reclaim leaked file descriptors periodically.
  3. Access Control: Restrict access to the Urwid web display endpoint using HTTP authentication or mutual TLS so that unauthenticated actors cannot obtain valid X-Urwid-ID session identifiers.

Official Patches

UrwidGitHub Security Advisory GHSA-cp25-j9x5-p5xm
UrwidPatch Commit b79d9a96843a6f5950bb2bfab5d059bdc61c538b
UrwidUrwid 4.0.7 Release Notes

Fix Analysis (1)

Technical Appendix

CVSS Score
5.3/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L
EPSS Probability
0.47%
Top 59% most exploited

Affected Systems

Urwid Console User Interface Library for Python (urwid)

Affected Versions Detail

Product
Affected Versions
Fixed Version
urwid
urwid
< 4.0.74.0.7
AttributeDetail
CVE IDCVE-2026-103022 (GHSA-cp25-j9x5-p5xm)
Primary CWECWE-775 (Missing Release of File Descriptor) / CWE-248 (Uncaught Exception)
Attack VectorNetwork (HTTP POST / CGI Request)
CVSS v3.1 Score5.3 Medium (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L)
EPSS Score0.472% (41.15th Percentile)
ImpactDenial of Service via CGI Crash and File Descriptor Exhaustion
Exploit StatusNone (No public PoC or active exploitation)
CISA KEV StatusNot Listed

MITRE ATT&CK Mapping

T1499.001Endpoint Denial of Service: OS Exhaustion Flood
Impact
T1499.004Endpoint Denial of Service: Application or System Exploitation
Impact
CWE-775
Missing Release of File Descriptor or Handle after Effective Lifetime

The software does not release a file descriptor or handle after its effective lifetime has ended, which can lead to resource exhaustion when exceptions bypass normal cleanup routines.

Vulnerability Timeline

Security patch committed to urwid/urwid repository via Pull Request #1201
2026-07-24
Urwid version 4.0.7 released containing the fix
2026-11-06
GitHub Security Advisory GHSA-cp25-j9x5-p5xm published
2026-11-06
CVE-2026-103022 published to NVD and CVE list
2026-11-06
EPSS score recorded at 0.00472 (41.15th percentile)
2026-11-10

References & Sources

  • [1]GitHub Security Advisory GHSA-cp25-j9x5-p5xm
  • [2]Urwid Pull Request #1201
  • [3]Fix Commit b79d9a96843a6f5950bb2bfab5d059bdc61c538b
  • [4]Urwid 4.0.7 Release Notes
  • [5]CVE-2026-103022 Record
  • [6]NVD Entry for CVE-2026-103022

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 1 hour ago•CVE-2026-115770
9.6

CVE-2026-115770: Email Verification Bypass and Downstream Account Takeover in Pocket ID

CVE-2026-115770 is a critical vulnerability (CVSS 9.6) in Pocket ID, an OpenID Connect (OIDC) provider that uses passkey authentication. In versions 2.2.0 through 2.11.0, email verification tokens are bound exclusively to the user account identifier (user_id) rather than the specific email address for which the token was issued. In addition, updating the account email address does not invalidate existing verification tokens. An authenticated user can request a verification token for an attacker-controlled address, modify their profile email to an arbitrary unverified target address, and redeem the previously issued token. This marks the target email address as verified (email_verified = true), enabling account takeover on downstream OIDC relying parties that link or provision accounts based on verified email claims.

Alon Barad
Alon Barad
1 views•11 min read
•about 2 hours ago•CVE-2026-102895
9.8

CVE-2026-102895: Unauthenticated Administrative Access and Remote Code Execution via Loopback Spoofing in GPUStack

CVE-2026-102895 is an improper authentication and origin validation vulnerability in GPUStack versions prior to 2.2.0. By default, the API authentication dependency in gpustack/api/auth.py grants built-in administrator privileges to unauthenticated requests resolved as originating from 127.0.0.1 when the force_auth_localhost flag is false. Because client IP resolution trusts same-host reverse proxy TCP peers and unvalidated X-GPUStack-Real-IP headers, remote unauthenticated attackers can bypass authentication, obtain full administrative control, extract sensitive credentials, and achieve remote code execution via the /v2/inference-backends/from-yaml endpoint.

Alon Barad
Alon Barad
3 views•9 min read
•about 3 hours ago•CVE-2026-87008
7.1

CVE-2026-87008: Out-of-Root Arbitrary File Read and Overwrite via Symlink Following in code-graph-rag

CVE-2026-87008 (GHSA-85gg-2gfq-q95m) is a high-severity symbolic link following and path traversal vulnerability in the `code-graph-rag` Python package prior to version 0.0.589. The flaw resides in the `should_skip_path` path validation routine inside `codebase_rag/utils/path_utils.py`, which performs directory containment checks using unresolved lexical paths rather than canonicalized filesystem paths. Consequently, symbolic links committed to an untrusted repository that point to targets outside the project root bypass boundary enforcement. Attackers who supply a crafted repository containing symbolic links can leverage the `AstGrepService` tool (`structural_search` and `structural_replace`) to read or overwrite arbitrary files on the host filesystem within the privileges of the running process.

Alon Barad
Alon Barad
3 views•9 min read
•about 4 hours ago•CVE-2026-94618
5.4

CVE-2026-94618: Missing Authorization and Weak Archive Identifier Generation in FileGator Batch Download

FileGator versions prior to 7.15.0 contain a missing authorization check (CWE-862) and predictable identifier generation flaw (CWE-330) in the batch download implementation. When a user initiates a batch download, FileGator generates a temporary ZIP archive identified by PHP's non-cryptographic uniqid() function without binding the resulting identifier to the creating user's session. Consequently, any authenticated user with read, download, and batchdownload permissions who obtains or predicts an active archive identifier can retrieve another user's private files via GET /batchdownload, bypassing per-user home directory isolation and deleting the victim's temporary archive upon download completion.

Alon Barad
Alon Barad
4 views•10 min read
•about 5 hours ago•CVE-2026-94619
5.9

CVE-2026-94619: Missing Authorization and Unnamespaced Temporary File Reassembly in FileGator UploadController

An authorization bypass and insecure direct object reference (IDOR) vulnerability exists in FileGator versions prior to 7.15.0 within the chunked file upload reassembly handler. When processing multipart uploads in backend/Controllers/UploadController.php, the application fails to namespace the target assembly filename with a user-specific prefix inside the shared temporary storage directory. Authenticated users with upload privileges can manipulate chunk metadata parameters to exfiltrate, corrupt, or delete arbitrary transient files belonging to other users or the system.

Alon Barad
Alon Barad
5 views•9 min read
•about 6 hours ago•CVE-2026-77596
9.6

CVE-2026-77596: Cross-Tenant Authorization Bypass in Kubeflow Pipelines Upload API

CVE-2026-77596 is an authorization bypass vulnerability in the Kubeflow Pipelines API server affecting multi-user deployments prior to version 2.18.0. The HTTP multipart upload handlers prematurely terminate authorization checks and return success when the target namespace parameter is empty or omitted. Because the default configuration allows pipelines without namespaces to function as cluster-wide shared resources, any low-privileged tenant can create shared pipelines or inject arbitrary pipeline versions into existing shared pipelines without undergoing Kubernetes SubjectAccessReview validation. When other tenants execute unpinned recurring runs tracking these shared pipelines, the attacker-supplied workload runs inside the victim tenant's namespace using the victim's service account credentials.

Alon Barad
Alon Barad
6 views•10 min read