Sep 16, 2026·6 min read·20 visits
A mass-assignment vulnerability in djust allows authenticated clients to modify arbitrary public server-side view attributes via crafted WebSocket payloads.
CVE-2026-61598 is a high-severity mass-assignment vulnerability (CWE-915) affecting the Python package djust prior to version 1.0.7. An authenticated client can supply arbitrary parameter names to modify public view attributes on the server via WebSocket events, leading to unauthorized state manipulation, authorization bypass, or price tampering.
The Python package djust is a reactive web framework designed to provide server-side rendering for Django applications. It implements a model binding mechanism over WebSockets to allow real-time synchronization between client-side input elements and server-side state. This functionality is exposed via the ModelBindingMixin class, which is included in the base Method Resolution Order (MRO) of LiveView instances.
CVE-2026-61598 describes a mass-assignment vulnerability within this binding mechanism. The framework permits authenticated clients to invoke the default update_model WebSocket event handler. Under vulnerable versions, arbitrary server-side view attributes can be manipulated if they are public and not protected by an explicit configuration.
The vulnerability is classified under CWE-915 (Improperly Controlled Modification of Dynamically-Determined Object Attributes). The lack of secure default controls results in a situation where any public attribute on an active LiveView session can be modified. This creates paths for unauthorized state manipulation and potential privilege escalation within affected Django applications.
The root cause of the vulnerability resides in the validation logic of the update_model event handler. The handler is designed to map client-side input field updates directly to server-side instance variables using Python's setattr built-in function. To prevent abuse, the developers introduced multiple validation checks, including an optional allowlist parameter named allowed_model_fields.
By default, the allowed_model_fields parameter was set to None. When this configuration was active, the framework implemented a fail-open behavior. The validation logic only verified that the requested field did not begin with an underscore, was not present in a static list of fourteen forbidden internal properties, and existed on the target object instance.
This behavior exposes any public variable bound to the LiveView instance to arbitrary mutation. If a developer defines a public helper variable or a state tracker on the view class, that variable is exposed to incoming WebSocket events. Because the type coercion utility automatically parses string parameters into matching primitive types, attackers can mutate booleans, integers, and other data types.
The vulnerable implementation depends on a permissive control structure that executes when a WebSocket frame containing an update_model payload is received. The following segment shows the logic path prior to the remediation in version 1.0.7.
# Vulnerable implementation in djust/mixins/model_binding.py
@event_handler
def update_model(self, field, value):
# Prevent modification of private or dunder attributes
if field.startswith('_'):
return
# Prevent modification of critical framework-level fields
if field in FORBIDDEN_MODEL_FIELDS:
return
# Verify that the attribute actually exists on the view
if not hasattr(self, field):
return
# Fail-open check: if no allowlist is configured, permit the update
if self.allowed_model_fields is not None and field not in self.allowed_model_fields:
return
# Dynamic modification of instance state
setattr(self, field, value)The patched version introduces an automated validation mechanism that extracts static template bindings during compilation. The template engine parses the abstract syntax tree (AST) to identify valid dj-model declarations and generates a safe auto-allowlist.
# Patched implementation in djust/mixins/model_binding.py
@event_handler
def update_model(self, field, value):
if field.startswith('_'):
return
if field in FORBIDDEN_MODEL_FIELDS:
return
if not hasattr(self, field):
return
# Retrieve developer-defined overrides
allowed = self.allowed_model_fields or set()
# Retrieve the AST-derived static bindings from the compiled template
auto_allowed = getattr(self, '_dj_model_fields', set())
# Fail-closed check: block updates if the field is not in either set
if field not in allowed and field not in auto_allowed:
return
# State mutation only occurs after validation passes
setattr(self, field, value)Exploitation requires an attacker to establish an active WebSocket connection to an application endpoint running a vulnerable LiveView instance. The attacker must target a view that contains sensitive state tracking within its public attributes. Because the handler executes without further session validation on individual attributes, any authenticated client can trigger the payload.
An attacker constructs a WebSocket event payload targeting the default event handler name. The payload contains parameters specifying the target field and the desired modification value. The following example represents the logical structure of an exploitation payload targeting a boolean privilege flag.
{
"type": "event",
"event": "update_model",
"params": {
"field": "is_admin",
"value": "true"
}
}Upon receiving this frame, the server-side component processes the request. The validation checks succeed because the field is public, not protected by a default allowlist, and exists on the target class. The framework automatically parses the string representation into a boolean value and updates the server-side state of the active connection.
The security impact of CVE-2026-61598 depends heavily on the design of the target Django application. Because LiveView instances manage local component state on the server, modifying public attributes allows attackers to bypass operational limits and application workflows. If a class exposes properties associated with user permissions or identifiers, attackers can elevate their privileges or access other accounts.
In transaction-oriented applications, this flaw can lead to financial tampering. For instance, if a checkout view stores a public variable representing a unit price or total quantity, an attacker can modify those values prior to final payment processing. The dynamic nature of the model binding means that changes propagate immediately through the server-side state machine.
The vulnerability has been assigned a CVSS score of 7.1, reflecting a high-severity rating. The attack complexity is low and no special privileges beyond basic authentication are required. The primary impact is to the integrity of the application state, as represented by the high integrity vector in the CVSS string.
The primary remediation path is upgrading the djust package to version 1.0.7 or later. The update switches the default validation mechanism to a secure, fail-closed posture. It leverages Rust AST compilation of templates to dynamically map valid client-side fields, ensuring that only declared inputs can trigger modifications.
If immediate patching is not feasible, developers must manually define the allowed_model_fields attribute on all existing LiveView components. This attribute must contain a set of strings representing the exact subset of variables intended for synchronization. Setting this value explicitly overrides the vulnerable fail-open default configuration.
class SecureLegacyView(LiveView):
# Manually declare permitted fields to neutralize the fail-open vulnerability
allowed_model_fields = {"search_query", "page_offset"}Additionally, developers must adhere to strict state isolation practices. Sensitive data, including database identifiers, privilege levels, and pricing values, should never be stored in public instance variables. Instead, use private variables or session storage to retain authorization contexts and critical business state.
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
djust djust-org | < 1.0.7 | 1.0.7 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-915 (Improperly Controlled Modification of Dynamically-Determined Object Attributes) |
| Attack Vector | Network |
| CVSS v4.0 | 7.1 |
| Exploit Status | None / No Public PoC |
| CISA KEV Status | Not Listed |
| Remediation | Upgrade to 1.0.7 or declare explicit allowed_model_fields |
The product allows an attacker to modify attributes of an object in a way that is not within the scope of the expected modification.
CVE-2026-118777 is a prototype pollution vulnerability (CWE-1321) in the Forge JavaScript cryptography and networking library (distributed on npm as node-forge) prior to version 1.4.1. The flaw resides in the forge.http client subsystem, where incoming HTTP cookie names and paths parsed from Set-Cookie response headers are stored directly into standard JavaScript objects without key validation or property isolation. By returning crafted Set-Cookie headers containing keys such as __proto__ or constructor, a malicious HTTP server can inject arbitrary properties into Object.prototype, attach attributes to the global Object constructor, or delete built-in prototype methods to cause an application-wide denial of service.
CVE-2026-118775 (GHSA-4642-5mcj-2rqw) is an integer overflow and validation flaw in the ASN.1 serialization module of Forge (node-forge) prior to version 1.4.1. The asn1.oidToDer() function fails to validate the range of the first two arcs of an Object Identifier (OID) and truncates the packed initial subidentifier to a single 8-bit integer. Applications that perform policy checks on untrusted OID strings before encoding them into Distinguished Encoding Rules (DER) format can be tricked into emitting DER bytes that correspond to a completely different, restricted OID.
CVE-2026-119194 is an algorithmic complexity vulnerability (CWE-407) in the pyasn1 library for Python, affecting versions 0.3.3 through 0.6.4. The flaw resides in the schemaless decoding routine ConstructedPayloadDecoderBase._decodeComponentsSchemaless used across BER, CER, and DER codecs. By sending an ASN.1 container populated with child components whose tag identifiers are separated by multiples of CPython's integer hash modulus (sys.hash_info.modulus), an unauthenticated attacker can force worst-case hash collisions in an unbounded Python set, degrading decoding performance from O(N) to O(N^2) and causing a Denial of Service (DoS).
CVE-2026-119195 is an algorithmic complexity vulnerability (CWE-407) in the pyasn1 library for Python, affecting all versions prior to 0.6.5. When decoding constructed strings or indefinite-length ANY types across BER, CER, and certain DER code paths, the payload decoders repeatedly concatenate incoming fragments onto an immutable bytes object using the += operator. Because Python bytes objects cannot be resized in-place, each fragment iteration allocates a new buffer and copies all previously accumulated bytes, resulting in quadratic O(N^2) time and memory copy complexity. An unauthenticated remote attacker can supply a highly fragmented ASN.1 stream to exhaust CPU cycles and stall application worker threads.
An algorithmic complexity vulnerability (CWE-407) in pyasn1 versions prior to 0.6.5 allows unauthenticated attackers to cause a Denial of Service (DoS) via crafted ASN.1 payloads. The flaw resides in the binary-encoded ASN.1 REAL mantissa deserialization routine within RealPayloadDecoder.valueDecoder. By performing iterative byte-string slicing and repeated bitwise shifts on a growing arbitrary-precision integer, the decoder exhibits quadratic time and memory allocation complexity relative to the mantissa length. Supplying a BER-, CER-, or DER-encoded payload with a large REAL mantissa causes excessive CPU consumption and blocks application worker processes.
A vulnerability classified under CWE-407 (Inefficient Algorithmic Complexity) exists in the pyasn1 library prior to version 0.6.5. When parsing constructed ASN.1 BIT STRING payloads under BER, CER, or schema-driven DER encoding rules, the BitStringPayloadDecoder repeatedly shifts and copies the entire accumulated bit value for each incoming fragment. Processing inputs with a large number of small constructed fragments induces quadratic CPU consumption, allowing unauthenticated remote attackers to cause a denial of service via resource exhaustion during the initial decoding phase.