From 38f616f620fd5c15451bbe6ca464f83f6450afdb Mon Sep 17 00:00:00 2001 From: Jake Smith Date: Tue, 8 Sep 2020 16:09:41 -0400 Subject: [PATCH 1/8] Create initial MySQL and Go gRPC server --- BLUESPAWN-common/bluespawnpb/bluespawn.proto | 35 ++++++++++ BLUESPAWN-server/docker-compose.yml | 37 +++++++++++ BLUESPAWN-server/mysql/Dockerfile | 1 + .../mysql/init/build_db_schema.sql | 64 +++++++++++++++++++ BLUESPAWN-server/rpc_server/Dockerfile | 38 +++++++++++ BLUESPAWN-server/rpc_server/rpc_server.go | 35 ++++++++++ 6 files changed, 210 insertions(+) create mode 100644 BLUESPAWN-common/bluespawnpb/bluespawn.proto create mode 100644 BLUESPAWN-server/docker-compose.yml create mode 100644 BLUESPAWN-server/mysql/Dockerfile create mode 100644 BLUESPAWN-server/mysql/init/build_db_schema.sql create mode 100644 BLUESPAWN-server/rpc_server/Dockerfile create mode 100644 BLUESPAWN-server/rpc_server/rpc_server.go diff --git a/BLUESPAWN-common/bluespawnpb/bluespawn.proto b/BLUESPAWN-common/bluespawnpb/bluespawn.proto new file mode 100644 index 00000000..e9864caa --- /dev/null +++ b/BLUESPAWN-common/bluespawnpb/bluespawn.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; +package bluespawn; + +option go_package = "BLUESPAWN/BLUESPAWN-common/bluespawnpb"; + +service LogReceiver { + rpc SendLog(LogMessageRequest) returns (LogMessageResponse) {} +} + +message LogMessageRequest { + string host = 1; + int64 timestamp = 2; + string message = 3; + + enum Severity { + LogError = 0; + LogWarn = 1; + LogInfo = 2; + LogVerbose = 3; + } + + Severity severity = 4; + + enum Detail { + Low = 0; + Moderate = 1; + High = 2; + } + + Detail detail = 5; +} + +message LogMessageResponse { + bool received = 1; +} diff --git a/BLUESPAWN-server/docker-compose.yml b/BLUESPAWN-server/docker-compose.yml new file mode 100644 index 00000000..cc3fec87 --- /dev/null +++ b/BLUESPAWN-server/docker-compose.yml @@ -0,0 +1,37 @@ +version: '3.8' +services: + # MySQL: https://hub.docker.com/_/mysql + mysql: + build: + context: . + dockerfile: ./mysql/Dockerfile + environment: + - MYSQL_ROOT_PASSWORD=Chiapet1 + - MYSQL_DATABASE=bluespawn_server + - MYSQL_USER=bluespawn + - MYSQL_PASSWORD=Chiapet1 + restart: on-failure + ports: + - "3306:3306" + networks: + - server_core + volumes: + - /data/mysql + - type: bind + source: ./mysql/init + target: /docker-entrypoint-initdb.d/ + read_only: true + # RPC Server for Client<->Server Communication written in Go/GRPC + rpc_server: + build: + context: ../ + dockerfile: ./BLUESPAWN-server/rpc_server/Dockerfile + ports: + - "50052:50052" + depends_on: + - mysql + networks: + - server_core +networks: + server_core: + driver: bridge diff --git a/BLUESPAWN-server/mysql/Dockerfile b/BLUESPAWN-server/mysql/Dockerfile new file mode 100644 index 00000000..57138cf0 --- /dev/null +++ b/BLUESPAWN-server/mysql/Dockerfile @@ -0,0 +1 @@ +FROM mysql:8 diff --git a/BLUESPAWN-server/mysql/init/build_db_schema.sql b/BLUESPAWN-server/mysql/init/build_db_schema.sql new file mode 100644 index 00000000..ce779269 --- /dev/null +++ b/BLUESPAWN-server/mysql/init/build_db_schema.sql @@ -0,0 +1,64 @@ +-- MySQL dump 10.13 Distrib 8.0.21, for Win64 (x86_64) +-- +-- Host: 127.0.0.1 Database: bluespawn_server +-- ------------------------------------------------------ +-- Server version 8.0.21 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +USE bluespawn_server; + +-- +-- Table structure for table `clients` +-- + +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE IF NOT EXISTS `clients` ( + `client_id` int unsigned NOT NULL AUTO_INCREMENT, + `client_guid` varchar(45) DEFAULT NULL, + `hostname` varchar(256) DEFAULT NULL, + `ip_address` varchar(46) DEFAULT NULL, + `last_heartbeat` datetime DEFAULT NULL, + PRIMARY KEY (`client_id`), + UNIQUE KEY `client_id_UNIQUE` (`client_id`), + UNIQUE KEY `client_guid_UNIQUE` (`client_guid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `messages` +-- + +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE IF NOT EXISTS `messages` ( + `message_id` int unsigned NOT NULL AUTO_INCREMENT, + `client_id` varchar(45) DEFAULT NULL, + `timestamp` datetime DEFAULT NULL, + `message` varchar(256) DEFAULT NULL, + `severity` int DEFAULT NULL, + `detail` int DEFAULT NULL, + PRIMARY KEY (`message_id`), + UNIQUE KEY `message_id_UNIQUE` (`message_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + diff --git a/BLUESPAWN-server/rpc_server/Dockerfile b/BLUESPAWN-server/rpc_server/Dockerfile new file mode 100644 index 00000000..a10baa6e --- /dev/null +++ b/BLUESPAWN-server/rpc_server/Dockerfile @@ -0,0 +1,38 @@ +FROM golang:1.15 + +ENV PROTOC_VER 3.11.4 +ENV PROTOC_GEN_GO_VER 1.3.5 + +# Install dependencies +RUN apt-get update --fix-missing && apt-get install -y \ + zip unzip build-essential curl wget + +# Install protoc for gRPC +WORKDIR /tmp +RUN wget -O protoc-${PROTOC_VER}-linux-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-linux-x86_64.zip \ + && unzip protoc-${PROTOC_VER}-linux-x86_64.zip \ + && cp -vv ./bin/protoc /usr/local/bin + +# Install go get +RUN wget -O protoc-gen-go.tar.gz https://github.com/golang/protobuf/archive/v${PROTOC_GEN_GO_VER}.tar.gz \ + && tar xvf protoc-gen-go.tar.gz \ + && cd protobuf-${PROTOC_GEN_GO_VER} \ + && make install + +# Install gRPC +RUN go get google.golang.org/grpc + +# Change to /usr/src +WORKDIR /usr/src + +# Copy BLUESPAWN source code into container +COPY ./ ./ + +# Compile Protobuf +RUN protoc --go_out="plugins=grpc:$GOPATH/src" ./BLUESPAWN-common/bluespawnpb/bluespawn.proto + +# Build and start rpc_server +RUN cd ./BLUESPAWN-server/rpc_server && \ + go build + +ENTRYPOINT ["./BLUESPAWN-server/rpc_server/rpc_server"] diff --git a/BLUESPAWN-server/rpc_server/rpc_server.go b/BLUESPAWN-server/rpc_server/rpc_server.go new file mode 100644 index 00000000..ec786bbc --- /dev/null +++ b/BLUESPAWN-server/rpc_server/rpc_server.go @@ -0,0 +1,35 @@ +package main + +import ( + "context" + "log" + "net" + + "google.golang.org/grpc" + pb "BLUESPAWN/BLUESPAWN-common/bluespawnpb" +) + +const ( + port = ":50052" +) + +type server struct { + pb.UnimplementedLogReceiverServer +} + +func (s *server) SendLog(ctx context.Context, in *pb.LogMessageRequest) (*pb.LogMessageResponse, error) { + log.Printf("Received: %v", in.GetMessage()) + return &pb.LogMessageResponse{Received: true}, nil +} + +func main() { + lis, err := net.Listen("tcp", port) + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + s := grpc.NewServer() + pb.RegisterLogReceiverServer(s, &server{}) + if err := s.Serve(lis); err != nil { + log.Fatalf("failed to serve: %v", err) + } +} From 5b8f50f7d66b2fcd9a604e9381dc15132f521de0 Mon Sep 17 00:00:00 2001 From: Jake Smith Date: Sun, 20 Sep 2020 18:49:38 -0400 Subject: [PATCH 2/8] Integrate gRPC with win-client --- .gitignore | 4 + BLUESPAWN-common/bluespawnpb/bluespawn.proto | 2 +- BLUESPAWN-win-client/BLUESPAWN-client.vcxproj | 20 ++- .../headers/util/log/LogLevel.h | 135 ++++++++---------- .../headers/util/log/ServerSink.h | 25 +--- .../headers/util/rpc/RpcClient.h | 32 +++++ BLUESPAWN-win-client/src/user/BLUESPAWN.cpp | 2 +- .../src/util/log/LogLevel.cpp | 74 +++++----- .../src/util/log/ServerSink.cpp | 39 +---- .../src/util/rpc/RpcClient.cpp | 33 +++++ vcpkg_response_file.txt | 4 +- 11 files changed, 197 insertions(+), 173 deletions(-) create mode 100644 BLUESPAWN-win-client/headers/util/rpc/RpcClient.h create mode 100644 BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp diff --git a/.gitignore b/.gitignore index bb4eba2e..9cbbfcde 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,10 @@ vcpkg/ *.lib *.aps +# BLUESPAWN-common +BLUESPAWN-common/bluespawnpb/*.h +BLUESPAWN-common/bluespawnpb/*.cc + # BLUESPAWN-win-client BLUESPAWN-win-client/external/ BLUESPAWN-win-client/resources/severe diff --git a/BLUESPAWN-common/bluespawnpb/bluespawn.proto b/BLUESPAWN-common/bluespawnpb/bluespawn.proto index e9864caa..c2b65076 100644 --- a/BLUESPAWN-common/bluespawnpb/bluespawn.proto +++ b/BLUESPAWN-common/bluespawnpb/bluespawn.proto @@ -8,7 +8,7 @@ service LogReceiver { } message LogMessageRequest { - string host = 1; + string client_id = 1; int64 timestamp = 2; string message = 3; diff --git a/BLUESPAWN-win-client/BLUESPAWN-client.vcxproj b/BLUESPAWN-win-client/BLUESPAWN-client.vcxproj index 1a81c275..a3e58f9d 100644 --- a/BLUESPAWN-win-client/BLUESPAWN-client.vcxproj +++ b/BLUESPAWN-win-client/BLUESPAWN-client.vcxproj @@ -19,6 +19,8 @@ + + @@ -98,6 +100,7 @@ + @@ -150,6 +153,8 @@ + + @@ -267,6 +272,7 @@ + @@ -299,12 +305,15 @@ true + + true + $(SolutionDir)build\$(PlatformTarget)\$(Configuration)\$(MSBuildProjectName).log - $(SolutionDir)BLUESPAWN-win-client\external\pe-sieve\libpeconv\libpeconv\include;$(SolutionDir)BLUESPAWN-win-client\external\pe-sieve\;$(SolutionDir)BLUESPAWN-win-client\external\pe-sieve\include;%(AdditionalIncludeDirectories) + $(SolutionDir)BLUESPAWN-win-client\external\pe-sieve\libpeconv\libpeconv\include;$(SolutionDir)BLUESPAWN-win-client\external\pe-sieve\;$(SolutionDir)BLUESPAWN-win-client\external\pe-sieve\include;$(SolutionDir);%(AdditionalIncludeDirectories) MultiThreaded MultiThreadedDebug Async @@ -318,6 +327,15 @@ Adding manifest to BLUESPAWN-client.exe + + Generate corresponding .h/cc files based on the current gRPC protobuf configuration + + + "$(SolutionDir)vcpkg\packages\protobuf_x86-windows-static\tools\protobuf\protoc.exe" --proto_path="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --cpp_out="$(SolutionDir)BLUESPAWN-common\bluespawnpb" "bluespawn.proto" && "$(SolutionDir)vcpkg\packages\protobuf_x86-windows-static\tools\protobuf\protoc.exe" --proto_path="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --grpc_out="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --plugin=protoc-gen-grpc="$(SolutionDir)vcpkg\packages\grpc_x86-windows-static\tools\grpc\grpc_cpp_plugin.exe" "$(SolutionDir)BLUESPAWN-common\bluespawnpb\bluespawn.proto" + + + "$(SolutionDir)vcpkg\packages\protobuf_x64-windows-static\tools\protobuf\protoc.exe" --proto_path="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --cpp_out="$(SolutionDir)BLUESPAWN-common\bluespawnpb" "bluespawn.proto" && "$(SolutionDir)vcpkg\packages\protobuf_x64-windows-static\tools\protobuf\protoc.exe" --proto_path="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --grpc_out="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --plugin=protoc-gen-grpc="$(SolutionDir)vcpkg\packages\grpc_x64-windows-static\tools\grpc\grpc_cpp_plugin.exe" "$(SolutionDir)BLUESPAWN-common\bluespawnpb\bluespawn.proto" + {159B2E72-9553-4E17-9BEC-CB92FCA8D0B0} diff --git a/BLUESPAWN-win-client/headers/util/log/LogLevel.h b/BLUESPAWN-win-client/headers/util/log/LogLevel.h index d9e4ba08..8f40e3de 100644 --- a/BLUESPAWN-win-client/headers/util/log/LogLevel.h +++ b/BLUESPAWN-win-client/headers/util/log/LogLevel.h @@ -2,131 +2,118 @@ #include -#include #include +#include #include namespace Log { - /** + /** * This denotes the severity of a log message. This is intended to be used by * LogSinks to choose how to record a given log message. */ - enum class Severity { - LogError = 0, - LogWarn = 1, - LogInfo = 2, - LogVerbose = 3 - }; - - /** + enum class Severity { LogError = 0, LogWarn = 1, LogInfo = 2, LogVerbose = 3 }; + + /** * This indicates the level of detail in the log level. */ - enum class Detail { - Low = 0, - Moderate = 1, - High = 2 - }; + enum class Detail { Low = 0, Moderate = 1, High = 2 }; - /// Forward declare log sink - class LogSink; + /// Forward declare log sink + class LogSink; - /** + /** * This class represents the "level" of a log message. This is similar to Severity * in that it categorizes logs, but it's inteded to extend the functionality present * in a manner that doesn't affect the log sinks by allowing the enabling or disabling * of certain logging levels. */ - class LogLevel { - private: - - /// Whether or not sinks should record log messages under this level - bool enabled; - - /// The sinks to which messages at this level will be recorded - std::vector sinks; - - public: - /// The severity at which this log level operates - const Severity severity; - - /// The level of detail present at this logging level - const std::optional detail; - - /// Default logging levels available, though custom ones can be created - static LogLevel - LogError, // Intended for logging errors - LogWarn, // Intended for logging warnings - LogInfo1, // Intended for logging high level operational information - LogInfo2, // Intended for logging moderately detailed operational information - LogInfo3, // Intended for logging very detailed operational information - LogVerbose1, // Intended for a low level of verbosity - LogVerbose2, // Intended for a moderate level of verbosity - LogVerbose3; // Intended for a high level of verbosity - - /** + class LogLevel { + private: + /// Whether or not sinks should record log messages under this level + bool enabled; + + /// The sinks to which messages at this level will be recorded + std::vector sinks; + + public: + /// The severity at which this log level operates + const Severity severity; + + /// The level of detail present at this logging level + const std::optional detail; + + /// Default logging levels available, though custom ones can be created + static LogLevel LogError, // Intended for logging errors + LogWarn, // Intended for logging warnings + LogInfo1, // Intended for logging high level operational information + LogInfo2, // Intended for logging moderately detailed operational information + LogInfo3, // Intended for logging very detailed operational information + LogVerbose1, // Intended for a low level of verbosity + LogVerbose2, // Intended for a moderate level of verbosity + LogVerbose3; // Intended for a high level of verbosity + + /** * Creates a new log level, enabled by default, with a given severity. * * @param severity The severity of messages under this logging level * @param detail The level of detail present at this logging level */ - LogLevel( - IN Severity severity, - IN CONST std::optional& detail = std::nullopt OPTIONAL - ); + LogLevel(IN Severity severity, IN CONST std::optional& detail = std::nullopt OPTIONAL); - /** + /** * Creates a new log level with a given severity. * * @param severity The severity of messages under this logging level * @param DefaultState Indicates whether or not log messages should be recorded * by default when logged at this logging level. */ - LogLevel( - IN Severity severity, - IN bool DefaultState, - IN CONST std::optional& detail = std::nullopt OPTIONAL - ); + LogLevel(IN Severity severity, + IN bool DefaultState, + IN CONST std::optional& detail = std::nullopt OPTIONAL); - /** + /** * Enables logging at this level */ - void Enable(); + void Enable(); - /** + /** * Disables logging at this level */ - void Disable(); + void Disable(); - /** + /** * Toggles logging at this level */ - bool Toggle(); + bool Toggle(); - /** + /** * Indicates whether or not this log level is enabled. * * @return A boolean indicating whether or not log messages at this level should * be recorded. */ - bool Enabled() const; + bool Enabled() const; - /** + /** * Adds a sink to which messages logged at this level are recorded. If the level already * is logging to the sink, this has no effect. * * @param sink The sink to add */ - void AddSink( - IN LogSink* sink - ); + void AddSink(IN LogSink* sink); - /** + /** * Logs the given message at this level in the sinks configured for this level * * @param message The message to log */ - void LogMessage( - IN CONST std::wstring& message - ); - }; -} \ No newline at end of file + void LogMessage(IN CONST std::wstring& message); + + /** + * Implicit cast to an int to return the severity + * + * @return severity as an int + */ + operator int() const; + }; +} // namespace Log diff --git a/BLUESPAWN-win-client/headers/util/log/ServerSink.h b/BLUESPAWN-win-client/headers/util/log/ServerSink.h index 48a20761..9ba1fa1d 100644 --- a/BLUESPAWN-win-client/headers/util/log/ServerSink.h +++ b/BLUESPAWN-win-client/headers/util/log/ServerSink.h @@ -1,20 +1,18 @@ #pragma once -#include +#include "util/rpc/RpcClient.h" #include "DetectionSink.h" #include "LogSink.h" -using json = nlohmann::json; - namespace Log { /** * ServerSink provides a sink for the logger that will send logs to a remote server, usually a BLUESPAWN-server installation */ class ServerSink : public LogSink, public DetectionSink { - /// Guards access to the server - CriticalSection hGuard; + /// Rpc Server Client + RpcClient::RpcClient client; /// The remote server (http(s)://)IP:PORT or (http(s)://)FQDN:PORT that will recieve the logs std::wstring wServerAddress; @@ -22,9 +20,6 @@ namespace Log { /// Tags for messages sent at different levels std::string MessageTags[4] = { "error", "warning", "info", "other" }; - /// A handle to a thread that periodically flushes the log to the file - HandleWrapper thread; - /// A set of IDs created for detections already sent to the server std::set detections; @@ -36,15 +31,6 @@ namespace Log { */ ServerSink(const std::wstring ServerAddress); - /// Delete copy and move constructors and assignment operators - ServerSink operator=(const ServerSink&) = delete; - ServerSink operator=(ServerSink&&) = delete; - ServerSink(const ServerSink&) = delete; - ServerSink(ServerSink&&) = delete; - - /// Custom destructor - ~ServerSink(); - /** * Outputs a message to the target server if its logging level is enabled. * @@ -63,11 +49,6 @@ namespace Log { */ virtual bool operator==(const LogSink& sink) const; - /** - * Flushes the log to the server. - */ - void Flush(); - /** * Updates the raw and combined certainty values associated with a detection * diff --git a/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h b/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h new file mode 100644 index 00000000..028225c7 --- /dev/null +++ b/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include + +#include "util/log/Loggable.h" +#include "util/wrappers.hpp" + +#include "BLUESPAWN-common/bluespawnpb/bluespawn.grpc.pb.h" + +using bluespawn::LogMessageRequest; +using bluespawn::LogMessageResponse; +using bluespawn::LogReceiver; +using grpc::Channel; +using grpc::ClientContext; +using grpc::Status; + +namespace RpcClient { + class RpcClient { + private: + std::unique_ptr stub_; + + public: + RpcClient(std::shared_ptr channel) : stub_(LogReceiver::NewStub(channel)){}; + + bool SendLogMessage(const std::wstring& msg, + bluespawn::LogMessageRequest_Severity severity, + bluespawn::LogMessageRequest_Detail); + }; + +}; // namespace RpcClient diff --git a/BLUESPAWN-win-client/src/user/BLUESPAWN.cpp b/BLUESPAWN-win-client/src/user/BLUESPAWN.cpp index dccd7437..f04b8de8 100644 --- a/BLUESPAWN-win-client/src/user/BLUESPAWN.cpp +++ b/BLUESPAWN-win-client/src/user/BLUESPAWN.cpp @@ -399,7 +399,7 @@ int main(int argc, char* argv[]) { ("o,output", "Specify the output folder for any logs written to a file", cxxopts::value()->default_value(".")) ("server", "Specify the remote address of the server in the form of IP, FQDN, IP:PORT, or FQDN:PORT. Must be provided if you specify server as a log sink", - cxxopts::value()) + cxxopts::value()->default_value("")) ; options.add_options("mitigate") diff --git a/BLUESPAWN-win-client/src/util/log/LogLevel.cpp b/BLUESPAWN-win-client/src/util/log/LogLevel.cpp index 0c76d76a..07be8c65 100644 --- a/BLUESPAWN-win-client/src/util/log/LogLevel.cpp +++ b/BLUESPAWN-win-client/src/util/log/LogLevel.cpp @@ -1,56 +1,52 @@ #include "util/log/LogLevel.h" + #include "util/log/LogSink.h" namespace Log { - LogLevel::LogLevel(IN Severity severity, - IN CONST std::optional& detail OPTIONAL) : - enabled{ true }, - severity{ severity }, - detail{ detail }{} - LogLevel::LogLevel(IN Severity severity, - IN bool DefaultState, - IN CONST std::optional& detail OPTIONAL) : - enabled{ enabled }, - severity{ severity }, - detail{ detail }{} + LogLevel::LogLevel(IN Severity severity, IN CONST std::optional& detail OPTIONAL) : + enabled{ true }, severity{ severity }, detail{ detail } {} + LogLevel::LogLevel(IN Severity severity, IN bool DefaultState, IN CONST std::optional& detail OPTIONAL) : + enabled{ enabled }, severity{ severity }, detail{ detail } {} + + LogLevel LogLevel::LogError{ Severity::LogError, true }; - LogLevel LogLevel::LogError{Severity::LogError, true }; + LogLevel LogLevel::LogWarn{ Severity::LogWarn, true }; - LogLevel LogLevel::LogWarn{Severity::LogWarn, true }; + LogLevel LogLevel::LogInfo1{ Severity::LogInfo, true, Detail::Low }; - LogLevel LogLevel::LogInfo1{ Severity::LogInfo, true, Detail::Low }; + LogLevel LogLevel::LogInfo2{ Severity::LogInfo, false, Detail::Moderate }; - LogLevel LogLevel::LogInfo2{ Severity::LogInfo, false, Detail::Moderate }; + LogLevel LogLevel::LogInfo3{ Severity::LogInfo, false, Detail::High }; - LogLevel LogLevel::LogInfo3{ Severity::LogInfo, false, Detail::High }; + LogLevel LogLevel::LogVerbose1{ Severity::LogVerbose, false, Detail::Low }; - LogLevel LogLevel::LogVerbose1{ Severity::LogVerbose, false, Detail::Low }; + LogLevel LogLevel::LogVerbose2{ Severity::LogVerbose, false, Detail::Moderate }; - LogLevel LogLevel::LogVerbose2{ Severity::LogVerbose, false, Detail::Moderate }; + LogLevel LogLevel::LogVerbose3{ Severity::LogVerbose, false, Detail::High }; - LogLevel LogLevel::LogVerbose3{ Severity::LogVerbose, false, Detail::High }; + void LogLevel::Enable() { enabled = true; } + void LogLevel::Disable() { enabled = false; } + bool LogLevel::Toggle() { return enabled = !enabled; } + bool LogLevel::Enabled() const { return enabled; } - void LogLevel::Enable(){ enabled = true; } - void LogLevel::Disable(){ enabled = false; } - bool LogLevel::Toggle(){ return enabled = !enabled; } - bool LogLevel::Enabled() const { return enabled; } + void LogLevel::AddSink(IN LogSink* sink) { + for(auto existing : sinks) { + if(*existing == *sink) { + return; + } + } - void LogLevel::AddSink(IN LogSink* sink){ - for(auto existing : sinks){ - if(*existing == *sink){ - return; - } - } + sinks.emplace_back(sink); + } - sinks.emplace_back(sink); - } + void LogLevel::LogMessage(IN CONST std::wstring& message) { + if(enabled) { + for(auto sink : sinks) { + sink->LogMessage(*this, message); + } + } + } - void LogLevel::LogMessage(IN CONST std::wstring& message){ - if(enabled){ - for(auto sink : sinks){ - sink->LogMessage(*this, message); - } - } - } -} \ No newline at end of file + LogLevel::operator int() const { return static_cast(severity); } +} // namespace Log diff --git a/BLUESPAWN-win-client/src/util/log/ServerSink.cpp b/BLUESPAWN-win-client/src/util/log/ServerSink.cpp index d0ee73fb..fa49ba4e 100644 --- a/BLUESPAWN-win-client/src/util/log/ServerSink.cpp +++ b/BLUESPAWN-win-client/src/util/log/ServerSink.cpp @@ -1,7 +1,5 @@ #include "util/log/ServerSink.h" -#include - #include #include #include @@ -9,28 +7,15 @@ #include "util/StringUtils.h" #include "util/Utils.h" -#include "util/configurations/CollectInfo.h" +#include "util/rpc/RpcClient.h" #include "user/bluespawn.h" namespace Log { - void UpdateLog(ServerSink* sink) { - HandleWrapper hRecordEvent{ CreateEventW(nullptr, false, false, L"Local\\FlushLogs") }; - while(true) { - WaitForSingleObject(hRecordEvent, INFINITE); - sink->Flush(); - } - } - ServerSink::ServerSink(const std::wstring ServerAddress) : - wServerAddress{ ServerAddress }, thread{ - CreateThread(nullptr, 0, PTHREAD_START_ROUTINE(UpdateLog), this, CREATE_SUSPENDED, nullptr) - } { - ResumeThread(thread); - } - - ServerSink::~ServerSink() { TerminateThread(thread, 0); } + wServerAddress{ ServerAddress }, + client(grpc::CreateChannel(WidestringToString(wServerAddress), grpc::InsecureChannelCredentials())) {} void ServerSink::UpdateCertainty(IN CONST std::shared_ptr& detection) {} @@ -42,7 +27,6 @@ namespace Log { } BeginCriticalSection __{ *detection }; - BeginCriticalSection _{ hGuard }; } void ServerSink::RecordAssociation(IN CONST std::shared_ptr& first, @@ -50,19 +34,10 @@ namespace Log { IN CONST Association& strength) {} void ServerSink::LogMessage(const LogLevel& level, const std::wstring& message) { - BeginCriticalSection _{ hGuard }; - if(level.Enabled()) { - json payload = json::object(); - payload["version"] = "1.1"; - payload["host"] = "WIN-2389DM8W"; - payload["short_message"] = WidestringToString(message); - payload["timestamp"] = std::time(0); - payload["level"] = 6; - - cpr::Response r = cpr::Post(cpr::Url{ WidestringToString(wServerAddress) }, - cpr::Header{ { "Content-Type", "application/json" } }, - cpr::Body{ payload.dump(-1) }); + auto severity = static_cast(level.severity); + auto detail = static_cast(*level.detail); + bool response = client.SendLogMessage(message, severity, detail); } } @@ -70,6 +45,4 @@ namespace Log { return (bool) dynamic_cast(&sink) && dynamic_cast(&sink)->wServerAddress == wServerAddress; } - - void ServerSink::Flush() {} }; // namespace Log diff --git a/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp b/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp new file mode 100644 index 00000000..3323747f --- /dev/null +++ b/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp @@ -0,0 +1,33 @@ +#include "util/rpc/RpcClient.h" + +#include +#include + +namespace RpcClient { + bool RpcClient::SendLogMessage(const std::wstring& msg, + bluespawn::LogMessageRequest_Severity severity, + bluespawn::LogMessageRequest_Detail detail) { + SYSTEMTIME st; + GetSystemTime(&st); + + LogMessageRequest request; + request.set_client_id("TO_BE_SET_IN_THE_FUTURE"); + request.set_timestamp(SystemTimeToInteger(st)); + request.set_message(WidestringToString(msg)); + request.set_severity(severity); + request.set_detail(detail); + + LogMessageResponse response; + ClientContext context; + + Status status = stub_->SendLog(&context, request, &response); + + if(status.ok() && response.received()) { + return true; + } else { + return false; + } + + return false; + } +} // namespace RpcClient diff --git a/vcpkg_response_file.txt b/vcpkg_response_file.txt index e139a15c..ce68bb73 100644 --- a/vcpkg_response_file.txt +++ b/vcpkg_response_file.txt @@ -6,5 +6,5 @@ libzip:x64-windows-static libzip:x86-windows-static nlohmann-json:x64-windows-static nlohmann-json:x86-windows-static -cpr:x64-windows-static -cpr:x86-windows-static \ No newline at end of file +grpc:x64-windows-static +grpc:x86-windows-static \ No newline at end of file From b5b5312a5c78baba12df710dcb0b840e150e33ce Mon Sep 17 00:00:00 2001 From: Jake Smith Date: Wed, 23 Sep 2020 11:19:58 -0400 Subject: [PATCH 3/8] Rewrite protobuf to include support for Detections --- BLUESPAWN-common/bluespawnpb/bluespawn.proto | 186 ++++++++++++++++-- BLUESPAWN-server/rpc_server/Dockerfile | 4 +- .../headers/util/rpc/RpcClient.h | 14 +- .../src/util/log/ServerSink.cpp | 4 +- .../src/util/rpc/RpcClient.cpp | 13 +- 5 files changed, 183 insertions(+), 38 deletions(-) diff --git a/BLUESPAWN-common/bluespawnpb/bluespawn.proto b/BLUESPAWN-common/bluespawnpb/bluespawn.proto index c2b65076..9c307ded 100644 --- a/BLUESPAWN-common/bluespawnpb/bluespawn.proto +++ b/BLUESPAWN-common/bluespawnpb/bluespawn.proto @@ -3,33 +3,181 @@ package bluespawn; option go_package = "BLUESPAWN/BLUESPAWN-common/bluespawnpb"; -service LogReceiver { - rpc SendLog(LogMessageRequest) returns (LogMessageResponse) {} +/* Core BLUESPAWN RPC Server */ +service BluespawnRPC { + // Clients logging to Server + rpc SendLogMessage(LogMessage) returns (ResponseMessage) {} + rpc RecordDetection(Detection) returns (ResponseMessage) {} + rpc RecordAssociation(DetectionAssociation) returns (ResponseMessage) {} + rpc UpdateCertainty(DetectionCertaintyUpdate) returns (ResponseMessage) {} } -message LogMessageRequest { - string client_id = 1; +/* Data Type Enums */ +// General and Log +enum LogDetail { + Low = 0; + Moderate = 1; + High = 2; +} + +enum LogSeverity { + LogError = 0; + LogWarn = 1; + LogInfo = 2; + LogVerbose = 3; +} + +// Windows Objects +enum RegistryType { + RegSz = 0; + RegExpandSz = 1; + RegMultiSz = 2; + RegDword = 3; + RegBinary = 4; +} + +// Detections +enum DetectionType { + ProcessDetection = 0; + RegistryDetection = 1; + FileDetection = 2; + ServiceDetection = 3; + OtherDetection = 4; +} + +enum RegistryDetectionType { + CommandReference = 0; + FileReference = 1; + FolderReference = 2; + PipeReference = 3; + ShareReference = 4; + UserReference = 5; + Configuration = 6; + Unknown = 7; +} + +enum ProcessDetectionType { + MaliciousProcess = 0; + MaliciousImage = 1; + MaliciousMemory = 2; + MaliciousCommand = 3; +} + +/* Message Serializers Types */ +// General and Log +message LogMessage { + string clientId = 1; int64 timestamp = 2; string message = 3; + LogSeverity severity = 4; + LogDetail detail = 5; +} + +message ResponseMessage { + bool received = 1; + bool success = 2; + string message = 3; // optional +} + +// Windows Objects +message RegistryKey { + string keyPath = 1; + bool exists = 2; +} + +message RegistryValue { + RegistryKey key = 1; + string valueName = 2; + string valueData = 3; +} + +// Detections +message DetectionAssociation { +} + +message DetectionCertaintyUpdate { + int64 id = 1; // NOTE: Request change to string - enum Severity { - LogError = 0; - LogWarn = 1; - LogInfo = 2; - LogVerbose = 3; +} + +message ProcessDetectionData { + ProcessDetectionType type = 1; + uint64 pid = 2; // optional + uint64 tid = 3; // optional + string processName = 4; // optional + string processPath = 5; // optional + string processCommand = 6; // optional + // TODO: add repeated ParentProcess which is a ProcessDetectionData + int64 baseAddress = 7; // optional + uint64 memorySize = 8; // optional + string imageName = 9; // optional +} +message FileDetectionData { + bool exists = 1; + string filePath = 2; + string fileName = 3; + string fileExtension = 4; // optional + string fileType = 5; // optional + string executor = 6; // optional + string md5 = 7; // optional + string sha1 = 8; // optional + string sha256 = 9; // optional + uint64 lastOpened = 10; // optional + uint64 fileCreated = 11; // optional + message YaraScanResult { + repeated string KnownBadRules = 1; + repeated string IndicatorRules = 2; + } + YaraScanResult yara = 12; // optional + bool fileSigned = 13; // optional + string signer = 14; // optional +} +message RegistryDetectionData { + string keyPath = 1; + RegistryKey key = 2; + RegistryValue value = 3; // optional + bytes data = 4; // optional + RegistryDetectionType type = 5; // optional +} +message ServiceDetectionData { + string serviceName = 1; // optional + string displayName = 2; // optional + string description = 3; // optional + string filePath = 4; // optional +} +message OtherDetectionData { + string type = 1; + map properties = 2; +} + +message Detection { + int64 id = 1; // NOTE: Request change to string + int64 timestamp = 2; + DetectionType type = 3; + message ScanInfo { + double rawCertainty = 1; + double certainty = 2; + map assocation = 3; // Detection Id (int64), certainity (double) } + ScanInfo info = 4; - Severity severity = 4; + message DetectionData { + oneof data { + ProcessDetectionData processData = 1; + FileDetectionData fileData = 2; + RegistryDetectionData registryData = 3; + ServiceDetectionData serviceData = 4; + OtherDetectionData otherData = 5; + } + } + DetectionData data = 5; - enum Detail { - Low = 0; - Moderate = 1; - High = 2; + message DetectionContext { + repeated string hunts = 1; + int64 FirstEvidenceTime = 2; // optional + int64 DetectionCreatedTime = 3; + string note = 4; // optional } - - Detail detail = 5; + DetectionContext context = 6; } -message LogMessageResponse { - bool received = 1; -} diff --git a/BLUESPAWN-server/rpc_server/Dockerfile b/BLUESPAWN-server/rpc_server/Dockerfile index a10baa6e..0f864a22 100644 --- a/BLUESPAWN-server/rpc_server/Dockerfile +++ b/BLUESPAWN-server/rpc_server/Dockerfile @@ -1,7 +1,7 @@ FROM golang:1.15 -ENV PROTOC_VER 3.11.4 -ENV PROTOC_GEN_GO_VER 1.3.5 +ENV PROTOC_VER 3.13.0 +ENV PROTOC_GEN_GO_VER 1.4.0 # Install dependencies RUN apt-get update --fix-missing && apt-get install -y \ diff --git a/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h b/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h index 028225c7..b46af1fa 100644 --- a/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h +++ b/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h @@ -9,9 +9,9 @@ #include "BLUESPAWN-common/bluespawnpb/bluespawn.grpc.pb.h" -using bluespawn::LogMessageRequest; -using bluespawn::LogMessageResponse; -using bluespawn::LogReceiver; +using bluespawn::BluespawnRPC; +using bluespawn::LogMessage; +using bluespawn::ResponseMessage; using grpc::Channel; using grpc::ClientContext; using grpc::Status; @@ -19,14 +19,12 @@ using grpc::Status; namespace RpcClient { class RpcClient { private: - std::unique_ptr stub_; + std::unique_ptr stub_; public: - RpcClient(std::shared_ptr channel) : stub_(LogReceiver::NewStub(channel)){}; + RpcClient(std::shared_ptr channel) : stub_(BluespawnRPC::NewStub(channel)){}; - bool SendLogMessage(const std::wstring& msg, - bluespawn::LogMessageRequest_Severity severity, - bluespawn::LogMessageRequest_Detail); + bool SendLogMessage(const std::wstring& msg, bluespawn::LogSeverity severity, bluespawn::LogDetail detail); }; }; // namespace RpcClient diff --git a/BLUESPAWN-win-client/src/util/log/ServerSink.cpp b/BLUESPAWN-win-client/src/util/log/ServerSink.cpp index fa49ba4e..9cb858c5 100644 --- a/BLUESPAWN-win-client/src/util/log/ServerSink.cpp +++ b/BLUESPAWN-win-client/src/util/log/ServerSink.cpp @@ -35,8 +35,8 @@ namespace Log { void ServerSink::LogMessage(const LogLevel& level, const std::wstring& message) { if(level.Enabled()) { - auto severity = static_cast(level.severity); - auto detail = static_cast(*level.detail); + auto severity = static_cast(level.severity); + auto detail = static_cast(*level.detail); bool response = client.SendLogMessage(message, severity, detail); } } diff --git a/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp b/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp index 3323747f..2f916115 100644 --- a/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp +++ b/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp @@ -4,23 +4,22 @@ #include namespace RpcClient { - bool RpcClient::SendLogMessage(const std::wstring& msg, - bluespawn::LogMessageRequest_Severity severity, - bluespawn::LogMessageRequest_Detail detail) { + bool + RpcClient::SendLogMessage(const std::wstring& msg, bluespawn::LogSeverity severity, bluespawn::LogDetail detail) { SYSTEMTIME st; GetSystemTime(&st); - LogMessageRequest request; - request.set_client_id("TO_BE_SET_IN_THE_FUTURE"); + LogMessage request; + request.set_clientid("TO_BE_SET_IN_THE_FUTURE"); request.set_timestamp(SystemTimeToInteger(st)); request.set_message(WidestringToString(msg)); request.set_severity(severity); request.set_detail(detail); - LogMessageResponse response; + ResponseMessage response; ClientContext context; - Status status = stub_->SendLog(&context, request, &response); + Status status = stub_->SendLogMessage(&context, request, &response); if(status.ok() && response.received()) { return true; From ca5149d820ab8e45b6ca828297caf9beb7a4f5a2 Mon Sep 17 00:00:00 2001 From: Jake Smith Date: Thu, 24 Sep 2020 20:37:25 -0400 Subject: [PATCH 4/8] Continued work on RpcClient + updating protobuf to standards --- BLUESPAWN-common/bluespawnpb/bluespawn.proto | 118 +++++++++------- BLUESPAWN-win-client/headers/util/Utils.h | 9 +- .../headers/util/rpc/RpcClient.h | 17 ++- BLUESPAWN-win-client/src/util/Utils.cpp | 85 +++++++----- .../src/util/log/ServerSink.cpp | 6 +- .../src/util/rpc/RpcClient.cpp | 130 +++++++++++++++++- 6 files changed, 254 insertions(+), 111 deletions(-) diff --git a/BLUESPAWN-common/bluespawnpb/bluespawn.proto b/BLUESPAWN-common/bluespawnpb/bluespawn.proto index 9c307ded..e166cf8e 100644 --- a/BLUESPAWN-common/bluespawnpb/bluespawn.proto +++ b/BLUESPAWN-common/bluespawnpb/bluespawn.proto @@ -1,5 +1,5 @@ syntax = "proto3"; -package bluespawn; +package bluespawn.protobuffer; option go_package = "BLUESPAWN/BLUESPAWN-common/bluespawnpb"; @@ -8,7 +8,7 @@ service BluespawnRPC { // Clients logging to Server rpc SendLogMessage(LogMessage) returns (ResponseMessage) {} rpc RecordDetection(Detection) returns (ResponseMessage) {} - rpc RecordAssociation(DetectionAssociation) returns (ResponseMessage) {} + rpc AddAssociation(DetectionAssociation) returns (ResponseMessage) {} rpc UpdateCertainty(DetectionCertaintyUpdate) returns (ResponseMessage) {} } @@ -45,6 +45,11 @@ enum DetectionType { OtherDetection = 4; } +enum DetectionRecordType { + PreScan = 0; + PostScan = 1; +} + enum RegistryDetectionType { CommandReference = 0; FileReference = 1; @@ -66,7 +71,7 @@ enum ProcessDetectionType { /* Message Serializers Types */ // General and Log message LogMessage { - string clientId = 1; + string client_id = 1; int64 timestamp = 2; string message = 3; LogSeverity severity = 4; @@ -81,103 +86,110 @@ message ResponseMessage { // Windows Objects message RegistryKey { - string keyPath = 1; + string key_path = 1; bool exists = 2; } message RegistryValue { RegistryKey key = 1; - string valueName = 2; - string valueData = 3; + string value_name = 2; + string value_data = 3; } // Detections message DetectionAssociation { + int64 detection_id = 1; // NOTE: Request change to string + int64 associated_id = 2; // NOTE: Request change to string + double strength = 3; } message DetectionCertaintyUpdate { int64 id = 1; // NOTE: Request change to string - + double raw_certainty = 2; + double certainty = 3; +} + +message YaraScanResult { + repeated string known_bad_rules = 1; + repeated string indicator_rules = 2; } message ProcessDetectionData { ProcessDetectionType type = 1; uint64 pid = 2; // optional uint64 tid = 3; // optional - string processName = 4; // optional - string processPath = 5; // optional - string processCommand = 6; // optional + string process_name = 4; // optional + string process_path = 5; // optional + string process_command = 6; // optional // TODO: add repeated ParentProcess which is a ProcessDetectionData - int64 baseAddress = 7; // optional - uint64 memorySize = 8; // optional - string imageName = 9; // optional + string base_address = 7; // optional + uint64 memory_size = 8; // optional + string image_name = 9; // optional } message FileDetectionData { bool exists = 1; - string filePath = 2; - string fileName = 3; - string fileExtension = 4; // optional - string fileType = 5; // optional + string file_path = 2; + string file_name = 3; + string file_extension = 4; // optional + string file_type = 5; // optional string executor = 6; // optional string md5 = 7; // optional string sha1 = 8; // optional string sha256 = 9; // optional - uint64 lastOpened = 10; // optional - uint64 fileCreated = 11; // optional - message YaraScanResult { - repeated string KnownBadRules = 1; - repeated string IndicatorRules = 2; - } + uint64 last_opened = 10; // optional + uint64 file_created = 11; // optional YaraScanResult yara = 12; // optional - bool fileSigned = 13; // optional + bool file_signed = 13; // optional string signer = 14; // optional } message RegistryDetectionData { - string keyPath = 1; + string key_path = 1; RegistryKey key = 2; RegistryValue value = 3; // optional bytes data = 4; // optional RegistryDetectionType type = 5; // optional } message ServiceDetectionData { - string serviceName = 1; // optional - string displayName = 2; // optional + string service_name = 1; // optional + string display_name = 2; // optional string description = 3; // optional - string filePath = 4; // optional + string file_path = 4; // optional } message OtherDetectionData { string type = 1; map properties = 2; } +message ScanInfo { + double raw_certainty = 1; + double certainty = 2; + map assocations = 3; // Detection Id (int64), certainity (double) +} + +message DetectionData { + oneof data { + ProcessDetectionData process_data = 1; + FileDetectionData file_data = 2; + RegistryDetectionData registry_data = 3; + ServiceDetectionData service_data = 4; + OtherDetectionData other_data = 5; + } +} + +message DetectionContext { + repeated string hunts = 1; + int64 first_evidence_time = 2; // optional + int64 detection_created_time = 3; + string note = 4; // optional +} + message Detection { int64 id = 1; // NOTE: Request change to string int64 timestamp = 2; DetectionType type = 3; - message ScanInfo { - double rawCertainty = 1; - double certainty = 2; - map assocation = 3; // Detection Id (int64), certainity (double) - } - ScanInfo info = 4; - - message DetectionData { - oneof data { - ProcessDetectionData processData = 1; - FileDetectionData fileData = 2; - RegistryDetectionData registryData = 3; - ServiceDetectionData serviceData = 4; - OtherDetectionData otherData = 5; - } - } - DetectionData data = 5; - - message DetectionContext { - repeated string hunts = 1; - int64 FirstEvidenceTime = 2; // optional - int64 DetectionCreatedTime = 3; - string note = 4; // optional - } - DetectionContext context = 6; + DetectionRecordType record_type = 4; + ScanInfo info = 5; + DetectionData data = 6; + DetectionContext context = 7; } diff --git a/BLUESPAWN-win-client/headers/util/Utils.h b/BLUESPAWN-win-client/headers/util/Utils.h index 79ad0470..8fd34f18 100644 --- a/BLUESPAWN-win-client/headers/util/Utils.h +++ b/BLUESPAWN-win-client/headers/util/Utils.h @@ -2,17 +2,18 @@ #include -#include #include +#include #define ADD_ALL_VECTOR(v1, v2) \ { \ auto& tmp = v2; \ - for(auto& v : tmp){ \ - v1.emplace_back(v); \ - } \ + for(auto& v : tmp) { \ + v1.emplace_back(v); \ + } \ } +int64_t FileTimeToInteger(const FILETIME& st); int64_t SystemTimeToInteger(const SYSTEMTIME& st); std::wstring FormatWindowsTime(const SYSTEMTIME& systemtime); std::wstring FormatWindowsTime(const FILETIME& systemtime); diff --git a/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h b/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h index b46af1fa..e4ccdf6b 100644 --- a/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h +++ b/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h @@ -4,14 +4,14 @@ #include +#include "util/log/DetectionSink.h" +#include "util/log/LogLevel.h" #include "util/log/Loggable.h" #include "util/wrappers.hpp" #include "BLUESPAWN-common/bluespawnpb/bluespawn.grpc.pb.h" -using bluespawn::BluespawnRPC; -using bluespawn::LogMessage; -using bluespawn::ResponseMessage; +using namespace bluespawn; using grpc::Channel; using grpc::ClientContext; using grpc::Status; @@ -19,12 +19,17 @@ using grpc::Status; namespace RpcClient { class RpcClient { private: - std::unique_ptr stub_; + std::unique_ptr stub_; + + protobuffer::Detection SerializeDetectionObject(IN CONST std::shared_ptr& detection, + IN CONST RecordType type = RecordType::PostScan); public: - RpcClient(std::shared_ptr channel) : stub_(BluespawnRPC::NewStub(channel)){}; + RpcClient(std::shared_ptr channel) : stub_(protobuffer::BluespawnRPC::NewStub(channel)){}; + + bool RecordDetection(IN CONST std::shared_ptr& detection, IN CONST RecordType type); - bool SendLogMessage(const std::wstring& msg, bluespawn::LogSeverity severity, bluespawn::LogDetail detail); + bool SendLogMessage(const std::wstring& msg, Log::Severity severity, Log::Detail detail); }; }; // namespace RpcClient diff --git a/BLUESPAWN-win-client/src/util/Utils.cpp b/BLUESPAWN-win-client/src/util/Utils.cpp index 65cf3ede..cb0f8818 100644 --- a/BLUESPAWN-win-client/src/util/Utils.cpp +++ b/BLUESPAWN-win-client/src/util/Utils.cpp @@ -1,58 +1,67 @@ #include "util/Utils.h" + #include + +#include #include #include -#include - -int64_t SystemTimeToInteger(const SYSTEMTIME& st){ - FILETIME ft; - SystemTimeToFileTime(&st, &ft); - ULARGE_INTEGER lv_Large; +int64_t FileTimeToInteger(const FILETIME& ft) { + ULARGE_INTEGER lv_Large; - lv_Large.LowPart = ft.dwLowDateTime; - lv_Large.HighPart = ft.dwHighDateTime; + lv_Large.LowPart = ft.dwLowDateTime; + lv_Large.HighPart = ft.dwHighDateTime; - return lv_Large.QuadPart; + return lv_Large.QuadPart; } -std::wstring FormatWindowsTime(const SYSTEMTIME& st){ - std::wostringstream w; - w << std::setfill(L'0') << st.wYear << "-" << std::setw(2) << st.wMonth << "-" << std::setw(2) << st.wDay << " " << - std::setw(2) << st.wHour << ":" << std::setw(2) << st.wMinute << ":" << std::setw(2) << st.wSecond << "." << - ((st.wMilliseconds % 10000000) * 100) << "Z"; - return w.str(); -} +int64_t SystemTimeToInteger(const SYSTEMTIME& st) { + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + + ULARGE_INTEGER lv_Large; + lv_Large.LowPart = ft.dwLowDateTime; + lv_Large.HighPart = ft.dwHighDateTime; -std::wstring FormatWindowsTime(const FILETIME& ft){ - SYSTEMTIME st; - FileTimeToSystemTime(&ft, &st); + return lv_Large.QuadPart; +} - std::wostringstream w; - w << std::setfill(L'0') << st.wYear << "-" << std::setw(2) << st.wMonth << "-" << std::setw(2) << st.wDay << " " << - std::setw(2) << st.wHour << ":" << std::setw(2) << st.wMinute << ":" << std::setw(2) << st.wSecond << "." << - st.wMilliseconds << "Z"; - return w.str(); +std::wstring FormatWindowsTime(const SYSTEMTIME& st) { + std::wostringstream w; + w << std::setfill(L'0') << st.wYear << "-" << std::setw(2) << st.wMonth << "-" << std::setw(2) << st.wDay << " " + << std::setw(2) << st.wHour << ":" << std::setw(2) << st.wMinute << ":" << std::setw(2) << st.wSecond << "." + << ((st.wMilliseconds % 10000000) * 100) << "Z"; + return w.str(); } +std::wstring FormatWindowsTime(const FILETIME& ft) { + SYSTEMTIME st; + FileTimeToSystemTime(&ft, &st); + + std::wostringstream w; + w << std::setfill(L'0') << st.wYear << "-" << std::setw(2) << st.wMonth << "-" << std::setw(2) << st.wDay << " " + << std::setw(2) << st.wHour << ":" << std::setw(2) << st.wMinute << ":" << std::setw(2) << st.wSecond << "." + << st.wMilliseconds << "Z"; + return w.str(); +} -std::wstring FormatWindowsTime(const std::wstring& windowsTime){ - SYSTEMTIME st; - FILETIME ft; +std::wstring FormatWindowsTime(const std::wstring& windowsTime) { + SYSTEMTIME st; + FILETIME ft; - ULONGLONG time = (ULONGLONG) stoull(windowsTime); - ULONGLONG nano = 0; + ULONGLONG time = (ULONGLONG) stoull(windowsTime); + ULONGLONG nano = 0; - ft.dwHighDateTime = (DWORD) ((time >> 32) & 0xFFFFFFFF); - ft.dwLowDateTime = (DWORD) (time & 0xFFFFFFFF); + ft.dwHighDateTime = (DWORD)((time >> 32) & 0xFFFFFFFF); + ft.dwLowDateTime = (DWORD)(time & 0xFFFFFFFF); - FileTimeToSystemTime(&ft, &st); - nano = (time % 10000000) * 100; // Display nanoseconds instead of milliseconds for higher resolution + FileTimeToSystemTime(&ft, &st); + nano = (time % 10000000) * 100; // Display nanoseconds instead of milliseconds for higher resolution - std::wostringstream w; - w << std::setfill(L'0') << st.wYear << "-" << std::setw(2) << st.wMonth << "-" << std::setw(2) << st.wDay << " " << - std::setw(2) << st.wHour << ":" << std::setw(2) << st.wMinute << ":" << std::setw(2) << st.wSecond << "." << - nano << "Z"; - return w.str(); + std::wostringstream w; + w << std::setfill(L'0') << st.wYear << "-" << std::setw(2) << st.wMonth << "-" << std::setw(2) << st.wDay << " " + << std::setw(2) << st.wHour << ":" << std::setw(2) << st.wMinute << ":" << std::setw(2) << st.wSecond << "." + << nano << "Z"; + return w.str(); } diff --git a/BLUESPAWN-win-client/src/util/log/ServerSink.cpp b/BLUESPAWN-win-client/src/util/log/ServerSink.cpp index 9cb858c5..ac529cb2 100644 --- a/BLUESPAWN-win-client/src/util/log/ServerSink.cpp +++ b/BLUESPAWN-win-client/src/util/log/ServerSink.cpp @@ -27,6 +27,8 @@ namespace Log { } BeginCriticalSection __{ *detection }; + + bool response = client.RecordDetection(detection, type); } void ServerSink::RecordAssociation(IN CONST std::shared_ptr& first, @@ -35,9 +37,7 @@ namespace Log { void ServerSink::LogMessage(const LogLevel& level, const std::wstring& message) { if(level.Enabled()) { - auto severity = static_cast(level.severity); - auto detail = static_cast(*level.detail); - bool response = client.SendLogMessage(message, severity, detail); + bool response = client.SendLogMessage(message, level.severity, *level.detail); } } diff --git a/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp b/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp index 2f916115..6c97f4d1 100644 --- a/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp +++ b/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp @@ -3,20 +3,136 @@ #include #include +#include + namespace RpcClient { - bool - RpcClient::SendLogMessage(const std::wstring& msg, bluespawn::LogSeverity severity, bluespawn::LogDetail detail) { + protobuffer::Detection RpcClient::SerializeDetectionObject(IN const std::shared_ptr& detection, + IN CONST RecordType type = RecordType::PostScan) { + protobuffer::Detection message; + message.set_record_type(static_cast(type)); + message.set_type(static_cast(detection->type)); + message.set_id(detection->dwID); + message.set_timestamp(FileTimeToInteger(detection->context.DetectionCreatedTime)); + + protobuffer::ScanInfo info; + info.set_certainty(detection->info.GetCertainty()); + info.set_raw_certainty(detection->info.GetIntrinsicCertainty()); + message.set_allocated_info(&info); + + protobuffer::DetectionData data; + if(std::holds_alternative(detection->data)) { + ProcessDetectionData& processData = std::get(detection->data); + protobuffer::ProcessDetectionData pbProcessData; + pbProcessData.set_type(static_cast(processData.type)); + pbProcessData.set_pid(*processData.PID); + pbProcessData.set_tid(*processData.TID); + pbProcessData.set_process_name(WidestringToString(*processData.ProcessName)); + pbProcessData.set_process_path(WidestringToString(*processData.ProcessPath)); + pbProcessData.set_process_command(WidestringToString(*processData.ProcessCommand)); + std::wstringstream baseAddress{}; + baseAddress << std::hex << *processData.BaseAddress; + pbProcessData.set_base_address(WidestringToString(baseAddress.str())); + pbProcessData.set_memory_size(*processData.MemorySize); + pbProcessData.set_image_name(WidestringToString(*processData.ImageName)); + data.set_allocated_process_data(&pbProcessData); + } else if(std::holds_alternative(detection->data)) { + FileDetectionData fileData = std::get(detection->data); + protobuffer::FileDetectionData pbFileData; + pbFileData.set_exists(fileData.FileFound); + pbFileData.set_file_path(WidestringToString(fileData.FilePath)); + pbFileData.set_file_name(WidestringToString(fileData.FileName)); + pbFileData.set_file_extension(WidestringToString(*fileData.FileExtension)); + pbFileData.set_file_type(WidestringToString(*fileData.FileType)); + pbFileData.set_executor(WidestringToString(*fileData.Executor)); + pbFileData.set_md5(WidestringToString(*fileData.MD5)); + pbFileData.set_sha1(WidestringToString(*fileData.SHA1)); + pbFileData.set_sha256(WidestringToString(*fileData.SHA256)); + pbFileData.set_last_opened(FileTimeToInteger(*fileData.LastOpened)); + pbFileData.set_last_opened(FileTimeToInteger(*fileData.FileCreated)); + protobuffer::YaraScanResult pbYaraScanResult; + if(fileData.yara.has_value()) { + for(auto& str : fileData.yara.value().vKnownBadRules) { + pbYaraScanResult.add_known_bad_rules(WidestringToString(str)); + } + for(auto& str : fileData.yara.value().vIndicatorRules) { + pbYaraScanResult.add_indicator_rules(WidestringToString(str)); + } + } + pbFileData.set_allocated_yara(&pbYaraScanResult); + pbFileData.set_file_signed(*fileData.FileSigned); + pbFileData.set_signer(WidestringToString(*fileData.Signer)); + data.set_allocated_file_data(&pbFileData); + } else if(std::holds_alternative(detection->data)) { + RegistryDetectionData registryData = std::get(detection->data); + protobuffer::RegistryDetectionData pbRegistryData; + pbRegistryData.set_key_path(WidestringToString(registryData.KeyPath)); + protobuffer::RegistryKey pbRegistryKey; + protobuffer::RegistryValue pbRegistryValue; + // set data + // set key/value + pbRegistryData.set_type(static_cast(registryData.type)); + + data.set_allocated_registry_data(&pbRegistryData); + } else if(std::holds_alternative(detection->data)) { + ServiceDetectionData serviceData = std::get(detection->data); + protobuffer::ServiceDetectionData pbServiceData; + data.set_allocated_service_data(&pbServiceData); + } else { + OtherDetectionData otherData = std::get(detection->data); + protobuffer::OtherDetectionData pbOtherData; + data.set_allocated_other_data(&pbOtherData); + } + message.set_allocated_data(&data); + + protobuffer::DetectionContext context; + if(detection->context.FirstEvidenceTime) { + context.set_first_evidence_time(FileTimeToInteger(*detection->context.FirstEvidenceTime)); + } + + if(detection->context.note) { + context.set_note(WidestringToString(*detection->context.note)); + } + + if(detection->context.hunts.size()) { + for(const auto& hunt : detection->context.hunts) { + context.add_hunts(WidestringToString(hunt)); + } + } + message.set_allocated_context(&context); + + return message; + } + + bool RpcClient::RpcClient::RecordDetection(IN const std::shared_ptr& detection, + IN CONST RecordType type) { + auto request = SerializeDetectionObject(detection, type); + + protobuffer::ResponseMessage response; + ClientContext context; + + Status status = stub_->RecordDetection(&context, request, &response); + + if(status.ok() && response.received()) { + return true; + } else { + return false; + } + + return false; + } + + bool RpcClient::SendLogMessage(const std::wstring& msg, Log::Severity sev, Log::Detail det) { SYSTEMTIME st; GetSystemTime(&st); - LogMessage request; - request.set_clientid("TO_BE_SET_IN_THE_FUTURE"); + protobuffer::LogMessage request; + request.set_client_id("TO_BE_SET_IN_THE_FUTURE"); request.set_timestamp(SystemTimeToInteger(st)); request.set_message(WidestringToString(msg)); - request.set_severity(severity); - request.set_detail(detail); + request.set_severity(static_cast(sev)); + request.set_detail(static_cast(det)); - ResponseMessage response; + protobuffer::ResponseMessage response; ClientContext context; Status status = stub_->SendLogMessage(&context, request, &response); From 9c0a8f873beee0df88af77a9b274dab0b2aaefe7 Mon Sep 17 00:00:00 2001 From: Jake Smith Date: Sat, 26 Sep 2020 12:11:26 -0400 Subject: [PATCH 5/8] Finish initial Rpc integration in client --- BLUESPAWN-common/bluespawnpb/bluespawn.proto | 3 +- .../headers/util/rpc/RpcClient.h | 4 +- .../src/util/log/ServerSink.cpp | 27 ++++++- .../src/util/rpc/RpcClient.cpp | 80 +++++++++++++++---- 4 files changed, 93 insertions(+), 21 deletions(-) diff --git a/BLUESPAWN-common/bluespawnpb/bluespawn.proto b/BLUESPAWN-common/bluespawnpb/bluespawn.proto index e166cf8e..e8c99124 100644 --- a/BLUESPAWN-common/bluespawnpb/bluespawn.proto +++ b/BLUESPAWN-common/bluespawnpb/bluespawn.proto @@ -146,8 +146,7 @@ message RegistryDetectionData { string key_path = 1; RegistryKey key = 2; RegistryValue value = 3; // optional - bytes data = 4; // optional - RegistryDetectionType type = 5; // optional + RegistryDetectionType type = 4; // optional } message ServiceDetectionData { string service_name = 1; // optional diff --git a/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h b/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h index e4ccdf6b..d7f27ea1 100644 --- a/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h +++ b/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h @@ -22,12 +22,14 @@ namespace RpcClient { std::unique_ptr stub_; protobuffer::Detection SerializeDetectionObject(IN CONST std::shared_ptr& detection, - IN CONST RecordType type = RecordType::PostScan); + IN CONST RecordType type); public: RpcClient(std::shared_ptr channel) : stub_(protobuffer::BluespawnRPC::NewStub(channel)){}; bool RecordDetection(IN CONST std::shared_ptr& detection, IN CONST RecordType type); + bool AddAssociation(IN DWORD detection_id, IN DWORD associated, IN double strength); + bool UpdateCertainty(IN const std::shared_ptr& detection); bool SendLogMessage(const std::wstring& msg, Log::Severity severity, Log::Detail detail); }; diff --git a/BLUESPAWN-win-client/src/util/log/ServerSink.cpp b/BLUESPAWN-win-client/src/util/log/ServerSink.cpp index ac529cb2..1c8348f9 100644 --- a/BLUESPAWN-win-client/src/util/log/ServerSink.cpp +++ b/BLUESPAWN-win-client/src/util/log/ServerSink.cpp @@ -17,9 +17,19 @@ namespace Log { wServerAddress{ ServerAddress }, client(grpc::CreateChannel(WidestringToString(wServerAddress), grpc::InsecureChannelCredentials())) {} - void ServerSink::UpdateCertainty(IN CONST std::shared_ptr& detection) {} + void ServerSink::UpdateCertainty(IN CONST std::shared_ptr& detection) { + BeginCriticalSection __{ *detection }; + + if(detections.find(detection->dwID) != detections.end()) { + bool response = client.UpdateCertainty(detection); + } + } - void ServerSink::AddAssociation(IN DWORD detection_id, IN DWORD associated, IN double strength) {} + void ServerSink::AddAssociation(IN DWORD detection_id, IN DWORD associated, IN double strength) { + if(detections.find(detection_id) != detections.end()) { + bool response = client.AddAssociation(detection_id, associated, strength); + } + } void ServerSink::RecordDetection(IN CONST std::shared_ptr& detection, IN RecordType type) { if(type == RecordType::PreScan && !Bluespawn::EnablePreScanDetections) { @@ -33,7 +43,18 @@ namespace Log { void ServerSink::RecordAssociation(IN CONST std::shared_ptr& first, IN CONST std::shared_ptr& second, - IN CONST Association& strength) {} + IN CONST Association& strength) { + UpdateCertainty(first); + UpdateCertainty(second); + + if(detections.find(first->dwID) != detections.end()) { + AddAssociation(first->dwID, second->dwID, strength); + } + + if(detections.find(second->dwID) != detections.end()) { + AddAssociation(second->dwID, first->dwID, strength); + } + } void ServerSink::LogMessage(const LogLevel& level, const std::wstring& message) { if(level.Enabled()) { diff --git a/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp b/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp index 6c97f4d1..49d6106f 100644 --- a/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp +++ b/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp @@ -6,13 +6,13 @@ #include namespace RpcClient { - protobuffer::Detection RpcClient::SerializeDetectionObject(IN const std::shared_ptr& detection, + protobuffer::Detection RpcClient::SerializeDetectionObject(IN CONST std::shared_ptr& detection, IN CONST RecordType type = RecordType::PostScan) { protobuffer::Detection message; - message.set_record_type(static_cast(type)); - message.set_type(static_cast(detection->type)); message.set_id(detection->dwID); message.set_timestamp(FileTimeToInteger(detection->context.DetectionCreatedTime)); + message.set_type(static_cast(detection->type)); + message.set_record_type(static_cast(type)); protobuffer::ScanInfo info; info.set_certainty(detection->info.GetCertainty()); @@ -67,37 +67,55 @@ namespace RpcClient { protobuffer::RegistryDetectionData pbRegistryData; pbRegistryData.set_key_path(WidestringToString(registryData.KeyPath)); protobuffer::RegistryKey pbRegistryKey; + pbRegistryKey.set_key_path(WidestringToString(registryData.key.GetName())); + pbRegistryKey.set_exists(registryData.key.Exists()); + pbRegistryData.set_allocated_key(&pbRegistryKey); protobuffer::RegistryValue pbRegistryValue; - // set data - // set key/value + pbRegistryValue.set_allocated_key(&pbRegistryKey); + if(registryData.value.has_value()) { + pbRegistryValue.set_value_name(WidestringToString((*registryData.value).wValueName)); + pbRegistryValue.set_value_data(WidestringToString((*registryData.value).ToString())); + } + pbRegistryData.set_allocated_value(&pbRegistryValue); pbRegistryData.set_type(static_cast(registryData.type)); - data.set_allocated_registry_data(&pbRegistryData); } else if(std::holds_alternative(detection->data)) { ServiceDetectionData serviceData = std::get(detection->data); protobuffer::ServiceDetectionData pbServiceData; + pbServiceData.set_service_name(WidestringToString(*serviceData.ServiceName)); + pbServiceData.set_display_name(WidestringToString(*serviceData.DisplayName)); + pbServiceData.set_description(WidestringToString(*serviceData.Description)); + pbServiceData.set_file_path(WidestringToString(*serviceData.FilePath)); data.set_allocated_service_data(&pbServiceData); } else { OtherDetectionData otherData = std::get(detection->data); protobuffer::OtherDetectionData pbOtherData; - data.set_allocated_other_data(&pbOtherData); + pbOtherData.set_type(WidestringToString(otherData.DetectionType)); + auto mutable_properties = pbOtherData.mutable_properties(); + for(const auto& [key, value] : otherData.DetectionProperties) { + mutable_properties->insert({ WidestringToString(key), WidestringToString(value) }); + } } message.set_allocated_data(&data); protobuffer::DetectionContext context; + + if(detection->context.hunts.size()) { + for(const auto& hunt : detection->context.hunts) { + context.add_hunts(WidestringToString(hunt)); + } + } + if(detection->context.FirstEvidenceTime) { context.set_first_evidence_time(FileTimeToInteger(*detection->context.FirstEvidenceTime)); } + context.set_first_evidence_time(FileTimeToInteger(detection->context.DetectionCreatedTime)); + if(detection->context.note) { context.set_note(WidestringToString(*detection->context.note)); } - if(detection->context.hunts.size()) { - for(const auto& hunt : detection->context.hunts) { - context.add_hunts(WidestringToString(hunt)); - } - } message.set_allocated_context(&context); return message; @@ -117,8 +135,42 @@ namespace RpcClient { } else { return false; } + } + + bool RpcClient::AddAssociation(IN DWORD detection_id, IN DWORD associated, IN double strength) { + protobuffer::DetectionAssociation request; + request.set_detection_id(detection_id); + request.set_associated_id(associated); + request.set_strength(strength); + + protobuffer::ResponseMessage response; + ClientContext context; + + Status status = stub_->AddAssociation(&context, request, &response); + + if(status.ok() && response.received()) { + return true; + } else { + return false; + } + } - return false; + bool RpcClient::UpdateCertainty(IN const std::shared_ptr& detection) { + protobuffer::DetectionCertaintyUpdate request; + request.set_id(detection->dwID); + request.set_raw_certainty(detection->info.GetIntrinsicCertainty()); + request.set_certainty(detection->info.GetCertainty()); + + protobuffer::ResponseMessage response; + ClientContext context; + + Status status = stub_->UpdateCertainty(&context, request, &response); + + if(status.ok() && response.received()) { + return true; + } else { + return false; + } } bool RpcClient::SendLogMessage(const std::wstring& msg, Log::Severity sev, Log::Detail det) { @@ -142,7 +194,5 @@ namespace RpcClient { } else { return false; } - - return false; } } // namespace RpcClient From a981ef4b65a394a378f50d9905f919cb6e6f7787 Mon Sep 17 00:00:00 2001 From: Jake Smith Date: Sat, 26 Sep 2020 18:01:39 -0400 Subject: [PATCH 6/8] Switch to using mutable instead of allocated objs in RPC Also update rpc_server with new methods --- BLUESPAWN-common/bluespawnpb/bluespawn.proto | 387 +++++++++--------- BLUESPAWN-server/rpc_server/rpc_server.go | 85 ++-- BLUESPAWN-win-client/BLUESPAWN-client.vcxproj | 8 +- .../headers/util/log/ServerSink.h | 154 +++---- .../headers/util/rpc/RpcClient.h | 3 + BLUESPAWN-win-client/src/user/BLUESPAWN.cpp | 25 +- .../src/util/log/ServerSink.cpp | 137 +++---- .../src/util/rpc/RpcClient.cpp | 123 +++--- config/buildsettings.props | 3 +- 9 files changed, 479 insertions(+), 446 deletions(-) diff --git a/BLUESPAWN-common/bluespawnpb/bluespawn.proto b/BLUESPAWN-common/bluespawnpb/bluespawn.proto index e8c99124..5420212c 100644 --- a/BLUESPAWN-common/bluespawnpb/bluespawn.proto +++ b/BLUESPAWN-common/bluespawnpb/bluespawn.proto @@ -1,194 +1,193 @@ -syntax = "proto3"; -package bluespawn.protobuffer; - -option go_package = "BLUESPAWN/BLUESPAWN-common/bluespawnpb"; - -/* Core BLUESPAWN RPC Server */ -service BluespawnRPC { - // Clients logging to Server - rpc SendLogMessage(LogMessage) returns (ResponseMessage) {} - rpc RecordDetection(Detection) returns (ResponseMessage) {} - rpc AddAssociation(DetectionAssociation) returns (ResponseMessage) {} - rpc UpdateCertainty(DetectionCertaintyUpdate) returns (ResponseMessage) {} -} - -/* Data Type Enums */ -// General and Log -enum LogDetail { - Low = 0; - Moderate = 1; - High = 2; -} - -enum LogSeverity { - LogError = 0; - LogWarn = 1; - LogInfo = 2; - LogVerbose = 3; -} - -// Windows Objects -enum RegistryType { - RegSz = 0; - RegExpandSz = 1; - RegMultiSz = 2; - RegDword = 3; - RegBinary = 4; -} - -// Detections -enum DetectionType { - ProcessDetection = 0; - RegistryDetection = 1; - FileDetection = 2; - ServiceDetection = 3; - OtherDetection = 4; -} - -enum DetectionRecordType { - PreScan = 0; - PostScan = 1; -} - -enum RegistryDetectionType { - CommandReference = 0; - FileReference = 1; - FolderReference = 2; - PipeReference = 3; - ShareReference = 4; - UserReference = 5; - Configuration = 6; - Unknown = 7; -} - -enum ProcessDetectionType { - MaliciousProcess = 0; - MaliciousImage = 1; - MaliciousMemory = 2; - MaliciousCommand = 3; -} - -/* Message Serializers Types */ -// General and Log -message LogMessage { - string client_id = 1; - int64 timestamp = 2; - string message = 3; - LogSeverity severity = 4; - LogDetail detail = 5; -} - -message ResponseMessage { - bool received = 1; - bool success = 2; - string message = 3; // optional -} - -// Windows Objects -message RegistryKey { - string key_path = 1; - bool exists = 2; -} - -message RegistryValue { - RegistryKey key = 1; - string value_name = 2; - string value_data = 3; -} - -// Detections -message DetectionAssociation { - int64 detection_id = 1; // NOTE: Request change to string - int64 associated_id = 2; // NOTE: Request change to string - double strength = 3; -} - -message DetectionCertaintyUpdate { - int64 id = 1; // NOTE: Request change to string - double raw_certainty = 2; - double certainty = 3; -} - -message YaraScanResult { - repeated string known_bad_rules = 1; - repeated string indicator_rules = 2; -} - -message ProcessDetectionData { - ProcessDetectionType type = 1; - uint64 pid = 2; // optional - uint64 tid = 3; // optional - string process_name = 4; // optional - string process_path = 5; // optional - string process_command = 6; // optional - // TODO: add repeated ParentProcess which is a ProcessDetectionData - string base_address = 7; // optional - uint64 memory_size = 8; // optional - string image_name = 9; // optional -} -message FileDetectionData { - bool exists = 1; - string file_path = 2; - string file_name = 3; - string file_extension = 4; // optional - string file_type = 5; // optional - string executor = 6; // optional - string md5 = 7; // optional - string sha1 = 8; // optional - string sha256 = 9; // optional - uint64 last_opened = 10; // optional - uint64 file_created = 11; // optional - YaraScanResult yara = 12; // optional - bool file_signed = 13; // optional - string signer = 14; // optional -} -message RegistryDetectionData { - string key_path = 1; - RegistryKey key = 2; - RegistryValue value = 3; // optional - RegistryDetectionType type = 4; // optional -} -message ServiceDetectionData { - string service_name = 1; // optional - string display_name = 2; // optional - string description = 3; // optional - string file_path = 4; // optional -} -message OtherDetectionData { - string type = 1; - map properties = 2; -} - -message ScanInfo { - double raw_certainty = 1; - double certainty = 2; - map assocations = 3; // Detection Id (int64), certainity (double) -} - -message DetectionData { - oneof data { - ProcessDetectionData process_data = 1; - FileDetectionData file_data = 2; - RegistryDetectionData registry_data = 3; - ServiceDetectionData service_data = 4; - OtherDetectionData other_data = 5; - } -} - -message DetectionContext { - repeated string hunts = 1; - int64 first_evidence_time = 2; // optional - int64 detection_created_time = 3; - string note = 4; // optional -} - -message Detection { - int64 id = 1; // NOTE: Request change to string - int64 timestamp = 2; - DetectionType type = 3; - DetectionRecordType record_type = 4; - ScanInfo info = 5; - DetectionData data = 6; - DetectionContext context = 7; -} - +syntax = "proto3"; +package bluespawn.protobuffer; + +option go_package = "BLUESPAWN/BLUESPAWN-common/bluespawnpb"; + +/* Core BLUESPAWN RPC Server */ +service BluespawnRPC { + // Clients logging to Server + rpc SendLogMessage(LogMessage) returns (ResponseMessage) {} + rpc RecordDetection(Detection) returns (ResponseMessage) {} + rpc AddAssociation(DetectionAssociation) returns (ResponseMessage) {} + rpc UpdateCertainty(DetectionCertaintyUpdate) returns (ResponseMessage) {} +} + +/* Data Type Enums */ +// General and Log +enum LogDetail { + Low = 0; + Moderate = 1; + High = 2; +} + +enum LogSeverity { + LogError = 0; + LogWarn = 1; + LogInfo = 2; + LogVerbose = 3; +} + +// Windows Objects +enum RegistryType { + RegSz = 0; + RegExpandSz = 1; + RegMultiSz = 2; + RegDword = 3; + RegBinary = 4; +} + +// Detections +enum DetectionType { + ProcessDetection = 0; + RegistryDetection = 1; + FileDetection = 2; + ServiceDetection = 3; + OtherDetection = 4; +} + +enum DetectionRecordType { + PreScan = 0; + PostScan = 1; +} + +enum RegistryDetectionType { + CommandReference = 0; + FileReference = 1; + FolderReference = 2; + PipeReference = 3; + ShareReference = 4; + UserReference = 5; + Configuration = 6; + Unknown = 7; +} + +enum ProcessDetectionType { + MaliciousProcess = 0; + MaliciousImage = 1; + MaliciousMemory = 2; + MaliciousCommand = 3; +} + +/* Message Serializers Types */ +// General and Log +message LogMessage { + string client_id = 1; + int64 timestamp = 2; + string message = 3; + LogSeverity severity = 4; + LogDetail detail = 5; +} + +message ResponseMessage { + bool received = 1; + bool success = 2; + string message = 3; // optional +} + +// Windows Objects +message RegistryKey { + string key_path = 1; + bool exists = 2; +} + +message RegistryValue { + string value_name = 1; + string value_data = 2; +} + +// Detections +message DetectionAssociation { + int64 detection_id = 1; // NOTE: Request change to string + int64 associated_id = 2; // NOTE: Request change to string + double strength = 3; +} + +message DetectionCertaintyUpdate { + int64 id = 1; // NOTE: Request change to string + double raw_certainty = 2; + double certainty = 3; +} + +message YaraScanResult { + repeated string known_bad_rules = 1; + repeated string indicator_rules = 2; +} + +message ProcessDetectionData { + ProcessDetectionType type = 1; + uint64 pid = 2; // optional + uint64 tid = 3; // optional + string process_name = 4; // optional + string process_path = 5; // optional + string process_command = 6; // optional + // TODO: add repeated ParentProcess which is a ProcessDetectionData + string base_address = 7; // optional + uint64 memory_size = 8; // optional + string image_name = 9; // optional +} +message FileDetectionData { + bool exists = 1; + string file_path = 2; + string file_name = 3; + string file_extension = 4; // optional + string file_type = 5; // optional + string executor = 6; // optional + string md5 = 7; // optional + string sha1 = 8; // optional + string sha256 = 9; // optional + uint64 last_opened = 10; // optional + uint64 file_created = 11; // optional + YaraScanResult yara = 12; // optional + bool file_signed = 13; // optional + string signer = 14; // optional +} +message RegistryDetectionData { + string key_path = 1; + RegistryKey key = 2; + RegistryValue value = 3; // optional + RegistryDetectionType type = 4; // optional +} +message ServiceDetectionData { + string service_name = 1; // optional + string display_name = 2; // optional + string description = 3; // optional + string file_path = 4; // optional +} +message OtherDetectionData { + string type = 1; + map properties = 2; +} + +message ScanInfo { + double raw_certainty = 1; + double certainty = 2; + map assocations = 3; // Detection Id (int64), certainity (double) +} + +message DetectionData { + oneof data { + ProcessDetectionData process_data = 1; + FileDetectionData file_data = 2; + RegistryDetectionData registry_data = 3; + ServiceDetectionData service_data = 4; + OtherDetectionData other_data = 5; + } +} + +message DetectionContext { + repeated string hunts = 1; + int64 first_evidence_time = 2; // optional + int64 detection_created_time = 3; + string note = 4; // optional +} + +message Detection { + int64 id = 1; // NOTE: Request change to string + int64 timestamp = 2; + DetectionType type = 3; + DetectionRecordType record_type = 4; + ScanInfo info = 5; + DetectionData data = 6; + DetectionContext context = 7; +} + diff --git a/BLUESPAWN-server/rpc_server/rpc_server.go b/BLUESPAWN-server/rpc_server/rpc_server.go index ec786bbc..9ad9871d 100644 --- a/BLUESPAWN-server/rpc_server/rpc_server.go +++ b/BLUESPAWN-server/rpc_server/rpc_server.go @@ -1,35 +1,50 @@ -package main - -import ( - "context" - "log" - "net" - - "google.golang.org/grpc" - pb "BLUESPAWN/BLUESPAWN-common/bluespawnpb" -) - -const ( - port = ":50052" -) - -type server struct { - pb.UnimplementedLogReceiverServer -} - -func (s *server) SendLog(ctx context.Context, in *pb.LogMessageRequest) (*pb.LogMessageResponse, error) { - log.Printf("Received: %v", in.GetMessage()) - return &pb.LogMessageResponse{Received: true}, nil -} - -func main() { - lis, err := net.Listen("tcp", port) - if err != nil { - log.Fatalf("failed to listen: %v", err) - } - s := grpc.NewServer() - pb.RegisterLogReceiverServer(s, &server{}) - if err := s.Serve(lis); err != nil { - log.Fatalf("failed to serve: %v", err) - } -} +package main + +import ( + "context" + "log" + "net" + + "google.golang.org/grpc" + pb "BLUESPAWN/BLUESPAWN-common/bluespawnpb" +) + +const ( + port = ":50052" +) + +type server struct { + pb.UnimplementedBluespawnRPCServer +} + +func (s *server) SendLogMessage(ctx context.Context, in *pb.LogMessage) (*pb.ResponseMessage, error) { + log.Printf("Received Log Message: %v", in.GetMessage()) + return &pb.ResponseMessage{Received: true, Success: true}, nil +} + +func (s *server) RecordDetection(ctx context.Context, in *pb.Detection) (*pb.ResponseMessage, error) { + log.Printf("Received Detection: ID %d", in.GetId()) + return &pb.ResponseMessage{Received: true, Success: true}, nil +} + +func (s *server) AddAssociation(ctx context.Context, in *pb.DetectionAssociation) (*pb.ResponseMessage, error) { + log.Printf("Received Association for Detection ID %d and ID %d with strength %f", in.GetDetection_Id(), in.GetAssociated_Id(), in.GetStrength()) + return &pb.ResponseMessage{Received: true, Success: true}, nil +} + +func (s *server) UpdateCertainty(ctx context.Context, in *pb.DetectionCertaintyUpdate) (*pb.ResponseMessage, error) { + log.Printf("Received Detection Certainty update for ID %d to %f", in.GetId(), in.GetCertainty()) + return &pb.ResponseMessage{Received: true, Success: true}, nil +} + +func main() { + lis, err := net.Listen("tcp", port) + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + s := grpc.NewServer() + pb.RegisterBluespawnRPCServer(s, &server{}) + if err := s.Serve(lis); err != nil { + log.Fatalf("failed to serve: %v", err) + } +} diff --git a/BLUESPAWN-win-client/BLUESPAWN-client.vcxproj b/BLUESPAWN-win-client/BLUESPAWN-client.vcxproj index 829f8e2e..6f65a868 100644 --- a/BLUESPAWN-win-client/BLUESPAWN-client.vcxproj +++ b/BLUESPAWN-win-client/BLUESPAWN-client.vcxproj @@ -98,6 +98,7 @@ + @@ -242,6 +243,7 @@ + @@ -325,14 +327,14 @@ Adding manifest to BLUESPAWN-client.exe - + Generate corresponding .h/cc files based on the current gRPC protobuf configuration - "$(SolutionDir)vcpkg\packages\protobuf_x86-windows-static\tools\protobuf\protoc.exe" --proto_path="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --cpp_out="$(SolutionDir)BLUESPAWN-common\bluespawnpb" "bluespawn.proto" && "$(SolutionDir)vcpkg\packages\protobuf_x86-windows-static\tools\protobuf\protoc.exe" --proto_path="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --grpc_out="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --plugin=protoc-gen-grpc="$(SolutionDir)vcpkg\packages\grpc_x86-windows-static\tools\grpc\grpc_cpp_plugin.exe" "$(SolutionDir)BLUESPAWN-common\bluespawnpb\bluespawn.proto" + "$(SolutionDir)vcpkg\packages\protobuf_x86-windows-static\tools\protobuf\protoc.exe" --error_format=msvs --proto_path="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --cpp_out="$(SolutionDir)BLUESPAWN-common\bluespawnpb" "bluespawn.proto" && "$(SolutionDir)vcpkg\packages\protobuf_x86-windows-static\tools\protobuf\protoc.exe" --error_format=msvs --proto_path="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --grpc_out="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --plugin=protoc-gen-grpc="$(SolutionDir)vcpkg\packages\grpc_x86-windows-static\tools\grpc\grpc_cpp_plugin.exe" "$(SolutionDir)BLUESPAWN-common\bluespawnpb\bluespawn.proto" - "$(SolutionDir)vcpkg\packages\protobuf_x64-windows-static\tools\protobuf\protoc.exe" --proto_path="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --cpp_out="$(SolutionDir)BLUESPAWN-common\bluespawnpb" "bluespawn.proto" && "$(SolutionDir)vcpkg\packages\protobuf_x64-windows-static\tools\protobuf\protoc.exe" --proto_path="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --grpc_out="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --plugin=protoc-gen-grpc="$(SolutionDir)vcpkg\packages\grpc_x64-windows-static\tools\grpc\grpc_cpp_plugin.exe" "$(SolutionDir)BLUESPAWN-common\bluespawnpb\bluespawn.proto" + "$(SolutionDir)vcpkg\packages\protobuf_x64-windows-static\tools\protobuf\protoc.exe" --error_format=msvs --proto_path="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --cpp_out="$(SolutionDir)BLUESPAWN-common\bluespawnpb" "bluespawn.proto" && "$(SolutionDir)vcpkg\packages\protobuf_x64-windows-static\tools\protobuf\protoc.exe" --error_format=msvs --proto_path="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --grpc_out="$(SolutionDir)BLUESPAWN-common\bluespawnpb" --plugin=protoc-gen-grpc="$(SolutionDir)vcpkg\packages\grpc_x64-windows-static\tools\grpc\grpc_cpp_plugin.exe" "$(SolutionDir)BLUESPAWN-common\bluespawnpb\bluespawn.proto" diff --git a/BLUESPAWN-win-client/headers/util/log/ServerSink.h b/BLUESPAWN-win-client/headers/util/log/ServerSink.h index 9ba1fa1d..2d7809a7 100644 --- a/BLUESPAWN-win-client/headers/util/log/ServerSink.h +++ b/BLUESPAWN-win-client/headers/util/log/ServerSink.h @@ -1,77 +1,77 @@ -#pragma once - -#include "util/rpc/RpcClient.h" - -#include "DetectionSink.h" -#include "LogSink.h" - -namespace Log { - - /** - * ServerSink provides a sink for the logger that will send logs to a remote server, usually a BLUESPAWN-server installation - */ - class ServerSink : public LogSink, public DetectionSink { - /// Rpc Server Client - RpcClient::RpcClient client; - - /// The remote server (http(s)://)IP:PORT or (http(s)://)FQDN:PORT that will recieve the logs - std::wstring wServerAddress; - - /// Tags for messages sent at different levels - std::string MessageTags[4] = { "error", "warning", "info", "other" }; - - /// A set of IDs created for detections already sent to the server - std::set detections; - - void AddAssociation(IN DWORD detection_id, IN DWORD associated, IN double strength); - - public: - /** - * Default constructor for ServerSink. Must provide a server address to send the logs - */ - ServerSink(const std::wstring ServerAddress); - - /** - * Outputs a message to the target server if its logging level is enabled. - * - * @param level The level at which the message is being logged - * @param message The message to log - */ - virtual void LogMessage(const LogLevel& level, const std::wstring& message); - - /** - * Compares this ServerSink to another LogSink. All LogSink objects referring to the same file are considered - * equal - * - * @param sink The LogSink to compare - * - * @return Whether or not the argument and this sink are considered equal. - */ - virtual bool operator==(const LogSink& sink) const; - - /** - * Updates the raw and combined certainty values associated with a detection - * - * @param detection The detection to update - */ - virtual void UpdateCertainty(IN CONST std::shared_ptr& detection); - - /** - * Records a detection, sending the information to the server. - * - * @param detection The detection to record - * @param type The type of record this is, either PreScan or PostScan - */ - virtual void RecordDetection(IN CONST std::shared_ptr& detection, IN RecordType type); - - /** - * Records an association between two detections and informs the server - * - * @param first The first detection in the assocation. This detection's ID will be lower than the second's. - * @param second The second detection in the association. - */ - virtual void RecordAssociation(IN CONST std::shared_ptr& first, - IN CONST std::shared_ptr& second, - IN CONST Association& strength); - }; -} // namespace Log +#pragma once + +#include "util/rpc/RpcClient.h" + +#include "DetectionSink.h" +#include "LogSink.h" + +namespace Log { + + /** + * ServerSink provides a sink for the logger that will send logs to a remote server, usually a BLUESPAWN-server installation + */ + class ServerSink : public LogSink, public DetectionSink { + /// Rpc Server Client + RpcClient::RpcClient client; + + /// The remote server (http(s)://)IP:PORT or (http(s)://)FQDN:PORT that will recieve the logs + std::string ServerAddress; + + /// Tags for messages sent at different levels + std::string MessageTags[4] = { "error", "warning", "info", "other" }; + + /// A set of IDs created for detections already sent to the server + std::set detections; + + void AddAssociation(IN DWORD detection_id, IN DWORD associated, IN double strength); + + public: + /** + * Default constructor for ServerSink. Must provide a server address to send the logs + */ + ServerSink(const std::string address); + + /** + * Outputs a message to the target server if its logging level is enabled. + * + * @param level The level at which the message is being logged + * @param message The message to log + */ + virtual void LogMessage(const LogLevel& level, const std::wstring& message); + + /** + * Compares this ServerSink to another LogSink. All LogSink objects referring to the same file are considered + * equal + * + * @param sink The LogSink to compare + * + * @return Whether or not the argument and this sink are considered equal. + */ + virtual bool operator==(const LogSink& sink) const; + + /** + * Updates the raw and combined certainty values associated with a detection + * + * @param detection The detection to update + */ + virtual void UpdateCertainty(IN CONST std::shared_ptr& detection); + + /** + * Records a detection, sending the information to the server. + * + * @param detection The detection to record + * @param type The type of record this is, either PreScan or PostScan + */ + virtual void RecordDetection(IN CONST std::shared_ptr& detection, IN RecordType type); + + /** + * Records an association between two detections and informs the server + * + * @param first The first detection in the assocation. This detection's ID will be lower than the second's. + * @param second The second detection in the association. + */ + virtual void RecordAssociation(IN CONST std::shared_ptr& first, + IN CONST std::shared_ptr& second, + IN CONST Association& strength); + }; +} // namespace Log diff --git a/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h b/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h index d7f27ea1..71e4bdea 100644 --- a/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h +++ b/BLUESPAWN-win-client/headers/util/rpc/RpcClient.h @@ -25,6 +25,9 @@ namespace RpcClient { IN CONST RecordType type); public: + RpcClient() : + stub_(protobuffer::BluespawnRPC::NewStub( + grpc::CreateChannel("localhost:50052", grpc::InsecureChannelCredentials()))){}; RpcClient(std::shared_ptr channel) : stub_(protobuffer::BluespawnRPC::NewStub(channel)){}; bool RecordDetection(IN CONST std::shared_ptr& detection, IN CONST RecordType type); diff --git a/BLUESPAWN-win-client/src/user/BLUESPAWN.cpp b/BLUESPAWN-win-client/src/user/BLUESPAWN.cpp index 5cde8b4e..8d534844 100644 --- a/BLUESPAWN-win-client/src/user/BLUESPAWN.cpp +++ b/BLUESPAWN-win-client/src/user/BLUESPAWN.cpp @@ -10,6 +10,7 @@ #include "util/log/CLISink.h" #include "util/log/DebugSink.h" #include "util/log/JSONSink.h" +#include "util/log/ServerSink.h" #include "util/log/XMLSink.h" #include "hunt/hunts/HuntT1036.h" @@ -255,7 +256,22 @@ void Bluespawn::check_correct_arch() { } } -void ParseLogSinks(const std::string& sinks, const std::string& logdir) { +void ParseLogSinks(const std::string& sinks, const std::string& logdir, const std::string& serverAddress) { + bool serverEnabled = false; + if(serverAddress.length() > 0 && sinks.find("server") == std::string::npos) { + LOG_WARNING("Specified a remote server for logs, but did not enable server log sink. Enabling..."); + Bluespawn::io.InformUser(L"Specified a remote server for logs, but did not enable server log sink. " + L"Enabling..."); + serverEnabled = true; + } else if(serverAddress.length() == 0 && sinks.find("server") != std::string::npos) { + LOG_ERROR("Tried to enable the server log sink, but did not specify a remote server. No logs will be sent!"); + Bluespawn::io.AlertUser(L"Tried to enable the server log sink, but did not specify a remote server. No logs " + "will be sent!", + 5000, ImportanceLevel::MEDIUM); + } else if(serverAddress.length() > 0 && sinks.find("server") != std::string::npos) { + serverEnabled = true; + } + std::set sink_set; for(unsigned startIdx = 0; startIdx < sinks.size();) { auto endIdx{ sinks.find(',', startIdx) }; @@ -298,6 +314,10 @@ void ParseLogSinks(const std::string& sinks, const std::string& logdir) { auto JSON = std::make_shared(outputFolderPath); Log::AddSink(JSON, levels); Bluespawn::detectionSinks.emplace_back(JSON); + } else if(serverEnabled) { + auto server = std::make_shared(serverAddress); + Log::AddSink(server, levels); + Bluespawn::detectionSinks.emplace_back(server); } else if(sink == "debug") { auto debug = std::make_shared(); Log::AddSink(debug, levels); @@ -422,7 +442,8 @@ int main(int argc, char* argv[]) { } } - ParseLogSinks(result["log"].as(), result["output"].as()); + ParseLogSinks(result["log"].as(), result["output"].as(), + result["server"].as()); if(result.count("hunt") || result.count("monitor")) { if(result.count("hunt")) { diff --git a/BLUESPAWN-win-client/src/util/log/ServerSink.cpp b/BLUESPAWN-win-client/src/util/log/ServerSink.cpp index 1c8348f9..73eaa81f 100644 --- a/BLUESPAWN-win-client/src/util/log/ServerSink.cpp +++ b/BLUESPAWN-win-client/src/util/log/ServerSink.cpp @@ -1,69 +1,68 @@ -#include "util/log/ServerSink.h" - -#include -#include -#include -#include - -#include "util/StringUtils.h" -#include "util/Utils.h" -#include "util/rpc/RpcClient.h" - -#include "user/bluespawn.h" - -namespace Log { - - ServerSink::ServerSink(const std::wstring ServerAddress) : - wServerAddress{ ServerAddress }, - client(grpc::CreateChannel(WidestringToString(wServerAddress), grpc::InsecureChannelCredentials())) {} - - void ServerSink::UpdateCertainty(IN CONST std::shared_ptr& detection) { - BeginCriticalSection __{ *detection }; - - if(detections.find(detection->dwID) != detections.end()) { - bool response = client.UpdateCertainty(detection); - } - } - - void ServerSink::AddAssociation(IN DWORD detection_id, IN DWORD associated, IN double strength) { - if(detections.find(detection_id) != detections.end()) { - bool response = client.AddAssociation(detection_id, associated, strength); - } - } - - void ServerSink::RecordDetection(IN CONST std::shared_ptr& detection, IN RecordType type) { - if(type == RecordType::PreScan && !Bluespawn::EnablePreScanDetections) { - return; - } - - BeginCriticalSection __{ *detection }; - - bool response = client.RecordDetection(detection, type); - } - - void ServerSink::RecordAssociation(IN CONST std::shared_ptr& first, - IN CONST std::shared_ptr& second, - IN CONST Association& strength) { - UpdateCertainty(first); - UpdateCertainty(second); - - if(detections.find(first->dwID) != detections.end()) { - AddAssociation(first->dwID, second->dwID, strength); - } - - if(detections.find(second->dwID) != detections.end()) { - AddAssociation(second->dwID, first->dwID, strength); - } - } - - void ServerSink::LogMessage(const LogLevel& level, const std::wstring& message) { - if(level.Enabled()) { - bool response = client.SendLogMessage(message, level.severity, *level.detail); - } - } - - bool ServerSink::operator==(const LogSink& sink) const { - return (bool) dynamic_cast(&sink) && - dynamic_cast(&sink)->wServerAddress == wServerAddress; - } -}; // namespace Log +#include "util/log/ServerSink.h" + +#include +#include +#include +#include + +#include "util/StringUtils.h" +#include "util/Utils.h" +#include "util/rpc/RpcClient.h" + +#include "user/bluespawn.h" + +namespace Log { + + ServerSink::ServerSink(const std::string address) : + ServerAddress{ address }, client{ grpc::CreateChannel(address, grpc::InsecureChannelCredentials()) } {} + + void ServerSink::UpdateCertainty(IN CONST std::shared_ptr& detection) { + BeginCriticalSection __{ *detection }; + + if(detections.find(detection->dwID) != detections.end()) { + bool response = client.UpdateCertainty(detection); + } + } + + void ServerSink::AddAssociation(IN DWORD detection_id, IN DWORD associated, IN double strength) { + if(detections.find(detection_id) != detections.end()) { + bool response = client.AddAssociation(detection_id, associated, strength); + } + } + + void ServerSink::RecordDetection(IN CONST std::shared_ptr& detection, IN RecordType type) { + if(type == RecordType::PreScan && !Bluespawn::EnablePreScanDetections) { + return; + } + + BeginCriticalSection __{ *detection }; + + bool response = client.RecordDetection(detection, type); + } + + void ServerSink::RecordAssociation(IN CONST std::shared_ptr& first, + IN CONST std::shared_ptr& second, + IN CONST Association& strength) { + UpdateCertainty(first); + UpdateCertainty(second); + + if(detections.find(first->dwID) != detections.end()) { + AddAssociation(first->dwID, second->dwID, strength); + } + + if(detections.find(second->dwID) != detections.end()) { + AddAssociation(second->dwID, first->dwID, strength); + } + } + + void ServerSink::LogMessage(const LogLevel& level, const std::wstring& message) { + if(level.Enabled()) { + bool response = client.SendLogMessage(message, level.severity, *level.detail); + } + } + + bool ServerSink::operator==(const LogSink& sink) const { + return (bool) dynamic_cast(&sink) && + dynamic_cast(&sink)->ServerAddress == ServerAddress; + } +}; // namespace Log diff --git a/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp b/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp index 49d6106f..407f245a 100644 --- a/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp +++ b/BLUESPAWN-win-client/src/util/rpc/RpcClient.cpp @@ -14,110 +14,103 @@ namespace RpcClient { message.set_type(static_cast(detection->type)); message.set_record_type(static_cast(type)); - protobuffer::ScanInfo info; - info.set_certainty(detection->info.GetCertainty()); - info.set_raw_certainty(detection->info.GetIntrinsicCertainty()); - message.set_allocated_info(&info); + protobuffer::ScanInfo* info = message.mutable_info(); + info->set_certainty(detection->info.GetCertainty()); + info->set_raw_certainty(detection->info.GetIntrinsicCertainty()); - protobuffer::DetectionData data; + protobuffer::DetectionData* data = message.mutable_data(); if(std::holds_alternative(detection->data)) { ProcessDetectionData& processData = std::get(detection->data); - protobuffer::ProcessDetectionData pbProcessData; - pbProcessData.set_type(static_cast(processData.type)); - pbProcessData.set_pid(*processData.PID); - pbProcessData.set_tid(*processData.TID); - pbProcessData.set_process_name(WidestringToString(*processData.ProcessName)); - pbProcessData.set_process_path(WidestringToString(*processData.ProcessPath)); - pbProcessData.set_process_command(WidestringToString(*processData.ProcessCommand)); + protobuffer::ProcessDetectionData* pbProcessData = data->mutable_process_data(); + pbProcessData->set_type(static_cast(processData.type)); + pbProcessData->set_pid(*processData.PID); + pbProcessData->set_tid(*processData.TID); + pbProcessData->set_process_name(processData.ProcessName ? WidestringToString(*processData.ProcessName) : + ""); + pbProcessData->set_process_path(processData.ProcessPath ? WidestringToString(*processData.ProcessPath) : + ""); + pbProcessData->set_process_command( + processData.ProcessCommand ? WidestringToString(*processData.ProcessCommand) : ""); std::wstringstream baseAddress{}; baseAddress << std::hex << *processData.BaseAddress; - pbProcessData.set_base_address(WidestringToString(baseAddress.str())); - pbProcessData.set_memory_size(*processData.MemorySize); - pbProcessData.set_image_name(WidestringToString(*processData.ImageName)); - data.set_allocated_process_data(&pbProcessData); + pbProcessData->set_base_address(WidestringToString(baseAddress.str())); + pbProcessData->set_memory_size(*processData.MemorySize); + pbProcessData->set_image_name(processData.ImageName ? WidestringToString(*processData.ImageName) : ""); } else if(std::holds_alternative(detection->data)) { FileDetectionData fileData = std::get(detection->data); - protobuffer::FileDetectionData pbFileData; - pbFileData.set_exists(fileData.FileFound); - pbFileData.set_file_path(WidestringToString(fileData.FilePath)); - pbFileData.set_file_name(WidestringToString(fileData.FileName)); - pbFileData.set_file_extension(WidestringToString(*fileData.FileExtension)); - pbFileData.set_file_type(WidestringToString(*fileData.FileType)); - pbFileData.set_executor(WidestringToString(*fileData.Executor)); - pbFileData.set_md5(WidestringToString(*fileData.MD5)); - pbFileData.set_sha1(WidestringToString(*fileData.SHA1)); - pbFileData.set_sha256(WidestringToString(*fileData.SHA256)); - pbFileData.set_last_opened(FileTimeToInteger(*fileData.LastOpened)); - pbFileData.set_last_opened(FileTimeToInteger(*fileData.FileCreated)); - protobuffer::YaraScanResult pbYaraScanResult; + protobuffer::FileDetectionData* pbFileData = data->mutable_file_data(); + pbFileData->set_exists(fileData.FileFound); + pbFileData->set_file_path(WidestringToString(fileData.FilePath)); + pbFileData->set_file_name(WidestringToString(fileData.FileName)); + pbFileData->set_file_extension(fileData.FileExtension ? WidestringToString(*fileData.FileExtension) : ""); + pbFileData->set_file_type(fileData.FileType ? WidestringToString(*fileData.FileType) : ""); + pbFileData->set_executor(fileData.Executor ? WidestringToString(*fileData.Executor) : ""); + pbFileData->set_md5(fileData.MD5 ? WidestringToString(*fileData.MD5) : ""); + pbFileData->set_sha1(fileData.SHA1 ? WidestringToString(*fileData.SHA1) : ""); + pbFileData->set_sha256(fileData.SHA256 ? WidestringToString(*fileData.SHA256) : ""); + pbFileData->set_last_opened(FileTimeToInteger(*fileData.LastOpened)); + pbFileData->set_last_opened(FileTimeToInteger(*fileData.FileCreated)); + protobuffer::YaraScanResult* pbYaraScanResult = pbFileData->mutable_yara(); if(fileData.yara.has_value()) { for(auto& str : fileData.yara.value().vKnownBadRules) { - pbYaraScanResult.add_known_bad_rules(WidestringToString(str)); + pbYaraScanResult->add_known_bad_rules(WidestringToString(str)); } for(auto& str : fileData.yara.value().vIndicatorRules) { - pbYaraScanResult.add_indicator_rules(WidestringToString(str)); + pbYaraScanResult->add_indicator_rules(WidestringToString(str)); } } - pbFileData.set_allocated_yara(&pbYaraScanResult); - pbFileData.set_file_signed(*fileData.FileSigned); - pbFileData.set_signer(WidestringToString(*fileData.Signer)); - data.set_allocated_file_data(&pbFileData); + pbFileData->set_file_signed(*fileData.FileSigned); + pbFileData->set_signer(fileData.Signer ? WidestringToString(*fileData.Signer) : ""); } else if(std::holds_alternative(detection->data)) { RegistryDetectionData registryData = std::get(detection->data); - protobuffer::RegistryDetectionData pbRegistryData; - pbRegistryData.set_key_path(WidestringToString(registryData.KeyPath)); - protobuffer::RegistryKey pbRegistryKey; - pbRegistryKey.set_key_path(WidestringToString(registryData.key.GetName())); - pbRegistryKey.set_exists(registryData.key.Exists()); - pbRegistryData.set_allocated_key(&pbRegistryKey); - protobuffer::RegistryValue pbRegistryValue; - pbRegistryValue.set_allocated_key(&pbRegistryKey); + protobuffer::RegistryDetectionData* pbRegistryData = data->mutable_registry_data(); + pbRegistryData->set_key_path(WidestringToString(registryData.KeyPath)); + protobuffer::RegistryKey* pbRegistryKey = pbRegistryData->mutable_key(); + pbRegistryKey->set_key_path(WidestringToString(registryData.key.GetName())); + pbRegistryKey->set_exists(registryData.key.Exists()); + protobuffer::RegistryValue* pbRegistryValue = pbRegistryData->mutable_value(); if(registryData.value.has_value()) { - pbRegistryValue.set_value_name(WidestringToString((*registryData.value).wValueName)); - pbRegistryValue.set_value_data(WidestringToString((*registryData.value).ToString())); + pbRegistryValue->set_value_name(WidestringToString((*registryData.value).wValueName)); + pbRegistryValue->set_value_data(WidestringToString((*registryData.value).ToString())); } - pbRegistryData.set_allocated_value(&pbRegistryValue); - pbRegistryData.set_type(static_cast(registryData.type)); - data.set_allocated_registry_data(&pbRegistryData); + pbRegistryData->set_type(static_cast(registryData.type)); } else if(std::holds_alternative(detection->data)) { ServiceDetectionData serviceData = std::get(detection->data); - protobuffer::ServiceDetectionData pbServiceData; - pbServiceData.set_service_name(WidestringToString(*serviceData.ServiceName)); - pbServiceData.set_display_name(WidestringToString(*serviceData.DisplayName)); - pbServiceData.set_description(WidestringToString(*serviceData.Description)); - pbServiceData.set_file_path(WidestringToString(*serviceData.FilePath)); - data.set_allocated_service_data(&pbServiceData); + protobuffer::ServiceDetectionData* pbServiceData = data->mutable_service_data(); + pbServiceData->set_service_name(serviceData.ServiceName ? WidestringToString(*serviceData.ServiceName) : + ""); + pbServiceData->set_display_name(serviceData.DisplayName ? WidestringToString(*serviceData.DisplayName) : + ""); + pbServiceData->set_description(serviceData.Description ? WidestringToString(*serviceData.Description) : ""); + pbServiceData->set_file_path(serviceData.FilePath ? WidestringToString(*serviceData.FilePath) : ""); } else { OtherDetectionData otherData = std::get(detection->data); - protobuffer::OtherDetectionData pbOtherData; - pbOtherData.set_type(WidestringToString(otherData.DetectionType)); - auto mutable_properties = pbOtherData.mutable_properties(); + protobuffer::OtherDetectionData* pbOtherData = data->mutable_other_data(); + pbOtherData->set_type(WidestringToString(otherData.DetectionType)); + auto mutable_properties = pbOtherData->mutable_properties(); for(const auto& [key, value] : otherData.DetectionProperties) { mutable_properties->insert({ WidestringToString(key), WidestringToString(value) }); } } - message.set_allocated_data(&data); - protobuffer::DetectionContext context; + protobuffer::DetectionContext* context = message.mutable_context(); if(detection->context.hunts.size()) { for(const auto& hunt : detection->context.hunts) { - context.add_hunts(WidestringToString(hunt)); + context->add_hunts(WidestringToString(hunt)); } } if(detection->context.FirstEvidenceTime) { - context.set_first_evidence_time(FileTimeToInteger(*detection->context.FirstEvidenceTime)); + context->set_first_evidence_time(FileTimeToInteger(*detection->context.FirstEvidenceTime)); } - context.set_first_evidence_time(FileTimeToInteger(detection->context.DetectionCreatedTime)); + context->set_first_evidence_time(FileTimeToInteger(detection->context.DetectionCreatedTime)); if(detection->context.note) { - context.set_note(WidestringToString(*detection->context.note)); + context->set_note(WidestringToString(*detection->context.note)); } - message.set_allocated_context(&context); - return message; } diff --git a/config/buildsettings.props b/config/buildsettings.props index 262ebcc7..0180035a 100644 --- a/config/buildsettings.props +++ b/config/buildsettings.props @@ -20,7 +20,8 @@ stdcpplatest Disabled MaxSpeed - DELAYLOAD_IMPORTS_DEFINED;_UNICODE;UNICODE;_ITERATOR_DEBUG_LEVEL=0;_WIN32_WINNT=0x601;%(PreprocessorDefinitions) + DELAYLOAD_IMPORTS_DEFINED;_UNICODE;UNICODE;_ITERATOR_DEBUG_LEVEL=0;_WIN32_WINNT=0x601;%(PreprocessorDefinitions) + DELAYLOAD_IMPORTS_DEFINED;_UNICODE;UNICODE;_ITERATOR_DEBUG_LEVEL=2;_WIN32_WINNT=0x601;%(PreprocessorDefinitions) MultiThreaded MultiThreadedDebug false From 27050fb501f33654a10be50f7eded635b68f36a1 Mon Sep 17 00:00:00 2001 From: Jake Smith Date: Sat, 26 Sep 2020 21:19:45 -0400 Subject: [PATCH 7/8] Update RPC Server based on new definitions & methods --- BLUESPAWN-server/rpc_server/Dockerfile | 14 +++++--------- BLUESPAWN-server/rpc_server/rpc_server.go | 20 ++++++++++++++++---- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/BLUESPAWN-server/rpc_server/Dockerfile b/BLUESPAWN-server/rpc_server/Dockerfile index 0f864a22..81e1fb15 100644 --- a/BLUESPAWN-server/rpc_server/Dockerfile +++ b/BLUESPAWN-server/rpc_server/Dockerfile @@ -1,26 +1,22 @@ FROM golang:1.15 ENV PROTOC_VER 3.13.0 -ENV PROTOC_GEN_GO_VER 1.4.0 # Install dependencies RUN apt-get update --fix-missing && apt-get install -y \ zip unzip build-essential curl wget -# Install protoc for gRPC +# Install protoc WORKDIR /tmp RUN wget -O protoc-${PROTOC_VER}-linux-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-linux-x86_64.zip \ && unzip protoc-${PROTOC_VER}-linux-x86_64.zip \ && cp -vv ./bin/protoc /usr/local/bin -# Install go get -RUN wget -O protoc-gen-go.tar.gz https://github.com/golang/protobuf/archive/v${PROTOC_GEN_GO_VER}.tar.gz \ - && tar xvf protoc-gen-go.tar.gz \ - && cd protobuf-${PROTOC_GEN_GO_VER} \ - && make install +# Install Go Protocol Buffers plugin +RUN go get -u google.golang.org/protobuf/cmd/protoc-gen-go && go install google.golang.org/protobuf/cmd/protoc-gen-go # Install gRPC -RUN go get google.golang.org/grpc +RUN go get -u google.golang.org/grpc && go install google.golang.org/grpc/cmd/protoc-gen-go-grpc # Change to /usr/src WORKDIR /usr/src @@ -29,7 +25,7 @@ WORKDIR /usr/src COPY ./ ./ # Compile Protobuf -RUN protoc --go_out="plugins=grpc:$GOPATH/src" ./BLUESPAWN-common/bluespawnpb/bluespawn.proto +RUN protoc --go_out="$GOPATH/src" --go-grpc_out="$GOPATH/src" ./BLUESPAWN-common/bluespawnpb/bluespawn.proto # Build and start rpc_server RUN cd ./BLUESPAWN-server/rpc_server && \ diff --git a/BLUESPAWN-server/rpc_server/rpc_server.go b/BLUESPAWN-server/rpc_server/rpc_server.go index 9ad9871d..4d859ada 100644 --- a/BLUESPAWN-server/rpc_server/rpc_server.go +++ b/BLUESPAWN-server/rpc_server/rpc_server.go @@ -13,8 +13,15 @@ const ( port = ":50052" ) -type server struct { - pb.UnimplementedBluespawnRPCServer +type server struct {} + +func (s *server) Svc() *pb.BluespawnRPCService { + return &pb.BluespawnRPCService{ + SendLogMessage: s.SendLogMessage, + RecordDetection: s.RecordDetection, + AddAssociation: s.AddAssociation, + UpdateCertainty: s.UpdateCertainty, + } } func (s *server) SendLogMessage(ctx context.Context, in *pb.LogMessage) (*pb.ResponseMessage, error) { @@ -28,7 +35,7 @@ func (s *server) RecordDetection(ctx context.Context, in *pb.Detection) (*pb.Res } func (s *server) AddAssociation(ctx context.Context, in *pb.DetectionAssociation) (*pb.ResponseMessage, error) { - log.Printf("Received Association for Detection ID %d and ID %d with strength %f", in.GetDetection_Id(), in.GetAssociated_Id(), in.GetStrength()) + log.Printf("Received Association for Detection ID %d and ID %d with strength %f", in.GetDetectionId(), in.GetAssociatedId(), in.GetStrength()) return &pb.ResponseMessage{Received: true, Success: true}, nil } @@ -37,13 +44,18 @@ func (s *server) UpdateCertainty(ctx context.Context, in *pb.DetectionCertaintyU return &pb.ResponseMessage{Received: true, Success: true}, nil } +func createServer() *server { + s := &server{} + return s +} + func main() { lis, err := net.Listen("tcp", port) if err != nil { log.Fatalf("failed to listen: %v", err) } s := grpc.NewServer() - pb.RegisterBluespawnRPCServer(s, &server{}) + pb.RegisterBluespawnRPCService(s, createServer().Svc()) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } From 1870ffd08a963596a50ee9517be6676805044f69 Mon Sep 17 00:00:00 2001 From: Jake Smith Date: Sun, 8 Nov 2020 13:24:02 -0500 Subject: [PATCH 8/8] Update pe-sieve submodule to v0.2.9 --- BLUESPAWN-win-client/external/pe-sieve | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BLUESPAWN-win-client/external/pe-sieve b/BLUESPAWN-win-client/external/pe-sieve index 78db1a1e..adf33da7 160000 --- a/BLUESPAWN-win-client/external/pe-sieve +++ b/BLUESPAWN-win-client/external/pe-sieve @@ -1 +1 @@ -Subproject commit 78db1a1e73614828f8d9e71a4502aae4414930b7 +Subproject commit adf33da795be5f0a345cb693578e2b3d568e1a53