Compromised Keys Hacks

  • December 02, 2021Badger DAO: A front-end attack, due to a basic OPSEC error of granting unlimited approvals to an EOA, $120 million taken in various forms of wBTC and ERC20.
  • December 05, 2021BitMart: the self-proclaimed “Most Trusted Crypto Trading Platform”, has lost ~$196M from two of its hot wallets on Ethereum and BSC.
  • December 07, 2021–8ight Finance: keys got compromised, no further details shared. $1.75 million taken.
  • December 11, 2021AscendEX: lost $77.7M, again from a “compromised” hot wallet.
  • December 13, 2021Vulcan Forged: $140M wiped from users wallets due to compromised managed wallet provider.



8ight Finance


Vulcan Forged

Preventative Techniques

For Users

1. Know what you are approving.

  • Is the contract brand new?
  • Who deployed it?
  • Where did the funds come from to the deployer
  • Is it a proxy?

2. Know how much you are approving.

3. Approvals are per token.

4. Be extra tight with your approvals on proxies.

5. Quarterly review of all your approvals.

6. If you are doing an infinite approval, you should have a good reason for it. It should not be your default.

For Developers


// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract MyToken is ERC721, Ownable {
using Counters for Counters.Counter;

Counters.Counter private _tokenIdCounter;

constructor() ERC721("MyToken", "MTK") {}

function safeMint(address to) public onlyOwner {
uint256 tokenId = _tokenIdCounter.current();
_safeMint(to, tokenId);

Role-based access controls

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract MyToken is ERC721, AccessControl {
using Counters for Counters.Counter;

bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
Counters.Counter private _tokenIdCounter;

constructor() ERC721("MyToken", "MTK") {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(MINTER_ROLE, msg.sender);

function safeMint(address to) public onlyRole(MINTER_ROLE) {
uint256 tokenId = _tokenIdCounter.current();
_safeMint(to, tokenId);

// The following functions are overrides required by Solidity.

function supportsInterface(bytes4 interfaceId)
override(ERC721, AccessControl)
returns (bool)
return super.supportsInterface(interfaceId);



  • Guardians (privileged accounts with special powers) can help in at least two ways:
  • Push for proposals if there’s no active community participation.
  • Stop proposals if there’s malicious intent.
  • Delegation mechanisms of voting power may also allow interested actors to participate on behalf of others that may not be always interested or available.
  • If governance has full control over a system, and there are no privileged accounts involved, setting up off-chain validations, checklists and documentation on procedures becomes crucial to ensure the system is always modified to follow standard procedures.

Key Management

Question the code

  • Who controls the privileged account? A single private key? Multiple keys with a multisig? A governance module?
  • Does the privileged account hold any other role in the system? If so, could that introduce any risk worth analyzing?
  • How are the private keys stored? Are there backups? Who’s got access to them? Are access operations logged, monitored and audited?
  • How are the private keys generated? Do they sign transactions in standard ways or is the system using some custom implementation?

Secure All Administrative Keys

import KMS from 'aws-sdk/clients/kms';

public async sign(kms: KMS, keyIdOrAlias: string, payload: Buffer): Promise<Buffer> {
const params: KMS.SignRequest = {
KeyId: keyIdOrAlias,
SigningAlgorithm: 'ECDSA_SHA_256',
Message: payload,
MessageType: 'DIGEST',

const response = await this.kms.sign(params).promise();
if (Buffer.isBuffer(response.Signature)) {
return response.Signature;
throw new Error(`Error using key ${keyIdOrAlias} signing: ${payload.toString()}`);

Use multiple signatures for critical administrative tasks

  • Assign different roles for different types of tasks. OpenZeppelin Contracts provides the Roles library for implementing role-based access control.
  • Define a hierarchy of permission levels linked to the roles.
  • Require a higher number of signatures for roles higher in the hierarchy.
  • Use independent multisignatures for every role.
  • Give ownership of every private key to a different individual.
  • Train every key-holder on the secure handling of their devices and communications. The Electronic Frontier Foundation published good resources for safe online communications.
  • Distribute the keys to individuals that guarantee diversity of geographic locations, hardware manufacturers, and software applications.
  • Do not use a 1 of N multisignature because losing control of a single private key means the system is immediately compromised.
  • Do not use an N of N multisignature because it does not provide any redundancy in case some of the private keys are lost.
  • Consider using an M of N multisignature, with M = (N/2) + 1 to balance concerns. A lower M allows for quicker response. A higher M requires a stronger majority support.
  • For configuration tasks, like changing parameters, a 2 of 3 multisignature might be enough. Consider using other kinds of fail-safes, like allowing the parameters to vary only by a small delta and with some time window before they are applied, so the functioning of system does not completely change with a single transaction. Consider separating the tasks that are adjustments from the ones that disable or enable functionality. For example, do not allow a parameter to be adjusted to 0 if it means a functionality will stop working; define a separate function with a separate role to disable it.
  • For more critical and less common tasks, like upgrading the implementation of a contract, a 3 of 5 multisignature might be better. If the tasks are time-sensitive, the key-holders must agree to be on-call to react immediately after being notified.
  • For tasks that require representation from different stakeholders, consider giving ownership of private keys to two representatives of each stakeholder category.
  • Document the roles, corresponding tasks, and key-holder contacts in a private repository.
  • Document the threat model for every role and task.
  • Automate the monitoring, administrative triggers, and notification of key-holders.
  • Test and run simulations of all the situations that will require multiple signatures.


Privileged Administration Transactions

  • Changes in sensitive parameters of core contracts
  • Calls to functions which add, renounce, or transfer ownership
  • Withdrawals of central funds
  • Calls to function which allow or block access to assets or capabilities for specific accounts
  • Calls using privileged accounts not related to expected administrator operations, detected via inspection of mined transactions

Spikes in Account Activity

Drop in System Funds





Oxford-based blockchain and zero knowledge consultancy and auditing firm

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Agent-Based vs Network-Based Internal Vulnerability Scanning

Hack The Box — Postman Write up

Generating pseudo-random passwords in Microsoft Flow Guide: Ledger Support for $XCT

Baseline CyberSecurity for Everyone

How to buy Mintaverse (MTV) on PancakeSwap using MetaMask?

🗃 How to choose a VPN ? 🗳 ━━━━━━━━━━━━━━━━

The Pilot

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store


Oxford-based blockchain and zero knowledge consultancy and auditing firm

More from Medium

Speed up your Waifu with ZMOK Front-running endpoint

How to create your own front running bot with ethers.js and FastlyNode : Part 02

Flashloan Arbitrage Trades are Still Profitable

Fork Uniswap v2 Smart-Contracts & UI on Remix