SQL Injection and Account Takeover on E-Commerce Platform
E-commerce / Retail
Summary
A self-initiated responsible disclosure engagement uncovering unauthenticated account takeover (including admin accounts), two independent SQL injection vulnerabilities, and exposure of live third-party credentials across two separate customer databases.
Full-Chain Compromise of a Regional E-Commerce Platform
Executive Summary
This case study documents a self-initiated responsible disclosure engagement against a regional e-commerce platform (referred to here as Retailer A), conducted independently after the organization's brand surfaced in an unrelated phishing investigation. What began as a basic reconnaissance pass — checking how well-defended the real platform was, given that scammers were already impersonating it — expanded into a multi-week engagement spanning authentication logic, transactional business logic, and ultimately two separate production databases.
Five confirmed findings resulted, four of them Critical: an unauthenticated account-takeover chain reachable against any user (including administrators, since admin and customer accounts share the same table), two independent SQL injection vulnerabilities of increasing severity, and — discovered late in the engagement — unauthenticated cross-schema access exposing live, currently-valid payment and messaging provider credentials alongside a second, previously-unknown customer database of over 1,100 records. A fifth finding grouped a cluster of lower-severity but high-volume authorization gaps across everyday cart and address features.
No remote code execution or destructive action was achieved or attempted; the organization's database access controls, while broader in reach than intended, correctly blocked file-write and code-execution escalation paths — one of the few unambiguously well-implemented controls encountered.
Engagement Scope and Methodology
Target Overview
Retailer A operates a public-facing e-commerce platform built on a bare PHP/jQuery stack, fronted by Cloudflare and a LiteSpeed/OpenResty server configuration, with cPanel-based hosting. A separate, structurally distinct internal administrative console was identified during reconnaissance and later confirmed reachable using credentials recovered through the engagement's core injection finding. Mid-engagement, a second, apparently newer e-commerce application (Laravel/Botble CMS-based) was found running in parallel on infrastructure reachable from the same vulnerable entry point, with its own independent customer base and administrator system.
Methodology
Testing proceeded in five structured phases — reconnaissance, authentication-flow testing, exploitation, escalation-boundary testing, and reporting — with three principles enforced throughout:
- No severity without demonstrated impact. Observed anomalies were tracked as open questions and only promoted to findings once reproduced with concrete evidence.
- Minimal necessary extraction. Once a data-access vulnerability class was proven, further extraction was capped at what was needed to demonstrate scope (a schema name, a table list, a single representative record, a count) rather than bulk retrieval.
- Hardening documented alongside failures. Controls that worked correctly — notably database-level privilege restrictions — were recorded with the same rigor as the vulnerabilities, both for accuracy and so remediation doesn't inadvertently remove something that was already working.
All testing was conducted against test/throwaway accounts created for this engagement. No production customer accounts were modified.
Findings
Finding 1 — Unauthenticated Account Takeover via Profile Update Endpoint
Description
A profile-update endpoint accepted a client-supplied numeric user identifier with no verification that it matched the authenticated caller. Combined with a phone-number-based password reset flow, this allowed an attacker to overwrite any account's phone number to one they control, then complete the standard "forgot password" flow to seize the account outright.
Technical Detail
The endpoint [REDACTED]/updateprofile_no_password.php accepted a userid parameter with no session/ownership check, updating the target account's phone number. The subsequent password-reset flow — [REDACTED]/forgot_password_api.php — sent an OTP to whatever phone number was on file, which by this point was attacker-controlled. Because user IDs were confirmed sequential, this was systematically exploitable, not limited to a single guessed target. Critically, administrator and staff accounts were later confirmed to reside in the same table as ordinary customers, distinguished only by a role flag — meaning this chain applied equally to admin accounts, independent of password strength.
Business Impact
Complete, unauthenticated compromise of any account on the platform, achievable with no prior credentials and minimal technical effort. Session persistence was also found to survive both logout and password reset, meaning a victim reclaiming their account did not automatically evict an attacker already inside it.
Scope Note
Confirmed end-to-end against test accounts only. No production account was taken over during testing.
Finding 2 — Blind Boolean-Based SQL Injection
Description
A parameter used in an address-deletion flow's pre-delete validation query was vulnerable to boolean-based blind SQL injection, allowing full database schema extraction with no authentication.
Technical Detail
The address_id parameter on [REDACTED]/delete_address.php was concatenated unsanitized into a database query. Differential testing distinguished a true/false oracle from the endpoint's two-stage check-then-delete logic, which was then used to extract the database name, table count, table names, and column presence via character-by-character boolean inference.
Business Impact
Full, unauthenticated read access to the underlying database schema, independent of any single table — a foundational data-exposure primitive, slower but no less complete than Finding 3 below.
Scope Note
Extraction was limited to schema-level metadata (names, lengths, counts) sufficient to confirm the vulnerability class; no row-level data was pulled through this specific injection point.
Finding 3 — Unauthenticated UNION-Based SQL Injection with Confirmed Credential Compromise
Description
A related endpoint was found vulnerable to UNION-based SQL injection, returning full query results directly in its JSON response with no authentication required — enabling direct extraction of administrator credentials, which were subsequently confirmed valid by live login.
Technical Detail
The branch parameter on [REDACTED]/fetchAddresses.php permitted a 4-column UNION injection, with results reflected through the response's user_latitude field. This was used to enumerate all 29 tables in the schema, recover column structures, and extract administrator account records — including password hashes later confirmed to be unsalted MD5, with several distinct accounts sharing an identical (and very weak) hash value. Two recovered credential pairs were validated by direct login to the platform's internal administrative console, one confirmed as a super-admin account.
Business Impact
This is the most severe finding of the engagement: unauthenticated, single-request access to arbitrary database contents, with directly demonstrated (not theoretical) compromise of a super-admin account. The organization's database account was confirmed restricted from file-write and code-execution paths, which prevented escalation to remote code execution — documented here as a genuinely effective control.
Scope Note
Row-level extraction was deliberately limited to a small number of representative records once the vulnerability and its severity were established; live administrator login was performed only to confirm credential validity, with no further action taken inside the resulting session.
Finding 4 — Cross-Database Exposure of Live Third-Party API Credentials and a Second Customer Database
Description
The same injection point behind Finding 3 was found to reach beyond its own application's database into a second, entirely separate application database, containing live payment gateway, SMS provider, and shipping provider credentials, plus an independent customer base of over 1,100 records.
Technical Detail
A schema enumeration query against information_schema.SCHEMATA via [REDACTED]/fetchAddresses.php revealed a second application schema, structurally consistent with a Laravel/Botble CMS deployment, running in parallel with the platform tested in Findings 1–3. Its settings tables were confirmed to contain live, production-labeled credentials for a mobile payment gateway, a messaging provider, and a shipping/logistics provider, alongside its own customer, address, and role/permission tables.
Business Impact
Distinct from data exposure alone: live credentials represent active, currently-usable capability. Anyone reaching them could potentially transact or send messages under the organization's identity with its own service providers, independent of any other vulnerability in this report. This finding also revises Finding 3's positive hardening note — the database account's privilege restrictions correctly block file-write/code-execution, but do not enforce schema-level isolation.
Scope Note
No credential was used, no transaction initiated, and no message sent. The read primitive alone was treated as sufficient evidence; specific credential values were withheld even from the technical report and shared only through a direct, private channel with the organization's engineering contact once established.
Finding 5 — Systemic IDOR Cluster on User-Owned Objects, Plus Amplifying Weaknesses
Description
Cart and address management endpoints uniformly accepted client-supplied object identifiers with no ownership verification, allowing any user's cart or saved addresses to be viewed, modified, or deleted by anyone. Two additional weaknesses — state-changing actions performed via GET, and no rate limiting on OTP verification — were found to amplify Finding 1 without being independently Critical.
Technical Detail
Endpoints including [REDACTED]/addtocart.php, [REDACTED]/editecart.php, [REDACTED]/update_address.php, and [REDACTED]/fetchAddresses.php all accepted a foreign UserID/address_id parameter and performed the requested action without checking it against the caller's session. Separately, sensitive actions (login, signup, cart/address mutation, OTP verification) were found to be triggered via GET requests with no CSRF protection, and the OTP verification endpoint permitted unlimited attempts within its validity window.
Business Impact
Rated High rather than Critical as server-side price revalidation at checkout was confirmed to correctly block the most severe potential consequence (financial fraud via price tampering). Still, the cluster allows broad, unauthenticated tampering with customer cart and address data, and the CSRF-via-GET amplifier meaningfully worsens the practical exploitability of Finding 1.
Scope Note
All IDOR endpoints were tested against test accounts and objects created specifically for this purpose.
Architecture Intelligence (Recon Output)
- Hosting: cPanel-based, LiteSpeed server, OpenResty reverse proxy, Cloudflare CDN in front.
- A distinct administrative console was identified at a separate subdomain during reconnaissance, later confirmed as the destination reachable via credentials recovered in Finding 3.
- The application stack is bare PHP/jQuery for the primary platform; the second, later-discovered database (Finding 4) belongs to a structurally distinct Laravel/Botble CMS-based application, evidenced by its migration history, package-style table naming conventions, and framework-specific token/permission table structures.
- Database engine: MariaDB. The application's database account holds only baseline (
USAGE) grants — noFILEprivilege, no accessible user-defined functions, and stacked queries are rejected — a meaningfully restrictive configuration that capped the ceiling of the SQL injection findings below remote code execution. - Rate limiting / WAF behavior: request throttling was encountered during rapid blind-injection extraction but was bypassed with request pacing; keyword-based WAF filtering blocked several known-signature SQLi payloads (e.g.,
EXTRACTVALUE,GTID_SUBSET) without preventing structurally different boolean- or UNION-based injection.
Observations on Program Quality
Retailer A has no published responsible disclosure policy, no security contact, and no bug bounty program. Initial outreach required contacting the organization through a general web contact form, a WhatsApp business number, and a public social media page simultaneously, without any guarantee of reaching a technically competent recipient. This is a common pattern among small and mid-sized platforms in the region and materially slows down the time between discovery and remediation — every day between "vulnerability confirmed" and "organization aware" is a day the exposure (particularly the live credentials in Finding 4) remains actionable by anyone else who might find it independently.
On the technical side, the engagement surfaced a mix of genuine strengths and significant gaps: checkout-time price revalidation and baseline database privilege restrictions both worked correctly under adversarial testing, while authorization checks were inconsistently applied — present in some flows, entirely absent in others — suggesting no unified authentication/authorization pattern was enforced across the codebase during development.
Recommendations
- Introduce parameterized queries platform-wide; the presence of two independent, unrelated injection points strongly suggests this is a systemic coding pattern rather than an isolated bug.
- Enforce session-derived ownership checks on every endpoint accepting a user- or object-scoped identifier, closing both the account-takeover chain and the IDOR cluster in a single architectural fix.
- Rotate all third-party credentials found exposed in Finding 4 immediately, independent of the timeline for fixing the underlying injection — the exposure is already live regardless of when the code is patched.
- Scope database accounts to a single application schema; the current configuration's cross-schema reach turned one vulnerability into exposure for two entirely separate applications and customer bases.
- Publish a responsible disclosure policy and a monitored security contact address — the absence of one directly extended this engagement's time-to-acknowledgment.
Business Impact
Unauthenticated read access to full customer PII across two databases, plus administrator credentials and live third-party API secrets.
Unauthenticated write access to any user's data, including administrator accounts.
Key Lessons
- •Never change more than one variable per test window once inside a live chain.
- •Stop pulling rows the moment a read primitive is proven.
- •Live, currently-usable credentials are a different risk category from static data exposure.
- •Admin accounts in same table as users, distinguished only by a flag, amplifies authorization bugs.
- •Self-initiated disclosure to organizations with no published security contact is materially slower.