Skip to content

Add Ekko / Sleep Obfuscation to Sliver#1805

Open
armysick wants to merge 1 commit into
BishopFox:masterfrom
armysick:master
Open

Add Ekko / Sleep Obfuscation to Sliver#1805
armysick wants to merge 1 commit into
BishopFox:masterfrom
armysick:master

Conversation

@armysick

Copy link
Copy Markdown

Card

This PR implements Ekko / in-memory sleep obfuscation for a beacon.

Details

Based on the work of https://github.com/scriptchildie/goEkko, adapted from https://github.com/Cracked5pider/Ekko, it pauses Go runtime and encrypts the beacon's memory region with the Ekko technique.

Command -B / --sleep-obfuscation added on generate beacon to support this feature.
Only applicable for Windows.

Beacon while performing operations / active:
Beacon_Active

Beacon while in its sleep duration:
Beacon_Sleeping

@moloch--

Copy link
Copy Markdown
Collaborator

Looks awesome we'll try to get this reviewed and merged shortly!

@rkervella rkervella left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of things to changes before I can dynamically test that one:

  • please don't print to stdout when you're not in debug mode
  • generate the XOR key dynamically at runtime instead of the hardcoded buffer of 0x55 values.

Comment thread implant/sliver/ekko/ekko.go Outdated
Comment thread implant/sliver/ekko/ekko.go Outdated
Comment thread implant/sliver/ekko/ekko.go Outdated
Comment thread implant/sliver/ekko/ekko.go Outdated
Comment thread implant/sliver/ekko/ekko.go Outdated
Comment thread implant/sliver/ekko/ekko.go Outdated
@armysick

Copy link
Copy Markdown
Author

Heya, @rkervella !

Thanks for the first review. Cleaned out the prints and randomised XOR key generation.
Tested it again and looking through process hacker, functionality remains the same after the changes.

@rkervella rkervella left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just found a few more things that need to be changed.

Comment thread client/command/generate/commands.go Outdated
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")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be in coreBeaconFlags instead since sleep obfuscation is only relevant for beacons (unless I'm mistaken, feel free to correct me).

Comment thread client/command/generate/generate.go Outdated
debug, _ := cmd.Flags().GetBool("debug")
evasion, _ := cmd.Flags().GetBool("evasion")
templateName, _ := cmd.Flags().GetString("template")
sleepObfuscation, _ := cmd.Flags().GetBool("sleep-obfuscation")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same idea, please move this to generate-beacon.go instead.

@rkervella

rkervella commented Dec 3, 2024

Copy link
Copy Markdown
Member

Alright, I finally started testing this, and I couldn't get the beacon process to self-encrypt when sleeping:
image

I tried with a debug beacon to confirm Ekko is properly called, and it is.
I also noticed the beacons using --sleep-obfuscation don't reach back to the server after a while, even if the process is still running.

@armysick

Copy link
Copy Markdown
Author

Hey, @rkervella,

Busy time of the year.

I believe the self-encryption is working as intended. If you reconfig the beacon to have short sleep time (a few seconds) you'll see the memory address changing from RW to RWX whenever it's time for the beacon callback.
To make this easier to spot, I've generated a beacon without shikata ga nai nor symbol obfuscation (-G -l) and if you re-read the same memory section you'll see it getting unencrypted/encrypted as expected:

while awake, RWX:
image

while sleeping, RW:
image

Well spotted on the process randomly hanging after some time. I've also found an old process hanging on a test server as you described. It appears that the memory region stays stuck in RWX and never self-encrypts/ goes back to sleep.
I'll investigate what part of the code is hanging and get back to you with a fix, together with moving around the flags as you suggested!

Comment thread implant/sliver/ekko/ekko.go Outdated

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

@Kharos102 Kharos102 Feb 28, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this create potential for an infinite loop?

E.g. we snapshot, one of the threads was short-lived and is no longer valid when we attempt to call OpenThread on it, error occurs and we loop to reattempt opening the same invalid thread ID.

Also, this code doesn't validate the size of the thread structure from the Thread32* calls, which as documented may be less than the total size of the structure (and may risk providing us with an invalid ThreadID)

Comment thread implant/sliver/ekko/ekko.go Outdated

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same infinite loop issue here right?

@ethanlacerenza

Copy link
Copy Markdown

Hey @armysick ,
I noticed that the transitions memory from RW to RWX instead of RX. I'm curious about the reasoning behind this choice.

Wouldn't changing to RWX make it easier for an EDR to detect suspicious behavior? A more common approach is to transition from RW to RX (Havoc example), which might be less noticeable. Is there a specific advantage to using RWX in this case?

Looking forward to your thoughts—thanks!

@Cyb3r-Monk

Copy link
Copy Markdown

Just curious if this can be fixed and imlemented with the help of Claude 👀

@moloch--

Copy link
Copy Markdown
Collaborator

Son we're a codex family :)

@MKMithun2806

Copy link
Copy Markdown
Contributor

Son we're a codex family :)

Traitor. The family group chat will hear about this.

@armysick
armysick requested a review from a team as a code owner July 9, 2026 03:27
@armysick
armysick requested a review from veslavabf July 9, 2026 03:27
@armysick

armysick commented Jul 9, 2026

Copy link
Copy Markdown
Author

Hey!

Thanks all for the suggestions.
Was at some local infosec gathering and got inspired by a talk to give this another try.
AI-era also helps testing ideas with a lot less effort!

Here's a companion post that dives deeper into the marvelous depths of this PR.

TL;DR:

  • Issue is (most likely) deadlocks due to how OS Suspend/Resume and Go Runtime interact.
  • Replaced Ekko with QueueUserAPC + hardcoded trampoline sleep obfuscation.
  • Implemented "watchdogs": ResumeThreads set off by CreateTimerQueueTimer (Ekko-style) that trigger if a deadlock is (likely) happening, fixing it.
  • Implemented miscellaneous suggestions from this PR: moved arguments to different places, dropped all RWX sections, etc.

Tests:

  • My first fully successful build ran for 8334 check-in/sleep cycles until I stopped it.
  • The current cleaned up code here in the PR (without the debug stuff) has been running as well for nearly 4 hours with sleep timers of ~2 to 3 seconds (should be getting to ~5000 successful cycles right about now).

While not the most elegant solution I've ever implemented, it appears to work fine with the limitation of only supporting Windows amd64 (arch of trampoline hardcode) as is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants