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!

Exception(al) Failure - Breaking the STM32F1 Read-Out Protection- 1024

Marc Schink & Johannes Obermaier    Reference →Posted 3 Years Ago
  • The debug interface of the STM32F1 chip cannot have the debugger attachment disabled. Instead, there is a Flash Memory Read Out Protection (RDP) instead; this will block all data access via the debug interface. This article is about bypassing RDP.
  • While playing around with a development board with RDP turned on, the authors ran the reset halt command. When doing this, they get the following output: xPSR: 0x01000000 pc: 0x08000268 msp: 0x20005000. Why is this interesting? Raw register values are sent back, which we shouldn't have access to.
  • Why does this happen? A reset is a special kind of exception. When an exception is called, the processor loads the exception entry address from the vector table to know what to do - vector fetch. Since this is stored in flash memory, how can that vector be accessed?
  • The reset vector is fetched via the ICode bus. So, the fetching of the reset information is done via the instruction fetch line instead of the standard data line. The bus being used in the reason why the read out protection doesn't work in this case!
  • In ARMv7-M there is a Vector Table Offset Register (VTOR) that determines the location of the vector table in the address space. This is normally used to relocate the VTOR when going between applications but can be abused. By changing the VTOR, we can relocate the vector table within the flash memory region!
  • Since the ICode bus passes back information via the PC and returns the address, with interrupts, we can abuse the trust mentioned above to slowly read out information we shouldn't have access to. Again, we can control everything besides flash via the debugging interface.
  • Several items in the VTOR are inaccessible for functional reasons. However, we can wrap around the values (max is 32) to still access the vectors! For instance, the normally inaccessible 1 and be accessed by using the interrupt 33 but there are some limitations to it.
  • Now, for the moment of truth - extracting the information. This is done by doing the following steps:
    1. Perform a device reset to put the microcontroller into a well-defined state.
    2. Configure the microcontroller to trigger an exception of our choosing. Only a handful can be truly triggered via this method though.
    3. Single step in order to make the exception active. At this point, we can extract the data we would like.
  • Overall, this method, even with its shortcomings, was able to extract around 90% of the code in less than an hour on all of the chips. This is a pretty incredible feat by a small oversight on the part of the developers. Amazing blog post!

Nereus Finance Flashloan Attack Analysed and Exploited- 1023

Faith    Reference →Posted 3 Years Ago
  • Nereus Finance is a lending / borrowing protocol. This allows users to deposit their tokens to earn interest on them and borrow funds from this protocol.
  • Why would somebody want to borrow assets if they can't be under-collateralized? In the case of this protocol, the NXUSD token is a stable coin alternative to USDC. The main way to obtain NXUSD is by borrowing it. For NXUSD, the main purpose of the token is staking it; this means giving a token for somebody else to use in order to earn rewards on it.
  • A liquidity pool (LP) is the main method for exchanging one token for another. An LP consists of one or more tokens. When putting your own funds into the pool, you receive a pair or LP token. This can be used to stake, collateral or many other things. In the case of this project and hack, the pool is USDC-WAVAX and this gives back the JLP token.
  • When calculating the price of a liquidity pool, the price is strictly dependent on the ratio between the tokens in the pool. For instance, if there was a 1:1 ratio, trading for one token would give you an equal amount of the other one. If it was 2:1, then trading for two of one token gets you a single token of the other.
  • The contract JLPWAVAXUSDCOracle is used to calculate the price of JLP. This is done via the following steps:
    1. Get USDC price from an external oracle.
    2. Get Avax (Avalanche) price via an external oracle.
    3. Get the reserves of each token within the contract.
    4. Price is the following formula: JLP = (AvaxReserve * AvaxPrice + USDCReserve * USDCPrice) / totalSupplyJLP
  • This isn't some weird injection issue or anything... it's a math issue where there is a case that is forgotten: an attacker can obtain an insane amount of money quickly via a flash loan. The variables AvaxReserve and USDCReserve are somewhat controllable, since we can swap in and out of the contract. These variables are also part of the price of the JLP token, as mentioned above.
  • If an attacker swaps a ^*&@ ton of one token for another, then the price of the JLP can be drastically skewed in either a high or low direction. To drop it low, we exchange in a large amount of USDC, since it is cheaper. At this point, the exchange rate is much cheaper for the NXUSD (which can be traded from JLP) to borrow WAYYYY more than we should be able to.
  • The author of the post puts the steps above:
    1. Use a flash loan to obtain a large amount of USDC and another currency to acquire JLP.
    2. Acquire JLP tokens at the normal price.
    3. Lower the exchange rate of the JLP token by swapping in a ton of USDC for wrapped AVAX.
    4. Using the JLP from before, use this to borrow the NXUSD. Remember, the exchange rate has been dropped, so we get more tokens than anticipated.
    5. Swap back the WAVAX for USDC to bring the exchange rate back.
    6. Pay back the flash loan after getting a huge profit from the NXUSDC.
  • An interesting note that the author makes... we are simply leaving the JLP in the contract since we profit from the NXUSDC. Unlike the bank coming after you in the real world, the only thing that makes you return the loan is the collateral deposited. Since we made more money than the JLP is worth, we simply leave it in the contract.
  • The author includes a very detailed proof of concept that is explained well with a Hardhat setup. So, how does one fix this problem with the JLP token? Having a Time Weighted Average Price (TWAP) or forcing these steps into multiple blocks would solve the problem. An absolutely amazing post and I look forward to more of these in the future!

SportsDAO Flashloan Attack Analysis- 1022

Faith    Reference →Posted 3 Years Ago
  • The SportDAO is a DAO centered around athletics. There are many collectables in sports that can easily moved to blockchain like playing cards and sneakers. The SportsDAO has its own sDAO tokens as well.
  • The sDAO appears to be a standard ERC20 token but has a few extra functions that override the standard IERC20 functions. A few to note for this vulnerability:
    • stakeLP(): Allows a user to stake BUSD-sDAO LP tokens directly in the contract.
    • getReward(): Collect sDAO tokens from the staked LP tokens.
    • withdrawTeam(): Transfers all LP tokens to a preset TEAM address.
    • tranferFrom(): Added functionality for calculating the total staking reward of the tokens accumulated when sDAO tokens are transfered from the BUSD-sDAO token contract.
  • The code for the transferFrom() math for the tokens is incorrect. When executing the code for staking, the functionality only checks to see if the address we're trying to transfer to is the BUSD-sDAO LP token. If it is, then 7% of that is added directly to the totalStakeReward.
  • Why is this a problem? The variable totalStakeReward is used directly in the rewards per token calculation AND is directly accessible to any user. Within the transfer() function, only msg.sender must be the owner, indicating this is a flaw in the contract.
  • The final piece of the puzzle is that the function withdrawTeam() can be called externally by anybody to send funds to the TEAM address, which is owned by the SportsDAO. By pairing this functionality of transferring with the direct control over some of the fields for rewards, we come across a vulnerability!
  • The issue relies in the control of sending funds and the control over the math happening. In the statement below, the funds of LPInstance can be removed by the second issue mentioned above. Since this is the denominator of the math, controlling this is a HUGE deal.
    newPerTokenReward = (totalStakeReward - lastTotalStakeReward) * 1e18 
                             / LPInstance.balanceOf(address(this));
    
  • The idea is to make the funds of LPInstance be extremely small; this can be done first by removing all of the funds via the withdrawTeam() then adding in a tiny amount via the bug in transferFrom(). Once the denominator is tiny, the math for the rewards is completely busted. The amount of rewards per token will be extremely high, since it believes it has very little of the token!
  • An attacker used this pair of bugs, alongside a small flash loan, to get a large amount of funds from the contract. The author of this post includes a full proof of concept as well. Overall, awesome explanation of the vulnerability and nice POC!

Hack Analysis: Saddle Finance, April 2022- 1021

Immunefi    Reference →Posted 3 Years Ago
  • Saddle Finance is an automated market maker (AMM) on Ethereum. In particular, they specialize in stable swaps and aim to reduce the slippage of users. The bulk of the code is based on Curve but has been widely re-implemented.
  • There are two types of pools: Standard and Meta. The standard pool is an AMM pool where the tokens provided for liquidity are swapped via the pool. In the meta tool, a pegged token and another token represent the liquidity in the standard pool. Meta allow liquidity in one pool to be used in additional pools.
  • When trying to figure out the fees for a given Liquidity Provider (LP) token, there is a mispricing. The LP token's virtual price grows and grows as more fees are taken. However, the swap function does not account for this virtual price. The base virtual price calculation was simply NOT applied to the contract.
  • Interestingly enough, forks of Saddle (Nerve and Synapse) were exploited for a similar issue. Patches were made to the forks and Saddle itself but they missed the patch on the swap calculation.
  • At a high level, we need make a very large swap for the LP token with the missing price calculation. How this is precisely done:
    • Obtain USDC funds via a flash loan.
    • Swap USDC for sUSD.
    • Swap the sUSD into the Saddle pool in order to get the LP token via the Meta pool.
    • Swap the LP token for sUSD via the Meta pool. Doing this over and over again allows for us to obtain a lot more sUSD because of the LP token mispricing.
    • Swap sUSD for USDC.
  • In the real world attack, the attacker swaps the sUSD and LP several times to remove all of the liquidity from the Meta pool. The article demonstrates a proof of concept of the exploit as well, which is pretty cool to look at!
  • Two takeaways for me. First, financial calculation mistakes are easy to make, hard to trace and costly- they are not obvious patterns to follow for auditors. Secondly, where there are bugs, there are likely more bugs! In this case, they patched the bulk of the functionality but missed a single location. Unfortunately, this was all the attacker needed.

Mt Pelerin Double Transaction Bugfix Review- 1020

Immunefi    Reference →Posted 3 Years Ago
  • Mt Pelerin is a financial intuition in Switzerland that leverages the blockchain to operate. The smart contract ComplianceRegistry is responsible for stores identity information linked to an address and the storage history of the address.
  • This Compliance Registry is managed by trusted intermediaries, such as KYC/AML providers. Based on the tokens used provider, the registry will return the compliance information of the token.
  • The function cancelOnHoldTransfer can be called to cancel transfers for a trusted intermediary to get their tokens back. This takes in a trusted intermediary and an array of transactions to cancel. There is a loop that verifies that the transfer.from is the same as msg.sender for the transaction to cancel.
  • While looping over the array, it does not check to see if the transaction has been cancelled. Since the cancelled funds are sent back to the user at the end of each transaction, infinite money can be stolen using this technique.
  • The transactions would be cancelled, but it's at the end of the loop. So, to exploit this, the same transaction needs to be added multiple times to steal all of the money. Overall, a good bug that seems to be common when looping over elements that own money.

The Defrauded Fraud Proof of A Bitcoin Bridge- 1019

pwning.eth    Reference →Posted 3 Years Ago
  • interBTC is a wrapper around Bitcoin on the Polkadot ecosystem. Each interBTC is backed 1 to 1 for Bitcoin.
  • Over-collateralization is when a loan of more money is used to obtain something of lesser value. In the context of cryptocurrencies, this is commonly used for Collateralized Debt Position (CDP) protocols for minting stable coins from valuable assets. For instance, the MakerDAO does this for the DAI token.
  • In the context of InterBTC, a mapping of a high value token (BTC) to the native tokens of INTR and KINT can be made. Usually, the price properties of these tokens are made by price oracles. How do we trust these anonymous entities though?
  • When using Bitcoins assets from the Bitcoin blockchain to the Moonlight blockchain, there has to be a way to verify that the things claimed from BTC are correct. This is done via a Simplified Payment Verification (SPV) system. The validation is done by storing minimum information about the header of each block then checking the merkle proof and it.
  • An off-chain relayer ensures that the vaults don't move BTC unless specific calls are made. If a vault moves BTC without authorization, then the collateral of the vault is slashed. A vault can be reported for fraud by calling the function report_vault_double_payment(), which takes two txs and verify that they reference the same user request.
  • When comparing if two tx's are the different, the check is flawed. The class BytesParser will ignore trailing bytes of a raw transaction. This results in different transactions being parsed into the same transaction. From looking at the fix, it appears that a duplication check for the two IDs needs to be done. Hence, an attacker can report ANY vault and steal 5% of the funds while the rest get dumped.
  • Within Moonlight, there are several different versions of an address. All of the balances are maintained by uxtos; every utxtos has a public key which can only be unlocked with the corresponding ScriptSig or private key.
  • One of the interesting types is Pay to Script Hash (P2SH). Any public key can be encoded in a vault address with just its hash and the actual public key. The rest of the ScriptSig can be edited to match our mutable public key.
  • This creates a signature malleability problem with trying to ensure duplicates aren't being used. This isn't a problem until we enter the SPV system.
  • This lack of consistency on the public key has interesting implications. Within the SPV system, the function report_vault_theft gets the ScriptPubKey address from the ScriptSig. However, as mentioned before, we can arbitrarily forge these to return other vaults. So, we can always bypass the misbehavior checks and, even worse, we can get a benign vault slashed to steal money! P2WSH had a similar parsing problem as well.
  • What's the solution to this? Since these are fundamental design flaws, they decided to remove the theft reporting functions entirely. Overall, this was an awesome post diving into the complicated parts of a blockchain ecosystem.

How to Steal $100M from Flawless Smart Contracts- 1018

PwningEth    Reference →Posted 3 Years Ago
  • The delegateCall() function in Solidity is used to share the state between two contracts. The msg.value and msg.sender are shared when using this call. In the context of native contracts or functions emitting events, this has weird consequences though.
  • In a different bug in the Aurora Engine, an abuse of this was found to make a delegateCall to an event emitter. Then, an offchain listener would add the funds to this user account. Since the msg.value was never actually sent to the contract, this essentially prints money. What else could go wrong with this? An interesting note is that the delegateCall() user is preserved the original user and NOT from the actual caller.
  • Moonbeam and Moonriver are EVM compatible platforms. The native tokens, MOVR and GLMR, are precompiled ERC-20 contracts. When making calls to EVM related functions, it preserves the msg.sender for the call.
  • So, what's the actual issue? The msg.sender preservation can be abused to perform an action as another user! Simply calling something with delegateCall() will preserve this, allowing the msg.sender to be the actual user on calls to other contracts.
  • Currently, we still need a way to get the user to execute our code. This could be done via a phishing attack to execute a contract, but could be hard to do. In reality, all we need is a callback in our contract to be hit. What has callbacks? Flash loan providers! This could have been used to steal 12M without any user interaction.
  • They also found a callback on a protocol called Glimmer. Although there is not too much being stored in the native MOVR contract, the amount deposited is consider collateral from the lending protocol. So, the steps of deposit, borrow, transfer and bad debt can be used over and over again to steal all of the funds from the contract!
  • delegateCall() is a dangerous function in the EVM. From user impersonation to logs to the older days of malicious calls, the consequences of it need to be well audited. Good find!

Velas Infinite Mint Vulnerability Writeup- 1017

Oren Yomtov    Reference →Posted 3 Years Ago
  • While reading blockchain security articles, the author of this post stumbled across this post talking about delegatecall() on a pre-compiled contract causing infinite money creation problems because of the events it was emitting.
  • From there, they went through a list of the most popular blockchains one by one. The author was looking for pre-compiled smart contracts to see if any of them were vulnerable to the delegatecall() attack mentioned above. Eventually, they stumbled upon Velas.
  • Velas is a side chain built on Solana but has a special instruction to invoke EVM programs as well. With the chains native currency VLX, this is held by the Native Type. When a program runs in the EVM space, it belongs to the EvmState account.
  • The bridge between VLX from the EVM space to the Native space is done by sending a transaction to a precompiled contract. By making a delegateCall to the function transferToNative the contract believes that the proper funds are there even though the contract making the delegateCall is the true owner of the funds.
  • The blockchain now executes code that will transfer the funds back to the native state, even though they were never removed from the ETH state. This can be done over and over again to create an infinite amount of VLX tokens in the EVM space. Overall, great bug discovery after a lot of time of looking!

CVE-2022-40300: SQL Injection in ManageEngine Privileged Access Management- 1016

Zero Day Initiative (ZDI)    Reference →Posted 3 Years Ago
  • Password Manager Pro is a secure vault for storing and managing shared sensitive information such as passwords, documents, and digital identities of enterprises. A user can access the web console via three different services.
  • When making a request to add a resource, there is a partially built SQL statement. With this statement, it uses a find and replace functionality to add in the resource from the request. Since this does not do any verification on the text, this creates a SQL injection issue on future calls.
  • This vulnerability is an authenticated issue but does lead to arbitrary SQL code using as SYSTEM on Windows. Overall, the bug explanation is okay... they seem to go too deep on things that don't matter, like the specifics of the HTTP protocol. Fun bug though! SQL injection is alive and well is 2022!

RCE in Tailscale, DNS Rebinding, and You- 1015

Emily Trau    Reference →Posted 3 Years Ago
  • Tailscale is a mesh VPN service. A network connection is established with Wireguard to one another on demand. To execute the website code for a VPN user, it will use the V8 engine. The security features for connected to privileged sockets and the Same Origin Policy (SOP) apply as well.
  • The setup was vulnerable to DNS rebinding attacks. This means that although the original DNS request mapped to some domain, on the next DNS request it will map to something else. What would be good to map to? 127.0.0.1 or localhost of course!
  • Since we're now on the localhost, we have the permissions to all the API without any authentication. This gives us the ability to introspect and reconfigure the tailscaled binary used for configuring the service. What can we do with this issue? Accessing private keys from the node is possible with this.
  • Using the previous vulnerability, many other things can be hit. A PATCH request to the preferences API can be used to update the control plane server. This allows us backdoored access to the network that this computer is on. When using this URL, it is specified as a parameter to open up a web browser. This can be used to open arbitrary binaries from the internet; absolutely amazing.
  • The issue above has the Mark of the Web, meaning a user is asked whether or not this can be executed. The application can be configured for the proxy to NOT mark stuff with the Mark of the Web! This means we can download an executable to the local machine and use the previous bug to execute it without any other popups.
  • After getting code execution on the device, they go more into how the DNS rebinding affects different browsers and different operators systems. They learned that the attack they were using worked fine locally but NOT remotely because of the browser protections in Chrome. FireFox was exploitable from a remote context though.
  • The Tailscaled server runs a web server at 100.100.100.100. Since this isn't considered a private address, this is vulnerable to rebinding attacks. They also found that the PeerAPI was vulnerable to the rebinding as well. The security model of Tailscale says to use authentication based upon network position. This is not a great thing to do with the threat of DNS rebinding.
  • Overall, an interesting piece of research into the VPN and a good representation of the dangers of DNS rebinding. Good work!