SMART CONTRACTS, DEFI & WEB3 SECURITY - Chapter 7, Exercise 2 Solution ========================================================== Exploiting a require(tx.origin == owner) Check, Step by Step PROBLEM ------- A contract uses require(tx.origin == owner) to protect a sensitive function. Describe, step by step, how a malicious contract could exploit this even though the real owner never intentionally called the sensitive function directly. SOLUTION -------- STEP 1: THE SETUP An attacker deploys a malicious contract and disguises it as something appealing or harmless - perhaps advertised as a free airdrop claim, a game, or some other tempting dApp (Chapter 5's own real interaction model is what makes this look legitimate to the user). STEP 2: THE VICTIM CONNECTS AND INTERACTS The real owner of the vulnerable contract, not realizing anything is wrong, connects their wallet to the attacker's malicious dApp and approves some seemingly ordinary transaction - for example, clicking a button that appears to just "claim a reward." STEP 3: THE MALICIOUS CONTRACT MAKES THE REAL CALL Behind that seemingly harmless button, the malicious contract's own code actually calls the vulnerable contract's sensitive function on the owner's behalf, as part of the same transaction the owner just signed. STEP 4: THE FLAWED CHECK PASSES Inside the vulnerable contract, require(tx.origin == owner) checks who originally started the whole transaction - and per this chapter's own explanation, tx.origin resolves all the way back to the real owner's own address, regardless of how many intermediate contract calls the transaction passed through along the way. So even though the immediate caller (msg.sender) of the sensitive function is actually the attacker's own malicious contract, tx.origin still correctly shows the real owner - and the check passes. STEP 5: THE SENSITIVE ACTION EXECUTES The vulnerable contract now executes its own sensitive, owner-only action - fully believing it was legitimately authorized by the real owner, when in fact the owner never knowingly or intentionally called that function themselves at all. They were tricked into triggering it indirectly, through the attacker's own intermediary contract. If the check had instead used require(msg.sender == owner), this attack would fail at step 4: msg.sender in that case would correctly show the attacker's own malicious contract as the immediate caller, not the real owner, and the check would reject it. ANSWER: The attacker gets a legitimate owner to interact with a disguised malicious contract, which then calls the vulnerable contract's sensitive function on the owner's behalf. Because tx.origin resolves through every intermediate call back to the original human sender, the check still sees the real owner's address and passes - even though the immediate caller is actually the attacker's own contract, not the owner acting directly. ---- WHY THIS WORKS AS AN ANSWER This walks through the full real attack chain in order, showing exactly where and why tx.origin's own "resolve to the original sender" behavior is what makes the exploit work, and explicitly contrasts it with what msg.sender would have correctly shown instead.