September 5, 2026
The $1 Full Video: How RedHunter Found and Self-Validated a Price-Integrity Bypass
An anonymized case study in autonomous business-logic hunting. Target details are redacted โ the value here is the mindset, not the domain.
By redhunter01
8 min read
An anonymized case study in autonomous business-logic hunting. Target details are redacted โ the value here is the mindset, not the domain.
TL;DR
An autonomous hunting harness I've been building โ RedHunter โ found a critical price-integrity / business-logic flaw in a large pay-per-item media store. The purchase endpoint charged a flat $1.00 for an entitlement the platform's own catalog priced at $14.25 โ and fulfillment granted the full product, every resolution up to 4K, not a cheap tier.
The bug isn't a clever memory corruption or an exotic injection. It's the most under-hunted, best-paid class in the field: the server trusted a price it should have recomputed. RedHunter found it because it was taught to ask one question relentlessly โ "Who decides the price, and does anyone check it later?"
This writeup walks through how the finding surfaced, how the harness self-validated it end-to-end without a human in the loop, the root cause, and the transferable methodology you can run by hand tomorrow.
Why business logic is where the money is
Scanners find XSS, SQLi, and misconfigurations. They do not find business-logic flaws, because a scanner has no concept of intent. It doesn't know that a video "should" cost $14.25, that a coupon "should" apply once, or that a refund "should not" exceed the purchase. Logic bugs live in the gap between what the code does and what the business meant.
That gap is exactly where the payouts concentrate. Price/payment tampering is a mature, well-precedented, well-paid class on every major platform. And it's under-hunted precisely because you can't automate it with off-the-shelf tooling โ you have to model the merchant's assumptions and then break them.
RedHunter's entire design philosophy is: encode the merchant's implicit assumptions as testable invariants, then try to violate each one.
The mindset RedHunter runs on
Before any request goes out, the harness (and any good human hunter) frames a purchase flow as a set of trust assumptions:
- Price authority โ Where is the amount decided? Client or server?
- Price consistency โ Does the same product return the same price everywhere it's referenced?
- Entitlement/payment coupling โ Does what you receive always match what you paid for?
- Cross-component trust โ When one service hands an order to another (a billing processor, a CDN), does the receiver re-verify, or does it trust the caller?
Each of those is a hypothesis waiting to fail. The bug in this case was assumption #2 and #3 failing together: the price was inconsistent and nobody reconciled the delivered product against the amount charged.
Discovery walkthrough
Step 0 โ Map the flows, not the pages
RedHunter's recon pass doesn't crawl for pages; it crawls for state transitions that involve value โ anything that grants access, moves money, or changes an entitlement. On this target it enumerated the purchase surface and catalogued every "offer" the API exposed.
Lesson for humans: don't start in the UI. Start by listing every endpoint that could change what your account is allowed to have. Purchase, redeem, upgrade, gift, refund, restore. Those are your hunting grounds.
Step 1 โ Establish ground truth (the baseline)
The harness first recorded the honest state of a target item from an unprivileged account:
// GET /contents/{id} (authenticated, non-paying account)
{
"is_purchased": false,
"price": { "usd": 14.25, "is_available_for_ppd": true },
"medias": [
{ "title": "Full video", "type": "paid",
"offer": { "message": "This video is available for paid access",
"price": "14.25", "video_modes": ["web","hd","vga","1080p","4k"] } }
]
}// GET /contents/{id} (authenticated, non-paying account)
{
"is_purchased": false,
"price": { "usd": 14.25, "is_available_for_ppd": true },
"medias": [
{ "title": "Full video", "type": "paid",
"offer": { "message": "This video is available for paid access",
"price": "14.25", "video_modes": ["web","hd","vga","1080p","4k"] } }
]
}Two facts pinned down: the item costs $14.25, and it is locked (is_purchased: false).
Lesson: always capture the baseline first. You cannot prove impact later if you never recorded the "before." A locked resource returning 403 pre-exploit is the other half of your proof.
Step 2 โ Ask who the offer really is
The platform exposed an authoritative offer lookup. For this item it returned:
// GET /offers/content?content_id={id}
{
"content": {
"message": "Access to Full Video",
"product_offer": "PRODUCT_OFFER_CONTENT_<SKU>",
"price": "14.25"
}
}// GET /offers/content?content_id={id}
{
"content": {
"message": "Access to Full Video",
"product_offer": "PRODUCT_OFFER_CONTENT_<SKU>",
"price": "14.25"
}
}Here's the pivotal observation, and it's a pure reading-comprehension win, not a technical one:
_The SKU's internal name contained a legacy resolution tag (implying a cheap, low-res tier), _but the catalog's own human-readable label called it "Access to Full Video" at $14.25.
A lazy hunter sees a "low-res"-sounding SKU and assumes it's a legitimate cheap product. RedHunter is taught to distrust internal names and trust the authoritative catalog label + the fulfillment it grants. The name was a red herring; the offer was the full product.
Lesson: an internal identifier is not a specification. When a SKU's name and its catalog meaning disagree, that disagreement is often the bug.
Step 3 โ Drive the purchase and read the number back
RedHunter initiated the purchase for that same SKU and inspected the order the server generated:
POST /offers/PRODUCT_OFFER_CONTENT_<SKU>/purchase
Content-Type: application/x-www-form-urlencoded
X-CSRF-Token: <token>
X-Requested-With: XMLHttpRequest
website_id=<id>&target_id=<content_id>&contentId=<content_id>
// 200 OK
{ "paymentUrl": "https://<billing-processor>/checkout?cart_code=โฆ&return_url=โฆ" }POST /offers/PRODUCT_OFFER_CONTENT_<SKU>/purchase
Content-Type: application/x-www-form-urlencoded
X-CSRF-Token: <token>
X-Requested-With: XMLHttpRequest
website_id=<id>&target_id=<content_id>&contentId=<content_id>
// 200 OK
{ "paymentUrl": "https://<billing-processor>/checkout?cart_code=โฆ&return_url=โฆ" }The generated checkout order was for $1.00 โ for the offer the catalog had just priced at $14.25. Same SKU, same item, two different prices depending on which endpoint you asked. Assumption #2 (price consistency) had failed.
Two small but instructive operational notes the harness recorded:
- A JSON body was rejected by the edge WAF with a tiny
403; the app expected form-encoding + CSRF token. RedHunter adapts request encoding to what the app actually accepts rather than assuming a content type. Humans should too โ a403is sometimes the WAF, not the auth layer. - The order was one-time, with no rebill/recurring terms โ ruling out the "it's a $1 trial that renews at full price" false positive early.
Step 4 โ Rule out "it's a feature"
This is the gate that separates a real finding from an embarrassing report. Before claiming a bug, prove it isn't intended behavior. RedHunter's checklist:
- Is there a legitimate cheap tier? โ The catalog labeled this SKU the full product at full price. No.
- Does the cheap price deliver a cheap product (e.g., only low-res)? โ No โ fulfillment granted every encoding (see Step 6). Price and product were mismatched.
- Is it a promotional/trial price that rebills? โ No recurring terms present.
- Wrong parameter causing a wrong lookup (e.g., wrong site/region ID)? โ The site/region parameter matched the content; the underprice was inherent to the SKU's server-side price resolution, not a parameter mismatch.
Only after all four cleared did the harness escalate.
Lesson: the "is it a feature?" gate is the single most valuable habit in logic hunting. Half of rejected reports die here. Do the ruling-out before you write, not after triage does it for you.
Step 5 โ Self-validate end-to-end (responsibly)
RedHunter completed a single, minimal proof transaction โ one $1.00 order โ and then confirmed the entitlement actually flipped. This is the difference between "the order form showed $1" (weak) and "the paywall is genuinely defeated" (undeniable). One transaction is enough; the harness is hard-capped to never iterate purchases or pull full content โ impact is proven with the smallest possible footprint.
Step 6 โ Confirm the entitlement flipped
Re-reading the item on the same account after the charge:
// GET /contents/{id} (post-purchase, same account)
{
"is_purchased": true,
"medias": [
{ "title": "Full video", "type": "paid", "offer": null,
"transcodings": [
{ "video_mode": "web", "status": "download" },
{ "video_mode": "hd", "status": "download" },
{ "video_mode": "1080p", "status": "download" },
{ "video_mode": "4k", "status": "download" } // full-quality master
] }
]
}// GET /contents/{id} (post-purchase, same account)
{
"is_purchased": true,
"medias": [
{ "title": "Full video", "type": "paid", "offer": null,
"transcodings": [
{ "video_mode": "web", "status": "download" },
{ "video_mode": "hd", "status": "download" },
{ "video_mode": "1080p", "status": "download" },
{ "video_mode": "4k", "status": "download" } // full-quality master
] }
]
}The paywall offer object was gone (null), and every resolution โ including the multi-gigabyte 4K master โ was marked available. Assumption #3 (payment/entitlement coupling) had failed: a $1 payment delivered the full $14.25 product.
To confirm the media was genuinely served without exfiltrating it, RedHunter issued a single ranged (206 Partial Content) request โ enough to verify a valid, signed, playable file header and the true file size, and nothing more. The same resource had returned 403 user_not_authenticated on the baseline. Before: 403. After: signed 200. That contrast is the whole ballgame.
Lesson: prove access, don't hoard it. A ranged request that reads a header proves the file is served just as well as downloading 50 GB โ and keeps you unambiguously inside "minimal proof of concept" instead of "I stole the catalog." Triagers and legal both notice the difference.
Root cause
The store computed the charge from a static price attached to the offer SKU instead of from the target content's authoritative, current price at order-creation time. A stale/legacy price row ($1.00) lived on a SKU whose real, catalog-defined product was the full video at $14.25. Nothing in the pipeline reconciled the amount charged against the entitlement granted, so an underpriced order was created and then honored.
There's also a cross-component trust dimension (assumption #4): the purchase service produced the underpriced order, and the separate billing processor honored it without independently re-deriving the price from content authority. When two services split responsibility for "decide the price" and "collect the money," neither may end up owning "verify the price is correct." That seam is a recurring home for this bug class.
Impact (how a triager reads it)
- Systemic pricing-integrity failure, not one mispriced item โ the flaw sits on the primary pay-per-item purchase path, so it generalizes across the catalog.
- Payment/entitlement decoupling โ the platform delivers strictly more than it charges for.
- Trivially reachable โ a single authenticated request creates the underpriced order; no victim interaction, no social engineering.
- Cross-component scope change โ the flaw in one service forces an underpriced order honored by a distinct billing domain.
Illustrative severity: CVSS 3.1 โ 9.1 (Critical) โ AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N. The S:C (scope change) is justified by the order crossing the trust boundary between the purchase app and the billing processor.
(Note: I deliberately don't quantify "attack this many items for this much money." Proving the class is critical is the report's job; enumerating how to strip-mine a live catalog is not, and leaning into it weakens rather than strengthens a submission.)
Remediation (what I recommended)
- Compute the charge server-side from the content's authoritative current price at order-creation time. Never trust a static price stored on an offer SKU.
- Add a fulfillment-time invariant:
charged_amount == content.current_pricefor direct purchases; reject on mismatch. - Reconcile entitlement against payment โ refuse any order where the granted entitlement (e.g., full-resolution set) exceeds what the paid tier authorizes.
- Retire or correct legacy price rows. If a genuine cheaper tier is intended, its fulfillment must be constrained to that tier โ a cheap price must deliver a cheap product.
- Make the billing processor re-derive or verify price from content authority rather than trusting the caller-supplied amount, closing the cross-service seam.
The transferable playbook
Everything RedHunter did compresses into a checklist you can run by hand on any store, subscription, or entitlement flow:
- Map value-changing flows first. Purchase, redeem, upgrade, gift, refund, restore.
- Capture the honest baseline. Real price + locked state (
403) before you touch anything. - Find every place a price is stated. Product page, cart, offer API, checkout, receipt. Diff them. Any disagreement is a lead.
- Distrust internal names. A SKU called "vga"/"basic"/"legacy" may deliver the premium product. Verify by what it grants, not what it's named.
- Read the amount the server generates, not the amount the client sends. The interesting bugs are where the server itself picks the wrong number.
- Run the "is it a feature?" gate โ cheap tier? cheap delivery? trial rebill? param mismatch? โ before writing.
- Validate with the minimum footprint. One transaction; a ranged request to confirm delivery; never bulk, never full exfiltration.
- Prove the flip.
before: locked/403โafter: unlocked/signed 200. That contrast is your entire report. - Report the invariant, not the loot. "Payment and entitlement are decoupled" is the finding. Frame impact by class and severity, not by how much you could steal.
Why "autonomous" mattered here
The reason a harness beat a scanner isn't speed โ it's that RedHunter was built to reason in invariants and hypotheses, the same way a strong human hunter thinks, and then to self-validate against a recorded baseline so a "finding" is never just a suspicious response but a proven before/after state change. Autonomy bought two things specifically:
- Consistency of the ruling-out discipline โ the "is it a feature?" gate ran identically every time, so weak leads died before they became noise.
- Restraint by construction โ the same automation that could have hammered the endpoint was hard-limited to a single minimal proof, because responsible footprint was encoded as a rule, not left to judgment under excitement.
That combination โ aggressive hypothesis generation, ruthless self-validation, built-in restraint โ is the whole thesis. The bug was simple. The discipline around finding and proving it is what made it a clean critical.
Written up for education. Target fully anonymized; no live credentials, working weaponized requests, or exfiltration guidance included by design โ the goal is to teach the mindset, not to arm anyone against a specific site.