Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
slaeryan authored and slaeryan committed Sep 20, 2020
0 parents commit 1b19b50
Show file tree
Hide file tree
Showing 17 changed files with 1,209 additions and 0 deletions.
Empty file added .gitignore
Empty file.
1 change: 1 addition & 0 deletions Goblin/Bin/NOTE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
See Releases section
339 changes: 339 additions & 0 deletions Goblin/LICENSE

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions Goblin/Python/ConvertToShellcode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Taken from sRDI project

import argparse
from ShellcodeRDI import *

__version__ = '1.2'

def main():
parser = argparse.ArgumentParser(description='RDI Shellcode Converter', conflict_handler='resolve')
parser.add_argument('-v', '--version', action='version', version='%(prog)s Version: ' + __version__)
parser.add_argument('input_dll', help='DLL to convert to shellcode')
parser.add_argument('-f', '--function-name', dest='function_name', help='The function to call after DllMain', default='SayHello')
parser.add_argument('-u', '--user-data', dest='user_data', help='Data to pass to the target function', default='dave')
parser.add_argument('-c', '--clear-header', dest='clear_header', action='store_true', help='Clear the PE header on load')
parser.add_argument('-i', '--obfuscate-imports', dest='obfuscate_imports', action='store_true', help='Randomize import dependency load order', default=False)
parser.add_argument('-d', '--import-delay', dest='import_delay', help='Number of seconds to pause between loading imports', type=int, default=0)
arguments = parser.parse_args()

input_dll = arguments.input_dll
output_bin = input_dll.replace('.dll', '.bin')

print('Creating Shellcode: {}'.format(output_bin))
dll = open(arguments.input_dll, 'rb').read()

flags = 0

if arguments.clear_header:
flags |= 0x1

if arguments.obfuscate_imports:
flags = flags | 0x4 | arguments.import_delay << 16

converted_dll = ConvertToShellcode(dll, HashFunctionName(arguments.function_name), arguments.user_data.encode(), flags)

with open(output_bin, 'wb') as f:
f.write(converted_dll)

if __name__ == '__main__':
main()
216 changes: 216 additions & 0 deletions Goblin/Python/ShellcodeRDI.py

Large diffs are not rendered by default.

Binary file not shown.
84 changes: 84 additions & 0 deletions Goblin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Goblin
[![](https://img.shields.io/badge/Category-Defense%20Evasion-E5A505?style=flat-square)]() [![](https://img.shields.io/badge/Language-C%20%2f%20C++%20%2f%20Python3-E5A505?style=flat-square)]()

## Summary
`Goblin` is a module to enumerate all the threads of the EventLog Service Module(`wevtsvc.dll`) and kill them in an effort to disable EventLog service from registering any new events. Disabling Windows Event Logging and Sysmon logging paves the way for operators to perform Post-Exploitation activities in a safe and stealthy manner.

![wevtsvc.dll Threads](Screenshots/evtlog-threads.png "wevtsvc.dll Threads")

Additionally, it also allows us to "revive" the EventLog service again after we are done with Post-Ex activities.

## Usage
### Using the pre-built binary
Grab it from the `Releases` section.
### Compiling yourself
Make sure you have a working VC++ 2019 dev environment set up beforehand and `Git` installed. Execute the following from a x64 Developer Command Prompt.
```
1. git clone https://github.com/slaeryan/AQUARMOURY
2. cd AQUARMOURY/Goblin
3. compile64.bat
```

`goblin_x64.dll` is the name of the module. It is converted to a PIC blob(shellcode) with the help of [sRDI](https://github.com/monoxgas/sRDI) courtesy of [@monoxgas](https://twitter.com/monoxgas?lang=en) and delivered straight to memory via your favourite C2 framework for inline execution/local execution in the implant process.

When `Goblin` module is executed on a host, it primarily has one of these two objectives to accomplish:
1. Kill `wevtsvc.dll` threads if they are running
2. If `wevtsvc.dll` threads are not running, "revive" the EventLog service

Ergo, run the module once to disable event logging and once again if you wish to re-enable event logging without requiring a reboot.

In case your C2 framework does not support inline execution of shellcode for stability issues, you can also use the `Fork&Run` feature but the former is preferred over the latter due to OPSEC concerns.

Keep in mind that this capability is meant to be run from an **Elevated** context and will not work if the process token does not have `SeDebugPrivilege`.

## OPSEC concerns
This tool is inspired from the great [Invoke-Phant0m](https://github.com/hlldz/Invoke-Phant0m) by [Halil Dalabasmaz](https://twitter.com/hlldz).

So why not use PowerShell?

Because, using PowerShell might not be the most OPSEC-safe way of doing this in 2020. There are numerous telling events generated by just running `Invoke-Phant0m` on a host from Enhanced PowerShell logging to Process Access Event(Sysmon Event ID 10).

Refer to [1](https://twitter.com/inzlain/status/867172350457925632/photo/1) and [2](https://malwarenailed.blogspot.com/2017/10/update-to-hunting-mimikatz-using-sysmon.html) for additional information on how to detect `Invoke-Phant0m`.

Enter Goblin.

![Goblin Overview](Screenshots/overview.png "Goblin Overview")

The first `notepad.exe` was started before running `Goblin` module on host and hence reported. The second one was launched after killing the EventLog service module threads and as expected the process creation event(Sysmon Event ID 1) never showed up in Sysmon logs. Note the time difference underlined in red, operators were successfuly able to conduct Post-Ex activities during this time without any of it showing up in Event Logs or being forwarded to SOC/SIEM.

After conducting Post-Exploitation we decided to enable logging again so as not to raise questions as to why a host has stopped sending events altogether to SOC.

So we run the tool again to "revive" the EventLog service. What this does under the hood is kill the process hosting the service(`svchost.exe`) and start the service again.
As is evident from the screenshot, this generates **two** potentially telling events which might give us away.

Key Takeaways:
1) **Killing the threads** do not report any kind of additional event itself so it should be **OPSEC-safe** compared to **restarting the EventLog service** which is **noisy** and will report additional events providing Blue-teams an oppurtunity to detect us.
2) While the `wevtsvc` threads are killed, the host will **not be reporting/forwarding any events** to SOC/SIEM which may or may not be noticed and alerted, ergo exercise caution.
3) This will not stop PSPs relying on ETW Tracing to detect malicious activity. For that we need to hook `NtTraceEvent` syscall from Kernel-mode(Ring-0).

For a more elegant solution that allows filtering of events reported, see [@bats3c](https://twitter.com/_batsec_) work on [EvtMute](https://github.com/bats3c/EvtMute).

## Detection
![CAPA Scan](Screenshots/capa.png "CAPA Scan")
Here is a mandatory [CAPA](https://github.com/fireeye/capa) scan result on the `Goblin` DLL.

![Detection](Screenshots/detection.png "Detection")
And here is an additional event reported as a result of "reviving" the EventLog service(System Event ID 7031)

Note that by killing the EventLog service threads, **NO** additional events show up in the Event Logs whatsoever. Detection from event logs is possible iff operator has restarted the service.

## Credits
1. This tool was inspired by [@spotheplanet](https://twitter.com/spotheplanet) lab on [Disabling Windows Event Logs by Suspending EventLog Service Threads](https://www.ired.team/offensive-security/defense-evasion/disabling-windows-event-logs-by-suspending-eventlog-service-threads). Although, suspending/resuming threads do not work in practice because all the events are going to be written to the EventLog once the threads are resumed, it is an excellent post that explains in great detail the process of finding `wevtsvc.dll` threads. The code and algorithm is hacked from the post and I'd highly recommend giving it a read.
2. [https://artofpwn.com/phant0m-killing-windows-event-log.html](https://artofpwn.com/2017/06/05/phant0m-killing-windows-event-log.html)
3. [@dtm](https://twitter.com/0x00dtm) for first bringing this to my attention while discussing ways to evade Sysmon.
4. As usual, [@reenz0h](https://twitter.com/Sektor7Net) and [RTO: MalDev course](https://institute.sektor7.net/red-team-operator-malware-development-essentials) for the templates that I keep using to this date.

## Author
Upayan ([@slaeryan](https://twitter.com/slaeryan)) [[slaeryan.github.io](https://slaeryan.github.io)]

## License
All the code included in this project is licensed under the terms of the GNU GPLv2 license.

#

[![](https://img.shields.io/badge/slaeryan.github.io-E5A505?style=flat-square)](https://slaeryan.github.io) [![](https://img.shields.io/badge/twitter-@slaeryan-00aced?style=flat-square&logo=twitter&logoColor=white)](https://twitter.com/slaeryan) [![](https://img.shields.io/badge/linkedin-@UpayanSaha-0084b4?style=flat-square&logo=linkedin&logoColor=white)](https://www.linkedin.com/in/upayan-saha-404881192/)
Binary file added Goblin/Screenshots/capa.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Goblin/Screenshots/detection.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Goblin/Screenshots/evtlog-threads.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Goblin/Screenshots/overview.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
87 changes: 87 additions & 0 deletions Goblin/Src/EnablePrivilege.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#pragma once

#include <Windows.h>

// Constants
// ------------------------------------------------------------------------

#define NtCurrentProcess() ( (HANDLE)(LONG_PTR) -1 )
#define STATUS_SUCCESS 0

// Function prototypes
// ------------------------------------------------------------------------

typedef NTSTATUS(NTAPI* _NtOpenProcessToken)(
IN HANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
OUT PHANDLE TokenHandle
);

typedef NTSTATUS(NTAPI* _NtAdjustPrivilegesToken)(
IN HANDLE TokenHandle,
IN BOOLEAN DisableAllPrivileges,
IN PTOKEN_PRIVILEGES TokenPrivileges,
IN ULONG PreviousPrivilegesLength,
OUT PTOKEN_PRIVILEGES PreviousPrivileges OPTIONAL,
OUT PULONG RequiredLength OPTIONAL
);

typedef BOOL (WINAPI* _LookupPrivilegeValueA)(
LPCSTR lpSystemName,
LPCSTR lpName,
PLUID lpLuid
);

// To enable a privilege by its constant
// ------------------------------------------------------------------------

BOOL enable_privilege(LPCTSTR name) {
// Dynamically resolve the API functions from Ntdll.dll
HMODULE ntdll = GetModuleHandleA("ntdll.dll");

_NtOpenProcessToken NtOpenProcessToken = (_NtOpenProcessToken)GetProcAddress(ntdll, "NtOpenProcessToken");
if (NtOpenProcessToken == NULL) {
return FALSE;
}

_NtAdjustPrivilegesToken NtAdjustPrivilegesToken = (_NtAdjustPrivilegesToken)GetProcAddress(ntdll, "NtAdjustPrivilegesToken");
if (NtAdjustPrivilegesToken == NULL) {
return FALSE;
}

// Dynamically resolve the API function from Advapi32.dll
HMODULE advapi32 = LoadLibraryA("Advapi32.dll");

_LookupPrivilegeValueA LookupPrivilegeValueA = (_LookupPrivilegeValueA)GetProcAddress(advapi32, "LookupPrivilegeValueA");
if (LookupPrivilegeValueA == NULL)
return FALSE;

// Init local variables
HANDLE hToken;
LUID luid;
TOKEN_PRIVILEGES TokenPrivileges = { 0 };

// Enable the privilege
NTSTATUS status = NtOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken);
if (status != STATUS_SUCCESS) {
return FALSE;
}

if(!LookupPrivilegeValueA(NULL, name, &luid)) {
CloseHandle(hToken);
return FALSE;
}

TokenPrivileges.PrivilegeCount = 1;
TokenPrivileges.Privileges[0].Luid = luid;
TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

status = NtAdjustPrivilegesToken(hToken, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
if (status != STATUS_SUCCESS) {
CloseHandle(hToken);
return FALSE;
}

CloseHandle(hToken);
return TRUE;
}
Loading

0 comments on commit 1b19b50

Please sign in to comment.