✅Miscellaneous
Assume ownership
Code Audit
pragma solidity ^0.4.21;
contract AssumeOwnershipChallenge {
address owner;
bool public isComplete;
function AssumeOwmershipChallenge() public {
owner = msg.sender;
}
function authenticate() public {
require(msg.sender == owner);
isComplete = true;
}
}Note that AssumeOwmershipChallenge() has a typo, so it is declared as a function instead of the constructor. Since this function is public, anyone can call it and reset the owner.
Solution
Copy and paste the challenge contract to Remix and interact with the instance via "At Address". Call AssumeOwmershipChallenge(), then call authenticate().
Token bank
Code Audit
Look at the withdraw() function:
This function violates the "checks-effects-interactions" pattern, hence vulnerable to reentrancy attack. When token.transfer(msg.sender, amount) is called, the control flow goes into msg.sender's fallback function. If we call challenge.withdraw() again inside the fallback flow, the control flow will "re-enter" challenge.withdraw(). That is, TokenBankChallenge.withdraw() can be called many many times until all the fund in that contract is drained.
ERC223 is a superset of ERC20. The difference is ERC-223 "notifies" the recipient of a transfer by calling the recipient's tokenFallback function in case the recipient is a contract. This logic was implemented in token.transfer():

In this challenge, we will be using tokenFallback instead of the standard fallback function when building reentrancy attack exploit due to the design of ERC223. Besides this, the attack will be same as a standard reentrancy attack.
Solution
Write an exploit contract and deploy it in Remix:
Pay attention to the challenge.withdraw() function:

Note that the msg.sender here will be the exploit contract we just deployed. In order to pass this requirement, we need at least 500000000000000000000000 tokens in the exploit contract account. Since the initial balance of the exploit contract account is 0 and the initial balance of the player account is 500000000000000000000000, we can 1) convert all balances to tokens for the player account and 2) transfer all tokens from the player account to the exploit contract account, then 3) convert those tokens back to balance for the exploit contract account.
To exploit this challenge, do the following things in order:
Step 0: Interact with the challenge contract via "At Address". Get the token address via the getter
challenge.token(). Interact with the token contract via "At Address".Step 1 (manually): Call
challenge.withdraw(500000000000000000000000)to convert all balances to tokens. Now the player account has 500000000000000000000000 tokens.Step 2 (manually): Call
token.approve(<exploit_contract_address>, 500000000000000000000000). This step prepares fortoken.transforFrom().Step 3: Call
stepThree(<your_Metamask_wallet_address>). This will transfer all the tokens from the player account to the exploit contract account.Step 4: Call
stepFour(). This will transfer all the token from the exploit contract account to the challenge contract account. Thechallenge.tokenFallback()function will be triggered, sochallenge.balanceOf(<exploit_contract_address>) == 500000000000000000000000now.Step 5: Call
tokenFallback(0x0000000000000000000000000000000000000000,1337,0x1337). This will trigger the reentrancy attack. Just pass in some junk parameters since they don't matter.Step 6: Call
challenge.isComplete()to verify if the reentrancy attack succeeded.
Here step 1 and step 2 are done manually because they are involved with msg.sender, which is supposed to be our Metamask wallet address.
Last updated