├── .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 |
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 |
--------------------------------------------------------------------------------