Wolf Game Herdsman’s report

Bernhard Mueller
4 min readNov 28, 2021

The Great Rescue of 13,809 sheep and wolves.

On November 22th, notstoops reported a reentrancy issue in Wolf Game that would have allowed malicious users to mint arbitrary amounts of WOOL. In response, the Shepherd paused the Barn (staking) smart contract. An attack was prevented but a herd of sheep and wolves remained helplessly locked in the barn.

I was looking for interesting projects to take on so I reached out to the Shepherd on Discord and shortly after, I was contracted to audit the affected smart contracts and help work out a plan to return the trapped animals safely to their rightful owners. Here’s a summary of what happened so far.

Initial review of Barn and Woolf contracts

Clearly, the original Barn smart contract would have to be replaced due to the reentrancy bug. The first step was to review the rest of the code and decide whether it was also necessary to migrate the existing wolves and sheep to a new ERC721 contract. In principle, the rescue function in the Barn contract would have allowed users to unstake their tokens but that function had reentrancy issues as well. It wasn’t immediately clear whether it was safe to activate it.

After some investigation, it turned out that the rescue function was vulnerable as well. By re-entering the function, it was possible to manipulate certain state variables, causing subsequent attempts to rescue tokens to fail. Additionally, while staking sheep was pausable, staking additional wolves was not. If the rescue function were to be enabled, an attacker could buy a wolf on OpenSea, stake it, and exploit the rescue function to lock other users’ wolves. Not ideal.

Attack: Decreasing totalAlphaStaked by re-entering via the onERC721Received callback. Once totalAlphaStake becomes to low, any further attempts to rescue wolves will fail.

Minting & randomness

Besides the reentrancy problem there’s also issues with the contract’s mint function. Users are supposed to receive wolves 10% of time and sheep 90% of the time. The pseudo-random numbers used are derived from block data, which isn’t a particularly safe thing to do in the first place.

Besides the weak randomness, there’s another fundamental problem: An attacker can call mint() many times but revert the transaction whenever a sheep is received, thus only minting the highly desirable wolves. An isContract() check was added that prevents the mint function from being called by another contract, but that’s insufficient: Using Flashbots, users can bundle a mint transaction with another transaction that reverts the minting transaction if a sheep was received. Thus, users could game the system to mint only wolves.

Since there was no viable way to fix the above issues, migrating to a new ERC721 smart contract appeared to be the best solution.

Introducing secure development practices

After the initial assessment we focused on improving the development process. While the initial game code did have a test suite for administrative access and gameplay, it’s important to have a comprehensive process that prevents bugs from slipping into production code. Starting now, all new code will go through the following stages:

  1. Design: Address security concerns during the planning phase. For example, the game architecture needs to to ensure that random events aren’t predictable or reversible.
  2. Implementation: Refactor existing migrations and tests for readability and continue writing unit tests for any new functionality, aiming for 100% coverage while also covering edge cases. Create a CI pipeline that runs code analyzers like Slither and Mythril to find trivial bugs.
  3. Audit: Have the code reviewed by a security person. Getting another pair of eyes to look at the code is always helpful as even great coders can miss stuff, especially when under pressure. The auditor can also build custom verifiers for complex or hard to understand code that can be integrated into the CI pipeline.
  4. Bug bounty: The code is added to the scope of the bug bounty program and published on Github. Depending on the complexity of the code, wait for at least 24h before deploying to mainnet.
  5. Mainnet release: Deploy an upgradeable version of the contract using the OpenZeppelin Upgradeability pattern. Upgradeability is not everyone’s cup of tea (both the Shepherd and the Herdsman are no fans of it philosophically) but on the other hand, it provides flexibility to deploy hotfixes. According to the Shepherd, ownership of the upgrader contract will be relinquished later once its in a proven stable state and governance decides it is the right action to take.

Next steps

The first new contract to be deployed is WoolfReborn, a minimal ERC721 implementation that contains the migration logic. After the migration, the plan is to release Wool Pouches, the Risky Game, and more mechanics on-chain that go beyond the scope of the original game. All new contracts need to pass the new testing and audit processes.

TL;DR

In the course of this engagement, improvements to the development process were made that should minimize smart contract risk going forward. It should be noted that the Wolf Game team has full administrative control over the mainnet smart contracts at this time — users should be aware that they’re trusting an anonymous team which is inherently risky. That said, I’ve had the impression that the team has good intentions and is working hard to remediate the situation.

— The Herdsman

Disclaimer: This article is not an endorsement or indictment of any particular project or team, and does not guarantee the security of any particular project. It provides no warranty or representation to any third party in any respect, including regarding the bug-free nature of code, the business model or proprietors of any such business model, and the legal compliance of any such business.

--

--

Bernhard Mueller

Hackers (1995) fan • “Best Research” Pwnie Awardee • Retired degen • G≡¬Prov(num(G))