CVE-2025-14341: DivvyDrive Mass Assignment + Resource Exhaustion RCE
DivvyDrive's sync engine accepts attacker-controlled attribute names without a whitelist and allocates unbounded buffers, enabling remote code execution via heap corruption and resource exhaustion.
# A Critical Flaw in DivvyDrive Could Let Hackers Take Control
DivvyDrive, a file-sharing and collaboration platform used by businesses and teams, has a serious security hole. Think of it like a building where someone forgot to check who's entering through the back door—and that door can be opened remotely.
The vulnerability lets attackers modify how the software works without needing a password or permission. They can change the internal rules the program follows, which is like someone sneaking into your office and rewiring your computers to do whatever they want. No login required.
There are actually two dangerous things happening here. First, attackers can flood the system with requests, overwhelming it until it crashes—imagine someone calling your office phone thousands of times per second. Second, and more seriously, they could potentially install malicious code directly onto the affected computer.
Who should worry? If your company uses DivvyDrive versions 4.8.2.19 through 4.8.3.1, this is urgent. Anyone storing sensitive files there—financial records, client data, internal documents—is at risk. The good news is that hackers aren't actively exploiting this yet, but that window is closing.
Here's what you need to do right now:
Check your DivvyDrive version. Go to settings or "about" and write down what you see. If it's in the affected range, contact your IT department immediately—don't wait.
Update to the latest version as soon as your IT team releases it. This is not optional security theater; it's critical.
If you're not sure you're using DivvyDrive, ask your IT department or check what file-sharing services your company has approved. Better to ask now than discover a breach later.
Want the full technical analysis? Click "Technical" above.
CVE-2025-14341 is a compound vulnerability in DivvyDrive (versions 4.8.2.19 through 4.8.3.1) combining two distinct weaknesses: CWE-915 (Improperly Controlled Modification of Dynamically-Determined Object Attributes — the "mass assignment" class) and CWE-770 (Allocation of Resources Without Limits or Throttling). Together they provide a reliable path from an unauthenticated network position to remote code execution on any platform DivvyDrive supports. CVSS 8.3 (HIGH) reflects network reachability, no authentication required, and high impact to integrity.
The vulnerability class will be immediately familiar to anyone who has audited Rails or Django APIs circa 2012-2016, but the implementation here is in a native sync daemon — making exploitation consequences significantly worse than a typical web mass-assignment bug. Heap corruption arises from the allocation side, not just privilege escalation.
Root cause: The DivvyDrive sync daemon deserialises peer-supplied JSON attribute maps directly into internal drive-object structs without a field whitelist, and then allocates accumulation buffers sized from attacker-controlled length fields with no upper bound, allowing heap overflow and object-attribute injection in a single protocol exchange.
Affected Component
The vulnerable code lives in the DivvyDrive cross-platform sync daemon (divvyd / DivvyDriveService.exe on Windows). The daemon exposes a local loopback API for the UI shell and a peer-to-peer sync protocol over TCP for device-to-device transfers. Both surfaces are reachable; the P2P protocol requires no shared secret by default in affected builds.
Relevant source paths inferred from binary symbol recovery:
sync/object_store.c — object attribute merging
net/transfer_session.c — chunked transfer allocation
The sync protocol represents shared drive objects as JSON dictionaries. When a remote peer sends an OBJECT_UPDATE message, object_merge_attrs() iterates the incoming key-value pairs and writes them into the live drive_object_t struct using a string-keyed lookup that falls back to dynamic attribute creation — the mass-assignment sink.
/* sync/object_store.c — DivvyDrive 4.8.2.x */
typedef struct drive_object {
/* +0x00 */ uint64_t obj_id;
/* +0x08 */ uint32_t obj_type; // FILE=1, DIR=2, SYMLINK=3
/* +0x0C */ uint32_t flags; // READONLY, SHARED, ENCRYPTED …
/* +0x10 */ char *display_name;
/* +0x18 */ char *remote_path;
/* +0x20 */ uint64_t size_bytes;
/* +0x28 */ uint64_t mtime;
/* +0x30 */ uint32_t chunk_count;
/* +0x34 */ uint32_t acl_mask; // BUG: writable via mass-assign
/* +0x38 */ attr_bag_t *dyn_attrs; // dynamic overflow bucket
/* +0x40 */ transfer_ctx_t *xfer;
} drive_object_t;
/* Called for every key-value pair in a peer OBJECT_UPDATE message */
int object_merge_attrs(drive_object_t *obj, json_obj_t *patch) {
json_iter_t it;
json_iter_init(&it, patch);
const char *key;
json_val_t val;
while (json_iter_next(&it, &key, &val)) {
/* BUG: no whitelist — attacker can name any struct field.
* Known-field fast path writes directly into the struct.
* 'acl_mask', 'flags', 'obj_type' are all reachable here. */
if (!object_set_known_field(obj, key, &val)) {
// fallback: store in dynamic bag (unbounded)
attr_bag_insert(obj->dyn_attrs, key, &val); // BUG: no quota
}
}
return 0; // always succeeds — no error propagation
}
object_set_known_field() uses a plain strcmp dispatch table. Any key that matches a known field name — including "acl_mask", "flags", and "obj_type" — is written without privilege checking. An attacker can flip READONLY off, set acl_mask to 0xFFFFFFFF, or downgrade obj_type to SYMLINK and redirect path resolution.
The second weakness lives in the chunked transfer allocator called immediately after object merging when the patch includes a new size_bytes:
/* net/transfer_session.c */
#define CHUNK_SIZE 0x40000 // 256 KB fixed read stride
transfer_ctx_t *transfer_alloc_recv(drive_object_t *obj,
uint64_t advertised_size) {
transfer_ctx_t *ctx = calloc(1, sizeof(transfer_ctx_t));
ctx->obj = obj;
ctx->total = advertised_size; // BUG: attacker-controlled, no cap
/* Allocate reassembly buffer for entire object up front */
ctx->buf = malloc(advertised_size); // BUG: no upper bound — OOM / integer wrap
ctx->written = 0;
return ctx;
}
int transfer_recv_chunk(transfer_ctx_t *ctx, peer_conn_t *conn) {
size_t stride = CHUNK_SIZE; // always 256 KB
/* BUG: stride is not clamped to (ctx->total - ctx->written).
* If advertised_size was crafted small and peer sends more data,
* or if integer wrap makes ctx->buf small, this overflows. */
peer_read(conn, ctx->buf + ctx->written, &stride); // BUG: heap overflow
ctx->written += stride;
if (ctx->written >= ctx->total)
transfer_finalize(ctx);
return 0;
}
Memory Layout
HEAP STATE — transfer_alloc_recv() with advertised_size = 0x1FFFFFFC0
(integer truncation on 32-bit target: 0x1FFFFFFC0 & 0xFFFFFFFF = 0xFFFFFFC0)
BEFORE transfer_recv_chunk():
[ chunk A: drive_object_t 0x148 bytes ]
[ chunk B: transfer_ctx_t 0x60 bytes ]
[ chunk C: ctx->buf malloc(0xFFFFFFC0) ] <- wraps; actual alloc ≈ 192 bytes on some
allocators (glibc bin reuse)
[ chunk D: attr_bag_t 0x80 bytes ] <- adjacant, targeted
AFTER first transfer_recv_chunk() (stride = 0x40000, buf ≈ 0xC0 bytes):
[ chunk C: ctx->buf 0xC0 bytes ]
[ OVERFLOW → chunk D: attr_bag_t ]
attr_bag_t+0x00 : 0x4141414141414141 (attacker data — fake next ptr)
attr_bag_t+0x08 : 0x4242424242424242 (fake capacity — controls realloc)
attr_bag_t+0x10 : ... (key/value pairs overwritten)
Subsequent attr_bag_insert() → realloc(fake_ptr, fake_capacity)
→ allocator operates on attacker-controlled pointer → arbitrary write primitive.
Exploitation Mechanics
EXPLOIT CHAIN — CVE-2025-14341 (unauthenticated P2P surface):
1. DISCOVERY
Connect to DivvyDrive P2P sync port (default TCP 47200).
Send valid HELLO handshake — no credentials required in affected builds.
2. MASS ASSIGNMENT — disable ACL enforcement
Send OBJECT_UPDATE for an existing shared object with patch:
{ "acl_mask": 4294967295, "flags": 0, "obj_type": 1 }
object_merge_attrs() writes 0xFFFFFFFF into obj->acl_mask via known-field
dispatch. Subsequent ACL checks on this object pass unconditionally.
3. SIZE CONFUSION — trigger integer truncation
Send OBJECT_UPDATE with:
{ "size_bytes": 8589934528 } // 0x200000100 — low 32 bits = 0x100
transfer_alloc_recv() called: malloc(0x100) returns small buffer,
ctx->total stores full 64-bit value (or truncated — target-ABI dependent).
4. HEAP GROOMING
Issue 12x rapid OBJECT_UPDATE messages for dummy objects to shape
the heap so that an attr_bag_t lands immediately after ctx->buf.
5. OVERFLOW — corrupt attr_bag_t
Send CHUNK_DATA payload of 0x40000 bytes.
transfer_recv_chunk() reads full stride into 0x100-byte buffer,
overwriting the adjacent attr_bag_t header with attacker data.
Craft overflow bytes:
+0x00: ptr = &fake_chunk - 0x10 (points into attacker-controlled data)
+0x08: cap = 0x1 (forces immediate realloc on next insert)
6. ARBITRARY WRITE — trigger realloc
Send second OBJECT_UPDATE with any new dynamic attribute key.
attr_bag_insert() sees cap exhausted, calls realloc(fake_ptr, new_cap).
Allocator interprets fake_ptr as a heap chunk; returns attacker-chosen
address as the new bag buffer.
7. FUNCTION POINTER OVERWRITE
Write shellcode address into transfer_finalize_cb stored in transfer_ctx_t
via the now-redirected attr_bag write.
8. CODE EXECUTION
Send final chunk that sets ctx->written >= ctx->total.
transfer_finalize(ctx) invokes ctx->finalize_cb → shellcode.
Patch Analysis
The fix in version 4.8.3.2 addresses both primitives independently:
/* BEFORE (vulnerable) — object_merge_attrs() */
while (json_iter_next(&it, &key, &val)) {
if (!object_set_known_field(obj, key, &val)) {
attr_bag_insert(obj->dyn_attrs, key, &val); // no quota
}
}
/* AFTER (patched 4.8.3.2) — object_merge_attrs() */
static const char *FIELD_WHITELIST[] = {
"display_name", "remote_path", "size_bytes", "mtime", NULL
};
while (json_iter_next(&it, &key, &val)) {
if (!is_whitelisted(key, FIELD_WHITELIST)) {
log_warn("object_merge_attrs: rejected field '%s' from peer", key);
continue; // drop — no fallback to known-field write
}
if (!object_set_known_field(obj, key, &val)) {
if (obj->dyn_attrs->count >= ATTR_BAG_MAX_ENTRIES) { // quota: 64
return -ENOSPC;
}
attr_bag_insert(obj->dyn_attrs, key, &val);
}
}
/* BEFORE (vulnerable) — transfer_alloc_recv() + transfer_recv_chunk() */
ctx->buf = malloc(advertised_size); // no cap, no overflow check
// ...
size_t stride = CHUNK_SIZE;
peer_read(conn, ctx->buf + ctx->written, &stride); // stride not clamped
/* AFTER (patched 4.8.3.2) */
#define TRANSFER_MAX_SIZE (1ULL * 1024 * 1024 * 1024) // 1 GB hard cap
if (advertised_size > TRANSFER_MAX_SIZE || advertised_size == 0)
return NULL; // reject before any allocation
ctx->buf = malloc(advertised_size);
if (!ctx->buf) return NULL;
// ...
size_t remaining = ctx->total - ctx->written;
size_t stride = (remaining < CHUNK_SIZE) ? remaining : CHUNK_SIZE;
peer_read(conn, ctx->buf + ctx->written, &stride); // clamped to remaining
ctx->written += stride;
Note that the whitelist approach for the mass-assignment fix is correct but incomplete in the patch as shipped — object_set_known_field() still handles the whitelisted size_bytes field without validating against an already-open transfer session, which could re-open a TOCTOU window. Researchers auditing 4.8.3.2 should verify whether a concurrent OBJECT_UPDATE { "size_bytes": X } mid-transfer can resize the live ctx->total.
Detection and Indicators
Network signatures: Look for OBJECT_UPDATE messages on TCP/47200 containing the keys acl_mask, flags, or obj_type — none of these should appear in legitimate peer traffic. A Suricata rule skeleton:
alert tcp any any -> any 47200 (
msg:"CVE-2025-14341 DivvyDrive mass-assign attempt";
content:"OBJECT_UPDATE"; content:"acl_mask"; distance:0;
sid:2025143410; rev:1;
)
alert tcp any any -> any 47200 (
msg:"CVE-2025-14341 DivvyDrive oversized transfer";
content:"size_bytes";
byte_test:8,>,1073741824,0,relative,big-endian;
sid:2025143411; rev:1;
)
Host indicators:
divvyd process RSS growing unboundedly without active user transfers
Crash dumps in %APPDATA%\DivvyDrive\logs\ or ~/.divvydrive/logs/ referencing transfer_recv_chunk or attr_bag_insert in the backtrace
acl_mask or flags keys present in object_store.db JSON blobs
Remediation
Update immediately to DivvyDrive 4.8.3.2 or later. No workaround is viable for the unauthenticated P2P surface short of firewall isolation.
If immediate upgrade is not possible, firewall TCP/47200 (and the local loopback API port) to trusted hosts only.
Disable P2P sync in DivvyDrive settings (Preferences → Sync → Disable peer-to-peer transfers) to eliminate the unauthenticated attack surface entirely until patched.
Audit all object_store.db files for unexpected acl_mask or flags values that may indicate prior exploitation against your fleet.
Vendors implementing similar JSON-to-struct dispatch: enforce an explicit allowlist at the protocol boundary, never at the struct-write level, and always cap allocation sizes with both a logical maximum and an integer-overflow-safe check (size > MAX || size == 0) before any malloc call.