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!

Solidity Deep Dive: New Opcode 'Prevrandao'- 1207

Markus - Injective    Reference →Posted 2 Years Ago
  • When Ethereum moved from proof of work to proof of stake, it added some new functionality. One of these with the replacement of block.difficulty with block.prevrandao. Although, the opcode is still the same, the values have different meanings now.
  • The blockchain is completely deterministic, forcing randomness to come from things like Chainlink VRF off-chain. prevrandao is meant to be a source of randomness on chain that is created using decentralized information. This is generated with the following steps for each validator:
    1. Sign over the current epoch number.
    2. Compute hash of signature.
    3. Calculate new randomness as hash XOR with the previous randomness.
  • Is this secure? That's a complicated question. The randomness is based upon signature data of the block. So, you cannot directly affect it. However, you can choose not to sign the data in your slot. If this is the case, the randao update is simply skipped. For every validator at the end of a slot, an attacker has control over a single bit of influence by deciding or not deciding on signing the data.
  • To use this in a secure way, we need to pick a prevrandao from the future. According to the EIP-4399 specification, this should be 4 epochs into the future. The reason for this is that we can limit the influence of an attacker by forcing them to guess earlier on.
  • A naive solution is to enforce a guess early then 4 epochs (greater than 128 blocks). in the future to do the validation. However, this is manipulable by censorship attacks by withholding a particular transaction until the prevrandao opcode returns a favorable value. To fix this problem, enforce that the transaction happens on a particular block in the future.
  • Overall, an interesting article on Ethereum randomness. It is still not secure enough for people to use on a lottery, but does it's job for simple on chain things, like ordering of validators.

saleReceiver and feeReceiver can steal refunds after sale has ended- 1206

Code4Rena    Reference →Posted 2 Years Ago
  • In this protocol, it's a standard auction but the lowest price wins. If a user gets outbid, they get a refund but must call a function in order to perform the refund.
  • The sale is considered ended when the last NFT is sold. This triggers the payout to the seller and fee collector. The verification for paying out is done by checking if the current ID matches the final sale id.
  • There is an additional check that checks that that the current id does not equal the final id. Otherwise, we've tried to get an NFT that already been sold.
  • In the code, the function buy(uint256 _amount) amount variable is for the AMOUNT of NFTs you want to buy. If a user buys 0 NFTs, then the currentId is not set properly. The check from before about the auction being over doesn't work since the currentId is not set from the iteration within a loop. It's obviously lower than the final ID, since it's set to 0.
  • This results in the sale being made with a larger value than the current bid. Since the auction has ended, we're able to get a refund from this still. More importantly, the check on whether to transfer the funds to the feeReceiver and saleReceiver can be hit over and over again because of the if statement failing.
  • Zero iteration in the array causes this problem. Overall, a pretty neat bug that is common in the web3 space.

Analysis CVE-2023-29300: Adobe ColdFusion Pre-Auth RCE- 1205

Harsh Jaiswal - Project Discovery    Reference →Posted 2 Years Ago
  • The authors knew about a potential RCE in Adobe ColdFusion. So, they went to the Java code and started diffing from the previous version.
  • While doing this, they found the function validateWddxFilter() had been added. This did verification on the type attribute of the object to ensure it starts with coldfusion.
  • The sink is a call to getClassbySignature() that gets an instance of an arbitrary class. Then, it calls a function that must start with set. Being able to call arbitrary calls with a semi-restricted function is a good primitive to start from!
  • Their test payload was java.util.Date.setDate(). After verifying that this worked in a debugger, they were set to look for more primitives. With the class com.sun.rowset.JdbcRowSetImpl, setDataSourceName() sets a JNDI lookup name. Then, by calling setAutoCommit(), we can create a JNDI injection vulnerability, like with log4shell.
  • To get code execution, the authors used a ysoserial java serialization payload with commons-beanutils to get code execution. Pretty neat bug and unique primitive.

Rodeo Finance Hack- 1204

Immunefi    Reference →Posted 2 Years Ago
  • Rodeo Finance is a leveraged yield farming protocol. Apparently, it also supports loan functionality.
  • The service was uses a price oracle to determine the price of assets when borrowing and lending. In particular, it was using a Time Weighted Average Prices (TWAP) which averages out the price over a given interval. This prevents instantaneous changes in the price from manipulation. In this case, updates were every 45 minutes and looking at the previous 4.
  • TWAP oracle are vulnerable when their price is being updated. By sandwiching price updates, it's possible to get a position briefly for some amount of money, see the update occur, then make money from the update even though very little funds were provided.
  • The attacker manipulated the price by performing a sandwich attack on its source - ETH-unshETH pool - over the 3 updates. By sandwiching the update process, the attacker was able to get all of their money back but was still able to manipulate the price.
  • This was a risky tactic. At any point, the oracle protocol could have been arbitraged to steal funds but was not. Once the TWAP had the inflated price, the attacker opened multiple large leveraged positions.
  • To eventually make money, they borrowed much more funds than they should have been able to. Since the oracle price was manipulated, they were also able to arbitrage it by swapping with the same pool as they manipulated. This led to a massive price difference from the amount that was swapped by the Rodeo Finance Protocol.
  • According the Quill Audits, there is another aspect to this though. The strategy address was unconfigured, which allowed for bypassing the sanity check on the values being used.
  • What was wrong then? The oracle used the reserve ratio to determine the price. Additionally, multiple oracles should be used to prevent attacks like this. Or, have limits on how much the price can change over a given interval.

One more problem with ERC777- 1203

Daniil Ogurtsov - MixBytes    Reference →Posted 2 Years Ago
  • ERC777 is a well known token standard for Non-Fungible Token (NFT). There are hooks defined on these NFTs in order to allow for users to perform operations on either receiving or sending NFTs.
  • How are these hooks implemented though? Instead of checking the sender/receiver of the check, it looks into the ERC1820 registry contract. This is done by taking a hash of the interface then checking if the sender/receiver has added a hook for this. Once this is set, it will execute the hook at the address provided.
  • Only the owner of an address can specify these hooks. This is where the novel technique comes into play... what if you have arbitrary call to an address within the context of the contract? An attacker could set the hook for the contract!
  • This can be used as an unexpected reentrancy hook. This becomes particular troublesome with swap paths being calculated before the transaction assuming that the hook will not modify the state. Additionally, an attacker could force all transactions within the context of the contract to fail.
  • Arbitrary calls within a contract are typically very bad anyway. This registry setting is just another way to make use of it.

StackRot Linux Privilege Escalation- 1202

Ruihan Li    Reference →Posted 2 Years Ago
  • When the mmap() system call is made, the kernel generates a structure to represent this allocated memory in the Virtual Memory Area (VMA). The structure vm_area_struct contains various items for flags and properties for the memory section.
  • VMAs were previously managed by red-black trees. Now, they use Maple Trees (a B-tree data type for storing non-overlapping ranges). The reason for this change is that the Maple Trees are RCU safe.
  • Read Copy Update (RCU) is a pattern to make reads memory safe even if they are being copied or updated. This allows for read to occur concurrently to writes. Within a maple tree, a maple node either represents a VMA or a gap; there shouldn't be a gap between two intervals.
  • When during concurrent modification, there are restrictions put in place; an exclusive lock is required by all writers. For reading, the MM read-write lock can be taken, but results in slower times. Instead, a read can be performed on the section without taking the lock, which is done in performance critical situations.
  • On a stack expansion, the memory automatically grows. This requires editing the VMA maple tree to add this change. In some situations, the operation can be performed atomically, meaning that it's not a problem. But, when the neighboring node has the MAP_GROWSDOWN flag, this operation CANNOT be performed atomically. When the gap needs to be removed (aka removal of the node), a new node must be created instead of simply altering the old one. This results in the old node being destroyed in a RCU callback.
  • The RCU callback is invoked only after all pre-existing RCU critical sections have concluded. When accessing VMAs with the MM read lock held, it does not enter the critical RCU section. Why is this bad? The callback can be invoked at any time, resulting in a use after free when attempting to access it once again.
  • According to the author, this was exploited in the locked down KCTF environment. This means it's exploitable on nearly all systems. Additionally, this claims to be the first use after free by RCU bug that's happened. Super interesting post and an extremely deep bug.

GHSL-2023-139: Use After Free (UAF) in accountsservice - CVE-2023-3297- 1201

Kevin Backhouse - Github Security Labs    Reference →Posted 2 Years Ago
  • Ubuntu accountsservice is a package that allows for the querying and manipulating of user account information. This is done via D-Bus interfaces, which is an IPC mechanism used by Linux desktops.
  • After receiving a D-Bus method call, the D-Bus server normally sends back either a METHOD_RETURN for a proper call or an ERROR message to the client. Since the D-Bus API is expected to follow the standard for the libraries that utilize it, it is super important that this is correct. This is not a return value though; it appears to be set in some internal structure.
  • What happens if the specification isn't followed? Madness! In the case of user_change_language_authorized_cb, both the error handler and the proper handler were being called. The error handler was called if the $HOME is not mounted and the main path always runs.
  • Since both of the calls were decrementing the reference counter, this creates the scenario for a use after free. By forcing the $HOME check to fail by deleting the home directory and decrementing the reference counter, another call could access this to cause memory corruption shenanigans.
  • The command below would cause this to crash:
    dbus-send --system --print-reply \ 
    --dest=org.freedesktop.Accounts \
    /org/freedesktop/Accounts/User`id \
    -u` org.freedesktop.Accounts.User.SetLanguage string:'**'
    
  • On 23.04 this causes a SIGSEV crash but 22.04 doesn't crash. According to the author, this is difference in the memory allocator for Glib. Additionally, exploitation is said to be difficult to not impossible.

LibreOffice Arbitrary File Write (CVE-2023-1883) - 1200

Greg - Secfault Security     Reference →Posted 2 Years Ago
  • Libre Office is an open source Microsoft office alternative. Libre Office has a word processor, spreadsheet, presentation tool and a desktop database. The author decided to take a look at the database part.
  • The saved file was a .odb output was simply a zip archive with various folders and files. With looking through the files, the author noticed the database/script file with SQL statements inside of it. Arbitrary SQL queries could potential lead to file writes and code execution!
  • The author found the SCRIPT statement, which allows us to write to an arbitrary file. However, the file cannot exist already in order to write it. Additionally, the content is somewhat controlled but not fully controlled.
  • How do we exploit this then? The author was reading their ~/.bashrc file when they noticed that both ~/.bash_aliases and ~/.dircolor did not exist. So, these were files that were being executed from the ~/.bashrc and we could write to them!
  • What can we write to the file though? After playing around with different database settings they realized that CREATE SEQUENCE "PAYLOAD HERE" would output the content to the file we choose. Then, when a user logs in, it would eventually get executed.
  • The author doesn't say the fix but says the bug was fixed. If I was the developer, killing the SCRIPT command in this context would make sense. Overall, a quick and easy issue!

How does the NEW Ethereum work?- 1199

Preethi Kasireddy    Reference →Posted 2 Years Ago
  • The old Ethereum was proof of work. Now, the system is proof of stake, where stake is an amount of money they are putting as collateral if they act maliciously. Anybody can stake 32 ETH to become a staker to select blocks, earn rewards and so on. The staking makes sybil (multi-voter) attacks impossible as well. This article is about the nitty-gritty details of how this works - Gasper.
  • Once a user stakes the 32 ETH to become a validator, they are put into a waiting queue. This is done in order to prevent network congestion on voting and preventing an attacker with sufficient funds from instantaneously making a big impact.
  • Once a user is in the network and voting, Gasper has slots of 12 seconds and epochs that last 32 slots (6.4 minutes). For every slot, one validator is selected at random to be the block proposer. The proposer constructs the block from the pending transactions in the mempool. The purpose of the epoche is that a validator does not vote on every block. Instead, a committee is made where a validator votes on one block per epoche.
  • What are validators even validating?
    • They validate checkpoint blocks (start of epoche) within previous epoches and the current ones.
    • The list of validators on the voting committee.
    • The data of the block. In particular, the slot, index, root hash of the block and some other data.
    • Signature.
  • On top of the coordination above, there is other fancy networking occurring to reduce network overhead further. This is about dividing the network into regional subnets which will then communicate with the main network. This is possible by crazy math that combines signatures then broadcasts this to the rest of the network.
  • When does a block become final? In Bitcoin, this is once 6 blocks have passed. With Ethereum, it is once another block has been verified (justified). A block becomes justified once 2/3 of staked Ether votes in favor of a block. What happens when a fork occurs? The network will always choose the fork with the most backed votes. Theoretically, independent users could fork the chain and bring it back later, only to confuse the users though.
  • To confirm that the system works as intended, there needs to be incentives and rewards/punishments. A user has two balances: effective and actual balance. The actual balance is the amount deposited for staking, plus rewards and minus value from penalties. The effective balance is derived from the actual balance in some way with a cap of 32 ETH and is what is used for picking validators.
  • The rewards are given based upon the actions performed. Attesting a block, proposing a new block and participating in a sync committee. The article has numbers for who gets what. However it's proportional to the amount of funds that a user has in the protocol.
  • For penalties, it depends on what went wrong. For missing an attestation, they lose the amount they would have been rewarded. There are several other benign issues as well. If a validator engages in malicious behavior, then they are slashed; forcing them to leave the network or a large monetary penalty. One of the validators must be a whistleblower in order to do this and receives a reward for doing so.
  • The penalties depend on how active the network is. For instance, if a single user does perform an action, then the penalty is small. If NONE of the users perform the assigned actions, then the penalty gets larger. The idea is that the inactive users on a stopped blockchain (from inactivity) will be removed fast so that others can validate properly.
  • Overall, an awesome post on the great merge to Proof of Stake. Many great details showcasing the innerworkings of the blockchain eco-system.

Silo Finance Logic Error Bugfix Review- 1198

Immunefi    Reference →Posted 2 Years Ago
  • Silo Finance creates isolated lending markets. This is done by having every token asset in its own lending market. Additionally, it is paired against the bridge assets ETH and XAI (Silo's over-collateralized stablecoin). By doing this, lenders are only exposed to the risk of ETH and XAI at any point as a result.
  • The Base Silo contract handles the core logic of the protocol. Users call deposit() to add collateral to the protocol so that they can borrow. In return, the contract mints a pro-rata share that is stored in _assetStorage[_asset].
  • Users who have deposited collateral can call borrow() to temporary gain access to the new asset. On this call, the accrued interest rate is updated and the loan-to-value (LTV) ratio is checked. In loan based protocols, the interest rate is calculated based upon the utilization of the asset.
  • By making the contract believe it has borrowed more than 100%, the math goes crazy. How is this possible though? Donations! By adding more value to the contract than what is being tracked, the utilization trade goes through the roof. Using this, the value of the loan is much higher than it should be, allowing for us to take almost all of the funds.
  • Practically, these are the steps to exploit this assuming that the market has 0 total deposits for one of the assets in a market:
    1. Become the main shareholder by depositing a little amount of the asset, such as 10^5 wei of WETH.
    2. Donate additional WETH to the market. To get even higher utilization, the bulk of the funds should be here.
    3. Use another account to deposit a seperate asset into the protocol.
    4. When accrueInterest() is called, the utilization rate of the deposit is over 100%, creating an insane interest rate.
    5. Our initial deposit (collateral) is now valued more than is should be. As a result, all of the funds can be borrowed from the protocol to steal it.
  • To fix this problem, there is now a cap on the utilization rate. However, I find it odd that the donation attack was possible at all. To me, it makes more sense to have the liquidity and borrowed funds amount match what's actually available. Weird report!