Resources

People often ask me "How did you learn how to hack?" The answer: by reading. This page is a collection of the blog posts and other articles that I have accumulated over the years of my journey. Enjoy!

Position Spoofing Post Mortem- 1755

PanaopticPosted 4 Months Ago
  • Panaoptic is a company that maintains trading and perpetual (longs/shorts) contracts. When you take out a short, you are stating that I think this token will drop. This contract is called a position that a user takes. The vulnerability was a spoofing issue around the position data. This was received as a bug bounty report. The user received a $250,000K payout - the maximum on the program.
  • The position is all stored within a uint256 with different sections to the value for gas efficiency reasons. These include four legs, each consisting of an asset ID, an option ratio, a boolean indicating whether it's a long, a strike price, and many other fields. Additionally, there's a one-time section for Uniswap pool information. So, what's the big deal?
  • A user can have up to 25 open positions. Instead of storing each position ID on-chain, they combine the hashes to create a single fingerprint. When the user wants to interact with the contract, the positions are passed in, hashed, and verified against the user's current fingerprint. The fingerprint generation is as follows:
    1. Hash the provided number associated with the position.
    2. Take the lower 248 bits of the hash output.
    3. XOR the lower 248 bits with the accumulated hash, which defaults to 0x00.
    4. Add the amount of legs associated with the position and add them to the upper bits of the fingerprint. This is to know how many legs are associated with the user.
    5. Repeat steps 1-4 until you're left with a value.
  • The usage of XOR is a significant problem here. XOR is associative, commutative, and self-canceling. This appears like the hashing function XHASH, which is known to be broken. By treating this as a set of linear combinations, it's possible to use Gaussian elimination to find overlaps with large sets of provided tokens. This allows you to create fake positions because the fingerprint will match. This requires brute-forcing the correct keccak256 hashes for the input values, but the problem isn't particularly computationally intensive.
  • Another issue that made this MUCH easier to do was that the usage of the fingerprint was not limited to 25 positions. Additionally, there was very little input validation placed on this either, since it was assumed the fingerprint couldn't be spoofed. This made it possible to use giant token lists, making the solving easier.
  • To exploit this, an attacker would do the following:
    1. Borrow assets via a flash loan.
    2. Use the funds to deposit a lot of collateral.
    3. Open two very deep in the money positions with both a call and a put with leverage.
    4. Call the withdrawal methods on the contract with a list of zero collateral requirement positions. This allows you to credit your account without triggering the collateral subtraction.
  • They were unable to upgrade the contracts while this major vulnerability was present. So, they commenced a migration process. Over the course of two days they messaged all Panoptic users to remove funds from the protocol. Additionally, two wallets had 70% of 1.6M in them. They were able to find the users who owned these wallets, and they withdrew. They led to $550K in the protocol.
  • Time for the crazy part: the whitehat hack. Their plan was the same as described above on the attack: take a flash loan, borrow a lot of tokens to open positions and withdraw the tokens with the spoofed IDs that were provided. They took the reporter's PoC and made it work within a single Tx. They didn't post to GitHub or other tools out of fear of leaking. So, the PoC was done on a single devs machine.
  • They tried using the Flashbots RPC to recover the funds. This is a requirement because, otherwise, a bot could frontrun the whitehat hack and steal the funds. Unfortunately, Flashbots rejected the transaction because it required more state reads than they allowed. After contacting Flashbots, they updated the rate limit for them, and the recovery succeeded. It was first done on the Ethereum L1 and done immediately after on the L2s. They claim that 98% of customer funds were safe. To claim their funds, the team created a Merkle root verification system for users.
  • Awesome post! They have some good lessons learned:
    • Several issues were discovered relating to these issues on the C4 contest platform but they didn't put the whole spoofing issue together. This quote is perfect: "If we had spent time ourselves trying to come up with a position-list-spoofing method, we may have been able to foresee this vulnerability."
    • Recovery methods, outside of whitehat hacks, are massively important. A large amount of the funds was recovered through reaching out to users. I find this risky because one of the users could have looked for the vulnerability and exploited it. The more people who know, the more likely it is to get exploited.
    • A rule as old as time: Use available tools and methods at all times. This is specifically true with cryptography.
    • If you need to do a whitehat hack, prepare to be perfect. RPC listening, mempool watching, compromised dev machines, GitHub snooping... all of these would have allowed an attacker to carry out this attack themselves. This is a case where perfection is required.