Skip to content

Commit

Permalink
Update 2024-10-14-TCP1P-CTF-2024-Blockchain-Writeup.md
Browse files Browse the repository at this point in the history
  • Loading branch information
bshyuunn authored Dec 29, 2024
1 parent d0a49f6 commit 72c161a
Showing 1 changed file with 13 additions and 1 deletion.
14 changes: 13 additions & 1 deletion _posts/2024-10-14-TCP1P-CTF-2024-Blockchain-Writeup.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ categories:
author: Songhyun Bae
---

On October 14, 2024, I participated in the TCP1P CTF 2024 as part of the CyKor team and successfully solved 4 out of 6 blockchain challenges. You can find all the solution code at [this repo](https://github.com/bshyuunn/TCP1P-CTF-2024-Blockchain-Writeup?tab=readme-ov-file).
On October 14, 2024, I participated in the TCP1P CTF 2024 with CyKor team and successfully solved 4 out of 6 blockchain challenges.

## **01. Baby ERC-20**

Expand Down Expand Up @@ -43,6 +43,7 @@ contract Setup {
}
}
```
<br>

An interesting point here is that the Solidity version being used is `0.6.12`, which is quite old. This version does not include built-in protections against overflow and underflow vulnerabilities. As expected, there is an underflow vulnerability in the `HCOIN::transfer()` function.

Expand All @@ -56,6 +57,8 @@ function transfer(address _to, uint256 _value) public returns (bool success) {
return true;
}
```
<br>

The transfer function checks if the sender has enough funds using the require statement. However, the line `balanceOf[msg.sender] - _value` can cause an underflow if `_value` is larger than `balanceOf[msg.sender]`. In such a case, the underflow allows the require statement to pass. The subsequent operation `balanceOf[msg.sender] -= _value` causes the sender’s balance to underflow, increasing their balance due to the underflow bug.

### Solution Script
Expand Down Expand Up @@ -177,6 +180,7 @@ contract Challenger2 {
}
}
```
<br>

In the `ChallengeManager::challengeCurrentOwner` function, the `challengeManager` value of the `Privileged` contract can be changed. However, you need to know the `masterKey` and obtain the `theChallenger` role to do so.

Expand All @@ -194,6 +198,7 @@ function challengeCurrentOwner(bytes32 _key) public onlyChosenChallenger{
}
}
```
<br>

The `masterKey` is a private variable, but since it is stored in a storage slot, its value can be read by directly accessing the storage. The `masterKey` variable is found to be located in Slot 1.

Expand All @@ -210,13 +215,15 @@ $ forge inspect ChallengeManager storage-layout --pretty
| challenger | address[] | 5 | 0 | 32 | src/ChallengeManager.sol:ChallengeManager |
| approached | mapping(address => bool) | 6 | 0 | 32 | src/ChallengeManager.sol:ChallengeManager |
```
<br>

You can retrieve the `masterKey` value using the following script.

```solidity
$ cast storage <ChallengeManager Address> 0 --rpc-url <Rpc Url>
0x494e4a55494e4a55494e4a5553555045524b45594b45594b45594b45594b4559
```
<br>

To obtain the `theChallenger` role, you need to use the `upgradeChallengerAttribute` function. If you input the same `challengerId` for both `challengerId` and `strangerId`, where the Player is the `challenger`, and the gacha value is 0 or 1 four times consecutively, the `theChallenger` will be updated to the Player’s address.

Expand Down Expand Up @@ -269,6 +276,7 @@ function upgradeChallengerAttribute(uint256 challengerId, uint256 strangerId) pu
}
}
```
<br>

Here, the `gacha` value is determined by `uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp))) % 4`. Since the `block.timestamp` is a predictable value, it can be exploited to manipulate the outcome.

Expand Down Expand Up @@ -447,6 +455,7 @@ contract Crain{
}
```
<br>

To call this function from the `CrainExecutive` contract, the `transfer` function must be used, and the `_message` argument can be utilized to perform a low-level function call. However, to invoke the `transfer` function, the `isExecutive` privilege must be obtained.

Expand All @@ -466,6 +475,7 @@ function transfer(address to, uint256 _amount, bytes memory _message) public _on
require(transfered, "Failed to Transfer Credit!");
}
```
<br>

To obtain the `isExecutive` privilege, you must first sequentially acquire the `isEmployee` and `isManager` privileges. Additionally, during this process, the `buyCredit` function must be used to increase the Player’s `balance`.

Expand Down Expand Up @@ -589,6 +599,7 @@ contract Setup {
}
}
```
<br>

In the `Money` Contract, funds can be deposited using the `save` function and withdrawn using the `load` function.

Expand All @@ -610,6 +621,7 @@ function load(uint256 userProvidedCaptcha) public {
balances[msg.sender] = 0;
}
```
<br>

The load function contains a reentrancy attack vulnerability because it updates `balances[msg.sender]` after transferring the funds.

Expand Down

0 comments on commit 72c161a

Please sign in to comment.