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!

Understanding the Astrid Finance Exploit - 1277

Neptune Mutual    Reference →Posted 2 Years Ago
  • Astrid Finance is a liquid staking protocol built on top of the EigenLayer. Users deposit tokens to receive back liquid staking tokens. The earnings are compounded and distributed back to the stakers.
  • After depositing funds into the protocol, a user is able to call withdraw(). Sadly, the token contract was not validated for being used by the protocol. Instead, only the existence of this was checked.
  • Since these were not validated, an attacker was able to send in their own set of tokens for this that had no value in them. By using these fake tokens, the protocol assumed they were getting a good deal between them. In reality, the withdraw stole all stETH, rETH and cbETH from the protocol about around 228K.
  • With some drama, they blamed the auditor for recommending a bad fix to them. However, I didn't see the audit being public and this is so obvious the devs should know better.
  • They offered the attacker a 20% bounty if they returned the rest of the funds. This actually happened. Is this the precedent we want to set though? Hey! If you hack us, we will give you some percentage of the money and no pursue legal action? Not a good move to me. More blackhats may come out of this.

Aztec Connect Claim Proof Bug- 1276

Aztec    Reference →Posted 2 Years Ago
  • Aztec Connect is a privacy zkRollup blockchain used for DeFi. One of the novel features is the ability to send funds between the contracts to the L1 privately.
  • At a high level, here's how the protocol works:
    1. User submits a DeFi interaction deposit proof to the mempool. The proof keeps the identity private.
    2. The sequencer groups these interactions from the same protocol together then rolls them up to be sent.
    3. Once receiving the rollup proof, the smart contract on Ethereum will act on behalf of users to exchange and perform other operations.
    4. On the next rollup the sequencer completes special claim proofs, splitting the newly received tokens between users.
  • The program is written using zero knowledge circuits. Because this runs over a finite field and not integers, this makes trivial math complicated to perform. Aztec Connect uses TurboPlonk to create the connected gates.
  • How do these circuits work? Simple math properties over a finite field must be discussed first:
    • Addition: Add a value and wrap around if necessary.
    • Multiplication: Multiply a value and wrap around if necessary.
    • Negation: Finding a value that is 0 within the finite field. For instance, if the field is length 5 and the value I have is 2, then the negation would be 2+3.
    • Inverse: The element becomes 1 when multiplied by the original. 4*4=1 mod 5.
    • Subtraction: Add in the negated element.
    • Division: Multiplication by the inverse.
  • There are two main parts to a gate: selectors and witness values. Selectors are choose by the circuit writer to define the logic of the circuit. The witness values are the intermediate states of the circuit. w values are connected together within the circuit. The gate turns into the following:
    qm * wl * wr + q1 * wl + q2 * wr + q3 * w0 + qc = 0 mod p
    
  • If we wanted to show that y = 4x3 + 2, then we need two gates. First, wl * wr - w0 = 0 mod p and 4 * wl * wr - w0 + 2 = 0 mod p. All in all, we don't need to prove the computation - we need to prove the witness value.
  • The amount should be user_output = total_output * (user_input/total_input) for a given trade. The variable user_input will be floored in most cases. So, the circuit tries for find the division reminder to give to the user as well. While trying to do this, they divided up the number into limbs (sections?). By doing this, the 1 to 1 correspondence over module p was lost! This means that any multiple of p for a given value was valid.
  • On top of this, the constraints for the remainder did not exist. According to the authors of the post, this could have resulted in the sequencer to create proofs that would assign the depositor much less funds than they should receive.
  • Overall, a good description on ZK circuits (which I don't fully get yet) and on missing constraints causing problems. Math is hard enough when there's no millions of dollars at stake. Good find!

Response Header Injection in SAP HTTP Content Server- 1275

Hero Lab    Reference →Posted 2 Years Ago
  • A URL parameter within a call was being used within the headers of a request. In particular, the pVersion parameter of the request. It was being copied into the x-errordescription header without checking the content of it.
  • Since an attacker controls this and the web server does not protect against it, an attacker can add in newlines to change the meaning of the request. This is called CRLF injection.
  • Since they can added content to the headers (and to the body!) they can change the Content-Type of the request! Additionally, they can add in their own body, which will be interpreted as HTML. So, the authors get XSS using this technique, which is pretty sick. Flask prevents this by default, citing that "Header values must not contain newline characters".

Tsshock- 1274

Verichain    Reference →Posted 2 Years Ago
  • At the core of Multi-party Computation (MPC) wallets are Threshold Signature Schemes (TSS). This allows for the decentralized ownership of a single key, which is pretty amazing. The TSS scheme is used to generate keys and sign then (t of n) with no trusted dealer.
  • The TSS system used in major blockchains commonly uses Elliptic Curve Digital Signature Algorithm (ECDSA). A protocol for TSS using ECDSA utilizing homomorphic encryption and zero knowledge proofs was created in 2019.
  • In an audit from Kudelshi Security of the TSS-lib of Binance, a report was made titled interface prone to collisions. Aka, hash collision by improperly concatenating values together. The issue was that a single array of values, such as [a,$,b,$] could be interpreted the same as two independent arrays like [a] and [b]. Originally, this was flagged as only a low severity finding.
  • The authors of this post found that the ambiguous encoding scheme could be used to recover the private key. When performing a Fiat-shamir transformation, an encoding scheme is involved to serialize the to-be-hashed transcript to bytes. Hence, if the encoding is ambiguous, major issues can occur.
  • Under the hood, this attacked the dlnproof mechanism. By attacking the alpha value with our ambiguous number issue, a bits can be leaked from the key one by one. This takes a lot of computation and interactions between all of the owners of the key but can be done.
  • The next issue was with the optimizations being made by the projects. In one project, the number of iterations for the dlnproof was reduced from 128 to 1. By guessing challenge bits when computing this proof with small iterations, we can forge the response. I don't understand the protocol but this seems to give us information about the key being used.
  • The final issue was another optimization problem. When doing the dlnproof, they only ran the check a single time in some projects. The challenge being performed here is similar to a log proof for a group of prime order. But, with this, the order is not prime but composite. As a result, it's possible to brute force a value that fits the mold. Now, the secret values are mod e instead of mod n. To recover the secret, another signing phase alongside lattice attacks can be performed.
  • Many of these issues come from the unsoundness of optimizations and a bad implementation flaw. For the optimization problems, following the paper would be have sufficient. It is worth modifying cryptographers algorithms? 100% not! Even the auditors looking at these protocols are probably not qualified to find those types of novel issues; these attacks are very complicated and take months to full of.

Crosschain Risk Framework - 1273

Crosschain Risk Framework     Reference →Posted 2 Years Ago
  • Ethereum was a great prototype for a blockchain that can execute arbitrary code. However, at this point, it's fairly slow and pricey. As a result, many projects are trying to scale Ethereum and move assets to/from it.
  • Doing so is very complicated! This wiki describes many of the patterns used for this, use cases and security threats. Just wanted to put here as documentation for later.

Uncovering a ZK-EVM Soundness Bug in zkSync Era- 1272

ChainLight    Reference →Posted 2 Years Ago
  • zkSync Era is one of the most popular l2 blockchains. It utilizes zero knowledge proofs to demonstrate knowledge of something without giving up said information. zk-SNARKs are a variant of ZK proofs that don't require any special interactions between users. The authors of this post found a substantial vulnerability in how this was done that is explained.
  • ZK Circuits are similar to boolean circuits with computers that have both gates and wires. Instead of using boolean, these use polynomials. With the arithmetic gates, the boolean operations become things like addition or multiplication. To do the proof, inputs are provided to a system which produce a specific output. By doing this in a zero-knowledge way, we can create arbitrary logic within the system.
  • In this ecosystem, they used it to build a zk EVM with some modifications to the opcode set and how the stuff functions. These circuits are incredibly complicated with a ton of stuff going on. The memory queue is where the bugs live at. This is all about reading and writing to memory. The main VM circuit cannot constrain the memory operations so its stored in a queue instead.
  • When calling creating the constraints in this library, specific functions need to be called. This takes the circuits and ensures that the expected values are being outputted.
  • When performing memory write operations, the constraint was NOT properly added on top of a linear combination. In particular, something like lc.enforce_zero(cs) was missing from the code. Practically, this meant that the upper 128 bits of the MemoryWriteQuery are unconstrained! To make this more clear, the upper 128 bits of any value in memory can be altered!
  • Having the ability to edit the upper 128 bits of memory is a trivial game over. The authors decided to exploit the L2EthToken within the bridge functionality. By sending a little amount of ETH to this contract then modifying the 128 upper bits, a small amount can be transitioned into a large amount! Turning 0.00002 ETH into 100K ETH.
  • Overall, it's a really interesting vulnerability with crazy impact. The creation of Circuits and constraints is very complicated with many footguns along the way. Good find by the chainlight people!

Super Hexagon: A Journey from EL0 to S-EL3- 1271

Grant Hernandez    Reference →Posted 2 Years Ago
  • This CTF challenge was a series of 6 challenges pertaining to AArch64 privilege escalation, which is similar to ARM64. The main differences are removal of Thumb instructions and doubling the general purpose registers. There are four exception levels for this: EL0, EL1, EL2 and EL3. EL0 is user mode, EL1 is supervisor, EL2 is hypervisor and EL3 is the firmware. On top of this, everything has a secure and non-secure world as well using ARM TrustZone.
  • To run this, the particpiants had to use QEMU (with patches) with a BIOS image. The first stage is a statically linked ELF binary with an arbitrary call vulnerability via a back of index checking. To get code execution within this stage, an attacker can call the gets() with stdin to overwrite the function pointers within the .bss section.
  • The LIBC is a modified version (of course), since it runs a custom minified OS. To attack the kernel, we're going to need the ability to execute arbitrary code easily. To do this, they wrote some shellcode then used the overflow to call mprotect() to make a section of memory executable. With this, we have escaped EL0 and can start attacking EL1.
  • They detail their process of reversing the EL1 code, including an IDAPython extension that comments about MSRs and very low level instructions. Once they understood the memory mapping they began hunting for SVC handler (syscall) vulnerabilities. There is a classic vulnerability: missing validation on the destination address. This is similar to copy_to_user in Linux. using this, it's an easy write-what-where primitive within the kernel without any KASLR.
  • Unfortunately, the read() syscall can only write a single byte at a time. This means we can't simply corrupt the return address on the stack to get code execution; we'll need to do something else. Eventually, they found a gadget that allows them to corrupt a single byte but jump to an arbitrary location on the PC. Before this, they need to write their shellcode into the kernel using the syscalls.
  • Right now, there is only a limited payload that can be run. So, the author wanted to go from having a flag to full on code execution. Modern kernels have Supervisor Mode Execution Prevention (SMEP) which prevents the kernel from executing code on userland pages. While stepping in GDB, they found that this setting was not turned on but got a page fault. Why is this?
  • When a processor receives a request to go from a virtual address to a physical address it does a page walk with multiple lookups. As a result, there is a Translation Lookaside Buffer (TLB) to speed up this process. These page tables have attributes like access permissions, execution permissions and more. After learning all of this, the author realized that their userland memory had bad permissions. So, they simply used their arbitrary write primitive to corrupt these bit to make the page executable from EL1. Nice!
  • To communicate with EL2, there are hypervisor calls (hvc). The only functions into EL2 were for memory allocation is via mmap(). This takes in two parameters: an address and attributes. While looking at this functionality, there is a check to ensure that the hypervisor memory region cannot be written. However, the check is performed on the physical address and attributes separately before oring the bytes together. So, we can put swap the inputs to bypass the validation but map the intended address!
  • This creates a window between the EL2 and EL1 address spaces. By calling a function in EL1 with the already mapped address from EL2, we can write into EL1 from EL2! They wrote EL2 code byte-by-byte into the RESET vector of the hypervisor. Then, once they triggered the RESET, their shellcode would execute.
  • This is a fire post on exploiting very low level systems. This is just part 1; we need to go into the secure world now. Overall, loved this post and the challenges.

A MEDIUM Smart Contract Vulnerability in Audit Contest Simple Explained- 1270

Johnny Time    Reference →Posted 2 Years Ago
  • SPARKN is a web3 protocol for people to post a problem where somebody can come up with a solution for them. This is really generally but the intent of the protocol.
  • The ProxyFactory contract serves as a gateway for users to interact with the contests. In particular, a proxy can be made for each contest to distribute rewards to its winners.
  • Within the ProxyFactory there is a function called getProxyAddress(). Given a salt and implementation address, it will return the address that the proxy will be deployed to. This is because users are expected to send funds prior to the proxy deployment to this address.
  • The vulnerability is that there is no validation that the implementation and salt will actually be correct. As a result, if they called this with bad parameters, then the funds would be sent to the wrong location, being lost forever. This was reported as a medium severity finding.
  • Bad user functionality as finding is so weird to me. As a developer, I expect my users to interact with the protocol the proper way. If they mess up, I treat this as an issue on their end. I don't agree with these types of findings. However, it does feel like easy money on contests so I'm not going to complain too loudly.

Numbers turned weapons: DoS in Osmosis’ math library- 1269

Sam Alws - Trail of Bits    Reference →Posted 2 Years Ago
  • Osmosis is a very popular decentralized exchange running on the Cosmos SDK. The authors of this post were looking at the math within this blockchain when they stumbled across an issue.
  • When performing exponentiation(ab), the program was using a Taylor series approximation. There is a point within this series that we need to say "good enough" though. So, what's the stopping point? When the changes to the approximation become so small that they don't matter any more in what the developers choose. They choose a change of less than 0.00000001.
  • There is an issue with this approach though: what if the approximation is never reached? This is bad since there was no maximum iteration on the amount of loops that could occur. For example, 1.999999999999990.1 takes over 2M iterations, taking 0.8 seconds in Go. By doing this multiple times, it leads to a denial of service via resource exhaustion.
  • Thus far, this was done via calling the PowApprox() directly. Can we trigger this on a real transaction? Yes! With the following steps on Osmosis, it was possible:
    1. Make a pool with a token weight of 0.1 and initialize it with 10. of token A.
    2. Deposit 0.99999999999999 more of tokenA.
    3. The above call triggers the approximation functionality.
    4. Do this over and over again to take down the blockchain.
  • What's interesting to me is that this leads to a transient denial of service. The attacker must continually do this in order to perform this attack. By including the new circuit breaker module to turn off this message, the blockchain could have continued for a bit. I enjoy that only two messages could be used to trigger this! Good find friends.

Footguns in Open Zeppelin- 1268

Antonio Viggiano    Reference →Posted 2 Years Ago
  • Open Zeppelin has created a large amount of contracts that are used by every EVM contract. This twitter threat is talking about several of the 'footguns' or easy ways to mess up.
  • ERC-7201 is a standard for what slots variables should go in. The idea is to stop vulnerabilities from storage layout changes when upgrading a contract. The NameSpaced storage is only done on the OpenZeppelin contracts and NOT the users contracts.
  • This means that the same classic bug classes apply. The author made a PR to make this generally available as a base class.
  • Ownable2StepUpgradeable() is a function meant to perform an other swap with an extra step in between. By doing this in 2 steps, if the new owner address is wrong or there is a mistake, then the owner will not be changed.
  • The contract inherits from OwnableUpgradable(). However, the child initializer does NOT automatically call the parent initializer. In a user doesn't call __Ownable2Step_init, then it will be left without an owner. There is a Github thread about doing this better though.
  • The researcher Dacian adds a good note as well. Many NFTs should NOT be transferable. This was done by overriding the _transfer() function. However, in V5 of the contracts, _update() is called instead. So, the previous override does not work.