Passing Mutable Objects to an Untrusted Method

Description

Passing Mutable Objects to an Untrusted Method is a vulnerability that occurs when a program transmits non-cloned mutable data as an argument to a method or function. Since the called function can alter or delete this data, it may violate assumptions the caller made about the program's state. When unverified or untrusted code receives references to mutable objects, it could modify them unexpectedly, potentially invalidating that data within the original execution context. This is particularly problematic when the mutable object contains security-relevant data, internal state, or data that other parts of the program assume to be constant.

Risk

Passing mutable objects to untrusted methods can lead to data corruption, security bypasses, and privilege escalation. An untrusted method might modify authentication tokens, permission sets, or configuration data. Internal state that the caller assumes is unchanged may be altered, causing subsequent operations to behave incorrectly. In multi-threaded environments, the modifications might occur at any time, creating race conditions. The risk is amplified when the mutable object is shared across multiple components - modifications by one untrusted method affect all components using that object.

Solution

Pass data as constants or immutable objects when possible. Clone all mutable data before passing it to external or untrusted functions. Use defensive copying for both input parameters and return values. Consider using immutable collections and objects. In Java, use Collections.unmodifiableList() or similar methods. Mark fields as final where appropriate. When cloning, ensure deep copies are made for objects containing other mutable objects. Validate that returned data hasn't been unexpectedly modified if the mutable reference was shared.

Common Consequences

ImpactDetails
IntegrityScope: Integrity

Data could be tampered with by another function which should not have been allowed to modify it.

Example Code

Vulnerable Code

// Vulnerable: Passing mutable object without cloning
public class VulnerableBookStore {
    private List<Book> inventory = new ArrayList<>();

    public void addBook(Book book) {
        inventory.add(book);

        // Vulnerable: Passing mutable book to external service
        // External service could modify the book
        externalAuditService.logNewBook(book);

        // Book in inventory may now have different values!
    }

    public List<Book> getInventory() {
        // Vulnerable: Returning mutable internal list
        return inventory;
        // Caller can modify internal state
    }
}

public class Book {
    private String title;
    private double price;

    // Getters and setters - object is mutable
}
# Vulnerable: Mutable default arguments and shared state
class VulnerableConfig:
    def __init__(self, settings={}):  # Vulnerable: mutable default
        self.settings = settings

    def get_settings(self):
        # Vulnerable: Returns reference to internal state
        return self.settings

def vulnerable_process(data_list):
    # Vulnerable: Modifying the passed list
    plugin.process(data_list)  # Plugin may modify list!
    # Original caller's list is now changed
// Vulnerable: Passing pointer to mutable data
typedef struct {
    char *username;
    int permissions;
} UserContext;

void vulnerable_log_action(UserContext *ctx, const char *action) {
    // Vulnerable: Passing mutable pointer to logger
    // Logger could modify ctx
    external_logger_log(ctx, action);

    // ctx->permissions may have been changed!
    if (ctx->permissions & ADMIN_PERMISSION) {
        perform_admin_action();  // May execute unexpectedly
    }
}

Fixed Code

// Fixed: Clone mutable objects before passing
public class SecureBookStore {
    private List<Book> inventory = new ArrayList<>();

    public void addBook(Book book) {
        // Fixed: Clone book before storing and passing
        Book bookCopy = book.clone();
        inventory.add(bookCopy);

        // Fixed: Pass clone to external service
        externalAuditService.logNewBook(book.clone());

        // Internal inventory is protected
    }

    public List<Book> getInventory() {
        // Fixed: Return defensive copy
        List<Book> copy = new ArrayList<>();
        for (Book book : inventory) {
            copy.add(book.clone());
        }
        return copy;

        // Or use unmodifiable wrapper:
        // return Collections.unmodifiableList(inventory);
    }
}

// Fixed: Immutable Book class
public final class ImmutableBook {
    private final String title;
    private final double price;

    public ImmutableBook(String title, double price) {
        this.title = title;
        this.price = price;
    }

    public String getTitle() { return title; }
    public double getPrice() { return price; }

    // No setters - object is immutable
}
# Fixed: Defensive copying
import copy

class SecureConfig:
    def __init__(self, settings=None):
        # Fixed: Don't use mutable default argument
        self.settings = copy.deepcopy(settings) if settings else {}

    def get_settings(self):
        # Fixed: Return copy of internal state
        return copy.deepcopy(self.settings)

def secure_process(data_list):
    # Fixed: Pass copy to plugin
    data_copy = copy.deepcopy(data_list)
    plugin.process(data_copy)
    # Original list is unchanged

# Fixed: Using immutable structures
from dataclasses import dataclass
from typing import FrozenSet

@dataclass(frozen=True)  # Immutable
class ImmutableConfig:
    name: str
    values: tuple  # Immutable sequence

def secure_process_immutable(config: ImmutableConfig):
    # Config cannot be modified
    plugin.process(config)
// Fixed: Passing copy of data
typedef struct {
    char username[64];  // Fixed size, can be copied
    int permissions;
} UserContext;

void secure_log_action(const UserContext *ctx, const char *action) {
    // Fixed: Create copy for logger
    UserContext ctx_copy;
    memcpy(&ctx_copy, ctx, sizeof(UserContext));

    external_logger_log(&ctx_copy, action);

    // Original ctx is unchanged
    if (ctx->permissions & ADMIN_PERMISSION) {
        perform_admin_action();
    }
}

// Fixed: Use const to indicate read-only intent
void secure_read_only(const UserContext *ctx) {
    // Fixed: Compiler enforces read-only access
    // ctx->permissions = 0;  // Would cause compiler error
    log_user(ctx->username);
}

CVE Examples

No specific CVEs are listed for this CWE. The vulnerability pattern appears in:

  • Java applications passing collections to plugins
  • Python code with mutable default arguments
  • APIs that modify caller-provided objects

References

  1. MITRE Corporation. "CWE-374: Passing Mutable Objects to an Untrusted Method." https://cwe.mitre.org/data/definitions/374.html
  2. Joshua Bloch. "Effective Java." Item 50: Make defensive copies when needed.