
Predicting MongoDB ObjectId continuously in Rocket.Chat
Aikido researchers detail an unauthenticated IDOR in Rocket.Chat's file-upload access control (Livechat room ID vs. file's actual room ID were never compared), letting an anonymous visitor read any uploaded file if the MongoDB ObjectId() file ID is known. They then show ObjectId()s are highly predictable (4-byte timestamp + static 5-byte machine value + incrementing 3-byte counter), and build an async Python tool that continuously probes an anchor ID and brute-forces the narrow timestamp/counter gaps to leak every file uploaded to a live instance in near real time. Reported April 21 via HackerOne, fixed in Rocket.Chat 8.5.1/8.4.4/8.3.6/8.2.6/8.1.6/8.0.7/7.13.9/7.10.13; the authors recommend UUIDv4 over ObjectId() for any secret-facing identifier.
What it is
A pre-auth arbitrary file read in Rocket.Chat's Livechat file-upload authorization, chained with MongoDB ObjectId predictability to enumerate every uploaded file. Reported to HackerOne (#3687142) on 21 April, disclosed and fixed by 12 June in 8.5.1, 8.4.4, 8.3.6, 8.2.6, 8.1.6, 8.0.7, 7.13.9, and 7.10.13. Found by Aikido's AI pentest agents.
How it works
Files from both authenticated channels and unauthenticated Livechat are stored at the same path, /file-upload/{fileId}. Authorization has two independent notions of Room ID that are never reconciled:
requestCanAccessFilesinFileUpload.tsreadsrc_ridfrom the query string and checks it againstrc_tokenviaLivechatRooms.findOneByIdAndVisitorToken(rid, token)— i.e. "do you hold a valid session for this room."- Separately, the file is fetched by path with
Uploads.findOneById(match[1]). That file carries its ownrid, which may differ from the query-stringrc_rid.
Nothing checks that the requested file's real rid matches the room you authorized against. So an attacker registers a throwaway Livechat visitor, obtains a valid dummy Room ID, then supplies any file's ObjectId in the path — bypassing the 403 a private file returns to an unauthenticated browser. Because MongoDB ObjectId()s embed a timestamp and are not cryptographically random, probing the current ID lets an attacker predict and enumerate all others, harvesting files continuously.
Exploit primitives (verbatim from the page):
HOST = "https://rocketchat.local"
FILE_ID = "6a325394876fbe9c70b1b03f"
s = requests.Session()
token = "x"
# Create anonymous visitor with token
s.post(f"{HOST}/api/v1/livechat/visitor",
json={"visitor": {"token": token, "name": "attacker", "email": "attacker@example.com"}})
# Get our Room ID
r = s.get(f"{HOST}/api/v1/livechat/room",
params={"token": token, "agentId": "rocket.cat"})Preconditions / Affected
Unauthenticated — anyone with network access. The key exposure detail the summary omits: Livechat is enabled by default, and even though its file-upload input is disabled by default (invisible in the widget), the upload API endpoint remains reachable. Reach it directly at /livechat. Default installs where self-registration is off are still vulnerable.
What to do
Upgrade to one of the fixed versions listed above or newer. Since the exploit is unauthenticated, treat internet-exposed instances as urgent. As a stopgap, consider disabling Livechat if unused.
Caveats
The published exploit script is truncated before the ObjectId-prediction loop, so the exact enumeration cadence isn't shown in this excerpt. The authors note the technique generalizes to other MongoDB apps sharing these primitives, not just Rocket.Chat.
Organisations named here
- HackerOne #3687142
Technologies named here
- Rocket.Chat
- Aikido AI Pentest
- MongoDB
- HackerOne
- Python requests library
- httpx
- tqdm
- asyncio
- Python
- Aikido Security
Techniques named here
- Unauthenticated Livechat abuse as attack primitive
- MongoDB ObjectId prediction / anchor-point attack
- Continuous probing with sliding window / gap detection
- Asynchronous concurrent brute forcing
- Counter wraparound handling
- IDOR
Related in AppSec
Flowise MCP Security Bypass Enables RCE
Inside an Oracle Database SQL Injection Attack | Huntress
OASIS
