mirror of
https://github.com/feather-wallet/feather.git
synced 2024-12-23 03:59:29 +00:00
CLI: add wallet bruteforce
This commit is contained in:
parent
bb85f020d5
commit
c929a7af40
7 changed files with 271 additions and 15 deletions
85
src/cli.cpp
85
src/cli.cpp
|
@ -5,8 +5,10 @@
|
||||||
|
|
||||||
// libwalletqt
|
// libwalletqt
|
||||||
#include "libwalletqt/TransactionHistory.h"
|
#include "libwalletqt/TransactionHistory.h"
|
||||||
|
#include "libwalletqt/WalletManager.h"
|
||||||
#include "model/AddressBookModel.h"
|
#include "model/AddressBookModel.h"
|
||||||
#include "model/TransactionHistoryModel.h"
|
#include "model/TransactionHistoryModel.h"
|
||||||
|
#include "utils/brute.h"
|
||||||
|
|
||||||
CLI::CLI(AppContext *ctx, QObject *parent) :
|
CLI::CLI(AppContext *ctx, QObject *parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
|
@ -17,23 +19,86 @@ CLI::CLI(AppContext *ctx, QObject *parent) :
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLI::run() {
|
void CLI::run() {
|
||||||
if(mode == CLIMode::CLIModeExportContacts ||
|
if (mode == CLIMode::ExportContacts || mode == CLIMode::ExportTxHistory)
|
||||||
mode == CLIMode::CLIModeExportTxHistory) {
|
{
|
||||||
if(!ctx->cmdargs->isSet("wallet-file")) return this->finishedError("--wallet-file argument missing");
|
if(!ctx->cmdargs->isSet("wallet-file"))
|
||||||
if(!ctx->cmdargs->isSet("password")) return this->finishedError("--password argument missing");
|
return this->finishedError("--wallet-file argument missing");
|
||||||
|
if(!ctx->cmdargs->isSet("password"))
|
||||||
|
return this->finishedError("--password argument missing");
|
||||||
ctx->onOpenWallet(ctx->cmdargs->value("wallet-file"), ctx->cmdargs->value("password"));
|
ctx->onOpenWallet(ctx->cmdargs->value("wallet-file"), ctx->cmdargs->value("password"));
|
||||||
}
|
}
|
||||||
|
else if (mode == CLIMode::BruteforcePassword)
|
||||||
|
{
|
||||||
|
QString keys_file = ctx->cmdargs->value("bruteforce-password");
|
||||||
|
if (!keys_file.endsWith(".keys")) {
|
||||||
|
return this->finishedError("Wallet file does not end with .keys");
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList words;
|
||||||
|
if (ctx->cmdargs->isSet("bruteforce-dict")) {
|
||||||
|
QString data = Utils::barrayToString(Utils::fileOpen(ctx->cmdargs->value("bruteforce-dict")));
|
||||||
|
words = data.split("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx->cmdargs->isSet("bruteforce-chars")) {
|
||||||
|
return this->finishedError("--bruteforce-chars argument missing");
|
||||||
|
}
|
||||||
|
QString chars = ctx->cmdargs->value("bruteforce-chars");
|
||||||
|
|
||||||
|
brute b(chars.toStdString());
|
||||||
|
if (words.isEmpty()) {
|
||||||
|
qDebug() << "No dictionairy specified, bruteforcing all chars";
|
||||||
|
while (true) {
|
||||||
|
QString pass = QString::fromStdString(b.next());
|
||||||
|
if (ctx->walletManager->verifyWalletPassword(keys_file, pass, false)) {
|
||||||
|
this->finished(QString("Found password: %1").arg(pass));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
qDebug() << pass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bruteword bb(chars.toStdString());
|
||||||
|
bool foundPass = false;
|
||||||
|
for (auto word: words) {
|
||||||
|
if (word.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bb.setWord(word.toStdString());
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
QString pass = QString::fromStdString(bb.next());
|
||||||
|
if (pass == "") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ctx->walletManager->verifyWalletPassword(keys_file, pass, false)) {
|
||||||
|
this->finished(QString("Found password: %1").arg(pass));
|
||||||
|
foundPass = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
qDebug() << pass;
|
||||||
|
}
|
||||||
|
if (foundPass) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundPass) {
|
||||||
|
this->finished("Search space exhausted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLI::onWalletOpened() {
|
void CLI::onWalletOpened() {
|
||||||
if(mode == CLIMode::CLIModeExportContacts){
|
if(mode == CLIMode::ExportContacts){
|
||||||
auto *model = ctx->currentWallet->addressBookModel();
|
auto *model = ctx->currentWallet->addressBookModel();
|
||||||
auto fn = ctx->cmdargs->value("export-contacts");
|
auto fn = ctx->cmdargs->value("export-contacts");
|
||||||
if(model->writeCSV(fn))
|
if(model->writeCSV(fn))
|
||||||
this->finished(QString("Address book exported to %1").arg(fn));
|
this->finished(QString("Address book exported to %1").arg(fn));
|
||||||
else
|
else
|
||||||
this->finishedError("Address book export failure");
|
this->finishedError("Address book export failure");
|
||||||
} else if(mode == CLIModeExportTxHistory) {
|
} else if(mode == ExportTxHistory) {
|
||||||
ctx->currentWallet->history()->refresh(ctx->currentWallet->currentSubaddressAccount());
|
ctx->currentWallet->history()->refresh(ctx->currentWallet->currentSubaddressAccount());
|
||||||
auto *model = ctx->currentWallet->history();
|
auto *model = ctx->currentWallet->history();
|
||||||
auto fn = ctx->cmdargs->value("export-txhistory");
|
auto fn = ctx->cmdargs->value("export-txhistory");
|
||||||
|
@ -45,14 +110,14 @@ void CLI::onWalletOpened() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLI::onWalletOpenedError(const QString &err) {
|
void CLI::onWalletOpenedError(const QString &err) {
|
||||||
if(mode == CLIMode::CLIModeExportContacts ||
|
if(mode == CLIMode::ExportContacts ||
|
||||||
mode == CLIMode::CLIModeExportTxHistory)
|
mode == CLIMode::ExportTxHistory)
|
||||||
return this->finishedError(err);
|
return this->finishedError(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLI::onWalletOpenPasswordRequired(bool invalidPassword, const QString &path) {
|
void CLI::onWalletOpenPasswordRequired(bool invalidPassword, const QString &path) {
|
||||||
if(mode == CLIMode::CLIModeExportContacts ||
|
if(mode == CLIMode::ExportContacts ||
|
||||||
mode == CLIMode::CLIModeExportTxHistory)
|
mode == CLIMode::ExportTxHistory)
|
||||||
return this->finishedError("invalid password");
|
return this->finishedError("invalid password");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,9 @@
|
||||||
#include "appcontext.h"
|
#include "appcontext.h"
|
||||||
|
|
||||||
enum CLIMode {
|
enum CLIMode {
|
||||||
CLIModeExportContacts,
|
ExportContacts,
|
||||||
CLIModeExportTxHistory
|
ExportTxHistory,
|
||||||
|
BruteforcePassword
|
||||||
};
|
};
|
||||||
|
|
||||||
class CLI : public QObject
|
class CLI : public QObject
|
||||||
|
|
|
@ -225,6 +225,11 @@ bool WalletManager::walletExists(const QString &path) const
|
||||||
return m_pimpl->walletExists(path.toStdString());
|
return m_pimpl->walletExists(path.toStdString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WalletManager::verifyWalletPassword(const QString &keys_file_name, const QString &password, bool no_spend_key, uint64_t kdf_rounds) const
|
||||||
|
{
|
||||||
|
return m_pimpl->verifyWalletPassword(keys_file_name.toStdString(), password.toStdString(), no_spend_key, kdf_rounds);
|
||||||
|
}
|
||||||
|
|
||||||
QStringList WalletManager::findWallets(const QString &path)
|
QStringList WalletManager::findWallets(const QString &path)
|
||||||
{
|
{
|
||||||
std::vector<std::string> found_wallets = m_pimpl->findWallets(path.toStdString());
|
std::vector<std::string> found_wallets = m_pimpl->findWallets(path.toStdString());
|
||||||
|
|
|
@ -105,6 +105,9 @@ public:
|
||||||
//! checks is given filename is a wallet;
|
//! checks is given filename is a wallet;
|
||||||
Q_INVOKABLE bool walletExists(const QString &path) const;
|
Q_INVOKABLE bool walletExists(const QString &path) const;
|
||||||
|
|
||||||
|
//! verify wallet password
|
||||||
|
Q_INVOKABLE bool verifyWalletPassword(const QString &keys_file_name, const QString &password, bool no_spend_key, uint64_t kdf_rounds = 1) const;
|
||||||
|
|
||||||
//! returns list with wallet's filenames, if found by given path
|
//! returns list with wallet's filenames, if found by given path
|
||||||
Q_INVOKABLE QStringList findWallets(const QString &path);
|
Q_INVOKABLE QStringList findWallets(const QString &path);
|
||||||
|
|
||||||
|
|
19
src/main.cpp
19
src/main.cpp
|
@ -78,6 +78,15 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||||
QCommandLineOption exportTxHistoryOption(QStringList() << "export-txhistory", "Output wallet transaction history as CSV to specified path.", "file");
|
QCommandLineOption exportTxHistoryOption(QStringList() << "export-txhistory", "Output wallet transaction history as CSV to specified path.", "file");
|
||||||
parser.addOption(exportTxHistoryOption);
|
parser.addOption(exportTxHistoryOption);
|
||||||
|
|
||||||
|
QCommandLineOption bruteforcePasswordOption(QStringList() << "bruteforce-password", "Bruteforce wallet password", "file");
|
||||||
|
parser.addOption(bruteforcePasswordOption);
|
||||||
|
|
||||||
|
QCommandLineOption bruteforceCharsOption(QStringList() << "bruteforce-chars", "Chars used to bruteforce password", "string");
|
||||||
|
parser.addOption(bruteforceCharsOption);
|
||||||
|
|
||||||
|
QCommandLineOption bruteforceDictionairy(QStringList() << "bruteforce-dict", "Bruteforce dictionairy", "file");
|
||||||
|
parser.addOption(bruteforceDictionairy);
|
||||||
|
|
||||||
auto parsed = parser.parse(argv_);
|
auto parsed = parser.parse(argv_);
|
||||||
if(!parsed) {
|
if(!parsed) {
|
||||||
qCritical() << parser.errorText();
|
qCritical() << parser.errorText();
|
||||||
|
@ -92,7 +101,8 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||||
bool quiet = parser.isSet(quietModeOption);
|
bool quiet = parser.isSet(quietModeOption);
|
||||||
bool exportContacts = parser.isSet(exportContactsOption);
|
bool exportContacts = parser.isSet(exportContactsOption);
|
||||||
bool exportTxHistory = parser.isSet(exportTxHistoryOption);
|
bool exportTxHistory = parser.isSet(exportTxHistoryOption);
|
||||||
bool cliMode = exportContacts || exportTxHistory;
|
bool bruteforcePassword = parser.isSet(bruteforcePasswordOption);
|
||||||
|
bool cliMode = exportContacts || exportTxHistory || bruteforcePassword;
|
||||||
|
|
||||||
if(cliMode) {
|
if(cliMode) {
|
||||||
QCoreApplication cli_app(argc, argv);
|
QCoreApplication cli_app(argc, argv);
|
||||||
|
@ -110,12 +120,15 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||||
if(exportContacts) {
|
if(exportContacts) {
|
||||||
if(!quiet)
|
if(!quiet)
|
||||||
qInfo() << "CLI mode: Address book export";
|
qInfo() << "CLI mode: Address book export";
|
||||||
cli->mode = CLIMode::CLIModeExportContacts;
|
cli->mode = CLIMode::ExportContacts;
|
||||||
QTimer::singleShot(0, cli, &CLI::run);
|
QTimer::singleShot(0, cli, &CLI::run);
|
||||||
} else if(exportTxHistory) {
|
} else if(exportTxHistory) {
|
||||||
if(!quiet)
|
if(!quiet)
|
||||||
qInfo() << "CLI mode: Transaction history export";
|
qInfo() << "CLI mode: Transaction history export";
|
||||||
cli->mode = CLIMode::CLIModeExportTxHistory;
|
cli->mode = CLIMode::ExportTxHistory;
|
||||||
|
QTimer::singleShot(0, cli, &CLI::run);
|
||||||
|
} else if(bruteforcePassword) {
|
||||||
|
cli->mode = CLIMode::BruteforcePassword;
|
||||||
QTimer::singleShot(0, cli, &CLI::run);
|
QTimer::singleShot(0, cli, &CLI::run);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
122
src/utils/brute.cpp
Normal file
122
src/utils/brute.cpp
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
#include "brute.h"
|
||||||
|
|
||||||
|
brute::brute(const std::string &chars) {
|
||||||
|
this->chars = chars;
|
||||||
|
this->len = chars.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string brute::next() {
|
||||||
|
std::string out;
|
||||||
|
int c = count;
|
||||||
|
int i;
|
||||||
|
while (c >= 0){
|
||||||
|
i = (c % len);
|
||||||
|
out = chars[i] + out;
|
||||||
|
c = (c / len) - 1;
|
||||||
|
}
|
||||||
|
count += 1;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bruteword::bruteword(const std::string &chars) {
|
||||||
|
this->m_chars = chars;
|
||||||
|
this->m_currentStrategy = strategy::ReplaceSingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bruteword::setWord(const std::string &word) {
|
||||||
|
this->m_currentStrategy = strategy::ReplaceSingle;
|
||||||
|
m_word = word;
|
||||||
|
resetIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
void bruteword::nextStrategy() {
|
||||||
|
m_currentStrategy = static_cast<strategy>(static_cast<int>(m_currentStrategy) + 1);
|
||||||
|
resetIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
void bruteword::resetIndex() {
|
||||||
|
iword = 0;
|
||||||
|
ichar = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string bruteword::next() {
|
||||||
|
std::string out = m_word;
|
||||||
|
|
||||||
|
switch(m_currentStrategy) {
|
||||||
|
case strategy::ReplaceSingle: {
|
||||||
|
if (iword >= m_word.length()) {
|
||||||
|
this->nextStrategy();
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
out[iword] = m_chars[ichar];
|
||||||
|
ichar++;
|
||||||
|
if (ichar == m_chars.length()){
|
||||||
|
ichar = 0;
|
||||||
|
iword++;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
case strategy::AppendSingle: {
|
||||||
|
if (ichar >= m_chars.length()) {
|
||||||
|
this->nextStrategy();
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
out += m_chars[ichar];
|
||||||
|
ichar++;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
case strategy::PrependSingle: {
|
||||||
|
if (ichar >= m_chars.length()) {
|
||||||
|
this->nextStrategy();
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
out = m_chars[ichar] + out;
|
||||||
|
ichar++;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
case strategy::SwitchLetters: {
|
||||||
|
if (iword+1 >= out.length()) {
|
||||||
|
this->nextStrategy();
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
char l = out[iword];
|
||||||
|
out[iword] = out[iword+1];
|
||||||
|
out[iword+1] = l;
|
||||||
|
iword++;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
case strategy::DeleteSingle: {
|
||||||
|
if (iword >= out.length()) {
|
||||||
|
this->nextStrategy();
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
out = out.substr(0, iword) + out.substr(iword+1);
|
||||||
|
iword++;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
case strategy::AddSingle: {
|
||||||
|
if (iword+1 >= out.length()) {
|
||||||
|
this->nextStrategy();
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
out = out.substr(0, iword+1) + m_chars[ichar] + out.substr(iword+1);
|
||||||
|
ichar++;
|
||||||
|
if (ichar == m_chars.length()) {
|
||||||
|
ichar = 0;
|
||||||
|
iword++;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
case strategy::END: {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
47
src/utils/brute.h
Normal file
47
src/utils/brute.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#ifndef FEATHER_BRUTE_H
|
||||||
|
#define FEATHER_BRUTE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class brute {
|
||||||
|
public:
|
||||||
|
explicit brute(const std::string &chars);
|
||||||
|
|
||||||
|
std::string next();
|
||||||
|
|
||||||
|
std::string chars;
|
||||||
|
std::string current;
|
||||||
|
int len;
|
||||||
|
int count = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class bruteword {
|
||||||
|
public:
|
||||||
|
enum strategy {
|
||||||
|
ReplaceSingle = 0,
|
||||||
|
AppendSingle,
|
||||||
|
PrependSingle,
|
||||||
|
SwitchLetters,
|
||||||
|
DeleteSingle,
|
||||||
|
AddSingle,
|
||||||
|
END
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit bruteword(const std::string &chars);
|
||||||
|
void nextStrategy();
|
||||||
|
void setWord(const std::string &word);
|
||||||
|
|
||||||
|
std::string next();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void resetIndex();
|
||||||
|
|
||||||
|
std::string m_chars;
|
||||||
|
std::string m_word;
|
||||||
|
strategy m_currentStrategy;
|
||||||
|
|
||||||
|
int iword = 0;
|
||||||
|
int ichar = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //FEATHER_BRUTE_H
|
Loading…
Reference in a new issue