Skip to content

Library that eases the use of indirect syscalls. Quite interesting AV/EDR bypass as PoC.

License

Notifications You must be signed in to change notification settings

0xsch1zo/NullGate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NullGate

This project implements a comfortable and modern way to use the NTAPI functions using indirect syscalls, coupled with the FreshyCalls method with a little twist for dynamic syscall number retrieval. It also uses a technique that I haven't seen being metioned to bypass windows defender's memory scanning. It also implements a classic PoC process injector.

Demo

Demonstration of the sample

Usage

The usage is pretty straight forward, here is a snippet demonstrating the main functionality:

nullgate::syscalls syscalls;
auto status = syscalls.Call(nullgate::obfuscation::fnv1Const("NtAllocateVirtualMemory"),
                         processHandle, &buf, 0, &regionSize,
                         MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

The fnv1Const method brings the joys of modern C++ to the maldev world. It is a consteval function, so it is guaranteed that it will get evaluated at compile time, replacing the readable function name with a fnv1 hash.

There is also a runtime equivalent called fnv1Runtime but of course it doesn't add the benefit of having our function names obfuscated. It is used by the implementation to check which function inside of ntdll to get the syscall number of.

There are routines that can xor encrypt/decrypt(multibyte key) and base64 encode/decode your payload or some message:

if (!NT_SUCCESS(status))
    throw std::runtime_error(
        nullgate::obfuscation::xorDecode("BQkEI1c0dkJ4LU4naSJhGCcIFSNWej5YeD5DNmkzM"
                               "x8lAwI8H3o3VzEmTjdpNCgELlxR") +
        std::to_string(status));

The key for now is FfqO3ZQ6XJ+SICAp. A hasher is also provided, after building the project, the binary will be accessible at <build_dir>/_deps/nullgate-build/src/hasher. Just pipe something into it and it will spit out a base64 encoded and xored string.

To ease the encryption of shellcode a special functon is provided:

auto decryptedShellcode =
      nullgate::obfuscation::hex2bin(nullgate::obfuscation::xorDecode(encryptedShellcode))

The hex2bin function just turns a hex string into a vector of bytes, thanks to this you can just pipe the shellcode from msfvenom with the -f hex flag straight into the hasher and not have worry about the special characters.

Adding nullgate to your project

CMake FetchContent is supported. Here is an example of a simple CMakeLists.txt:

cmake_minimum_required(VERSION 3.25)

include(FetchContent)

FetchContent_Declare(nullgate
    GIT_REPOSITORY https://github.com/0xsch1zo/NullGate
    GIT_TAG 1.0.0
)

FetchContent_MakeAvailable(nullgate)

project(test)

add_executable(test
    main.cpp
)

target_link_libraries(test
    PRIVATE nullgate
)

The linking is done statically so you don't have to worry about symbols being visible.

Build

To build the sample use -DNULLGATE_BUILD_SAMPLE=ON. It will be accessible at <build_dir>/_deps/nullgate-build/sample.exe. It takes a PID that you want to inject shellcode into as an argument.

Warning

If you are using linux you need to have the mingw crosscompiler installed. On Arch for example you can do pacman -S mingw-w64-gcc. Then use the -DNULLGATE_CROSSCOMPILE=ON option to set mingw as the default compiler for the relevant parts of the program.

git clone https://github.com/0xsch1zo/NullGate
cd NullGate
cmake . -B build -DNULLGATE_BUILD_SAMPLE=ON
cmake --build build/

Windows defender memory scan bypass

The core of the issue is that when we call NtCreateRemoteThreadEx or NtCreateProcess, a memory scan gets triggered and our signatured as hell msfvenom payload gets detected.

How to bypass that?

A known solution is to first when calling NtAllocateVirtualMemory set the page permissions as PAGE_NOACCESS, then create the thread in a suspended state. When windows defender will scan the memory of our process it will fail to do that. We can then resume the execution of our thread with NtResumeThread. This works, but what if a more competent security solution is being used? What would it do? It would of course just use VirtualProtect to change the permissions of our page and detect msfvenom. To bypass that I changed the strategy a bit. Instead of setting the page as PAGE_NOACCESS, during our first write to the memory of the process we can just put some junk data into the process(Yes it is required, or I'm just too stupid to find a way to get it working wihout this). Then we create a thread in suspended state. After that we write to the process our desired shellcode and finally we resume the thread using NtResumeThread. With this technique we don't have to worry about our memory being accessed after the call to NtCreateThreadEx because there is nothing in there. Only after the fact the decrypted shellcode is written and the execution is resumed.

Credits:

  • To @ElephantSe4l and @MarioBartolome for a great method of dynamic syscall number retrieval and generally the whole project from which I've taken great inspiration of of.
  • To @cr-0w for the amazing blog post and video discussing direct and indirect syscalls.
  • To bordergate for the article that describes the initial method of bypass.

About

Library that eases the use of indirect syscalls. Quite interesting AV/EDR bypass as PoC.

Resources

License

Stars

Watchers

Forks

Packages

No packages published