-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Incorrect implementation of access control in MIMOProxy:execute #159
Comments
We were not able to recreate the provided POC. The explanation is also incomplete - we don't see how an attacker could bypass the permissions check through providing extra calldata in a signature. Please provide more details, or a working POC, on how the extra data can bypass the permissions check. |
This POC looks valid to me. Basically what the warden mean is if you construct the calldata like execute(some_addr, "") + 0xffffff
|
This might be clearer pragma solidity ^0.8.0;
contract PoC {
bytes4 public signatureWithPermision = bytes4(0xdead1337);
// Call this function with calldata that can be prepared in `getAttackerCalldata`
function execute(address target, bytes calldata data) view external returns(bytes memory) {
bytes4 selector;
assembly {
selector := calldataload(data.offset)
}
require(selector == signatureWithPermision, "bad selector");
return data;
}
// Function that prepare attacker calldata
function getAttackerCalldata() public view returns(bytes memory) {
bytes memory usualCalldata = abi.encodeWithSelector(this.execute.selector, msg.sender, new bytes(0));
return abi.encodePacked(usualCalldata, signatureWithPermision);
}
function exploit() external returns(bytes memory data) {
(, data) = address(this).call(getAttackerCalldata());
}
} If you call The exploit here is if you permitted contract A to run function foo only, A.fallback() is also permitted. Your Consider the calldata layout data.offset = 0x04 + 0x20 (data offset) + 0x20 (1 word for length) = 0x44 |
Lines of code
https://github.com/code-423n4/2022-08-mimo/blob/main/contracts/proxy/MIMOProxy.sol#L54
https://github.com/code-423n4/2022-08-mimo/blob/main/contracts/proxy/MIMOProxy.sol#L104
Vulnerability details
Description
There is a function
execute
inMIMOProxy
smart contract. The function performs a delegate call to the user-specified address with the specified data. As an access control, the function checks that either it was called by the owner or the owner has previously approved that the sender can call a specified target with specified calldata. See https://github.com/code-423n4/2022-08-mimo/blob/main/contracts/proxy/MIMOProxy.sol#L104.The check itself:
The problem is how the
selector
is calculated. Specifically,calldataload(data.offset)
- reads first 4 bytes ofdata
. Imaginedata.length == 0
, does it mean thatcalldataload(data.offset)
will returnbytes4(0)
? No.Let's see how calldata are accepted by functions in Solidity. The solidity function checks that the calldata length is less than needed, but does NOT check that there is no redundant data in calldata. That means, the function
execute(address target, bytes calldata data)
will definitely accept data that havetarget
anddata
, but also in calldata can be other user-provided bytes. As a result,calldataload(data.offset)
can read trash, but not thedata
bytes.And in the case of
execute
function, an attacker can affect the execution by providingtrash
data at the end of the function. Namely, if the attacker has permission to call the function with somesignature
, the attacker can call proxy contract bypass check for signature and make delegate call directly with zero calldata.Please see proof-of-concept (PoC),
getAttackerCalldata
returns a calldata with which it is possible to bypass check permission for signature. Functionexecute
from PoC simulate check for permission to callsignatureWithPermision
, and enforce thatdata.length == 0
. With calldata fromgetAttackerCalldata
it works.Impact
Any account that have permission to call at least one function (signature) to the contract can call fallback function without without permission to do so.
Proof of Concept
Recommended Mitigation Steps
Add
require(data.length >= 4);
The text was updated successfully, but these errors were encountered: