This is a "Balance Retreivar" for the recursive call vulnerability for Maker-Otc.
Block: 1679390
Address | Ammount |
---|---|
0x9a63d185a79129fdab19b58bb631ea36a420544e | 0 |
0xf5c7bcb44ebafc72d46dbc70b717272e19024da4 | 0 |
0x1695f4bf231688057ffe247e29bc033cb31d7e60 | 800000000000000002 |
0xefbadcad52f6baec237731123e638fd4e34f63c4 | 8000000000010826 |
0x01c4f84a43bc3162a6aa911b54044a9f2345325e | 0 |
0xbb101ae8ac3cec6e26575b7d6446ee1f91d83c1d | 0 |
0x76e21054127ab5fdb3ad8ba1590d2c9cf92c8959 | 200000000000000000 |
0x3b4ded25c77c7034c3c336f888285df24bd23dd1 | 0 |
0xe25e3a1947405a1f82dd8e3048a9ca471dc782e1 | 603430701484 |
0x095f5a51d06f6340d80b6d29ea2e88118ad730fe | 0 |
0xd25658414d41ab0da3f7e28f592863c5b4218101 | 1 |
0x201d38287aa29bdb16fc512776826d1c37082070 | 0 |
0xba75f066005108d58dc5c4aae3a9be53a0ea920d | 450000999999963371 |
0x9f73bc871764c879fd9e0f524278373fa7875068 | 147775869041 |
0xa96c7266e081c08c911b91081a8c0b5fdc5d3f8b | 12 |
0x26bf32bb4efc2c6c73c3819ad7adf18c494d9afe | 1960000000000000260 |
0x4406a2c7867470130b06e688c3542122081b7e4a | 1567 |
0x9fff802e6bdcbcc47c20c33446e1fd99eb72c705 | 31000 |
0x859d231d76c0d796bcf46a7304b086e6328682d5 | 336047014800 |
0x660cf0d7ba6aba3d5c5ff2131e1a1404afa7a000 | 71000000000000000 |
0x9006a41cbdfbdde6802f63b1795207b56cf0fd99 | 1004210615581750 |
0x0b6a778c8f6bffce3d484cdc78329eb29175bef4 | 505439423237 |
0xf27960b52ef2cf8c62b145cc3238cb5344386aa2 | 999999999686 |
0x7d1cd61f6153efd679963d101c5c49374989c7e7 | 0 |
0x26bdce6e4ea9afd060049993ed11f153eb1e322f | 2 |
0x57573706e969b480d1fc3e38e7537d71b5e3cdcd | 0 |
0x0d53ac8163d98791e77b23da0b5097552f6b6753 | 888888888889 |
0x0d05b9f9ed0b149d91d91ce7ba89df11e9abb53f | 12592999000000000 |
0xb7e4e93fbabf3904d28564574b3692293e6ea476 | 0 |
0xdc99b79555385ab2fe0ff28c3c954a07b28aac5e | 920000000439 |
0x131ce849aadf6624e73b06dd7b7ba7e3c98a6a9b | 460952381000 |
0x6a6af0730497634e8c6be9ee2b88eb0e696f1a55 | 0 |
0x8fdb7441bd9263499af346135153a737aa9b3fbb | 0 |
0x271b45faf56b0ec406e64ab39b80ec187de71e8c | 0 |
0x0009be88c3bf9e0fd0c43ad84ae994d8424fee03 | 0 |
0x76df61770a702069fe1381eae55ad11a03deca93 | 50000000000000000000 |
0xf8bda96b67036ee48107f2a0695ea673479dda56 | 3060000000000000000 |
0x59b03f715990e90bf7f1317d3dcf50de67fad351 | 0 |
0x154a7453cec1d76ea38e630c9f852a96e2a97ac5 | 850000000000000000 |
0xd8da6bf26964af9d7eed9e03e53415d37aa96045 | 22022399999999999180 |
0x5586b392b5127598e9b3a4635131bc7bbda20c2b | 40000111111110591 |
0xcd185269dbaf38ff5d2b5845dd11c62d6d833c22 | 180000000000000000 |
0xf9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6 | 35103958542668000000 |
0xf764bd7cd53cf07ec70b9c6df16e6c9239b03be9 | 0 |
0x417ab2c56f9509c2cc07a1ada72fbb382f662280 | 820 |
0xc44f4ab5bc60397c737eb0683391b633f83c48fa | 10000000000000000 |
0x46b515cd71a19221ee768c88f492a741108ef3f0 | 0 |
0x35d68fb24df0e54d44e6cc32f75f1cc891f57811 | 999999999561 |
0xc33b624c14147e8f5d22266c8cd56241fa90d1ae | 335545090012554050 |
0x90f011a326b6ecb37284eb5e2113d71228d0187f | 619047619000 |
0xa8c8b89fd99a25b4a085dff3d967b47b10b37034 | 0 |
0x5781aab44ceb682eed8afd9b63cbf0f4edefb5e3 | 15000000000000000000 |
Address | Token | Ammount |
---|---|---|
0x095f5a51d06f6340d80b6d29ea2e88118ad730fe | ETH | 600000000000000000000 |
0x0d05b9f9ed0b149d91d91ce7ba89df11e9abb53f | ETH | 320000001000000000 |
0x0d05b9f9ed0b149d91d91ce7ba89df11e9abb53f | MKR | 90000000000000000 |
0xb7e4e93fbabf3904d28564574b3692293e6ea476 | ETH | 50000000000000000000 |
0xf9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6 | MKR | 1717247649891466400000 |
0xf9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6 | ETH | 4796250000000000000000 |
0x154a7453cec1d76ea38e630c9f852a96e2a97ac5 | ETH | 3020000000000000000 |
0x8af93c5312e91fdb0a4c07e5e27256b2967fc926 | MKR | 4000000000000000000000 |
0xcd185269dbaf38ff5d2b5845dd11c62d6d833c22 | ETH | 2000000000000000000 |
0x90f011a326b6ecb37284eb5e2113d71228d0187f | MKR | 59250000000000000000 |
0x201d38287aa29bdb16fc512776826d1c37082070 | ETH | 60000000000000000000 |
0xd8da6bf26964af9d7eed9e03e53415d37aa96045 | ETH | 182315100000000000000 |
0xd8da6bf26964af9d7eed9e03e53415d37aa96045 | MKR | 24486215538847118000 |
0x26bdce6e4ea9afd060049993ed11f153eb1e322f | ETH | 48110000000000000000 |
make sure you have dapple installed on your system and an ethereum homestead rpc node running on localhost:8545
- clone this repo in git and make sure all submodules are present
git clone [email protected]:nexusdev/hack-recovery.git --recursive
cd hack-recovery
git submodule update --init --recursive
- install the needed npm dependencies
npm i
- run the script
make
"use strict";
The block height before the attack is 1679390 so we will retreive the needed information from this block.
const blockHeight = 1679390;
- DSEthToken
- find all addresses which could hold tokens
- find balances for each address
- generate and save as object
- SimpleMarket
- find number of offers
- find all offers
- filter for valid offers
- sort and group offers by address and token
- generate and save as object
first we set up our environment
var Web3 = require('web3');
var _ = require('lodash');
var async = require('async');
var BigNumber = require('bignumber.js');
var fs = require('fs');
var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
The first contract we want to rescue is the DSEthToken contract.
The known vulnerability was discovered in
dappsys@92a4fd915b69ccb2553994af3bded429a9467417
Here we load its interface after we build it previously
var dappsys = require('./dappsys.json');
var DSEthToken = web3.eth.contract(JSON.parse(dappsys.DSEthToken.interface));
var dsEthToken = DSEthToken.at('0xd654bdd32fc99471455e86c2e7f7d7b6437e9179');
We are interested in the balances of each address.
Unfortunately we are dealing here with a mapping:
mapping( address => uint ) _balances;
therefore its not possible to query all addresses which hold tokens.
However, we can filter all the Deposit and Transfer events to learn
about all possible addresses, which could hold any tokens
var deposits = dsEthToken.Deposit({},{fromBlock: 0, toBlock: blockHeight});
var transfears = dsEthToken.Transfer({},{fromBlock: 0, toBlock: blockHeight});
var getAllEvents = async.parallel.bind(async, [
deposits.get.bind(deposits),
transfears.get.bind(transfears)
]);
After we retrieve those events, we have to filter them for addresses, which could possibly hold ether: In case of a Deposit, the sender(who) could have ether In case of a Transfer, the receiver(to) could have ether
var filterDeposits = function (event) {
return event.args.who;
}
var filterTransfear = function (event) {
return event.args.to;
}
Apply the filter and concatenate the addresses to one array also we are just interested in unique addresses
var filterAddresses = function (res, cb) {
let deposits = res[0];
let transfears = res[1];
let addresses =
deposits.map(filterDeposits)
.concat(transfears.map(filterTransfear))
cb(null, _.uniq(addresses));
}
Now we have just to lookup and output the balances of each address.
var getAllBalances = (addresses, cb) => {
async.parallel(
addresses.map(address => dsEthToken.balanceOf.bind(dsEthToken, address, blockHeight)),
(err, balances) => {cb(err, addresses, balances)})
};
Generate a balances object, which is a mapping (address => balance) and save it as dsEthToken.json
var saveBalances = function (addresses, balances, cb) {
let totalSum = new BigNumber(0);
let savedBalances = {};
console.log('\nResults for DSEthToken:');
balances.forEach((balance, index) => {
totalSum = totalSum.plus(balance);
savedBalances[addresses[index]] = balance.toString(10);
console.log(addresses[index], balance.toString(10) );
});
fs.writeFileSync('dsEthToken.json', JSON.stringify(savedBalances, false, 2));
console.log("Total Sum:", web3.fromWei(totalSum,'ether').toString(10)+"eth");
console.log("balances saved to ./dsEthToken.json");
cb(null, savedBalances);
}
Now we created all our tasks to to retrieve all important information for dsEthToken.
var getDsEthTokenBalances = async.waterfall.bind(this, [
getAllEvents,
filterAddresses,
getAllBalances,
saveBalances
]);
Next we will rescue the funds from Maker-OTC, in particular all active orders from SimpleMarket@0xf51bc4633f5924465c8c6317169faf3e4312e82f
var makerotc = require('./maker-otc.json');
var SimpleMarket = web3.eth.contract(JSON.parse(makerotc.SimpleMarket.interface));
var simpleMarket = SimpleMarket.at('0xf51bc4633f5924465c8c6317169faf3e4312e82f');
First we need to know how many orders there are:
var getOrderNumber = simpleMarket.last_offer_id.bind(simpleMarket);
With that info we can get all orders with.
var getOffer = simpleMarket.offers.bind(simpleMarket);
We can get all offers provided we know how many there are.
var getAllOffers = (number, cb) => async.mapSeries.bind(async, _.range(number), getOffer, cb)()
Also we are just interested in active orders and in particular in who sells how much of what.
var filterOffers = (offers, cb) => {
Get only active offers
let interestingOffers = offers.filter(offer => offer[5]);
and return interesting properties.
let interestingProperties = interestingOffers.map(offer => ({
owner: offer[4],
token: web3.toAscii(offer[1]).replace(/\u0000/g,''),
ammount: offer[0]
}));
cb(null, interestingProperties);
}
After we got all the interesting stuff, we can sum over each user and toke to get the total offered amount.
var constructInterestingObject = (offers, cb) => {
var balances = {}; // mapping (address => token => balance)
offers.forEach(offer => {
Ensure we are aware of the owner.
if (!(offer.owner in balances)) balances[offer.owner] = {};
In case we are (owner => token) aware => we add the token to the known balance in case we are not token aware => we simply add it
if (offer.token in balances[offer.owner]) {
balances[offer.owner][offer.token] =
balances[offer.owner][offer.token].plus(offer.ammount);
} else {
balances[offer.owner][offer.token] =
offer.ammount;
}
});
After this we also format the balance to decimals and save it as simpleMarket.json
console.log('\nResults for SimpleMarket:');
_.each(balances, (tokens, owner) => {
_.each(tokens, (balance, token) => {
balances[owner][token] = balance.toString(10);
console.log(owner, token, balance.toString(10));
});
});
fs.writeFileSync('simpleMarket.json', JSON.stringify(balances, false, 2));
cb(null, balances);
}
Now we combine all our tasks to retrieve the balances for SimpleMarket
var getSimpleMarketBalances = async.waterfall.bind(this, [
getOrderNumber,
getAllOffers,
filterOffers,
constructInterestingObject
])
After this we generate a human readable document with all relevant information:
var genDoc = function (err, docs) {
let dsEthToken = docs[0];
let simpleMarket = docs[1];
var readmeTemplate = fs.readFileSync('README.md.tmp', {encoding: 'utf8'});
var indexMd = fs.readFileSync('index.md', {encoding: 'utf8'});
Generate dsEthToken table. Generate simpleMarket table.
var dsEthTokenTable = `| Address | Ammount |\n| ------------- | ------------- |\n`
+ _.map(dsEthToken, (balance, address) => `| ${address} | ${balance} |`).join('\n');
var simpleMarketTable = `| Address | Token | Ammount |\n| ------------- | ------------- | ------------- |\n`
+ _.flatten(_.map(simpleMarket, (tokens, address) =>
_.map(tokens, (balance, token) =>
`| ${address} | ${token} | ${balance} |`))
).join('\n');
var scope = {
how: indexMd,
dsEthToken: dsEthTokenTable,
simpleMarket: simpleMarketTable
};
Generate and save the readme file.
var template = _.template(readmeTemplate);
var readme = template(scope);
fs.writeFileSync('README.md', readme);
}
Run the tasks.
async.parallel([
getDsEthTokenBalances,
getSimpleMarketBalances
], genDoc);
console.log('running the tasks, this may take several minutes...');