parent
ccaa94825c
commit
240ecdc3d5
|
@ -15,6 +15,7 @@ add_library (HELPERS STATIC
|
||||||
request_builder.h
|
request_builder.h
|
||||||
screenshot.cpp screenshot.h
|
screenshot.cpp screenshot.h
|
||||||
stopreply.cpp stopreply.h
|
stopreply.cpp stopreply.h
|
||||||
|
string_helpers.h
|
||||||
system_helpers.h
|
system_helpers.h
|
||||||
ui_helpers.h
|
ui_helpers.h
|
||||||
hotkeys/hotkeymap.h
|
hotkeys/hotkeymap.h
|
||||||
|
|
|
@ -6,19 +6,6 @@
|
||||||
|
|
||||||
class FileHelpers {
|
class FileHelpers {
|
||||||
public:
|
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.
|
/// writeFile write the provided content to the provided path.
|
||||||
/// returns false if failed.
|
/// returns false if failed.
|
||||||
static bool writeFile(QString fileName, QByteArray content) {
|
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
|
/// 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(); }
|
static QString getDirname(QString filepath) { return QFileInfo(filepath).dir().path(); }
|
||||||
|
|
||||||
private:
|
|
||||||
inline static const QString _chars = QStringLiteral("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,104 +1,46 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* File Name : multipartparser.cpp
|
* File Name : multipartparser.cpp
|
||||||
* Created By : Ye Yangang
|
* Created By : Ye Yangang, ReWritten for Qt by Chris Rizzitello
|
||||||
* Creation Date : [2017-02-20 16:50]
|
* Creation Date : [2017-02-20 16:50] Modified for Qt 7/5/2022
|
||||||
* Last Modified : [AUTO_UPDATE_BEFORE_SAVE]
|
* Last Modified : [AUTO_UPDATE_BEFORE_SAVE]
|
||||||
* Description : Generate multipart/form-data POST body
|
* Description : Generate multipart/form-data POST body
|
||||||
**********************************************************************************/
|
**********************************************************************************/
|
||||||
|
|
||||||
#include "multipartparser.h"
|
#include "multipartparser.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <QFileInfo>
|
||||||
#include <fstream>
|
|
||||||
#include <future>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
const std::string MultipartParser::boundary_prefix_("----ASHIRTTrayApp");
|
#include "string_helpers.h"
|
||||||
const std::string MultipartParser::rand_chars_(
|
|
||||||
"0123456789"
|
MultipartParser::MultipartParser()
|
||||||
"abcdefghijklmnopqrstuvwxyz"
|
: m_boundary(QStringLiteral("----ASHIRTTrayApp%1").arg(StringHelpers::randomString(16)))
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
{ }
|
||||||
MultipartParser::MultipartParser() {
|
|
||||||
int i = 0;
|
const QByteArray &MultipartParser::generateBody()
|
||||||
int len = rand_chars_.size();
|
{
|
||||||
boundary_ = boundary_prefix_;
|
m_body.clear();
|
||||||
while (i < 16) {
|
for (const auto ¶m : m_paramList) {
|
||||||
int idx = rand() % len;
|
m_body.append(m_contentHeader.arg(m_boundary).toUtf8());
|
||||||
boundary_.push_back(rand_chars_[idx]);
|
m_body.append(m_contentParam.arg(param.first).toUtf8());
|
||||||
++i;
|
m_body.append(param.second.toUtf8());
|
||||||
}
|
}
|
||||||
}
|
for (const auto &pair : m_fileList) {
|
||||||
|
QFileInfo info = QFileInfo(pair.second);
|
||||||
const std::string &MultipartParser::GenBodyContent() {
|
QString name = info.fileName();
|
||||||
std::vector<std::future<std::string>> futures;
|
QString ext = info.completeSuffix().toLower();
|
||||||
body_content_.clear();
|
QString type = QStringLiteral("application/octet-stream");
|
||||||
for (auto &file : files_) {
|
QByteArray data;
|
||||||
std::future<std::string> content_futures = std::async(std::launch::async, [&file]() {
|
QFile file(pair.second);
|
||||||
std::ifstream ifile(file.second, std::ios::binary | std::ios::ate);
|
if (file.open(QIODevice::ReadOnly))
|
||||||
std::streamsize size = ifile.tellg();
|
data = file.readAll();
|
||||||
ifile.seekg(0, std::ios::beg);
|
if(ext.endsWith(QStringLiteral("jpg")) || ext.endsWith(QStringLiteral("jpeg")))
|
||||||
char *buff = new char[size];
|
type = QStringLiteral("image/jpeg");
|
||||||
ifile.read(buff, size);
|
else if(ext.endsWith(QStringLiteral("txt")) || ext.endsWith(QStringLiteral("log")))
|
||||||
ifile.close();
|
type = QStringLiteral("text/plain");
|
||||||
std::string ret(buff, size);
|
m_body.append(m_contentHeader.arg(m_boundary).toUtf8());
|
||||||
delete[] buff;
|
m_body.append(m_contentFile.arg(pair.first, name, type).toUtf8());
|
||||||
return ret;
|
m_body.append(data);
|
||||||
});
|
}
|
||||||
futures.push_back(std::move(content_futures));
|
m_body.append(QStringLiteral("\r\n--%1--\r\n").arg(m_boundary).toUtf8());
|
||||||
}
|
return m_body;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,34 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* File Name : multipartparser.h
|
* File Name : multipartparser.h
|
||||||
* Created By : Ye Yangang
|
* Created By : Ye Yangang, ReWritten for Qt by Chris Rizzitello
|
||||||
* Creation Date : [2017-02-20 16:50]
|
* Creation Date : [2017-02-20 16:50] Modified for Qt 7/5/2022
|
||||||
* Last Modified : [AUTO_UPDATE_BEFORE_SAVE]
|
* Last Modified : [AUTO_UPDATE_BEFORE_SAVE]
|
||||||
* Description : Generate multipart/form-data POST body
|
* Description : Generate multipart/form-data POST body
|
||||||
**********************************************************************************/
|
**********************************************************************************/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <QList>
|
||||||
#include <vector>
|
#include <QPair>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
class MultipartParser {
|
class MultipartParser {
|
||||||
public:
|
public:
|
||||||
MultipartParser();
|
MultipartParser();
|
||||||
inline const std::string &body_content() { return body_content_; }
|
inline const QString &boundary() {return m_boundary;}
|
||||||
inline const std::string &boundary() { return boundary_; }
|
inline void addParameter(const QString &name = QString(), const QString &value = QString()) {
|
||||||
inline void AddParameter(const std::string &name, const std::string &value) {
|
m_paramList.append(QPair<QString, QString>(name, value));
|
||||||
params_.push_back(std::pair<std::string, std::string>(name, value));
|
|
||||||
}
|
}
|
||||||
inline void AddFile(const std::string &name, const std::string &value) {
|
inline void addFile(const QString &name = QString(), const QString &value = QString()) {
|
||||||
files_.push_back(std::pair<std::string, std::string>(name, value));
|
m_fileList.append(QPair<QString, QString>(name, value));
|
||||||
}
|
}
|
||||||
const std::string &GenBodyContent();
|
const QByteArray &generateBody();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _get_file_name_type(const std::string &file_path, std::string *filenae,
|
inline static const auto m_contentHeader = QStringLiteral("\r\n--%1\r\n");
|
||||||
std::string *content_type);
|
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");
|
||||||
private:
|
QString m_boundary;
|
||||||
static const std::string boundary_prefix_;
|
QByteArray m_body;
|
||||||
static const std::string rand_chars_;
|
QList<QPair<QString, QString>> m_paramList;
|
||||||
std::string boundary_;
|
QList<QPair<QString, QString>> m_fileList;
|
||||||
std::string body_content_;
|
|
||||||
std::vector<std::pair<std::string, std::string>> params_;
|
|
||||||
std::vector<std::pair<std::string, std::string>> files_;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -69,8 +69,8 @@ class NetMan : public QObject {
|
||||||
|
|
||||||
/// ashirtFormPost generates a basic POST request with content type multipart/form-data.
|
/// ashirtFormPost generates a basic POST request with content type multipart/form-data.
|
||||||
/// No authentication is provided (use addASHIRTAuth to do this)
|
/// No authentication is provided (use addASHIRTAuth to do this)
|
||||||
RequestBuilder* ashirtFormPost(QString endpoint, QByteArray body, QString boundry) {
|
RequestBuilder* ashirtFormPost(QString endpoint, QByteArray body, QString boundary) {
|
||||||
return RequestBuilder::newFormPost(boundry)
|
return RequestBuilder::newFormPost(boundary)
|
||||||
->setHost(AppConfig::getInstance().apiURL)
|
->setHost(AppConfig::getInstance().apiURL)
|
||||||
->setEndpoint(endpoint)
|
->setEndpoint(endpoint)
|
||||||
->setBody(body);
|
->setBody(body);
|
||||||
|
@ -159,8 +159,8 @@ class NetMan : public QObject {
|
||||||
/// rather than the time of capture.
|
/// rather than the time of capture.
|
||||||
QNetworkReply *uploadAsset(model::Evidence evidence) {
|
QNetworkReply *uploadAsset(model::Evidence evidence) {
|
||||||
MultipartParser parser;
|
MultipartParser parser;
|
||||||
parser.AddParameter("notes", evidence.description.toStdString());
|
parser.addParameter(QStringLiteral("notes"), evidence.description);
|
||||||
parser.AddParameter("contentType", evidence.contentType.toStdString());
|
parser.addParameter(QStringLiteral("contentType"), evidence.contentType);
|
||||||
|
|
||||||
// TODO: convert this time below into a proper unix timestamp (mSecSinceEpoch and secsSinceEpoch
|
// TODO: convert this time below into a proper unix timestamp (mSecSinceEpoch and secsSinceEpoch
|
||||||
// produce invalid times)
|
// produce invalid times)
|
||||||
|
@ -170,13 +170,13 @@ class NetMan : public QObject {
|
||||||
for (const auto& tag : evidence.tags) {
|
for (const auto& tag : evidence.tags) {
|
||||||
list << QString::number(tag.serverTagId);
|
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);
|
addASHIRTAuth(builder);
|
||||||
return builder->execute(nam);
|
return builder->execute(nam);
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,11 +59,11 @@ class RequestBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// newFormPost constructs a request builder with method POST and contentType multipart/form-data
|
/// 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();
|
RequestBuilder* builder = new RequestBuilder();
|
||||||
return builder
|
return builder
|
||||||
->setMethod(METHOD_POST)
|
->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
|
/// newJSONPost constructs a request builder with method POST and contentType application/json
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
|
||||||
#include "appconfig.h"
|
#include "appconfig.h"
|
||||||
#include "helpers/file_helpers.h"
|
#include "helpers/string_helpers.h"
|
||||||
#include "helpers/system_helpers.h"
|
#include "helpers/system_helpers.h"
|
||||||
|
|
||||||
Screenshot::Screenshot(QObject *parent) : QObject(parent) {}
|
Screenshot::Screenshot(QObject *parent) : QObject(parent) {}
|
||||||
|
@ -20,7 +20,7 @@ void Screenshot::captureWindow() { basicScreenshot(AppConfig::getInstance().capt
|
||||||
|
|
||||||
QString Screenshot::mkName()
|
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)
|
void Screenshot::basicScreenshot(QString cmdProto)
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
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");
|
||||||
|
};
|
|
@ -4,6 +4,7 @@
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
|
||||||
#include "helpers/file_helpers.h"
|
#include "helpers/file_helpers.h"
|
||||||
|
#include "helpers/string_helpers.h"
|
||||||
#include "helpers/system_helpers.h"
|
#include "helpers/system_helpers.h"
|
||||||
#include "helpers/jsonhelpers.h"
|
#include "helpers/jsonhelpers.h"
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ Codeblock::Codeblock(QString content)
|
||||||
|
|
||||||
QString Codeblock::mkName()
|
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()
|
QString Codeblock::extension()
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "system_manifest.h"
|
#include "system_manifest.h"
|
||||||
|
|
||||||
|
#include "helpers/string_helpers.h"
|
||||||
|
|
||||||
using namespace porting;
|
using namespace porting;
|
||||||
|
|
||||||
void SystemManifest::applyManifest(SystemManifestImportOptions options, DatabaseConnection* systemDb) {
|
void SystemManifest::applyManifest(SystemManifestImportOptions options, DatabaseConnection* systemDb) {
|
||||||
|
@ -93,7 +95,7 @@ QString SystemManifest::contentSensitiveFilename(const QString& contentType)
|
||||||
return Codeblock::mkName();
|
return Codeblock::mkName();
|
||||||
if(contentType == Screenshot::contentType())
|
if(contentType == Screenshot::contentType())
|
||||||
return Screenshot::mkName();
|
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) {
|
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++) {
|
for (size_t evidenceIndex = 0; evidenceIndex < allEvidence.size(); evidenceIndex++) {
|
||||||
auto evi = allEvidence.at(evidenceIndex);
|
auto evi = allEvidence.at(evidenceIndex);
|
||||||
auto newName = QStringLiteral("ashirt_evidence_%1.%2")
|
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 item = porting::EvidenceItem(evi.id, relativeEvidenceDir + "/" + newName);
|
||||||
auto dstPath = m_fileTemplate.arg(baseExportPath, item.exportPath);
|
auto dstPath = m_fileTemplate.arg(baseExportPath, item.exportPath);
|
||||||
QFile srcFile(evi.path);
|
QFile srcFile(evi.path);
|
||||||
|
|
Loading…
Reference in New Issue