HardhatDeFi
15,000 USDC
View results
Submission Details
Severity: medium
Invalid

Hardcoded Owner Address

Summary

The contract WToken.sol sets _owner to a specific address in the constructor but does not provide a way to change ownership.

Vulnerability Details

Currently, the contract's owner is set in the constructor:

// contract WToken.sol, line 14
constructor(string memory symbol_, uint8 decimals_, address owner_) ERC20(symbol_, symbol_) {
_owner = owner_;
_decimals = decimals_;
}

Since _owner is private and there is no function to change it, ownership is permanently fixed. If the owner address needs to be updated due to key loss, governance changes, or delegation needs, it is impossible.

Impact

  • If the owner loses access to their private key, minting and burning operations become permanently unavailable.

  • The contract lacks flexibility for governance upgrades.

  • No way to transfer ownership to a new entity.

PoC for Hardcoded Owner Address

Overview

The WToken contract assigns _owner in the constructor but does not provide a way to change it later. This means if the deployment is incorrect or the owner loses access to their wallet, the contract cannot be managed properly.

Actors

  • Attacker: No direct attacker, but the vulnerability impacts protocol flexibility.

  • Victim: The contract owner, who cannot transfer ownership.

  • Protocol: WToken contract.

Working Test Case

This test will verify that ownership is permanently set at deployment and cannot be changed.

Test Setup

  • Deploy WToken with an owner.

  • Attempt to change the _owner variable after deployment.

  • Verify that ownership is immutable.

import { expect } from "chai";
import { ethers } from "hardhat";
import { WToken } from "../typechain-types";
describe("WToken - Hardcoded Owner Address", function () {
let wToken: WToken;
let owner: any;
let newOwner: any;
before(async function () {
[owner, newOwner] = await ethers.getSigners();
const WTokenFactory = await ethers.getContractFactory("WToken");
wToken = await WTokenFactory.deploy("WTK", 18, owner.address);
await wToken.deployed();
});
it("should not allow changing owner after deployment", async function () {
// Try changing the owner via storage override (not possible)
await expect(
wToken.connect(newOwner).mint(newOwner.address, ethers.parseEther("10"))
).to.be.revertedWith("WToken: caller is not owner");
// Confirm the owner remains unchanged
expect(await wToken.owner()).to.equal(owner.address);
});
});

Exploit Scenario

  • The deployer mistakenly sets the wrong address as _owner.

  • The assigned owner loses their private key or wallet.

  • The contract can no longer mint or burn tokens, making it unusable.

Tools Used

Manual code review

Recommendations

Implement an Ownable pattern with a transferOwnership function:

event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0), "WToken: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
Updates

Lead Judging Commences

bube Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.