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!

DFX Finance Rounding Error Bugfix Review- 1187

Immunefi    Reference →Posted 2 Years Ago
  • DFX Finance is a decentralized foreign exchange protocol that allows users to swap many stablecoins. DFX is an AMM that exchanges tokens according to a bonding curve, which is dynamically calculated from Chainlink prices. Each one of the currencies is paired with USDC in a pool.
  • There are two main parts to the DFX protocol: Assimilators and Curve. Assimilators allow the AMM to handle pairs of different values for fixed point arithmetic, since Solidity doesn't support floating point numbers. In particular, it's responsible for converting all amounts to numeraire, a base value for all computations. DFX Finance maintains the assimilators which integrate with Curve to provide proportional liquidity to pools.
  • When users would provide liquidity to a pool to receive yield on their stablecoins, they call the deposit() for a Curve pool and receive LP tokens. During this process, a check occurs to see if the deposit amount is greater than zero.
  • The EURS token is a token with only two decimals. When creating these sorts of protocols, the developers may not have considered these small decimal tokens. The amount to transfer from the user is calculated based upon the following values:
    amount_ = (_amount.mulu(10**tokenDecimals) * 1e6) / _rate;
    token.safeTransferFrom(msg.sender, address(this), amount_);
    
  • This code leads to zero tokens being transferred from the user. Despite this transferring zero tokens, the attacker gets some value being put into the contract. However, it is an extremely small amount of LP tokens that are sent back to the user.
  • Normally, tokens have at least six decimals. So, the amount of tokens gained from this would be tiny with these larger tokens. Because of how small the values normally are, the amount spent on gas would dwarf this. However, since the EURS token only has two decimals, this attack becomes viable! By depositing a tiny amount 10K times, an attacker can get $190 per attack by withdrawing the CURVE LP tokens.
  • To fix this vulnerability, there is a simple check to make sure the amount of funds transferred is not zero. The hacker got a 100K bounty for a 230K pool; this is a good look for the protocol. Overall, this is a fascinating vulnerability that was only possible because of a single token. Precision issues are super interesting!

Hacking My "Smart" ToothBrush- 1186

Cyrill Kunzi    Reference →Posted 2 Years Ago
  • The author of this post had recently bought a Phillips Sonicare toothbrush. When reviewing the documentation, it says that the product operates at 13.56MHz, which indicates this uses NFC. The communicate happens from the toothbrush handle to the toothbrush head to tell you that a new one is needed.
  • Using the NFC tools app, there is a lot we can learn about the tag. Some interesting notes from this:
    • The tag is NTAG213 and uses NfcA.
    • The device is password protected.
    • Address 0x24 contains the total brush time.
    • Various other fields such as checksums, IDs, links and more.
  • The goal of the attack is to overwrite the brushed time of the device. Reversing engineering the format is easy: observe the value, brush your teeth and observe again. However, it's password protected. So, what do we do?
  • Software Defined Radio (SDR) time! The author pulled out a HackRF (which goes down to 1MHz for listening). Then, they used Gnu Radio to take the raw IQ signals and convert the data into a WAV file. The WAV file is composed of a seris of complex numbers, which is why the author pulled out the real and imaginary portions to convert it to a WAV.
  • The tool NFC-laboratory takes in a WAV file and decoding the bytes manually for us. By cross-referencing the data being sent and the stored data, we can learn what's being sent from the WAV file.
    • Lines 0-6: Establish communication between the systems.
    • Lines 7: Send password - 0x1B command.
    • Lines 9: The counter is updated to a new value - 0xA2 command.
  • Reading line 7 shows that the password is 67:B3:8B:98! Woah, that's awesome. It's super crazy to me that the data is sent in plaintext over NFC; I figured some type of encryption would be done by default.
  • Using NFC tools, we can set the password of the field then attempt to edit the field like we tried before. Since the password is set, it works! We've got a NEW toothbrush as far as the device thinks.
  • Through the post, the password changes. What gives? Attempting the password incorrectly 3 times will permanently disable write access. Additionally, the passwords are unique per toothbrush. NXP recommends that the password should be generated from the UID but the author couldn't find the transformation function.
  • Overall, an amazing post! I learned all about WAV files and NFC hacking.

CSP Bypass Unveiled: The Hidden Threat of Bookmarklets- 1185

socradar    Reference →Posted 2 Years Ago
  • A bookmarklet is a primitive version of a browser extension. It's a simple version of a browser extension that can contain JavaScript code. Users can add bookmarklets by creating a bookmark, pasting the bookmarklets code as a URL or dragging/dropping the link onto the toolbar.
  • When the CSP came out in 2014, the idea was that the bookmarklets were going to die. However, this never happened, with references in the RFC for CSPs ignoring addons. Regardless of implementing a CSP or not, the bookmarklet can bypass this protections to interact with websites.
  • These bookmarklets were used for phishing attacks. In particular, Discords in the cryptocurrency space were being hit by these attacks. These attacks worked by asking users to drag a button to their bookmarks bar. Once this happened, the JavaScript within the bookmarklet code can be accepted.
  • With the admins knowledge within the account, the bookmarklet discreetly retrieved their Discord token when it was dragged, subsequently transmitting it to the attacker’s website. This led to posts on Discord channels with more malicious links and bookmarks.
  • Overall, an interesting attack vector to consider; I had no idea that bookmarklets could execute code like this. I'm sure we'll see some response from the browsers soon.

CosmWasm allocate stack overflow- 1184

claudijd    Reference →Posted 2 Years Ago
  • CosmWasm is a smart contract platform that can be used on Cosmos. This allows for a similar interaction of Solidity based smart contracts on the EVM.
  • Being able to find a denial of service (DoS) within a smart contract platform would be catastrophic. It could be used to stop the chain altogether for each node that was running. To me, it's weird that the virtual machine running the code wouldn't handle the error, toss out the error and move onto the next transaction though.
  • CosmWasm has a several runtime imports. This functions exist to offload expensive operations (like cryptography), perform validations and write state changes. All of these functions use a helper method called write_to_contract() to write error messages to the WASM address space.
  • To do this, write_to_contract() calls allocate. This function allocates a large block of memory in the address space. Normally, this is a standard library from CosmWasm but can be overwritten by a developer.
  • A classic problem that developers run into is recursively calling functions; this creates a stack to deep, otherwise known as a stack overflow. By adding a call to addr_validate() within our custom allocate() function, an infinite recursion call can be created.
  • This is a really simple bug that has horrible consequences. I bet there are many other issues in the layer 1 eco-system on newer blockchains. Just got to go look!

Bypassing SELinux with init_module - 1183

Sean Pesce    Reference →Posted 2 Years Ago
  • Security Enhanced Linux (SELinux) is an added layer of security to the OS kernel. Using it, access controls can be put on applications, processes and file on a system. Just because you have root doesn't mean you've won with SELinux.
  • The author of this post had a reverse shell on the box but had some serious restrictions in place that prevented exploitation. SELinux tools like getenforce were removed as well.
  • The SELinux is just a list of rules for the processes other actions that can occur. What if we could load a kernel module? The call init_module was restricted but finit_module was not! They are the same exact call except one takes in a file and the other takes in a file descriptor.
  • Calling finit_module allows the author to get into the kernel and disable SELinux. They had to write a custom loader for this though, which is interesting. Overall, an interesting bypass for SELinux.

Catch me if you can!- 1182

Red Guild    Reference →Posted 2 Years Ago
  • Solidity has error handling like most languages do. It looks similar to JavaScript with try and catch blocks. The docs can be read at here.
  • In the initial example, the author gives a fairly simple code for the try with 3 options for the catch. First, catch Error(...) catches a revert from the external contract with a particular reason string. Second, Panic catches a serious error like division by zero, an integer underflow or assert.
  • Finally, there is catch (bytes memory) that will catch all other unhandled errors. So, what if we could cause a denial of service by triggering one of these outcomes? Or, if we could bypass the checks implemented in a specific catch but not another? Let's hack things!
  • The first option that the author considers is a wrong data type. Naturally, the compiler makes an assumption that the proper data type is returned. When the data returns uint256, it will use the address or any other data that is provided.
  • The next case the user considers is returning data from a different location, such as dynamic arrays and strings. In the case of the string, the return value is 32. Although the author doesn't say this directly, I'm guessing this is the location of the dynamic byte array in the return data.
  • What if no data is returned at all? The EVM returns an error that there is an odd-length and the argument is invalid. Since there is no data being sent back, this triggers the general catch case.
  • What else can hit this catch? An empty fallback or executing code at an address with no code. Overall, an interesting article on the intricacies of try/catch statements in Solidity. If errors are not handled properly, it could lead to security issues.

A Mathematical View of Automated Market Maker (AMM) Algorithms and Its Future- 1181

AnchorDAO Lab    Reference →Posted 2 Years Ago
  • The article goes into the finance between many different DeFi algorithms. This is a large article with many protocols in it, including Bancor, Uniswap, Curve, Clipper and more.

Why DeFi is Broken and How to Fix It, Pt 1: Oracle-Free Protocols- 1180

Dan Elitzer - Nascent    Reference →Posted 2 Years Ago
  • Decentralized Finance (DeFi) is great eco-system for opening up everyone to many financial instruments. You know what's not great? DeFi hacks. A large percentage of these occur from manipulating the oracle or pricer of the assets in some way to purchase/sell assets at a bad price for the other party. This article talks about how to fix this problem.
  • First, the author mentions primitive protocols. This is contracts that have no governance, no upgradeability and no oracles. Why? If the main contract gets manipulated, then everything gets manipulated, since it's an underlying protocol for everything.
  • The argument is that if everything is self-contained, then oracle manipulation from other protocols subtleties is not possible. The only true example of this is Uniswap but it's now upgradable, which is a double-edged sword.
  • What about lending protocols? There are several cases of lending protocols being being oracle-free. Instead of the collateral factors being set by oracles like Chainlink, the lenders are responsible for evaluating the risks and deciding how much collateral they want from the borrower.
  • To me, this feels like a cop out though. The safety of the protocol goes onto the lender instead of the protocol. If a bad rate was set by a lender, it would immediately be swept up and stolen. The solution to this is having a good user interface that sets these automatically. How does it get these quotes though? A price oracle, but off-chain, which prevents serious manipulation.
  • The article is a tad odd to me but made some good points. Oracles cannot be manipulated if the values are set by the lenders. By having this off-chain, a subtle flaw in a contract doesn't destroy the whole eco-system. Thanks for writing up your thoughts friend!

Cross-chain re-entrancy- 1179

Mateocesaroni    Reference →Posted 2 Years Ago
  • Reentrancy is a fundamental attack in the Solidity security space. This is when a user can recursively call a contract while it has not had it's state fully updated. Developers should follow the Check, Effect, Interactions (CEI) pattern. However, the term cross-chain caught my eye for this post.
  • The application the author was testing was a cross-chain NFT contract. Naturally, NFTs should only exist on a single chain at once. So, verification has to be done on both chains before minting.
  • To send funds from one contract to another, a bridge is needed. Since the contract owns the asset, it can mint to create an asset or burn to remove it from existence. Then, using a proof, the other chain would know whether we owned an asset on it or not.
  • The code for the mint function did not follow the CEI pattern. The increment should occur before the call to _safeMint. This worked as follows:
    function mint(address to) public returns (uint256) {
           uint256 newWarriorId = tokenIds.current();
           _safeMint(to, newWarriorId); 
     
           tokenIds.increment();
     
           return newWarriorId;
       }
    
  • The _safeMint() function has an external function call for onERC721Received inside of it. Since the CEI pattern is not followed and the contract is not using the standard library for preventing reentrancy, there is a major problem.
  • How can this be exploited? If we simply call the _mint function again, it will fail because the token id wasn't incremented and already exists. However, there are other functions within the contract that could be interesting to us.
  • The function crossChainTransfer() is used to send the assets from one chain to another. Calling this with a particular token ID will send the token from one chain to another.
  • Since the ID is not incremented until after the mint call, we can exploit this to get two copies of it.
    1. Call mint() with the attacker contract as the recipient. NOTE: The ID hasn't been incremented.
    2. Re-enter the contract to call crossChainTransfer to transfer the new id to chain B.
    3. Call mint() again from the recipient contract to mint the same NFT once again.
  • Cross-contract reentrancy is a little bit deceiving but technically correct here. Overall, a usual bug class exploited in a unique way!

Polygon zkEVM DoS Bug- 1178

iczc    Reference →Posted 2 Years Ago
  • Since Ethereum and other layer 1 blockchains are slow and expensive, there are many layer 2 (L2) protocols appearing. The idea is to roll all the transactions on the L2 EVM into a single transaction on the L1 EVM.
  • To get assets on the L2, a bridge is used. Practically, this is done by locking the assets on the L1 then submitting a Merkle Proof on the L2 to mint the same amount of tokens that are locked in the L1. Any user can call the claimAsset(), but it always goes to the proper user.
  • While reviewing the code, they noticed that the claimAsset() was built to be gasless (free). This is because a new user will not have any assets on the L2, since they are currently transferring them over. Because of this, a malicious actor could send lots of invalid free claim tx's to cause a DoS. Well, not exactly.
  • To prevent a denial of service attack the claim tx is validated before it's put into the pool. This is a good remediation for this exact problem; great job by the development team for thinking of this in advance. Next, the author pulls up the code to look for logic flaws.
  • The code below is the logic explained above. If it's free and the execution reverted, then revert the transaction.
    isFreeTx := poolTx.GasPrice().Cmp(big.NewInt(0)) <= 0
    // if the tx is free and it was reverted in the pre execution, reject the transaction
    if isFreeTx && preExecutionResponse.isReverted {
        return fmt.Errorf("free claim reverted")
    } else { // otherwise
       ...
  • There's a subtle flaw in the code above though... the transaction can have gas! Since the revert only happens when the transaction is free, we can send very little money to put the transaction into the queue unexpectedly. This bypasses the pre-execute check on the claim tx, leading to the earlier denial of service vulnerability.
  • Overall, a pretty interesting bug that required a deep understanding of the application and the protections in place. To remediate this bug, they removed the special gas logic for claims. I'm unsure how this remediates the issue and still allows people to claim stuff from the L2 for the first time though.