최근 글로벌 암호화폐 거래소인 바이비트(Bybit)가 해킹 공격을 받아 약 15억 달러(약 2조 원) 규모의 암호화폐를 탈취당하는 사건이 발생했습니다. 이는 단일 해킹 사건으로는 역대 최대 규모로 기록되고 있습니다.kr.beincrypto.com+6cio.com+6donga.com+6youtube.com+2donga.com+2voakorea.com+2
미국 연방수사국(FBI)은 이번 공격의 배후로 북한의 해킹 조직인 '트레이더트레이터(TraderTraitor)', 일명 라자루스 그룹을 지목했습니다. 이들은 바이비트의 이더리움 지갑을 해킹하여 약 401,000 ETH를 탈취했으며, 이는 약 15억 달러에 해당합니다. 해커들은 탈취한 자산을 빠르게 비트코인 등 다른 가상 자산으로 전환하고, 여러 블록체인 주소로 분산시켜 자금 세탁을 시도하고 있는 것으로 알려졌습니다. cio.com+5digitaltoday.co.kr+5voakorea.com+5businessinsider.comdonga.com+2voakorea.com+2digitaltoday.co.kr+2
바이비트는 해킹 발생 직후 고객 자산 보호를 위해 신속한 조치를 취했으며, 현재 플랫폼은 정상적으로 운영되고 있다고 밝혔습니다. 또한, 탈취된 자산을 회수하기 위해 총 1억 4,000만 달러의 보상금을 내걸고 관련 정보를 수집하고 있습니다. youtube.com+3donga.com+3chosun.com+3voakorea.com+2digitaltoday.co.kr+2businessinsider.com+2
이번 사건은 암호화폐 거래소의 보안 체계에 대한 경각심을 높이는 계기가 되었으며, 향후 유사한 공격을 방지하기 위한 보안 강화의 필요성이 대두되고 있습니다.








바이비트(Bybit) 거래소의 해킹 고도화된 수법
- 악성 스마트 컨트랙트 업그레이드: 해커는 바이비트의 이더리움 콜드월렛에 접근하여, 정상적인 거래로 위장한 악성 스마트 컨트랙트를 업그레이드했습니다. 이를 통해 다중 서명 지갑의 보안 취약점을 악용하여 자금을 탈취했습니다. brunch.co.kr+1brunch.co.kr+1
- 다중 서명 지갑의 취약점 악용: 공격자는 지갑 인터페이스(UI)에서 정상적인 거래처럼 보이도록 위장하여 서명자들을 속였습니다. 이를 통해 세 개의 유효한 서명을 확보하고, 지갑의 구현 계약을 악성 스마트 컨트랙트로 교체하여 자금을 탈취했습니다. brunch.co.kr
- 거래 내역 변조 및 delegatecall 기능 활용: 해커는 거래 내역을 변조하는 고도의 기술을 사용했습니다. 특히 Safe 지갑의 'delegatecall' 기능을 활용하여 마스터 카피 주소를 변경함으로써, 지갑 내 모든 자산을 이동할 수 있도록 조작했습니다. brunch.co.kr
이러한 정교한 해킹 수법은 암호화폐 거래소의 보안 체계에 대한 새로운 위협을 제기하고 있습니다.
바이비트 해킹에서 사용된 악성 스마트 컨트랙트 업그레이드 및 다중 서명 지갑의 취약점 악용을 방지하기 위한 보안 조치를 설명하고, 이를 코드로 구현하는 방법을 제시하겠습니다.
1. 해킹 방지를 위한 방안
① 다중 서명 (Multisig) 강화
- Threshold 증가: 3/5 서명 방식 대신 4/6 또는 5/7 같은 높은 서명 요구
- 서명자 분리: 서로 다른 네트워크(예: 온체인 + 오프체인)에서 서명을 검증
② 스마트 컨트랙트 업그레이드 보호
- 업그레이드 가능한 컨트랙트 제한: 업그레이드 기능을 제거하거나, 특정 관리자만 실행 가능하도록 제한
- 타임락(TimeLock) 적용: 변경 사항이 즉시 적용되지 않고 일정 기간 후에 적용되도록 설정
③ Delegatecall 공격 방어
- delegatecall을 남용하면 외부 컨트랙트의 코드를 실행할 수 있으므로, safe 지갑 사용 시 delegatecall을 금지하는 로직 추가
④ 트랜잭션 모니터링 및 알림 시스템 도입
- 비정상적인 거래 감지 후, 자동 롤백 시스템 도입
2. 방어 코드 예제
① 다중 서명 (Multisig) 강화
Gnosis Safe 같은 다중 서명 지갑을 사용할 때, 서명 요청을 필터링하는 방식을 적용할 수 있습니다.
contract SecureMultiSig {
address[] public owners;
mapping(address => bool) public isOwner;
uint public requiredConfirmations;
struct Transaction {
address to;
uint value;
bytes data;
bool executed;
uint confirmations;
}
Transaction[] public transactions;
mapping(uint => mapping(address => bool)) public confirmations;
event TransactionSubmitted(uint indexed txIndex, address indexed to, uint value, bytes data);
event TransactionConfirmed(uint indexed txIndex, address indexed owner);
event TransactionExecuted(uint indexed txIndex);
modifier onlyOwner() {
require(isOwner[msg.sender], "Not an owner");
_;
}
modifier txExists(uint _txIndex) {
require(_txIndex < transactions.length, "Tx does not exist");
_;
}
modifier notExecuted(uint _txIndex) {
require(!transactions[_txIndex].executed, "Tx already executed");
_;
}
modifier notConfirmed(uint _txIndex) {
require(!confirmations[_txIndex][msg.sender], "Tx already confirmed");
_;
}
constructor(address[] memory _owners, uint _requiredConfirmations) {
require(_owners.length > 0, "Owners required");
require(_requiredConfirmations > 0 && _requiredConfirmations <= _owners.length, "Invalid confirmations count");
for (uint i = 0; i < _owners.length; i++) {
require(_owners[i] != address(0), "Invalid owner");
require(!isOwner[_owners[i]], "Owner not unique");
isOwner[_owners[i]] = true;
}
owners = _owners;
requiredConfirmations = _requiredConfirmations;
}
function submitTransaction(address _to, uint _value, bytes memory _data) public onlyOwner {
uint txIndex = transactions.length;
transactions.push(Transaction({
to: _to,
value: _value,
data: _data,
executed: false,
confirmations: 0
}));
emit TransactionSubmitted(txIndex, _to, _value, _data);
}
function confirmTransaction(uint _txIndex) public onlyOwner txExists(_txIndex) notExecuted(_txIndex) notConfirmed(_txIndex) {
Transaction storage transaction = transactions[_txIndex];
transaction.confirmations += 1;
confirmations[_txIndex][msg.sender] = true;
emit TransactionConfirmed(_txIndex, msg.sender);
if (transaction.confirmations >= requiredConfirmations) {
executeTransaction(_txIndex);
}
}
function executeTransaction(uint _txIndex) internal txExists(_txIndex) notExecuted(_txIndex) {
Transaction storage transaction = transactions[_txIndex];
require(transaction.confirmations >= requiredConfirmations, "Not enough confirmations");
transaction.executed = true;
(bool success, ) = transaction.to.call{value: transaction.value}(transaction.data);
require(success, "Tx failed");
emit TransactionExecuted(_txIndex);
}
}
✅ 방어 기능
- 서명자가 추가로 검증되지 않으면 트랜잭션이 실행되지 않음
- 서명자가 3/5 이상이어야 실행되도록 설정 가능
- 비정상적인 트랜잭션을 검출하여 롤백 가능
② 스마트 컨트랙트 업그레이드 보호
스마트 컨트랙트 업그레이드를 타임락 방식으로 제한하는 코드
pragma solidity ^0.8.19;
contract SecureUpgradeable {
address public admin;
address public pendingAdmin;
address public implementation;
uint256 public upgradeTime;
event NewAdminPending(address indexed pendingAdmin);
event ImplementationUpgraded(address indexed newImplementation);
modifier onlyAdmin() {
require(msg.sender == admin, "Not admin");
_;
}
constructor() {
admin = msg.sender;
}
function setPendingAdmin(address _pendingAdmin) external onlyAdmin {
pendingAdmin = _pendingAdmin;
emit NewAdminPending(_pendingAdmin);
}
function acceptAdmin() external {
require(msg.sender == pendingAdmin, "Not pending admin");
admin = pendingAdmin;
pendingAdmin = address(0);
}
function proposeUpgrade(address _newImplementation) external onlyAdmin {
require(_newImplementation != address(0), "Invalid address");
implementation = _newImplementation;
upgradeTime = block.timestamp + 48 hours; // 48시간 타임락 적용
}
function executeUpgrade() external onlyAdmin {
require(block.timestamp >= upgradeTime, "Upgrade is time-locked");
(bool success, ) = implementation.delegatecall(abi.encodeWithSignature("initialize()"));
require(success, "Upgrade failed");
emit ImplementationUpgraded(implementation);
}
}
✅ 방어 기능
- 즉시 업그레이드 불가 (48시간 타임락 적용)
- 관리자가 승인하지 않으면 업그레이드 불가
- 의심스러운 업그레이드는 롤백 가능
③ Delegatecall 악용 방지
delegatecall은 외부 컨트랙트에서 악용될 가능성이 크므로, 특정 함수에서만 사용되도록 제한하는 코드
contract SecureContract {
address public owner;
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
function secureDelegateCall(address target, bytes memory data) external onlyOwner {
require(target != address(0), "Invalid target");
(bool success, ) = target.delegatecall(data);
require(success, "Delegate call failed");
}
}
✅ 방어 기능
- 오직 owner만 delegatecall 실행 가능
- target이 정상적인 컨트랙트인지 확인하여 우회 공격 방지
결론
- 다중 서명 지갑을 강화하여 승인되지 않은 트랜잭션 실행을 방지
- 스마트 컨트랙트 업그레이드에 타임락을 적용하여 악성 코드 업그레이드를 방지
- Delegatecall 사용을 제한하여 임의의 코드 실행을 차단
이러한 보안 조치를 적용하면 바이비트 같은 해킹을 예방할 수 있습니다.
'투자정보' 카테고리의 다른 글
삼성전자의 조직문화 개선 방향 제안 (0) | 2025.03.06 |
---|---|
엔비디아 성과보상체계 RSU(Restricted Stock Unit), ESPP(Employee Stock Purchase Plan)와 삼성의 성과 보상체계 비교 및 한계 극복 방안 (0) | 2025.03.06 |
금리 곡선,중립금리 (0) | 2025.02.16 |
지표를 활용하여 거래 시그널을 탐지 (0) | 2025.02.06 |
머니레터(MoneyLetter) , 유사한 뉴스레터 비교 (0) | 2025.01.30 |