SQUID Token 'Rug Pull' Analysis (Entire Crypto Space Needs to Pay Attention)

Squid Game Token Nov 09, 2021

In the month of October, a project called, 'SQUID Token' launched on the 'Binance Smart Chain' ecosystem through a Decentralized Exchange (DEX) platform called, 'Pancake Swap'.

The project was launched with the intentions of defrauding any and all users that chose to invest in it via sending $BNB to the smart contract address (to receive 'SQUID token', in return). Unfortunately, all those that chose to pay for these tokens, fell prey to the project's insidious 'rug pull' scheme.

What is a 'Rug Pull'?

A "rug pull" is a term unique to the blockchain space. Specifically, it refers to smart contracts purposefully coded with 'backdoors' in the form of hidden functions or poorly understood constructs in the code that the developers of these smart contracts later leverage to drain the project's investment pool of all of its funds.

The idea of someone publishing software designed with a purposeful 'backdoor' is far from new. Typically, said code fails to be detected by the software's victims or other cybersecurity researchers due to the fact its mainly embedded in 'closed-source' projects.

What makes blockchain (specifically, the world of smart contracts), a really unique and intriguing instance of successful malicious programming is the fact that all impacted smart contracts that could or will be leveraged to 'rug pull' have their source code in the public domain.

Reason for Independent Post-Mortem Analysis: Correcting a False Narrative

Almost immediately after news of the SQUID token exploit, there was a flurry of news outlets, influencers, cyber security researchers, and blockchain industry leaders that concluded (with virtual unanimity), that end users (i.e., those that were the victims of said "rug pull"), must take care to be more diligent in the future.

'The Wired' was the Most Egregious Example of Inaccurate, False Reporting

In an article titled, "How a Squid Game Crypto Scam Got Away", Wired published several dense blocks of misinformation related to the project and how the 'rug pull' itself occurred.

The first few paragraphs of the article are devoted to covering the token's price action on the markets shortly after launch. We're not going to address that here since it holds no relevance to the actual 'rug pull'.

https://www.wired.com/story/squid-game-coin-crypto-scam/

Trusting Prestige Over Fact

About halfway through the article, an individual named, 'Patrick McCorry', introduced as the 'CEO of Pisa Research' and 'formerly an assistant professor in cryptocurrencies and security engineering at King's College London' is cited for his opinion on the matter.

The narrative building begins with the following unqualified statement by 'McCorry' (quoted directly in the piece): "Anyone can spin up a token and liquidity pool, so it is a common risk for new projects run by anons."

Saying, "Anyone can spin up a token and liquidity pool", is a gross overstatement. In theory, yes, 'anyone' could 'spin up' such a project, but the knowledge needed to do so successfully (regardless of intent), is actually considerable.

The project founders being "anons" is irrelevant in this situation. Since we've already established that this was a 'rug pull', the means behind the exploit should be observable in the smart contract code itself. Code is objective. Assessments of identity are subjective.

A bit further into the article, another unqualified statement by 'McCorry' is made.

Specifically, he states:

"Rug pulls happen when there are large holders of the coin who can freely trade it, and the market for that token is not deep or highly liquid."
  • This is wrong on several different fronts. The first error can be found in the over-generalized definition of a 'rug pull' in cryptocurrency. While there have been many 'rug pulls' this year (specifically), that have involved effectively "gaming" the smart contract by creating an imbalance in the distribution of 'pooled' assets (typically pools that are designed to 'rebalance' the portfolio of pooled assets [like the Yearn stablecoin pool], this is far from the only means of 'rug pulling' a smart contract.
  • Assuming 'McCorry' was correct in his assessment that the smart contract's handling of the "liquidity pool" allowed its developers to 'rug pull' everyone else, his assessment of how such an attack would be leveraged against a smart contract is  still not correct. Specifically, Wired quotes him as saying that, "Rug pulls happen when there are large holders of who can freely trade it." Let's go back to the 'Yearn' example alluded to in the previous bullet point. The stablecoin pool created by 'Yearn Finance', held a baset of three different stablecoins. They were USDT, USDC, and DAI (the '4' pool option included 'TUSD', originally). Not one of those stablecoins was or is native to Yearn. This fact did not stop the stablecoin pool from being exploited (more than once). One of the project's native assets was used in the exploits, but its use was not contingent on the attacker being a 'large holder' of the "coin" [token]. Rather, the native asset was minted ('y'-based derivative; 'yCrv'), as a payout from the liquidity pool flood (via the deposit of 'flash loan' assets). Once the 'yCrv' token was successfully extracted, it was then used within the 'execution stack trace' of that same transaction to cause a greater unbalance in the pool, effectively allowing the attacker to drain the Vault.
  • 'McCorry' is describing an attack vector that is typically restricted to projects that are 'AMMs' (i.e., 'Uniswap', 'SushiSwap', 'PancakeSwap', etc.); 'SQUID' was not an AMM. If McCorry is a credible expert in the field of cryptocurrency, specifically the security of protocols and/or smart contracts, then this fact should've been obvious to him coming out of the gate. Unfortunately, it appears that it wasn't, which should call into question whether this individual even possesses a working knowledge of smart contracts.

Wired Completely Whiffs From Here

With enough confidence to declare, "The way the Squid Games coin scam worked is simple", the author proceeds to **narrate a 100% work of fiction about how users were deprived of their funds by the 'SQUID' token.

See below:

"The way the Squid Games coin scam worked is simple, as far as crypto goes. It takes advantage of the liquidity pool that exists between the Squid tokens issued and commensurate tokens (BNB tokens) issued by Binance, the cryptocurrency exchange. The team behind the Squid Games coin issued their tokens and held the majority of the supply. That allowed them to transfer the value of the Squid Games coins into BNB tokens, which they then stole away."

To begin with, this scam didn't utilize the 'liquidity pool' mechanism at all. In fact, they locked up their liquidity.

None of that happened.

It didn't take advantage of the, "Liquidity pool that exists between the Squid tokens issued and the commensurate tokens". The mechanism that was used to keep investors 'at bay' was the introduction of a project called, 'Marble' (which we'll get into shortly)

The idea that the, "Team behind the Squid Games coin issued their tokens and held the majority of the supply", is unequivocally false as well. At the time the smart contract launched, there was actually no owner of any of the tokens. Users reporting that they were unable to sell the tokens that they allegedly had received made it patently obvious that whatever they received was of no value at the time that they received it. The smart contract was gamed to ensure that only one entity could extract value from the smart contract, period. The 'team' never needed to take "ownership". Looking at this situation from that point of view reflects a gross misunderstanding of every facet of the 'rug pull' that occurred.

How the Hell was 'Wired' this Incorrect?

Because they have no clue what the fuck they're talking about. That's part of the reason for why this article is being written.

Too much time is being spent oggling at the idea behind fraud / theft in the blockchain space vs. getting to the bottom of what's going on.

The author of this Wired piece simply took the word of "Patrick McCorry", without even attempting to validate the information with another knowledgeable source (or even asking this individual to provide evidence to back the claims that they've made).

In a final display of egregious narcissism, condescension and arrogance, the author concludes the article by writing, "The Squid Game scam almost certainly won't be the last, says McCorry - especially if people buy in without doing their due diligence."

Dissecting the 'SQUID' Token Smart Contract

I thought this was going to be brief to be entirely honest with you (I was definitely fucking wrong).

  1. Start out by fixing a 'telegram search engine' ; find a link on Google, open it up within a container (the browser itself is sandboxed within a /home partition that runs in-memory anyway - so if I fucked up and went to a "virus" site, its of no consequence)
  2. Landed on one called gramxly.com ; turns out that this one was fruitful for me (to some extent).
  3. Once there, I poke around the site with a few sample search terms. Lucky me, turns out that they work. From there, I throw in 'SQUID Token' (or some other variant) & search back a few pages to see what I get.
  4. That's when I wind up finding this channel: t.me/ChartExNewDexListingBot .

From an objective glance, the channel's sole purpose seems to be informing its viewers of new 'BSC' releases.

Impossible to Find the Correct Contract

Its interesting to note that there are so many different iterations of the 'SQUID Token' token contract that were out there, but this information overload is just 'noise' to us at this point.

Sleuthing Online Slightly

At this point in time, I was still naive enough to believe that maybe one of these niche, cybersecurity researcher blogs had conducted a 'deep dive' of the 'SQUID Token' smart contract.

After a cursory search, I saw the outlet, 'ThreatPost' had covered the theft. Great - let's see what they have to say.

Disappointing - But Still Helpful (Inadvertently)

Here's the link to the article in question I'm referring to:

Squid Game Crypto Scammers Rip Off Investors for Millions
Anti-dumping code kept investors from selling SQUID while fraudsters cashed out.

Although not as egregious (by any means) as the 'Wired' piece we took a look at prior, this article was still 100% devoid of any critical insight (no smart contract analysis, deeper digs/takes, or insight revelations...anywhere, at all).

See for yourselves:

Twitter Was Our Savior

threatpost piece was ZERO help

Many outlets did reference one specific Twitter user (citing him as the one that brought the nefarious nature of the smart contract to everyone's attention).

That tweet is re-published below (for convenience):

The picture the user uploaded in the first tweet of their thead is of particular importance for us here.

For some reason, this user failed to transcribe the actual TX ID (which is clearly shown in the picture).

Fortunately, I'm always down for a little old fashioned pen to paper. After a minute or so, I was able to confirm the transaction ID in the picture of the transaction depicted in this user's tweet (above).

That TX ID = 0x68e3be78a982c7f7acd2d9a374a6200df228445c32c835c46dd0651c580f6da7

BSC Scan Investigation

Throwing that raw TX ID in Google pulls up the bscscan link specifically:

Squid Game (SQUID) Token Tracker | BscScan
Squid Game (SQUID) Token Tracker on BscScan shows the price of the Token $0.00, total supply 800,000,000, number of holders 118,667 and updated information of the token. The token tracker page also shows the analytics and historical data.
curiously, it pulled up an address-scoped view of the token holdings; wasn't sure the significance of the '0x71d' address would be / was at the time either

The addresses in the picture above are boxed (red) for user edification (we're not going to investigate them right now).

Before moving on, we'll quickly see what transaction funded (i.e., 'created'), this address.

https://bscscan.com/tx/0xf7c9d0e5a81999f9e06fe78df7ce41da112d8bd4f2da7b16cfdbbe46c92cb6af*

We're going to keep this TX in mind - but for right now, let's go take a look at the raw smart contract code underpinning the 'SQUID Game Token'.

The 'main' smart contract address = 0x87230146e138d3f296a9a77e497a2a83012e9bc5 (note that we find out soon that this is a proxy address)

After you arrive at the address' transaction history page (same interface as 'etherscan'; bscscan is an exact duplicate)

Sifting for Duplicates First

Let's see if we can find any duplicate smart contracts out there lying on the blockchain (it is productive).

Before we do that though, let's take a note of the time bscscan claims that this contract was "Submitted for verification".

Why? -  In my experience (from prior on-chain investigations on $ETH), whenever there's a significant hack/theft involving a smart contract, there were typically some 'practice dummies'.

How to Check - Process is super simple (below is step #1):

After requesting bscscan to provide us with the same or similar matches,  the explorer did provide some identical (exact) matches, which is notable.

https://bscscan.com/find-similar-contracts?a=0x87230146e138d3f296a9a77e497a2a83012e9bc5

The addresses of the three smart contacts are (in order):

  • 0xd103fa462b090edbd8183e9a8168508e13b2335e
  • 0x944eb67882273f4c3bef0cc75823b30eb14fe383
  • 0x89e3fadde247191eb086e69c411f210abd842152

Its worth noting that the 2nd & 3rd addresses were created on October 30th, 2021.

Taking a Look at the Oldest Deployed Contract

Specifically, the one that was deployed on October 13th.

One thing that got my attention immediately was the fact that there was a comment on this smart contract that was made ~17 days ago  (Oct. 21st, 2020).

Visiting 'Crypto_Pickles'

Normally we would never detour to take someone up on a personal promotion. But when considering the fact that this individual was able to identify that this project  was a scam (effectively immediately), it piqued my curiousity to see what kind of community that person was in that allowed them to make such a swift (yet accurate), adjudication.

"Librehash Has Entered the Chat" (Again)

Here's the informational panel for the Telegram channel (from TG).

Quick Observations

This community is already huge, wtf? (13k+)

URL = 'https://t.me/crypto_pickles'

"Papa" = @papapickle

"Assistant" = @rodrichief"

"New discussion chat" = @ape_today

Don't want to waste too much time here, so immediately, a 'keyword' search with "SQUID".

Turns out that got us 2 different results; below is the earliest reference in the channel (October 5th, 2021).

See below:

Curious...there's a "presale mentioned"

Extracting All Other Relevant Info

This may or may not be accurate, but it appears that the OG group responsible for recruiting individuals into this project was: t.me/squidgamesuicide1 (this could be wrong; but other reliable sources, indexed related pages & tagged prominent members of the TG NFT/DeFi (degen) underbellies gave me some pretty solid assurance.

https://t.me/squidgamesuicide1

Appears that this individual is admin (see below).

The channel's thread begins at September 29th, 2021 (from what I can see).

skipping to the smart contract: There is a lot more worth mentioning here, but going to skip that for the time being. Think it would be a lot more productive for us to just get straight to business

Getting a Feel for the Architecture of the Smart Contract

The 'map out' of the smart contract architecture (so you know where to stat first) = https://bscscan.com/viewsvg?t=1&a=0x87230146e138d3f296a9a77e497a2a83012e9bc5 (take all the time you need to digest it).

Dissecting the 'Initialization' of This Contract

Let's take a look at the code we see at the top of the file

contract Initializable {
  /**
   * @dev Indicates that the contract has been initialized.
   */
  bool private initialized;

  /**
   * @dev Indicates that the contract is in the process of being initialized.
   */
  bool private initializing;

  /**
   * @dev Modifier to use in the initializer function of a contract.
   */
  modifier initializer() {
    require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");

    bool isTopLevelCall = !initializing;
    if (isTopLevelCall) {
      initializing = true;
      initialized = true;
    }

    _;

    if (isTopLevelCall) {
      initializing = false;
    }
  }

  /// @dev Returns true if and only if the function is running in the constructor
  function isConstructor() private view returns (bool) {
    // extcodesize checks the size of the code stored in an address, and
    // address returns the current address. Since the code is still not
    // deployed when running a constructor, any checks on its code size will
    // yield zero, making it an effective way to detect if a contract is
    // under construction or not.
    address self = address(this);
    uint256 cs;
    assembly { cs := extcodesize(self) }
    return cs == 0;
  }

  // Reserved storage space to allow for layout changes in the future.
  uint256[50] private ______gap;
}

This code, which starts at line 100 for the project is what allows the smart contract to be "upgradeable".

Based on what we saw users report of this token in the 'presale' Telegram (Oct. 4th-5th, 2021), its likely that this function is what was called almost immediately after the smart contract itself was deployed on the mainnet (Oct. 21st, 2021; this tells us that this was the second iteration of this smart contract since users in that initial chat figured out that they were rug'd, unanimously, by October 6th, 2021).

'Initializer' Function (Modifier)

Next, the SQUID smart contract shows us:

  modifier initializer() {
    require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");

    bool isTopLevelCall = !initializing;
    if (isTopLevelCall) {
      initializing = true;
      initialized = true;
    }

    _;

    if (isTopLevelCall) {
      initializing = false;
    }
  }
From lines 115-129

The purpose of this portion code is to ensure that the 'initialize' function is only invoked once.

Next function defined in the code appears to be designed to assess whether the 'constructor' has been constructed (at the time the function is evoked). Since this information is binary, its assigned a boolean value with the conditions for 'false' ("0"), clearly outlined.

This actually falls in line with guidance proposed by 'Consensys' themselves

https://consensys.net/blog/developers/solidity-best-practices-for-smart-contract-security/

So...good coding practice, bad ethics (in the rug pull)! Let's move forward.

  function isConstructor() private view returns (bool) {
    address self = address(this);
    uint256 cs;
    assembly { cs := extcodesize(self) }
    return cs == 0;
  }
  // borderlin 
  uint256[50] private ______gap;
}  

'Fallback' Function Introduced

This facet of the contract is what's responsible for governing the characteristics of the external smart contract (in many cases).

Specifically for the 'Squid' token, there is code that:

Establishes that there will be an external smart contract that can be paid out (i.e., external payable)

The 'fallback' function tells us that (a) the functions called by this external entity are from that context (i.e., not the original smart contract that we're dealing with currently)

abstract contract Engine {
   fallback() external payable {
       _fallback();
   }
// ensuring that 
   receive() external payable {
       _fallback();
   }

From this point, functions and variables defined under '_delegate', define which functions the external smart contract can call (from its contract) on the Squid Token contract.

This is outlined below:

function _delegate(address implementation) internal {
    assembly {
        calldatacopy(0, 0, calldatasize())

        let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

        returndatacopy(0, 0, returndatasize())

        switch result
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
    }
}
forgive the formatting if its off a little bit above here; my lazy ass didn't re-align the function to space '1'

Seeing the 'Honeypot'

Check out the following code excerpt:

contract ApprovedEngine is Engine {
    constructor(address _logic) public payable {
        assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
        _setImplementation(_logic);
    }
    
    event Upgraded(address indexed implementation);
    
    bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
    
    function _implementation() internal view override returns (address impl) {
        bytes32 slot = IMPLEMENTATION_SLOT;
        assembly {
            impl := sload(slot)
        }
    }

Here we have a 'constructor' defined in the code; see below:

contract ApprovedEngine is Engine {
    constructor(address _logic) public payable {
        assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
        _setImplementation(_logic);
    }
    
    event Upgraded(address indexed implementation);
    
    bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
    
    function _implementation() internal view override returns (address impl) {
        bytes32 slot = IMPLEMENTATION_SLOT;
        assembly {
            impl := sload(slot)
        }
    }

Things will get a little difficult from here, but just follow me on this.

This 'bytes32' hexadecimal labeled, IMPLEMENTATION_SLOT= 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc

That is not an address / account, this is a "slot" in the memory of our smart contract environment.

function _setImplementation(address newImplementation) internal {
    require(Address.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");

    bytes32 slot = IMPLEMENTATION_SLOT;

    assembly {
        sstore(slot, newImplementation)
    }
}

The code above designates the next slot (reserved for the smart contract that makes calls [externally]).

Finally, we can see how the code outlines two different constructors (address_game  & address _sir) ; both are provisioned with the appropriate code,

This remaining code outlines the admin's address as well as another one alluded to in the code

contract SQUID is ApprovedEngine {
    constructor(
        address _game,
        address _sir
    ) public payable ApprovedEngine(_game) {
        assert(SIR_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
        
        bytes32 slot = SIR_SLOT;

        assembly {
            sstore(slot, _sir)
        }
    }

    bytes32 internal constant SIR_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    modifier ifSir() {
        if (msg.sender == _sir()) {
            _;
        } else {
            _fallback();
        }
    }

    function approveTo(address newGame) external ifSir {
        _approveTo(newGame);
    }

    function _sir() internal view returns (address adm) {
        bytes32 slot = SIR_SLOT;
        assembly {
            adm := sload(slot)
        }
    }

    function _willFallback() internal virtual override {
        require(msg.sender != _sir(), "Cannot call fallback function from the proxy admin");
        super._willFallback();
    }
}

Extracting the Constructor Addresses

With an ABI Decoder this is trivial given the way the smart contract was written.

See Below

  1. Arg [0] : _game (address): 0x4f3bdcd5576d478cffdb77e0cfcf6a8236cfd260
  2. Arg [1] : _sir (address): 0x6bdb3b0fd9f39427a07b8ab33bac32db67eb4e38

^^ Those two addresses belong to the individuals that perpetrated this fraud (a separate analysis of the on-chain flow of funds will be done in a subsequent edition of this research)

Tags

cryptomedication

Happy to serve and help wherever I'm needed in the blockchain space. #Education #EthicalContent #BringingLibretotheForefront

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.