diff --git a/src/helpers/CMakeLists.txt b/src/helpers/CMakeLists.txt index 7bc2bb3..7849f23 100644 --- a/src/helpers/CMakeLists.txt +++ b/src/helpers/CMakeLists.txt @@ -15,6 +15,7 @@ add_library (HELPERS STATIC request_builder.h screenshot.cpp screenshot.h stopreply.cpp stopreply.h + string_helpers.h system_helpers.h ui_helpers.h hotkeys/hotkeymap.h diff --git a/src/helpers/file_helpers.h b/src/helpers/file_helpers.h index 5539f3f..e43b570 100644 --- a/src/helpers/file_helpers.h +++ b/src/helpers/file_helpers.h @@ -6,19 +6,6 @@ class FileHelpers { public: - /** - * @brief randomString Generates a random String of N chars - * Each character can be a-z either be upper or lower case. - * @param numberOfChars Length of the string to return default is 6 - * @return The resulting randomString - */ - static QString randomString(int numberOfChars = 6) { - QString rString; - for(int i = 0; i < numberOfChars; i++) - rString.append(_chars.at(QRandomGenerator::global()->bounded(_chars.length()))); - return rString; - } - /// writeFile write the provided content to the provided path. /// returns false if failed. static bool writeFile(QString fileName, QByteArray content) { @@ -49,7 +36,4 @@ class FileHelpers { /// getDirname is a small helper to convert a filepath to a file into a path to the file's parent static QString getDirname(QString filepath) { return QFileInfo(filepath).dir().path(); } - - private: - inline static const QString _chars = QStringLiteral("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); }; diff --git a/src/helpers/multipartparser.cpp b/src/helpers/multipartparser.cpp index b527833..0738dcb 100644 --- a/src/helpers/multipartparser.cpp +++ b/src/helpers/multipartparser.cpp @@ -1,104 +1,46 @@ /********************************************************************************* * File Name : multipartparser.cpp - * Created By : Ye Yangang - * Creation Date : [2017-02-20 16:50] + * Created By : Ye Yangang, ReWritten for Qt by Chris Rizzitello + * Creation Date : [2017-02-20 16:50] Modified for Qt 7/5/2022 * Last Modified : [AUTO_UPDATE_BEFORE_SAVE] * Description : Generate multipart/form-data POST body **********************************************************************************/ #include "multipartparser.h" -#include -#include -#include -#include +#include -const std::string MultipartParser::boundary_prefix_("----ASHIRTTrayApp"); -const std::string MultipartParser::rand_chars_( - "0123456789" - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); -MultipartParser::MultipartParser() { - int i = 0; - int len = rand_chars_.size(); - boundary_ = boundary_prefix_; - while (i < 16) { - int idx = rand() % len; - boundary_.push_back(rand_chars_[idx]); - ++i; - } -} - -const std::string &MultipartParser::GenBodyContent() { - std::vector> futures; - body_content_.clear(); - for (auto &file : files_) { - std::future content_futures = std::async(std::launch::async, [&file]() { - std::ifstream ifile(file.second, std::ios::binary | std::ios::ate); - std::streamsize size = ifile.tellg(); - ifile.seekg(0, std::ios::beg); - char *buff = new char[size]; - ifile.read(buff, size); - ifile.close(); - std::string ret(buff, size); - delete[] buff; - return ret; - }); - futures.push_back(std::move(content_futures)); - } - - for (auto ¶m : params_) { - body_content_ += "\r\n--"; - body_content_ += boundary_; - body_content_ += "\r\nContent-Disposition: form-data; name=\""; - body_content_ += param.first; - body_content_ += "\"\r\n\r\n"; - body_content_ += param.second; - } - - for (size_t i = 0; i < files_.size(); ++i) { - std::string filename; - std::string content_type; - std::string file_content = futures[i].get(); - _get_file_name_type(files_[i].second, &filename, &content_type); - body_content_ += "\r\n--"; - body_content_ += boundary_; - body_content_ += "\r\nContent-Disposition: form-data; name=\""; - body_content_ += files_[i].first; - body_content_ += "\"; filename=\""; - body_content_ += filename; - body_content_ += "\"\r\nContent-Type: "; - body_content_ += content_type; - body_content_ += "\r\n\r\n"; - body_content_ += file_content; - } - body_content_ += "\r\n--"; - body_content_ += boundary_; - body_content_ += "--\r\n"; - return body_content_; -} - -void MultipartParser::_get_file_name_type(const std::string &file_path, std::string *filename, - std::string *content_type) { - if (filename == NULL || content_type == NULL) return; - - size_t last_spliter = file_path.find_last_of("/\\"); - *filename = file_path.substr(last_spliter + 1); - size_t dot_pos = filename->find_last_of("."); - if (dot_pos == std::string::npos) { - *content_type = "application/octet-stream"; - return; - } - std::string ext = filename->substr(dot_pos + 1); - std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); - if (ext == "jpg" || ext == "jpeg") { - *content_type = "image/jpeg"; - return; - } - if (ext == "txt" || ext == "log") { - *content_type = "text/plain"; - return; - } - *content_type = "application/octet-stream"; - return; +#include "string_helpers.h" + +MultipartParser::MultipartParser() + : m_boundary(QStringLiteral("----ASHIRTTrayApp%1").arg(StringHelpers::randomString(16))) +{ } + +const QByteArray &MultipartParser::generateBody() +{ + m_body.clear(); + for (const auto ¶m : m_paramList) { + m_body.append(m_contentHeader.arg(m_boundary).toUtf8()); + m_body.append(m_contentParam.arg(param.first).toUtf8()); + m_body.append(param.second.toUtf8()); + } + for (const auto &pair : m_fileList) { + QFileInfo info = QFileInfo(pair.second); + QString name = info.fileName(); + QString ext = info.completeSuffix().toLower(); + QString type = QStringLiteral("application/octet-stream"); + QByteArray data; + QFile file(pair.second); + if (file.open(QIODevice::ReadOnly)) + data = file.readAll(); + if(ext.endsWith(QStringLiteral("jpg")) || ext.endsWith(QStringLiteral("jpeg"))) + type = QStringLiteral("image/jpeg"); + else if(ext.endsWith(QStringLiteral("txt")) || ext.endsWith(QStringLiteral("log"))) + type = QStringLiteral("text/plain"); + m_body.append(m_contentHeader.arg(m_boundary).toUtf8()); + m_body.append(m_contentFile.arg(pair.first, name, type).toUtf8()); + m_body.append(data); + } + m_body.append(QStringLiteral("\r\n--%1--\r\n").arg(m_boundary).toUtf8()); + return m_body; } diff --git a/src/helpers/multipartparser.h b/src/helpers/multipartparser.h index e342215..ab73574 100644 --- a/src/helpers/multipartparser.h +++ b/src/helpers/multipartparser.h @@ -1,38 +1,34 @@ /********************************************************************************* * File Name : multipartparser.h - * Created By : Ye Yangang - * Creation Date : [2017-02-20 16:50] + * Created By : Ye Yangang, ReWritten for Qt by Chris Rizzitello + * Creation Date : [2017-02-20 16:50] Modified for Qt 7/5/2022 * Last Modified : [AUTO_UPDATE_BEFORE_SAVE] * Description : Generate multipart/form-data POST body **********************************************************************************/ #pragma once -#include -#include +#include +#include +#include class MultipartParser { public: MultipartParser(); - inline const std::string &body_content() { return body_content_; } - inline const std::string &boundary() { return boundary_; } - inline void AddParameter(const std::string &name, const std::string &value) { - params_.push_back(std::pair(name, value)); + inline const QString &boundary() {return m_boundary;} + inline void addParameter(const QString &name = QString(), const QString &value = QString()) { + m_paramList.append(QPair(name, value)); } - inline void AddFile(const std::string &name, const std::string &value) { - files_.push_back(std::pair(name, value)); + inline void addFile(const QString &name = QString(), const QString &value = QString()) { + m_fileList.append(QPair(name, value)); } - const std::string &GenBodyContent(); - + const QByteArray &generateBody(); private: - void _get_file_name_type(const std::string &file_path, std::string *filenae, - std::string *content_type); - - private: - static const std::string boundary_prefix_; - static const std::string rand_chars_; - std::string boundary_; - std::string body_content_; - std::vector> params_; - std::vector> files_; + inline static const auto m_contentHeader = QStringLiteral("\r\n--%1\r\n"); + inline static const auto m_contentParam = QStringLiteral("Content-Disposition: form-data; name=\"%1\"\r\n\r\n"); + inline static const auto m_contentFile = QStringLiteral("Content-Disposition: form-data; name=\"%1\"; filename=\"%2\"\r\nContent-Type: %3\r\n\r\n"); + QString m_boundary; + QByteArray m_body; + QList> m_paramList; + QList> m_fileList; }; diff --git a/src/helpers/netman.h b/src/helpers/netman.h index ac91b6c..4d5b115 100644 --- a/src/helpers/netman.h +++ b/src/helpers/netman.h @@ -69,8 +69,8 @@ class NetMan : public QObject { /// ashirtFormPost generates a basic POST request with content type multipart/form-data. /// No authentication is provided (use addASHIRTAuth to do this) - RequestBuilder* ashirtFormPost(QString endpoint, QByteArray body, QString boundry) { - return RequestBuilder::newFormPost(boundry) + RequestBuilder* ashirtFormPost(QString endpoint, QByteArray body, QString boundary) { + return RequestBuilder::newFormPost(boundary) ->setHost(AppConfig::getInstance().apiURL) ->setEndpoint(endpoint) ->setBody(body); @@ -159,8 +159,8 @@ class NetMan : public QObject { /// rather than the time of capture. QNetworkReply *uploadAsset(model::Evidence evidence) { MultipartParser parser; - parser.AddParameter("notes", evidence.description.toStdString()); - parser.AddParameter("contentType", evidence.contentType.toStdString()); + parser.addParameter(QStringLiteral("notes"), evidence.description); + parser.addParameter(QStringLiteral("contentType"), evidence.contentType); // TODO: convert this time below into a proper unix timestamp (mSecSinceEpoch and secsSinceEpoch // produce invalid times) @@ -170,13 +170,13 @@ class NetMan : public QObject { for (const auto& tag : evidence.tags) { list << QString::number(tag.serverTagId); } - parser.AddParameter("tagIds", ("[" + list.join(",") + "]").toStdString()); + parser.addParameter(QStringLiteral("tagIds"), QStringLiteral("[%1]").arg(list.join(QStringLiteral(",")))); - parser.AddFile("file", evidence.path.toStdString()); + parser.addFile(QStringLiteral("file"), evidence.path); - auto body = QByteArray::fromStdString(parser.GenBodyContent()); + auto body = parser.generateBody(); - auto builder = ashirtFormPost(QStringLiteral("/api/operations/%1/evidence").arg(evidence.operationSlug), body, parser.boundary().c_str()); + auto builder = ashirtFormPost(QStringLiteral("/api/operations/%1/evidence").arg(evidence.operationSlug), body, parser.boundary()); addASHIRTAuth(builder); return builder->execute(nam); } diff --git a/src/helpers/request_builder.h b/src/helpers/request_builder.h index 6f29c38..ed67108 100644 --- a/src/helpers/request_builder.h +++ b/src/helpers/request_builder.h @@ -59,11 +59,11 @@ class RequestBuilder { } /// newFormPost constructs a request builder with method POST and contentType multipart/form-data - static RequestBuilder* newFormPost(QString formBoundry) { + static RequestBuilder* newFormPost(QString formboundary) { RequestBuilder* builder = new RequestBuilder(); return builder ->setMethod(METHOD_POST) - ->addKnownHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("multipart/form-data; boundary=%1").arg(formBoundry)); + ->addKnownHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("multipart/form-data; boundary=%1").arg(formboundary)); } /// newJSONPost constructs a request builder with method POST and contentType application/json diff --git a/src/helpers/screenshot.cpp b/src/helpers/screenshot.cpp index bdfa47c..0ced98c 100644 --- a/src/helpers/screenshot.cpp +++ b/src/helpers/screenshot.cpp @@ -9,7 +9,7 @@ #include #include "appconfig.h" -#include "helpers/file_helpers.h" +#include "helpers/string_helpers.h" #include "helpers/system_helpers.h" Screenshot::Screenshot(QObject *parent) : QObject(parent) {} @@ -20,7 +20,7 @@ void Screenshot::captureWindow() { basicScreenshot(AppConfig::getInstance().capt QString Screenshot::mkName() { - return QStringLiteral("ashirt_screenshot_%1.%2").arg(FileHelpers::randomString(), extension()); + return QStringLiteral("ashirt_screenshot_%1.%2").arg(StringHelpers::randomString(), extension()); } void Screenshot::basicScreenshot(QString cmdProto) diff --git a/src/helpers/string_helpers.h b/src/helpers/string_helpers.h new file mode 100644 index 0000000..97a0344 --- /dev/null +++ b/src/helpers/string_helpers.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +class StringHelpers { + public: + /** + * @brief randomString Generates a random String of N chars + * Each character can be a-z either be upper or lower case. + * @param numberOfChars Length of the string to return default is 6 + * @return The resulting randomString + */ + static QString randomString(int numberOfChars = 6) { + QString rString; + for(int i = 0; i < numberOfChars; i++) + rString.append(_chars.at(QRandomGenerator::global()->bounded(_chars.length()))); + return rString; + } + + private: + inline static const QString _chars = QStringLiteral("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); +}; diff --git a/src/models/codeblock.cpp b/src/models/codeblock.cpp index cf63f00..f3925ae 100644 --- a/src/models/codeblock.cpp +++ b/src/models/codeblock.cpp @@ -4,6 +4,7 @@ #include #include "helpers/file_helpers.h" +#include "helpers/string_helpers.h" #include "helpers/system_helpers.h" #include "helpers/jsonhelpers.h" @@ -25,7 +26,7 @@ Codeblock::Codeblock(QString content) QString Codeblock::mkName() { - return QStringLiteral("ashirt_codeblock_%1.%2").arg(FileHelpers::randomString(), extension()); + return QStringLiteral("ashirt_codeblock_%1.%2").arg(StringHelpers::randomString(), extension()); } QString Codeblock::extension() diff --git a/src/porting/system_manifest.cpp b/src/porting/system_manifest.cpp index d8d5c4c..39188f8 100644 --- a/src/porting/system_manifest.cpp +++ b/src/porting/system_manifest.cpp @@ -1,5 +1,7 @@ #include "system_manifest.h" +#include "helpers/string_helpers.h" + using namespace porting; void SystemManifest::applyManifest(SystemManifestImportOptions options, DatabaseConnection* systemDb) { @@ -93,7 +95,7 @@ QString SystemManifest::contentSensitiveFilename(const QString& contentType) return Codeblock::mkName(); if(contentType == Screenshot::contentType()) return Screenshot::mkName(); - return QStringLiteral("ashirt_unknown_type_%1.bin").arg(FileHelpers::randomString()); + return QStringLiteral("ashirt_unknown_type_%1.bin").arg(StringHelpers::randomString()); } SystemManifest* SystemManifest::readManifest(const QString& pathToExportFile) { @@ -154,7 +156,7 @@ porting::EvidenceManifest SystemManifest::copyEvidence(const QString& baseExport for (size_t evidenceIndex = 0; evidenceIndex < allEvidence.size(); evidenceIndex++) { auto evi = allEvidence.at(evidenceIndex); auto newName = QStringLiteral("ashirt_evidence_%1.%2") - .arg(FileHelpers::randomString(10), contentSensitiveExtension(evi.contentType)); + .arg(StringHelpers::randomString(10), contentSensitiveExtension(evi.contentType)); auto item = porting::EvidenceItem(evi.id, relativeEvidenceDir + "/" + newName); auto dstPath = m_fileTemplate.arg(baseExportPath, item.exportPath); QFile srcFile(evi.path);