스마트 컨트랙트관련 문제인데 처음 접해봐서 좀 헤맸는데 풀고나니 엄청 쉬운 문제였다.
문제 자체는 solidity에서 일어날 수 있는 몇 가지 issue에 관해서 시큐어 코딩을하면 된다.
처음에는 solidity에서 일어났던 보안 issue 들을 찾고 패치하는 식으로 진행하였는데,
hitcon 2018에서 취약점 점검 도구인 MyThril를 공개하여 이를 사용하여 진행하였다.
사용법은 다음과 같다.
$ docker pull mythril/myth
$ sudo docker run -v SCTFBank.sol:/SCTFBank.sol mythril/myth -x /SCTFBank.sol
실행하면 다음과 같이 reporting을 해준다.
issue를 모두 패치 한 후 서버에 전달하면 flag를 획득할 수 있다.
Solve code:
#!/usr/bin/env python
from pwn import *
import json
addr = "bankrobber.eatpwnnosleep.com"
port = 4567
s = remote(addr, port)
def auth():
s.recvuntil("API key required : ")
s.sendline("349b7ec9c6b3caa710b03589aede7a9bcf2c1466307e7f6a3ce3ef1b8c30aa0e")
s.recv(4096)
def solver():
f = open("SCTFBank.sol", "r")
s.send(f.read())
f.close()
auth()
solver()
s.interactive()
s.close()
SCTFBank.sol
pragma solidity ^0.4.18;
contract SCTFBank{
event LogBalance(address addr, uint256 value);
mapping (address => uint256) private balance;
mapping (address => bool) private claimedBonus;
uint256 private donation_deposit;
address private owner;
constructor() public{
owner = msg.sender;
}
function showBalance(address addr) public {
emit LogBalance(addr, balance[addr]);
}
function withdraw(uint256 value) public{
require(balance[msg.sender] >= value);
balance[msg.sender] -= value;
msg.sender.transfer(value);
}
function transfer(address to, uint256 value) public {
require(balance[msg.sender] >= value && balance[to]+value >= balance[to]);
balance[msg.sender] -= value;
balance[to]+=value;
}
function multiTransfer(address[] to_list, uint256 value) public {
uint256 tmp = value*to_list.length;
require(tmp / value == to_list.length);
require(balance[msg.sender] >= (value*to_list.length));
balance[msg.sender] -= (value*to_list.length);
for(uint i=0; i < to_list.length; i++){
require(balance[to_list[i]]+value >= balance[to_list[i]]);
balance[to_list[i]] += value;
}
}
function donate(uint256 value) public {
require(balance[msg.sender] >= value);
balance[msg.sender] -= value;
require(donation_deposit+value >= donation_deposit);
donation_deposit += value;
}
function deliver(address to) public {
require(!claimedBonus[to]);
require(msg.sender == owner);
claimedBonus[to] = true;
to.transfer(donation_deposit);
donation_deposit = 0;
}
function () payable public {
require(balance[msg.sender]+msg.value >= balance[msg.sender]);
balance[msg.sender]+=msg.value;
}
}
//END