My toxic relationship with Meta's bug bounty program
Update, Nov 5 (evening): A few hours after I published this, Meta's bug bounty team reached me by email. They had replied before, but a bug in their platform kept those messages from reaching me. The report is confirmed
Update, Nov 5 (evening): A few hours after I published this, Meta's bug bounty team reached me by email. They had replied before, but a bug in their platform kept those messages from reaching me. The report is confirmed and resolved and is waiting for a bounty decision. They can't help with the Facebook account, but we can now talk by email. So less ghosting than it looked like, more of a broken mailbox.
Every relationship starts with good intentions. On October 9, I created my first Facebook account for one reason: to report a security bug to Meta.
Within about two hours, the account was permanently disabled. I did the video selfie verification they asked for. That felt fast for a relationship that was two hours old. My appeal was rejected anyway.
Here is the catch: Meta's bug bounty portal sends you through Facebook to open a report. I can see that my report exists. I just can't open it, answer questions or add evidence.
I don't think anybody did this on purpose. To me it looks like an automated false positive: brand new account, no history, and the only thing it ever did was file a security report.
How it went
- Oct 9: I submit the report and lose the account. I email security@ and bugbounty@. No reply reaches me.
- Oct 13: I open a public GitHub issue, without technical details, asking how to reach the team.
- Oct 24: Day 15. I ping the maintainers. One of them answers the same day and asks me to email the details. The report now shows as "Triaged".
- Oct 25: The fix lands in mvfst.
- Oct 26: A Meta engineer confirms by email that the fix came from my report and says the bug bounty team will reach out.
- Nov 3: CERT-Bund, Germany's national CERT, forwards my request to Meta. They take no position on the account or the bounty. So yes, we have reached the stage where a federal agency passes notes between us.
- Nov 5: A few hours after this post went up, the bug bounty team reaches me by email. Their earlier replies had been lost to a delivery bug on their side.
To be fair, the people who write the code were great. Two engineers on the mvfst team answered a stranger's ping and took it seriously. The silence turned out to be a process problem, not a people problem.
The bug
mvfst is Meta's open source QUIC implementation. QUIC has a packet called Retry, which arrives before the handshake and carries a 16-byte integrity tag. RFC 9001 says what the tag is for: only someone who has seen the client's Initial packet should be able to send a valid Retry.
mvfst computed that tag correctly. Then it ignored the answer.
// verifyRetryIntegrityTag returns Expected<bool, QuicError>
if (!clientConn_->clientHandshakeLayer->verifyRetryIntegrityTag(
*originalDstConnId, *retryPacket)) {
// drop the packet
}
With a bad tag, the function returns a successful Expected that contains false. But !expected asks "is there an error?", not "is the value false?". So the drop branch never ran for a bad tag.
The existing unit test made the same mistake. EXPECT_TRUE(verifyRetryIntegrityTag(...)) passes for both true and false.
The result: the client accepted a Retry with any tag, took the attacker's connection ID and token, reset its state and restarted the handshake.
What it actually means
The attacker here is blind and off-path. They have to spoof the server's address, know the client's IP and UDP port, and land one datagram in a short window during connection setup. That is a real hurdle. An attacker who is on the path gains nothing from this bug, because they can compute a valid tag anyway.
I tested it with mvfst's own echo client and server over real UDP on my own machine, using a Retry with an all-zero tag:
-
QUIC v1: the connection attempt fails with
TRANSPORT_PARAMETER_ERROR. -
mvfst's own version (
0xfaceb002, the client default): the handshake completes with the attacker's connection ID and token, and no error is raised. - With the fix: the Retry is dropped and the connection completes normally.
What I did not do: send this over the internet, test any Meta system, or check current Facebook or Instagram app builds. The attacker does not get the handshake or application keys. My own rating is Medium (CVSS 6.5). Meta's rating? See above, I can't open the report.
The fix
auto integrityResult =
clientConn_->clientHandshakeLayer->verifyRetryIntegrityTag(
*originalDstConnId, *retryPacket);
if (!integrityResult.has_value() || !*integrityResult) {
// drop the packet
}
If you embed the mvfst client, use v2026.10.26.00 or later.
What I took away
-
Expected<bool, E>has two kinds of "no". If you only need yes or no, returnbool. If you only need success or error, returnExpected<void, E>. - A test that never sees bad input doesn't test validation. The bug survived because nothing ever fed the check a wrong tag and looked at the result.
- A social media account should not be the only door to a security report. When automation closes that door, researchers need another way in.
Where we stand
The bug is fixed, and that matters most. The report is confirmed and resolved, and the bounty decision is pending. Thanks to the mvfst engineers, to CERT-Bund, and to the bug bounty team for following up.
The Facebook account is still gone, and the security team can't help with that. If you're new to Meta's program, expect a brand-new Facebook account to get flagged, and keep a second channel like email or a coordinator open.
Meta, it was partly your mailbox after all. Your account integrity system and I still need to talk, though.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.