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!
new BN(msg, 16) removes the leading zeros. When it's used later, some offset math is wrong as a result. This bug was never fixed._calc_supply() is used for generating the values of the curve. Notably, it's figuring out what the supply is from the constant-product and constant sum values. This is done with an iterative approximation to converge to a new supply. The constant product term r is recomputed each iterate as the current value multiples by the new supply and divided by the previous supply. The goal is for the smoothness of the curve to get better over as more tokens are put into the pool.sp, there are several unsafe math functions being used; this means that integer overflow protections are not enabled. In the math (l - s * r) / d it's possible to make s*r larger than l to cause an integer overflow. This mints a crazy amount of LP tokens, which they use to steal even more money. It should be noted that this is only possible to do because of the first vulnerability above.view modifier on a function that changed the caller's funds. By repeatedly calling this, they were able to claim all funds under the contract. The bot was able to completely steal funds and sell them for a profit on its own. Crazy!sol_invoke_signed_rust as long as the proper data is in place on the Stack. To store the fake parameters, they can be stored directly in the instruction's input data. Using the write primitive, pointers to this information can be added to the stack. solana-program, it performs zero-copy operations by reading data directly from the byte array without copying anything. This eliminates serialization/deserialization overhead. TryFrom() is used for deserializing account data into a structure to use. On an instruction context, there is another TryFrom trait that will perform necessary validations. Proper amount of accounts, signer checks, account ownership checks... all of the good Solana account checks. A similar validation process is done on the instruction data with TryFrom again. __builtin_ct_select to a family of intrinsic. This function contains a special intermediate representation that says "this must be constant-time" and "do not do optimizations on this". According to the authors, this fixed almost all of the constant-time issues discussed in the paper. cmov is used for conditional moves. On i386, which doesn't have this instruction, masked arithmetic is performed with bitwise operations. On ARM, CSEL is used. On AAarch64, masked arithmetic is performed with bitwise operations. On all other architectures, bitwise arithmetic is ton.validateUserOp() and executeUserOp(). validatePaymasterUserOp and postOp().innerHandleOp() to forward the operation to the intended destination. Next, it calls postOp() on the Paymaster (if provided). Finally, the bundler is compensated for gas costs.postOp() fails, then the error is just handled. If it's not one of two specific errors, then the state isn't rolled back. In practice, this means that the bundler will still get paid for the failed transaction.postOp() call fails because it can't collect funds from the user, the paymaster still needs to pay the bundler's gas costs. This is how the attack would work:
postOp() executes.postOp() fails. The paymaster pays Bundler for their high gas costs without receiving any funds.