
개요
본 포스팅은 LZ Finance에서 2023년 2월 27일 발생한 약 $1,000,000 해킹 사건을 분석한 포스팅입니다.
계약 취약성, 해킹 기술 및 취약성 수정에 중점을 둔 게시물을 작성했습니다.
약한 계약
손상된 계약의 주소는 다음과 같습니다. 0x6D8981847Eb3cc2234179d0F0e72F6b6b2421a01보지마.
이 계약은 LZ Finance에서 배포합니다. SwapXProxy의 실행 계약입니다.
취약한 계약은 Uniswap 라우터처럼 작동합니다.
LZ Finance는 코드를 공개하지 않기 때문에 바이트코드를 디컴파일하고 분석할 수 있습니다.
이번 포스팅에서는 트랜잭션 분석을 통해 컨트랙트의 특징과 취약점을 분석해 보도록 하겠습니다.
트랜잭션 분석
동일한 계약으로 여러 번의 공격이 이루어졌지만 거래 기능과 취약점을 찾기 위해 하나를 분석해 봅시다.

취약한 계약(0x6d89…)의 함수 4f1f05bc를 호출합니다.
이 함수는 실제로 두 번의 호출(LZtoken.transferfrom, BiswapPair.swap)을 수행합니다.
두 함수 호출을 보면 이 함수가 UniswapRouter의 스왑처럼 작동한다는 것을 짐작할 수 있습니다.
그러나 위의 함수 호출에서 transferfrom의 보낸 사람이 msg.sender가 아닌 다른 주소로 가는 것을 볼 수 있으며 여기서 취약점이 발생합니다.
(UniswapRouter를 사용하면 아래 코드와 같이 sender에 msg.sender가 입력됩니다.)
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::transferFrom: transferFrom failed'
);
}
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address() calldata path,
address to,
uint deadline
) external virtual override ensure(deadline) returns (uint() memory amounts) {
amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
require(amounts(amounts.length - 1) >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
TransferHelper.safeTransferFrom(
path(0), msg.sender, UniswapV2Library.pairFor(factory, path(0), path(1)), amounts(0)
);
_swap(amounts, path, to);
}
취약점 분석
위에서 확인된 계약의 약점은 다른 사람의 스왑을 대체할 수 있다는 것입니다.
uniswapV2의 구조를 알면 이 취약점이 얼마나 중요한지 쉽게 이해할 수 있습니다.
(uniswapV2에 익숙하지 않으신 분들은 이 게시물참조하십시오.)
공격 순서는 다음과 같습니다.
- 다른 사람의 재산을 사용하여 UniswapPair에서 특정 토큰의 가격을 높입니다.
- 공격자의 토큰은 쌍으로 교환됩니다.
위와 같은 방식으로 귀하의 재산이 증가하고 인플레이션으로 인한 피해는 다른 사람에게 전가됩니다.
트랜잭션 분석에 사용 거래한 사용자에 대해 공격을 수행했지만 다른 사용자 거래의 경우 다수의 사용자를 대상으로 공격이 이루어졌다.