CLI: add wallet bruteforce

This commit is contained in:
tobtoht 2021-02-24 11:57:35 +01:00
parent bb85f020d5
commit c929a7af40
No known key found for this signature in database
GPG key ID: 1CADD27F41F45C3C
7 changed files with 271 additions and 15 deletions

View file

@ -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");
} }

View file

@ -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

View file

@ -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());

View file

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

View file

@ -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
View 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
View 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