Skip to content

Latest commit

ย 

History

History
462 lines (272 loc) ยท 15.1 KB

mystiz.md

File metadata and controls

462 lines (272 loc) ยท 15.1 KB
timezone
Asia/Taipei

Mystiz

  1. ่‡ชๆˆ‘ไป‹็ดน

Software Engineer @google. CTF since 2017. Blog here.

  1. ไฝ ่ช็‚บไฝ ๆœƒๅฎŒๆˆๆœฌๆฌกๆฎ˜้…ทๅญธ็ฟ’ๅ—Ž๏ผŸ

Can't say yes, but I hope this could get me motivated.

Notes

2024.08.28

  1. ่จญๅฎš Foundry ็’ฐๅขƒใ€‚https://book.getfoundry.sh/getting-started/installation
  2. ๆŠŠ Damn Vulnerable DeFiใ€EthTaipei CTF 2023 ่ทŸ MetaTrust CTF 2023 ็š„้กŒ็›ฎๅ…ˆๆ‰“ๅŒ…ไธ‹ไพ†ๅ‚™็”จใ€‚
    • ๆŠŠ EthTaipei CTF 2023 ็š„็ญ”ๆกˆๅˆชๆŽ‰ใ€‚
    • MetaTrust CTF 2023 repo ่ฆ่ฝ‰ๆ›ๆˆ Foundry ็š„ๆจกๅผ๏ผŒๅˆฐ่งฃ้กŒ็š„ๆ™‚ๅ€™ๅ†ๆŠŠๅฎƒๅ€‘่ฝ‰ๆ›้ŽๅŽปใ€‚

็›ฎๆจ™๏ผšๅฎŒๆˆ Damn Vulnerable DeFi + EthTaipei CTF 2023 ๅŠ MetaTrust CTF 2023ใ€‚

Note

2024.09.07๏ผšๆˆ‘่ฆบๅพ—่ƒฝไธ็œ‹้กŒ่งฃๅšๅฎŒ Damn Vulnerable DeFi ๅทฒ็ถ“ๅพˆ่ถณๅค ไบ†...

2024.08.29

Progress

  • Damn Vulnerable DeFi (2/18)
  • EthTaipei CTF 2023 (0/5)
  • MetaTrust CTF 2023 (0/22)

๐Ÿ“š Reading: EIP for Flash Loans

Reference: https://eips.ethereum.org/EIPS/eip-3156

A flash loan is a smart contract transaction in which a lender smart contract lends assets to a borrower smart contract with the condition that the assets are returned, plus an optional fee, before the end of the transaction.

๐Ÿ”จ Foundry debugging

pragma solidity =0.8.25;

import {IERC3156FlashBorrower} from "@openzeppelin/contracts/interfaces/IERC3156.sol";

contract ExploitContract is IERC3156FlashBorrower {
    function onFlashLoan(
        address initiator,
        address token,
        uint256 amount,
        uint256 fee,
        bytes calldata data
    ) external override returns (bytes32) {
        // Take the money from receiver
        

        return keccak256("IERC3156FlashBorrower.onFlashLoan");
    }
}
# We can use -vvvv to make the log super-verbose
forge test --match-test test_unstoppable -vvvv

This is what it looks when vault.flashLoan(exploit, address(token), 1e18, bytes("00")); is called, when my exploit contract is given above.

๐Ÿ Damn Vulnerable DeFi: Unstoppable

Time used: ~1h 55m

The goal of the challenge is to pause the vault. One way to trigger is to make the vault.flashLoan raise an exception -- thus the vault will be stopped when isSolved is called.

try vault.flashLoan(this, asset, amount, bytes("")) { /* omitted */ } catch { /* pauses the vault here! */ }
// called in src/unstoppable/UnstoppableMonitor.sol:checkFlashLoan(100e18) (line 41)
// called in test/unstoppable/Unstoppable.t.sol:_isSolved() (line 106)

To make this happen, we can make convertToShares(totalSupply) != balanceBefore. In that's the case, the transaction will be reverted -- and thus raising an exception. We can simply transfer 1 DVT to the vault to halt the contract.

๐Ÿ Damn Vulnerable DeFi: Naive Receiver

Time used: ~5h 35m

The goal of the challenge is to drain the WETH from the NaiveReceiverPool and the FlashLoanReceiver contracts (which initially had 1000 WETH and 10 WETH).

Ideas:

  1. We can call pool.flashLoan(receiver, address(weth), 0, bytes("")); and it will take 1 WETH away from FlashLoanReceiver each time.
  2. We can use BasicForwarder to make _msgSender() to be the NaiveReceiverPool. However, it will always append request.from (you need its private key). We can bypass by calling Multicall.multicall from BasicForwarder.execute. In that way, the appended request.from will not be used.

2024.08.30

Progress

  • Damn Vulnerable DeFi (4/18)
  • EthTaipei CTF 2023 (0/5)
  • MetaTrust CTF 2023 (0/22)

๐Ÿ Damn Vulnerable DeFi: Truster

Time used: ~1h 40m

The goal of the challenge is to drain the token from TrusterLenderPool. Additionally flashLoan is protected by the re-entrancy guard.

Function definition for Address.functionCall: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v5.1/contracts/utils/Address.sol#L62-L64

Since token is a ERC20 token, why not use the approve method? This allows our attacking contract to spend money on behalf of the pool, thus we can transfer funds out of the pool afterwards.

๐Ÿ“š Reading: Re-entrancy attack

References:

๐Ÿ Damn Vulnerable DeFi: Side Entrance

Time used: ~45m

We create an ExploitContract to drain the funds from SideEntranceLenderPool. To start with, we call flashLoan to get 1000 ETH. We then deposit the money to the pool. Since the pool's balance is unchanged, it is considered repayed. The only difference is, we have 1000 ETH deposited to the pool.

After that, we can simply withdraw the amount to the recovery wallet.

2024.08.31

Progress

  • Damn Vulnerable DeFi (5/18)
  • EthTaipei CTF 2023 (0/5)
  • MetaTrust CTF 2023 (0/22)

๐Ÿ Damn Vulnerable DeFi: The Rewarder

Time used: ~1h 15m

Important: We are also rewarded. 0x44E97aF4418b7a17AABD8090bEA0A471a366305C appeared on line 755 in both files. We are the 188th entry.

To (almost) drain the reward distributor, we can repeatedly send the same claim in the same transaction. This is because _setClaimed will be called once.

2024.09.01

๐Ÿ“š Reading: ERC20 votes?

Preparation for Damn Vulnerable DeFi: Selfie... Maybe?

2024.09.02

Progress

  • Damn Vulnerable DeFi (6/18)
  • EthTaipei CTF 2023 (0/5)
  • MetaTrust CTF 2023 (0/22)

๐Ÿ Damn Vulnerable DeFi: Compromised

Time used: ~45m

The two lines in README.md corresponds to the first two private keys of the trusted accounts:

0x7d15bba26c523683bfc3dc7cdc5d1b8a2744447597cf4da1705cf6c993063744
0x68bd020ad186b647a691c6a5c0c1529f21ecd09dcc45241402ac60ba377c4159

Therefore, we can control the price by updating the median. Since we have 2 out of 3 trusted accounts compromised, it is easily achievable.

We can follow the procedures to drain the pool:

  1. Update the price of the NFT to 0.1 ETH and buy it.
  2. Update the price of the NFT to 999.1 ETH and sell it. We will gain 999 ETH here.
  3. Update the price of the NFT to 999 ETH, pretending that nothing has happened.

2024.09.03

Progress

  • Damn Vulnerable DeFi (6/18)
  • EthTaipei CTF 2023 (1/5)
  • MetaTrust CTF 2023 (0/22)

๐Ÿ EthTaipei CTF 2023: Arcade ๐Ÿคฏ

Time used: ~35m

If we call arcade.changePlayer(address(0x0));, we can see that the event Transfer is called before PlayerChanged. Thus we are able to steal other's account by transferring the current player to the victim.

From Solidity Underhanded Contest 2022, a submission mentioned that the parameters had a bizarre evaluation order:

  1. the indexed parameters will first be evaluated right-to-left;
  2. the non-indexed parameters will then be evaluated left-to-right.

In our case, newPlayer will be evaluated earlier than oldPlayer in event PlayerChanged(address indexed oldPlayer, address indexed newPlayer);. Hence, the actual behaviour of the changePlayer function being:

  1. sets the current player to newPlayer
  2. redeems the current player's (newPlayer's) score to the sender (us).

Therefore we are able to steal 190 PRIZE. If we call .earn and .redeem before we exploit, we will be able to loot for another 10 PRIZE.

2024.09.04

Progress

  • Damn Vulnerable DeFi (6/18)
  • EthTaipei CTF 2023 (2/5)
  • MetaTrust CTF 2023 (0/22)

๐Ÿ EthTaipei CTF 2023: NFT

Time used: ~1h 15m

Re-entrance attack: During withdraw, the NFT is first transferred from the pool to our address then decreases the _balances[msg.sender]. Also onERC721Received on the receipient's contract will be called.

We can make onERC721Received to transfer (not deposit) the NFT, then withdraw that immediately. This heuristic should be called only once.

In that case, _balances[msg.sender] -= 1 ether will be executed twice. For Solidity < 0.8, SafeMath is required to prevent integer overflows -- and it isn't used. Therefore, we eventually have _balances[msg.sender] == uint256(-1 ether).

2024.09.06

๐Ÿณ๏ธ Damn Vulnerable DeFi: Climber

Time used: 5h 30m and ongoing...

๐Ÿ“š Reading: ERC1967 - Universal Upgradeable Proxy Standard (UUPS)

https://hackmd.io/@KryptoCampDev/Web3-Proxy-Contract?utm_source=preview-mode&utm_medium=rec#ERC1967

2024.09.07

Progress

  • Damn Vulnerable DeFi (7/18)
  • EthTaipei CTF 2023 (2/5)
  • MetaTrust CTF 2023 (0/22)

๐Ÿ Damn Vulnerable DeFi: Climber

Time used: ~6h 20m

The vulnerability comes from ClimberTimelock.execute. The "ready for execution" check comes after the user-provided functions are called.

The goal is to find a way to make getOperationState(id) to be OperationState.ReadyForExecution. Otherwise, the entire call will be reverted and the exploit will be useless.

I thought of something like making keccak256(abi.encode(...))returning the same values of two items, but that would require either (1) hash collision, or (2) ambiguity fromabi.encode`. Of course that wouldn't be (1), and (2) is not doable according to https://ethereum.stackexchange.com/questions/113188/can-abi-encode-receive-different-values-and-return-the-same-result.

Eventually, I created a contract (took me so long to figure out) that enrolls the proper parameters to ClimberTimelock.schedule. Also, we would need to make the exploit contract an admin/proposer; and to update the delay to zero for immediate action.

With the exploit contract promoted to an admin, we can upgrade ClimberVault and inject a function to drain the tokens in the vault.

2024.09.08

Progress

  • Damn Vulnerable DeFi (8/18)
  • EthTaipei CTF 2023 (2/5)
  • MetaTrust CTF 2023 (0/22)
  • OnlyPwner.xyz (7/16)

๐Ÿ Damn Vulnerable DeFi: Shards

Time used: ~45m

A vulnerability here being, when one fills a offer, the price is rounded down. On the other hand, when an offer is cancelled, the price is rounded up.

Additionally, it is possible to fill the first offer with 133 shards for free. Cancelling the offer, one could steal 9.975e-6 DVT. We can buy more shards and generate more benefits afterwards.

๐Ÿ OnlyPwner.xyz

Warning

No public writeups allowed, but I finished Freebie (~1h 40m), Tutorial (~5m), Reverse Rugpull (~15m), Under the Flow (~35m) and Please Sign Here (~15m), All or Nothing (~1h 05m), Multisig (~2h 15m).

2024.09.09

Progress

  • Damn Vulnerable DeFi (8/18)
  • EthTaipei CTF 2023 (2/5)
  • MetaTrust CTF 2023 (0/22)
  • OnlyPwner.xyz (9/16)

๐Ÿ OnlyPwner.xyz

Warning

Finished Proof of Work (~1h 10m), Payday (~1h 10m).

2024.09.10

๐Ÿณ๏ธ OnlyPwner.xyz: Liquid Staking

Time used: 2h 20m and ongoing...

2024.09.11

๐Ÿ“š Reading: Ethereum's Yellow Paper

๐Ÿณ๏ธ OnlyPwner.xyz: Wrapped Ether

Time used: 55m and ongoing...

2024.09.12

๐Ÿ“š Reading: Hacks in Solidity by Example

๐Ÿณ๏ธ OnlyPwner.xyz: 13th Airdrop

Time used: 1h25m and ongoing...

2024.09.13

Progress

  • Damn Vulnerable DeFi (8/18)
  • EthTaipei CTF 2023 (2/5)
  • MetaTrust CTF 2023 (0/22)
  • OnlyPwner.xyz (10/16)

๐ŸŽฎ Playing: Foundry's debugger ๐Ÿคฉ

forge debug --debug src/bridge-takeover/Bridge.sol --sig "voteForNewRoot(bytes)" "0x"

๐Ÿ OnlyPwner.xyz

Warning

Finished Bridge Takeover (~2h 15m)... Finally a solve after few days.

2024.09.14

Progress

  • Damn Vulnerable DeFi (8/18)
  • EthTaipei CTF 2023 (2/5)
  • MetaTrust CTF 2023 (0/22)
  • OnlyPwner.xyz (11/16)

๐Ÿ OnlyPwner.xyz

Warning

Finished 13th Airdrop (~2h 15m).

2024.09.15

๐Ÿณ๏ธ OnlyPwner.xyz: Liquid Staking

Time used: 4h 30m and ongoing...

2024.09.16

Progress

  • Damn Vulnerable DeFi (8/18)
  • EthTaipei CTF 2023 (2/5)
  • MetaTrust CTF 2023 (0/22)
  • OnlyPwner.xyz (12/16)

๐Ÿ OnlyPwner.xyz

Warning

Finished Wrapped Ether (~2h 20m).

I was stuck for 2 hours because of a weird Solidity behaviour on public vs external. I still couldn't figure out why...

2024.09.17

๐Ÿณ๏ธ OnlyPwner.xyz: Diversion

Time used: 6h 30m and ongoing...

2024.09.18

Progress

  • Damn Vulnerable DeFi (9/18)
  • EthTaipei CTF 2023 (2/5)
  • MetaTrust CTF 2023 (0/22)
  • OnlyPwner.xyz (12/16)

๐Ÿ“š Reading: Uniswap stuffs

๐Ÿ Damn Vulnerable DeFi: Puppet

Time used: ~1h 10m

The goal is to drain the 100,000 DVTs in the lending pool. The deposit to borrow depends on the balance of ETH & DVT in the Uniswap exchange (namely, $\text{ETH}{ex}$ and $\text{DVT}{ex}$). The below expression is the price (in ETH) to borrow 1 DVT:

$$2 \times \frac{\text{ETH}{ex}}{\text{DVT}{ex}}.$$

Additionally, we can call uniswapV1Exchange.tokenToEthSwapOutput to buy some ETH. Since $x \cdot y = k$, we can make DVT less worthy by selling it. In particular, if we want to buy 9.9 ETH, we would need to pay ~993 DVT. This is because we would need to send 990 DVT to make $10 \cdot 10 = k = (10 - 9.9) \cdot (10 + 990)$, and we need to pay an additional ~0.3% fee for using Uniswap.

In that case, we will make the price (in ETH) to borrow 1 DVT to be $2 \times 0.1 / 1000 = 0.0002$. Thus we can buy out the 100,000 DVT using 20 ETH.

2024.09.19

Progress

  • Damn Vulnerable DeFi (11/18)
  • EthTaipei CTF 2023 (2/5)
  • MetaTrust CTF 2023 (0/22)
  • OnlyPwner.xyz (12/16)

๐Ÿ Damn Vulnerable DeFi: Puppet v2

Time used: ~35m

Puppet v2 is similar to the last challenge, except that

  • it is using Uniswap v2, and
  • the numbers (e.g. the player balance, the price, etc) are different.

This time, even if we swap all of our 10000 DVTs to ETH, we are unable to "borrow" all of the DVTs using the swapped ETH.

We can instead do the following:

  1. swap 10000 DVT to ETHs,
  2. borrow 100000 DVTs,
  3. swap 100000 DVTs to ETHs,
  4. borrow 900000 DVTs,
  5. swap 100000 DVTs from ETHs.

๐Ÿ Damn Vulnerable DeFi: Free Rider

Time used: ~1h 10m

There is an vulnerability that we can buy the NFT even without the token. This is because that in _buyOne, only msg.value >= priceToPay is required. Thus we are able to pay 15 ETH to buyMany to buy all of the NFTs.

However, we only have 0.1 ETH. We can use the "flash loan" provided by Uniswap v2. There is a (WETH, DVT) pair that has sufficient liquidity.

2024.09.21

Solved Doju, 8Inch and Tonyallet in BlazCTF 2024.

2024.09.22

Solved TonyLend and Cyber Cartel in BlazCTF 2024.