[{"content":"TL;DR While analyzing a JavaScript bundle on redacted.com, I found a reference to an internal staging/development site. The staging site shared the same invite ID namespace as production — meaning invite codes generated on staging resolved to real, unrelated servers on production. By automating invite generation on staging, an attacker could forcefully join random invite-only servers on production without authorization.\nTarget \u0026amp; Scope Program: Roblox Asset: *.redacted.com Severity: Medium (CVSS 4.2) Weakness: Improper Access Control (CWE-284) Recon \u0026amp; Discovery I started with some JavaScript recon on the main site. While digging through a publicly accessible admin bundle at a path like https://www.redacted.com/REDACTED/adminBundle.js, I noticed a hardcoded reference to what appeared to be a staging or development domain — something like staging.redacted.com.\nVisiting the staging site, it was running the same application as production. I tried logging in with my existing production account, but got an error indicating the account didn\u0026rsquo;t exist — confirming this was a separate environment with its own user database.\nSo I created a fresh account on staging and set up a test server.\nVulnerability Details The core issue was that staging and production shared the same invite ID namespace. Invite codes weren\u0026rsquo;t scoped to the environment that generated them.\nHere\u0026rsquo;s how I discovered this:\nOn staging, I created a server and used the \u0026ldquo;Invite People\u0026rdquo; feature to generate an invite link — something like https://staging.redacted.com/i/AbCd1234 I took just the invite ID (AbCd1234) and plugged it into the production URL: https://www.redacted.com/i/AbCd1234 Instead of returning an error, production resolved the invite ID to a completely different, real server — one I had no connection to I repeated this several times. Each invite ID generated on staging mapped to a different random server on production. The invite IDs were valid and accepted — clicking \u0026ldquo;Continue\u0026rdquo; would add me to that server. This meant the invite ID pool was shared across environments. Staging was essentially a free invite ID oracle for production.\nAutomating the Attack To demonstrate the scalability of this issue, I wrote a Python script that:\nAuthenticates to the staging site Generates a new invite ID via the staging API (POST /api/teams/REDACTED/invites) Takes the returned invite ID and queries the production API (GET /api/invites/{inviteId}) Extracts the target server\u0026rsquo;s ID, name, and visibility setting from the response import requests session = requests.session() # Step 1: Generate an invite ID on staging staging_url = \u0026#34;https://staging.redacted.com/api/teams/REDACTED/invites\u0026#34; staging_cookies = {\u0026#34;hmac_signed_session\u0026#34;: \u0026#34;REDACTED\u0026#34;, \u0026#34;authenticated\u0026#34;: \u0026#34;true\u0026#34;} staging_headers = {\u0026#34;Content-Type\u0026#34;: \u0026#34;application/json\u0026#34;} staging_json = {\u0026#34;gameId\u0026#34;: None, \u0026#34;teamId\u0026#34;: \u0026#34;REDACTED\u0026#34;} req = session.post(staging_url, headers=staging_headers, cookies=staging_cookies, json=staging_json) # Parse out the invite ID from the response invite_id = parse_invite_id(req.text) # Step 2: Use the invite ID on production prod_url = f\u0026#34;https://www.redacted.com/api/invites/{invite_id}\u0026#34; response = requests.get(prod_url) # Step 3: Extract server info server_data = response.json() print(f\u0026#34;Server ID: {server_data[\u0026#39;team\u0026#39;][\u0026#39;id\u0026#39;]}\u0026#34;) print(f\u0026#34;Server Name: {server_data[\u0026#39;team\u0026#39;][\u0026#39;name\u0026#39;]}\u0026#34;) print(f\u0026#34;Visibility: {server_data[\u0026#39;team\u0026#39;][\u0026#39;visibility\u0026#39;]}\u0026#34;) Each run returned a different server from production — including servers with \u0026quot;visibility\u0026quot;: \u0026quot;default\u0026quot; (invite-only). Running this in a loop would let an attacker mass-join private servers without any invitation.\nImpact An attacker could forcefully join invite-only servers belonging to other users without authorization. Specifically:\nPrivacy violation — gain access to private community content, messages, and member lists Scalable abuse — the script could be looped to join hundreds of servers, limited only by rate limiting (which was not enforced on the staging API) No victim interaction required — the attacker didn\u0026rsquo;t need to know anything about the target servers; they were joined at random The attack complexity was assessed as High because the attacker could not target a specific server — the mapping between staging invite IDs and production servers was unpredictable. However, for an attacker whose goal is broad unauthorized access rather than targeted intrusion, this was trivially exploitable. Remediation The fix was to scope invite IDs to their originating environment. After the patch, using a staging-generated invite ID on production returned:\n{\u0026#34;code\u0026#34;: \u0026#34;NotFoundError\u0026#34;, \u0026#34;message\u0026#34;: \u0026#34;\u0026#34;} This confirmed that the invite ID namespaces were properly isolated between environments.\nTimeline Date Event 2022-07-21 Vulnerability discovered and reported 2022-07-29 HackerOne triage requested additional info 2022-07-29 Provided video PoC demonstrating the issue 2022-08-02 Triaged and handed off to Roblox security team 2022-08-02 Roblox acknowledged the finding 2022-09-01 Fix deployed, retest requested 2022-09-01 Confirmed fix — invite IDs now return NotFoundError 2022-09-21 Bounty awarded Takeaways JS bundles are recon goldmines. The entire attack chain started from reading a publicly accessible JavaScript file that leaked the staging domain. This is a reminder to always check for hardcoded URLs, API keys, and internal references in client-side code. Tools like LinkFinder, JSLuice, or even a simple grep through beautified bundles can surface things that aren\u0026rsquo;t meant to be public.\nStaging environments are often softer targets. Staging and development environments frequently have weaker security controls, fewer rate limits, and — as in this case — shared resources with production that create unexpected attack paths. Always check if staging assets interact with production in any way.\nShared namespaces across environments are dangerous. If your staging and production systems share database state, ID pools, or any kind of namespace, a vulnerability in one environment can cascade into the other. Environment isolation should extend beyond just network segmentation — it includes data, identifiers, and authentication state.\nAutomation turns low-probability into certainty. The individual odds of hitting a specific private server were low, but the ability to generate unlimited invite IDs at no cost turned this into a reliable mass-exploitation vector. When evaluating severity, always consider what happens when a bug is scripted and run at scale.\n","permalink":"https://elsayyay.github.io/bounty-blog/posts/shared-name-invitespace-improper-access-control/","summary":"How I discovered that a staging environment shared its invite ID namespace with production, allowing an attacker to generate invite codes on staging and use them to forcefully join random private servers on production — including invite-only ones.","title":"Forcefully Joining Private Servers via Shared Invite ID Namespace"},{"content":"I\u0026rsquo;m Youssef, a Software Engineering student at McMaster and a bug bounty hunter on HackerOne since 2021. I started this blog to document my security research — both as a reference for myself and to share methodology with the community.\nWhat I\u0026rsquo;ll Be Posting Bug bounty writeups after disclosure is approved Methodology breakdowns — how I approach recon, manual testing, and chaining vulnerabilities CTF solutions from TryHackMe and other platforms Android security research as I expand deeper into mobile Why Write It Up? Writing forces you to understand what you actually did vs. what you think you did. Half the time I\u0026rsquo;m drafting a writeup, I realize there\u0026rsquo;s a cleaner exploitation path I missed, or that my initial assumptions about impact were wrong.\nIt also builds a public track record. A portfolio of well-documented findings says more than a resume bullet point ever could.\nMore posts coming soon. Stay tuned.\n","permalink":"https://elsayyay.github.io/bounty-blog/posts/hello-world/","summary":"Introducing my security research blog — what to expect and why I\u0026rsquo;m writing.","title":"Hello World — Why I Started This Blog"},{"content":"Who I Am I\u0026rsquo;m Youssef Elsayyad — a Software Engineering student at McMaster University (B.Eng, class of 2028) and an active bug bounty hunter since 2021. I go by agentgeneric on HackerOne and TryHackMe.\nI focus on web application security — finding and responsibly disclosing vulnerabilities in real-world production apps. My work spans XSS (stored and reflected), SQL injection, IDOR, CSRF, LFI/RFI, authentication bypass, business logic errors, and privilege escalation.\nBeyond web, I\u0026rsquo;ve been expanding into Android application security through dedicated coursework and hands-on research.\nWhat You\u0026rsquo;ll Find Here Bug bounty writeups — detailed breakdowns of vulnerabilities I\u0026rsquo;ve reported through HackerOne, published after resolution and disclosure approval Methodology posts — how I approach recon, manual testing, and exploitation CTF writeups — solutions to challenges from TryHackMe and other platforms Tools \u0026amp; techniques — anything useful I\u0026rsquo;ve picked up along the way Technical Background Offensive Security: XSS (stored/reflected), SQLi, CSRF, IDOR, LFI/RFI, authentication bypass, business logic flaws, privilege escalation Reconnaissance: Nmap, Burp Suite, directory/subdomain fuzzing, OSINT Exploitation \u0026amp; Testing: Burp Suite, Metasploit, Netcat, Hydra Languages: Java, C#, Python, Bash Frameworks/APIs: GraphQL, REST, SOAP Environments: Kali Linux, Debian, Windows Projects Ultrasonic Obstacle Detection System — Designed and prototyped an Arduino-based assistive navigation device using ultrasonic sensing for real-time haptic feedback, built with a collaborative engineering team at McMaster.\nContact GitHub: elsayyay HackerOne: agentgeneric TryHackMe: agentgeneric LinkedIn: Youssef Elsayyad Email: youssefelsayyad2006@gmail.com This blog is built with Hugo + PaperMod and hosted on GitHub Pages.\n","permalink":"https://elsayyay.github.io/bounty-blog/about/","summary":"About me and this blog.","title":"About"}]