사실 Call Attack이라는 이름은 그냥 붙인 것 같고,
전반적으로 발생할 수 있는 실수들에 대해 몇 가지 소개가 되어 있다.
delegate
call을 할 때 호출할 contract의 storage layout에 대해 신경쓰지 않으면 변수가 덮어써질 위험이 있다.
더욱이, 임의의 유저에게 delegate
call의 target과 calldata 모두 마음대로 할 수 있는 권한을 준다면 그냥 그 contract는 이미 박살난 것이나 상관이 없다.
Proxy Pattern을 사용할 때에는 반드시 initialize에 대해 신경써야 한다.
function initialize(address[] calldata _operators) external {
initialized = true;
require(
_operators.length <= 10,
"Can't set more than 10 operators at once"
);
for (uint8 i = 0; i < _operators.length; i++) {
operators[_operators[i]] = true;
emit NewOperator(_operators[i]);
}
}
이 예시를 보면, initialized가 false인지에 대해 체크를 하지 않고 있다.
require(!initialized);
이렇게 반드시 체크를 해주어야 한다.
Proxy가 아닌 그 implementation 원본 contract에 대해서도 취약점이 발생할 수 있다.
function initialize(address[] calldata _operators) external {
require(!initialized, "Can not be initialized twice");
initialized = true;
require(
_operators.length <= 10,
"Can't set more than 10 operators at once"
);
for (uint8 i = 0; i < _operators.length; i++) {
operators[_operators[i]] = true;
emit NewOperator(_operators[i]);
}
}
이렇게 최초에 배포되는 contract의 경우에, Access Control이 되어 있지 않다면,
배포 후 deployer가 initialize를 호출하는 과정에서 transaction이 frontrun될 가능성이 있으므로 주의하여야 한다.