Skip to content

nexusdev/hack-recovery

Repository files navigation

Balance Retreival

This is a "Balance Retreivar" for the recursive call vulnerability for Maker-Otc.

Results

Block: 1679390

DSEthToken @ 0xd654bdd32fc99471455e86c2e7f7d7b6437e9179

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
0xf51bc4633f5924465c8c6317169faf3e4312e82f 5742025100001000000000
0x35d68fb24df0e54d44e6cc32f75f1cc891f57811 999999999561
0xc33b624c14147e8f5d22266c8cd56241fa90d1ae 335545090012554050
0x90f011a326b6ecb37284eb5e2113d71228d0187f 619047619000
0xa8c8b89fd99a25b4a085dff3d967b47b10b37034 0
0x5781aab44ceb682eed8afd9b63cbf0f4edefb5e3 15000000000000000000

SimpleMarket @ 0xf51bc4633f5924465c8c6317169faf3e4312e82f

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

Verify

make sure you have dapple installed on your system and an ethereum homestead rpc node running on localhost:8545

  1. clone this repo in git and make sure all submodules are present
git clone git@github.com:nexusdev/hack-recovery.git --recursive
cd hack-recovery
git submodule update --init --recursive
  1. install the needed npm dependencies npm i
  2. run the script make

How it Work

"use strict";

The block height before the attack is 1679390 so we will retreive the needed information from this block.

const blockHeight = 1679390;

Overview

  1. DSEthToken
    1. find all addresses which could hold tokens
    2. find balances for each address
    3. generate and save as object
  2. SimpleMarket
    1. find number of offers
    2. find all offers
    3. filter for valid offers
    4. sort and group offers by address and token
    5. 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"));

DSEthToken

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
]);

SimpleMarket

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...');


About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published