{
  "compartment_id": "section-6",
  "number": "Section 6",
  "title_local": "Interactive Exploit & Hijack Simulator",
  "title_english": "Interactive Exploit & Hijack Simulator",
  "source_url": "https://phishdestroy.io/steam_dossier/#hijack-simulation",
  "source_document": "index.html",
  "content": {
    "html": "<section class=\"border-t border-gray-800 pt-6 avoid-break\" id=\"hijack-simulation\">\n<div class=\"max-w-7xl mx-auto space-y-8\">\n<div class=\"section-head section-head-balanced\"><div class=\"section-head-primary\">\n<span class=\"kicker\">Part A — Technical record · Section 6</span>\n<h2>Interactive Exploit &amp; Hijack Simulator</h2>\n\n<section class=\"dossier-note chapter-context\"><div class=\"dossier-note-title\">Context &amp; source limits</div><p class=\"chapter-context-text\">A reconstruction of the path from the phishing message to the server-side key registration and the trade swap. Before 2023, a Web API key registered on an account could read its inventory, cancel a pending trade offer and create a new one, and no trade-confirmation screen was shown to the account holder for any of those actions. That is the absence of least privilege (OWASP A01:2021). The timings in this simulation are illustrative and are not live measurements of the current platform.</p></section>\n</div><div class=\"section-head-intro\"><p class=\"lede\">Step through the reconstructed hijack. Each phase shows what the account holder sees and, beside it, the call Steam receives at the same moment. <strong>Every one of those calls is correctly authenticated.</strong> Nothing in the sequence needs Steam's authentication to fail — it needs the key it issues to carry no scope (OWASP A01:2021).</p></div></div>\n<div aria-label=\"Interactive Exploit &amp; Hijack Simulator\" class=\"story-scene\" data-story=\"\"><div class=\"story-scene-header\"><strong>What the player sees. What changes behind it.</strong><span>WALKTHROUGH SIMULATOR</span></div><div class=\"story-lane\"><div class=\"story-route ds-6f71d3e5d96d\"><div class=\"story-node\" data-story-step=\"0\"><small>01 / THE CHAT LURE</small><strong>A friend asks to vote</strong><span>Victim receives a message with a link.</span></div><div class=\"story-node\" data-story-step=\"1\"><small>02 / THE LOGIN</small><strong>A lookalike portal</strong><span>Victim enters sign-in credentials.</span></div><div class=\"story-node\" data-story-step=\"2\"><small>03 / THE ACCESS</small><strong>Silent Web API creation</strong><span>Attacker registers a developer key.</span></div><div class=\"story-node\" data-story-step=\"3\"><small>04 / THE SWITCH</small><strong>Legitimate trade swapped</strong><span>Clone bot duplicates target recipient.</span></div><div class=\"story-node\" data-story-step=\"4\"><small>05 / CONFIRMATION</small><strong>Counterfeit approved</strong><span>Wrong offer approved on mobile.</span></div></div></div><div class=\"story-caption\"><p data-story-caption=\"0\" hidden=\"\"><strong>Phase A: Phishing Entry.</strong> The attacker compromises a friend's account and uses it to send a deceptive link, inviting the victim to vote or join a tournament team.</p><p data-story-caption=\"1\" hidden=\"\"><strong>The Phishing Trap.</strong> Believing they are logging in securely to Steam, the victim enters credentials and Steam Guard codes. The phishing server captures these details and the active <code>steamLoginSecure</code> session cookie.</p><p data-story-caption=\"2\" hidden=\"\"><strong>Phase B: Silent Key Registration.</strong> Using the stolen session cookie, the attacker's server POSTs to the key registration endpoint to register a master developer Web API key. Historically (pre-2023), this required no secondary Steam Guard approval or notification.</p><p data-story-caption=\"3\" hidden=\"\"><strong>Phase C: Automated Trade Swap.</strong> The attacker's script polls <code>IEconService/GetTradeOffers</code>. When the victim initiates a legitimate trade, the script immediately cancels it and uses a clone bot to send an identical offer with the same name and avatar to the recipient.</p><p data-story-caption=\"4\"><strong>The Exploit Completed.</strong> Expecting a mobile prompt for their original trade, the victim approves the replacement trade. This is an integrated forensic walkthrough showing how the absence of Least Privilege (OWASP A01:2021) enables automated, sub-second asset hijacking.</p></div><div class=\"story-controls\" hidden=\"\"><button data-story-play=\"\" type=\"button\">Replay</button><button data-story-next=\"\" type=\"button\">Start again</button><span aria-live=\"polite\" role=\"status\"></span></div>\n<div class=\"story-wire\">\n<div class=\"story-wire-head\"><span>The same step, seen from the network</span><span>Reconstruction of the pre-2023 pattern · illustrative call shapes</span></div>\n<div class=\"story-wire-panel\" data-wire-step=\"0\" hidden=\"\">\n<div class=\"story-wire-actors\">\n<div class=\"story-wire-actor\" data-role=\"attacker\"><b>Attacker</b><span>Sends the link from an account the victim already trusts.</span></div>\n<div class=\"story-wire-actor\" data-role=\"victim\"><b>Victim</b><span>Reads a message from a friend, not from a stranger.</span></div>\n<div class=\"story-wire-actor\" data-role=\"steam\"><b>Steam sees</b><span>A chat message between two friended accounts.</span></div>\n</div>\n<pre class=\"story-wire-call\"><b>Channel</b> Steam chat / Steam group invite\n<b>Payload</b> a link to an external host\n<i>No Steam API call is made at this stage. Nothing in this step is anomalous to the platform.</i></pre>\n<p class=\"story-wire-verdict\"><strong>Why it works:</strong> the trust being spent belongs to the compromised friend account, not to the attacker.</p>\n</div>\n<div class=\"story-wire-panel\" data-wire-step=\"1\" hidden=\"\">\n<div class=\"story-wire-actors\">\n<div class=\"story-wire-actor\" data-role=\"victim\"><b>Victim</b><span>Enters the password and the Steam Guard code into a lookalike page.</span></div>\n<div class=\"story-wire-actor\" data-role=\"attacker\"><b>Attacker server</b><span>Captures the credentials and the resulting session cookie.</span></div>\n<div class=\"story-wire-actor\" data-role=\"steam\"><b>Steam sees</b><span>A successful sign-in that satisfied two-factor authentication.</span></div>\n</div>\n<pre class=\"story-wire-call\"><b>Captured</b> <u>steamLoginSecure</u> = &lt;session cookie&gt;\n<b>Captured</b> Steam Guard code (single use, already spent)\n<i>From this point the attacker holds an authenticated session. The second factor has been used, not bypassed.</i></pre>\n<p class=\"story-wire-verdict\"><strong>Why it works:</strong> two-factor authentication protects the moment of login. It does not protect what the resulting session can do afterwards.</p>\n</div>\n<div class=\"story-wire-panel\" data-wire-step=\"2\" hidden=\"\">\n<div class=\"story-wire-actors\">\n<div class=\"story-wire-actor\" data-role=\"attacker\"><b>Attacker server</b><span>Registers a developer Web API key on the victim's account.</span></div>\n<div class=\"story-wire-actor\" data-role=\"victim\"><b>Victim</b><span>Receives no prompt and no notification.</span></div>\n<div class=\"story-wire-actor\" data-role=\"steam\"><b>Steam sees</b><span>An authenticated session using a documented endpoint.</span></div>\n</div>\n<pre class=\"story-wire-call\"><b>POST</b> steamcommunity.com/dev/ajaxregisterkey\n<b>Cookie:</b> steamLoginSecure=&lt;stolen session&gt;\n<b>Body:</b> agreeToTerms=agreed&amp;domain=<u>localhost</u>\n<i>A script has no real domain to give, so it sends <u>localhost</u> — the value found on the account in <a href=\"#gdpr-live-case\" class=\"ds-809db67f767e\">Section 18</a>. Pre-2023 this needed no password, no Steam Guard, no notification, and the key came with no scope limit. Mobile confirmation was added 2023-12-04, without a changelog.</i></pre>\n<p class=\"story-wire-verdict\"><strong>This is the structural step.</strong> A session cookie is converted into a durable, full-privilege credential — the defect recorded in Section 5 as OWASP ASVS 2.8 and A01:2021.</p>\n</div>\n<div class=\"story-wire-panel\" data-wire-step=\"3\" hidden=\"\">\n<div class=\"story-wire-actors\">\n<div class=\"story-wire-actor\" data-role=\"attacker\"><b>Attacker script</b><span>Polls for trade offers, cancels the real one, sends a duplicate.</span></div>\n<div class=\"story-wire-actor\" data-role=\"victim\"><b>Victim</b><span>Has just initiated a legitimate trade and is waiting to confirm it.</span></div>\n<div class=\"story-wire-actor\" data-role=\"steam\"><b>Steam sees</b><span>Valid API calls carrying a valid key issued to that account.</span></div>\n</div>\n<pre class=\"story-wire-call\"><b>GET</b> IEconService/GetTradeOffers/v1 <i class=\"ds-6b8b63d565c4\">← polled</i>\n<b>POST</b> IEconService/CancelTradeOffer/v1 <i class=\"ds-6b8b63d565c4\">← voids the real offer</i>\n<b>POST</b> new offer from a clone bot (matching name and avatar)\n<i>Every call is correctly authenticated. Nothing in the sequence requires a defect in Steam's authentication to succeed.</i></pre>\n<p class=\"story-wire-verdict\"><strong>Why it works:</strong> the key carried no read-only scope, so an inventory-reading credential could also cancel and create trades.</p>\n</div>\n<div class=\"story-wire-panel\" data-wire-step=\"4\">\n<div class=\"story-wire-actors\">\n<div class=\"story-wire-actor\" data-role=\"victim\"><b>Victim</b><span>Approves the mobile prompt that was expected for the original trade.</span></div>\n<div class=\"story-wire-actor\" data-role=\"attacker\"><b>Attacker</b><span>Receives the items at the clone account.</span></div>\n<div class=\"story-wire-actor\" data-role=\"steam\"><b>Steam sees</b><span>A transfer confirmed by the account holder on a registered device.</span></div>\n</div>\n<pre class=\"story-wire-call\"><b>Confirmation</b> genuine — made by the account holder\n<b>Recipient</b> the clone bot, not the intended counterparty\n<i>The confirmation is authentic. The offer it confirms is not the one the user initiated.</i></pre>\n<p class=\"story-wire-verdict\"><strong>The record left behind:</strong> a trade the user approved. That is the record Steam Support reads when the complaint arrives, and the reason the archived complaints were closed as user error. The complaints are in <a href=\"steam_enforcement_evidence#support-records\" class=\"ds-dbfafd4e9e75\">the enforcement archive</a>.</p>\n</div>\n</div>\n</div>\n</div>\n</section>"
  },
  "original_content_sha256": "823a7d33b14e20beae2343105211d25eebe7d61b6af3c154fd6a841e433636bf",
  "author_pseudonym": "Agent Carla",
  "schema": "phishdestroy.macro-fact.v1",
  "language": "en",
  "aggregator": "PhishDestroy",
  "exports": {
    "pdf": "/steam_dossier/exports/agent-protocol/files/en/steam_dossier_section_6_agent_carla.pdf",
    "json": "/steam_dossier/exports/agent-protocol/files/en/steam_dossier_section_6_agent_carla.json",
    "signature": "/steam_dossier/exports/agent-protocol/files/en/steam_dossier_section_6_agent_carla.json.asc"
  },
  "integrity": {
    "signed_object": "steam_dossier_section_6_agent_carla.json",
    "signature_file": "steam_dossier_section_6_agent_carla.json.asc",
    "signing_key_fingerprint": "129FC7F39C40C69D2EF3C329BFCF9E8DD295EE01",
    "scope": "Exact JSON bytes, including macro-fact author and corresponding PDF SHA-256. PhishDestroy is the integrity signer; original evidence attribution is unchanged."
  },
  "pdf": {
    "filename": "steam_dossier_section_6_agent_carla.pdf",
    "sha256": "9d40b140de96024773c399206ca4bb00f3e5b356d2e953885993ee8a94865dab",
    "pages": 3
  }
}
