├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .gitignore ├── Makefile ├── README.md ├── beacon.h ├── beacon.nim ├── bin └── .gitignore ├── bof.c └── bof.nim /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/debian/.devcontainer/base.Dockerfile 2 | 3 | # [Choice] Debian version (use bullseye or stretch on local arm64/Apple Silicon): bullseye, buster, stretch 4 | ARG VARIANT="bullseye" 5 | FROM mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT} 6 | 7 | # ** [Optional] Uncomment this section to install additional packages. ** 8 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 9 | && apt-get -y install --no-install-recommends build-essential mingw-w64 binutils 10 | 11 | USER vscode 12 | ENV CHOOSENIM_NO_ANALYTICS 1 13 | ENV PATH $HOME/.nimble/bin:$PATH 14 | RUN echo 'export PATH="$HOME/.nimble/bin:$PATH"' >> ~/.profile -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/debian 3 | { 4 | "name": "bof-nim", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | // Update 'VARIANT' to pick an Debian version: bullseye, buster, stretch 8 | // Use bullseye or stretch on local arm64/Apple Silicon. 9 | "args": { "VARIANT": "bullseye" } 10 | }, 11 | 12 | // Set *default* container specific settings.json values on container create. 13 | "settings": {}, 14 | 15 | // Add the IDs of extensions you want installed when the container is created. 16 | "extensions": [ 17 | "kosz78.nim", 18 | "ms-vscode.cpptools" 19 | ], 20 | 21 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 22 | // "forwardPorts": [], 23 | 24 | // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker. 25 | // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ], 26 | 27 | // Uncomment when using a ptrace-based debugger like C++, Go, and Rust 28 | // "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], 29 | 30 | "postCreateCommand": "curl https://nim-lang.org/choosenim/init.sh -sSf | sh -s -- -y && nimble -y install winim c2nim", 31 | 32 | // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 33 | "remoteUser": "vscode" 34 | /* 35 | "features": { 36 | "git": "latest" 37 | } 38 | */ 39 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean 2 | 3 | c: 4 | x86_64-w64-mingw32-gcc -v -c bof.c -o bin/bof_c.o 5 | objdump -t bin/bof_c.o 6 | 7 | nim: 8 | nim c --noLinking=on -d:danger -d:strip --opt:size -d=mingw --cpu=amd64 --out=bin/bof_nim.o bof.nim 9 | cp /home/vscode/.cache/nim/bof_r/@mbof.nim.c.o bin/bof_nim.o 10 | objdump -t bin/bof_nim.o 11 | 12 | clean: 13 | rm -rf bin/* 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BOF-Nim 2 | 3 | oh yeah baby 4 | 5 | Help welcome!~ 6 | 7 | Build `bof.nim` by running `make nim` in the root of this repo. This will produce `bof_nim.o` in the `bin/` directory. 8 | 9 | ## Problems 10 | 11 | 1. ~~Getting the `go` function to be present in the Symbol table.~~ 12 | 2. ~~Making import `Beacon*` functions exported in the symbol table with the `__imp_` prefix~~ 13 | 3. Making it not crash when running it through [Invoke-BOF](https://github.com/airbus-cert/Invoke-Bof) 14 | 15 | image 16 | 17 | ## Nim BOF vs "Normal" C BOF 18 | 19 | Nim BOF: 20 | ``` 21 | bin/bof_nim.o: file format pe-x86-64 22 | 23 | SYMBOL TABLE: 24 | [ 0](sec -2)(fl 0x00)(ty 0)(scl 103) (nx 1) 0x0000000000000000 @mbof.nim.c 25 | File 26 | [ 2](sec 1)(fl 0x00)(ty 20)(scl 2) (nx 1) 0x0000000000000000 PreMainInner 27 | AUX tagndx 0 ttlsiz 0x0 lnnos 0 next 0 28 | [ 4](sec 1)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000005 NimMainInner 29 | [ 5](sec 1)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000006 PreMain 30 | [ 6](sec 1)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x000000000000003c NimMain 31 | [ 7](sec 1)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000068 go 32 | [ 8](sec 7)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000000 main 33 | [ 9](sec 10)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .rdata$.refptr.nim_program_result 34 | AUX scnlen 0x8 nreloc 1 nlnno 0 checksum 0x0 assoc 0 comdat 2 35 | [ 11](sec 1)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x000000000000008d NimMainModule 36 | [ 12](sec 1)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .text 37 | AUX scnlen 0x8e nreloc 8 nlnno 0 38 | [ 14](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .data 39 | AUX scnlen 0x0 nreloc 0 nlnno 0 40 | [ 16](sec 3)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .bss 41 | AUX scnlen 0x14 nreloc 0 nlnno 0 42 | [ 18](sec 4)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .xdata 43 | AUX scnlen 0x24 nreloc 0 nlnno 0 44 | [ 20](sec 5)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .pdata 45 | AUX scnlen 0x48 nreloc 18 nlnno 0 46 | [ 22](sec 6)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .rdata 47 | AUX scnlen 0x10 nreloc 0 nlnno 0 48 | [ 24](sec 7)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .text.startup 49 | AUX scnlen 0x3f nreloc 6 nlnno 0 50 | [ 26](sec 8)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .xdata.startup 51 | AUX scnlen 0xc nreloc 0 nlnno 0 52 | [ 28](sec 9)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .pdata.startup 53 | AUX scnlen 0xc nreloc 3 nlnno 0 54 | [ 30](sec 3)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000008 cmdLine 55 | [ 31](sec 3)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000010 cmdCount 56 | [ 32](sec 3)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000000 gEnv 57 | [ 33](sec 10)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000000 .refptr.nim_program_result 58 | [ 34](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000000 __main 59 | [ 35](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000000 stdlib_ioInit000 60 | [ 36](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000000 stdlib_ioDatInit000 61 | [ 37](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000000 systemDatInit000 62 | [ 38](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000000 nimGC_setStackBottom 63 | [ 39](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000000 systemInit000 64 | [ 40](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x0000000000000000 __imp_BeaconPrintf 65 | [ 41](sec 0)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000000 nim_program_result 66 | ``` 67 | 68 | C BOF: 69 | ``` 70 | bin/bof_c.o: file format pe-x86-64 71 | 72 | SYMBOL TABLE: 73 | [ 0](sec -2)(fl 0x00)(ty 0)(scl 103) (nx 1) 0x0000000000000000 hello.c 74 | File 75 | [ 2](sec 1)(fl 0x00)(ty 20)(scl 2) (nx 1) 0x0000000000000000 go 76 | AUX tagndx 0 ttlsiz 0x0 lnnos 0 next 0 77 | [ 4](sec 1)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .text 78 | AUX scnlen 0x2f nreloc 2 nlnno 0 79 | [ 6](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .data 80 | AUX scnlen 0x0 nreloc 0 nlnno 0 81 | [ 8](sec 3)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .bss 82 | AUX scnlen 0x0 nreloc 0 nlnno 0 83 | [ 10](sec 4)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .rdata 84 | AUX scnlen 0x10 nreloc 0 nlnno 0 85 | [ 12](sec 5)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .xdata 86 | AUX scnlen 0xc nreloc 0 nlnno 0 87 | [ 14](sec 6)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .pdata 88 | AUX scnlen 0xc nreloc 3 nlnno 0 89 | [ 16](sec 7)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x0000000000000000 .rdata$zzz 90 | AUX scnlen 0x1d nreloc 0 nlnno 0 91 | [ 18](sec 0)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000000 __imp_BeaconPrintf 92 | ``` -------------------------------------------------------------------------------- /beacon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Beacon Object Files (BOF) 3 | * ------------------------- 4 | * A Beacon Object File is a light-weight post exploitation tool that runs 5 | * with Beacon's inline-execute command. 6 | * 7 | * Additional BOF resources are available here: 8 | * - https://github.com/Cobalt-Strike/bof_template 9 | * 10 | * Cobalt Strike 4.x 11 | * ChangeLog: 12 | * 1/25/2022: updated for 4.5 13 | */ 14 | 15 | /* data API */ 16 | typedef struct { 17 | char * original; /* the original buffer [so we can free it] */ 18 | char * buffer; /* current pointer into our buffer */ 19 | int length; /* remaining length of data */ 20 | int size; /* total size of this buffer */ 21 | } datap; 22 | 23 | DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size); 24 | DECLSPEC_IMPORT char * BeaconDataPtr(datap * parser, int size); 25 | DECLSPEC_IMPORT int BeaconDataInt(datap * parser); 26 | DECLSPEC_IMPORT short BeaconDataShort(datap * parser); 27 | DECLSPEC_IMPORT int BeaconDataLength(datap * parser); 28 | DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size); 29 | 30 | /* format API */ 31 | typedef struct { 32 | char * original; /* the original buffer [so we can free it] */ 33 | char * buffer; /* current pointer into our buffer */ 34 | int length; /* remaining length of data */ 35 | int size; /* total size of this buffer */ 36 | } formatp; 37 | 38 | DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz); 39 | DECLSPEC_IMPORT void BeaconFormatReset(formatp * format); 40 | DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, char * text, int len); 41 | DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, char * fmt, ...); 42 | DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size); 43 | DECLSPEC_IMPORT void BeaconFormatFree(formatp * format); 44 | DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value); 45 | 46 | /* Output Functions */ 47 | #define CALLBACK_OUTPUT 0x0 48 | #define CALLBACK_OUTPUT_OEM 0x1e 49 | #define CALLBACK_OUTPUT_UTF8 0x20 50 | #define CALLBACK_ERROR 0x0d 51 | 52 | DECLSPEC_IMPORT void BeaconOutput(int type, char * data, int len); 53 | DECLSPEC_IMPORT void BeaconPrintf(int type, char * fmt, ...); 54 | 55 | 56 | /* Token Functions */ 57 | DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token); 58 | DECLSPEC_IMPORT void BeaconRevertToken(); 59 | DECLSPEC_IMPORT BOOL BeaconIsAdmin(); 60 | 61 | /* Spawn+Inject Functions */ 62 | DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length); 63 | DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len); 64 | DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len); 65 | DECLSPEC_IMPORT BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * si, PROCESS_INFORMATION * pInfo); 66 | DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo); 67 | 68 | /* Utility Functions */ 69 | DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max); 70 | -------------------------------------------------------------------------------- /beacon.nim: -------------------------------------------------------------------------------- 1 | #[ 2 | Beacon Object Files (BOF) 3 | ------------------------- 4 | A Beacon Object File is a light-weight post exploitation tool that runs 5 | with Beacon's inline-execute command. 6 | 7 | Additional BOF resources are available here: 8 | - https://github.com/Cobalt-Strike/bof_template 9 | 10 | Cobalt Strike 4.x 11 | ChangeLog: 12 | 1/25/2022: updated for 4.5 13 | ]# 14 | 15 | import winim/lean 16 | 17 | type 18 | wchar_t {.importc.} = object 19 | 20 | datap* {.bycopy.} = object 21 | original*: cstring ## the original buffer [so we can free it] 22 | buffer*: cstring ## current pointer into our buffer 23 | length*: cint ## remaining length of data 24 | size*: cint ## total size of this buffer 25 | 26 | 27 | formatp* {.bycopy.} = object 28 | original*: cstring ## the original buffer [so we can free it] 29 | buffer*: cstring ## current pointer into our buffer 30 | length*: cint ## remaining length of data 31 | size*: cint ## total size of this buffer 32 | 33 | const 34 | CALLBACK_OUTPUT* = 0x0 35 | CALLBACK_OUTPUT_OEM* = 0x1e 36 | CALLBACK_OUTPUT_UTF8* = 0x20 37 | CALLBACK_ERROR* = 0x0d 38 | 39 | ## data API 40 | proc BeaconDataParse*(parser: ptr datap; buffer: cstring; size: cint) {.importc, exportc: "__imp_$1".} 41 | proc BeaconDataPtr*(parser: ptr datap; size: cint): cstring {.importc, exportc: "__imp_$1".} 42 | proc BeaconDataInt*(parser: ptr datap): cint {.importc, exportc: "__imp_$1".} 43 | proc BeaconDataShort*(parser: ptr datap): cshort {.importc, exportc: "__imp_$1".} 44 | proc BeaconDataLength*(parser: ptr datap): cint {.importc, exportc: "__imp_$1".} 45 | proc BeaconDataExtract*(parser: ptr datap; size: ptr cint): cstring {.importc, exportc: "__imp_$1".} 46 | 47 | ## format API 48 | proc BeaconFormatAlloc*(format: ptr formatp; maxsz: cint) {.importc, exportc: "__imp_$1".} 49 | proc BeaconFormatReset*(format: ptr formatp) {.importc, exportc: "__imp_$1".} 50 | proc BeaconFormatAppend*(format: ptr formatp; text: cstring; len: cint) {.importc, exportc: "__imp_$1".} 51 | proc BeaconFormatPrintf*(format: ptr formatp; fmt: cstring) {.varargs, importc, exportc: "__imp_$1".} 52 | proc BeaconFormatToString*(format: ptr formatp; size: ptr cint): cstring {.importc, exportc: "__imp_$1".} 53 | proc BeaconFormatFree*(format: ptr formatp) {.importc, exportc: "__imp_$1".} 54 | proc BeaconFormatInt*(format: ptr formatp; value: cint) {.importc, exportc: "__imp_$1".} 55 | 56 | ## Output Functions 57 | proc BeaconOutput*(`type`: cint; data: cstring; len: cint) {.importc, exportc: "__imp_$1".} 58 | proc BeaconPrintf*(`type`: cint; fmt: cstring) {.varargs, importc, exportc: "__imp_$1".} 59 | 60 | ## Token Functions 61 | proc BeaconUseToken*(token: HANDLE): BOOL {.importc, exportc: "__imp_$1".} 62 | proc BeaconRevertToken*() {.importc, exportc: "__imp_$1".} 63 | proc BeaconIsAdmin*(): BOOL {.importc, exportc: "__imp_$1".} 64 | 65 | ## Spawn+Inject Functions 66 | proc BeaconGetSpawnTo*(x86: BOOL; buffer: cstring; length: cint) {.importc, exportc: "__imp_$1".} 67 | proc BeaconInjectProcess*(hProc: HANDLE; pid: cint; payload: cstring; p_len: cint; p_offset: cint; arg: cstring; a_len: cint) {.importc, exportc: "__imp_$1".} 68 | proc BeaconInjectTemporaryProcess*(pInfo: ptr PROCESS_INFORMATION; payload: cstring; p_len: cint; p_offset: cint; arg: cstring; a_len: cint) {.importc, exportc: "__imp_$1".} 69 | proc BeaconSpawnTemporaryProcess*(x86: BOOL; ignoreToken: BOOL; si: ptr STARTUPINFO; pInfo: ptr PROCESS_INFORMATION): BOOL {.importc, exportc: "__imp_$1".} 70 | proc BeaconCleanupProcess*(pInfo: ptr PROCESS_INFORMATION) {.importc, exportc: "__imp_$1".} 71 | 72 | ## Utility Functions 73 | proc toWideChar*(src: cstring; dst: ptr wchar_t; max: cint): BOOL {.importc, exportc: "__imp_$1".} -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /bof.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "beacon.h" 3 | 4 | void go(char * args, int alen) { 5 | BeaconPrintf(CALLBACK_OUTPUT, "Hello World: %s", args); 6 | } -------------------------------------------------------------------------------- /bof.nim: -------------------------------------------------------------------------------- 1 | import beacon 2 | 3 | proc NimMain() {.cdecl, importc.} 4 | 5 | proc go*(args: cstring, alen: cint) {.cdecl, exportc} = 6 | NimMain() 7 | BeaconPrintf(CALLBACK_OUTPUT, "Hello World: %s", args) 8 | --------------------------------------------------------------------------------