Projects STRLCPY ekko-rs Commits ee765259
🤬
  • Minor bug fixes but getting STATUS_ACCESS_VIOLATION at the end.

  • Loading...
  • memN0ps committed 2 years ago
    ee765259
    1 parent 76745699
  • ■ ■ ■ ■ ■ ■
    README.md
    1 1  # Ekko in Rust
    2 2   
    3  -Work in progress (incomplete)
     3 +A small sleep obfuscation technique that uses `CreateTimerQueueTimer` Win32 API in Rust. This is ported from C https://github.com/Cracked5pider/Ekko/ to Rust.
    4 4   
    5 5  ## Credits / References
    6 6   
    7 7  - [@C5pider](https://twitter.com/C5pider) https://github.com/Cracked5pider/Ekko/
    8 8  - [Austin Hudson (@SecIdiot)](https://twitter.com/ilove2pwn_) https://suspicious.actor/2022/05/05/mdsec-nighthawk-study.html
    9 9  - Originally discovered by [Peter Winter-Smith](peterwintrsmith) and used in MDSec’s Nighthawk
    10  -- [@trickster012](https://twitter.com/trickster012)
    11  - 
     10 +- Thanks for contributing [@trickster012](https://twitter.com/trickster012)
     11 +- https://learn.microsoft.com/
     12 +- Rust Lang Community Discord: https://discord.com/invite/rust-lang-community
  • ■ ■ ■ ■ ■
    src/ekko.rs
    1  -use windows_sys::Win32::{System::{Memory::{VirtualProtect, PAGE_READWRITE, PAGE_EXECUTE_READ}, Diagnostics::Debug::{CONTEXT, IMAGE_NT_HEADERS64, RtlCaptureContext}, Threading::{WaitForSingleObject, SetEvent, CreateEventW, CreateTimerQueue, CreateTimerQueueTimer, WT_EXECUTEINTIMERTHREAD, DeleteTimerQueue}, LibraryLoader::{LoadLibraryA, GetProcAddress, GetModuleHandleA}, SystemServices::IMAGE_DOS_HEADER}, Foundation::{HANDLE, UNICODE_STRING}};
    2  -use std::{mem::{zeroed, size_of}, ptr::{null_mut, copy}, ffi::c_void};
     1 +use std::{
     2 + ffi::c_void,
     3 + mem::{zeroed},
     4 + ptr::{null_mut},
     5 +};
     6 +use ntapi::winapi::um::errhandlingapi::GetLastError;
     7 +use windows_sys::Win32::{
     8 + Foundation::{HANDLE, UNICODE_STRING},
     9 + System::{
     10 + Diagnostics::Debug::{CONTEXT, IMAGE_NT_HEADERS64},
     11 + LibraryLoader::{GetModuleHandleA, GetProcAddress, LoadLibraryA},
     12 + Memory::{VirtualProtect, PAGE_EXECUTE_READ, PAGE_READWRITE},
     13 + SystemServices::IMAGE_DOS_HEADER,
     14 + Threading::{
     15 + CreateEventW, CreateTimerQueue, CreateTimerQueueTimer, DeleteTimerQueue, SetEvent,
     16 + WaitForSingleObject, WT_EXECUTEINTIMERTHREAD,
     17 + },
     18 + WindowsProgramming::INFINITE,
     19 + },
     20 +};
    3 21   
    4 22  pub fn ekko(sleep_time: u32) {
    5  - 
     23 + // Contains processor-specific register data. The system uses CONTEXT structures to perform various internal operations.
     24 + // https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-context
    6 25   let ctx_thread: CONTEXT = unsafe { zeroed::<CONTEXT>() };
    7  - let mut rop_prot_rw: CONTEXT = unsafe { zeroed::<CONTEXT>() };
    8  - let mut rop_mem_enc: CONTEXT = unsafe { zeroed::<CONTEXT>() };
    9  - let mut rop_delay: CONTEXT = unsafe { zeroed::<CONTEXT>() };
    10  - let mut rop_mem_dec: CONTEXT = unsafe { zeroed::<CONTEXT>() };
    11  - let mut rop_prot_rx: CONTEXT = unsafe { zeroed::<CONTEXT>() };
    12  - let mut rop_set_evt: CONTEXT = unsafe { zeroed::<CONTEXT>() };
    13 26   
    14  - //let h_timer_queue: HANDLE = 0;
    15 27   let mut h_new_timer: HANDLE = 0;
    16  - //let h_event: HANDLE = 0;
    17  - //let image_base: *mut c_void = null_mut();
    18  - //let image_size: u32 = 0;
    19  - let mut old_protect = 0;
     28 + let mut old_protect: u32 = 0;
    20 29   
    21 30   // This can be a randomly generated key
    22  - let mut key_buf: [i8; 16] = [0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55];
     31 + let mut key_buf: [i8; 16] = [
     32 + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
     33 + 0x55,
     34 + ];
    23 35   let mut key: UNICODE_STRING = unsafe { zeroed::<UNICODE_STRING>() };
    24 36   let mut img: UNICODE_STRING = unsafe { zeroed::<UNICODE_STRING>() };
    25 37   
    26  - //let nt_continue: *mut c_void = null_mut();
    27  - //let sys_func032: *mut c_void = null_mut();
    28  - 
     38 + // Creates or opens a named or unnamed event object.
     39 + // https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventw
    29 40   let h_event = unsafe { CreateEventW(null_mut(), 0, 0, null_mut()) };
     41 + 
     42 + // Creates a queue for timers. Timer-queue timers are lightweight objects that enable you to specify a callback function to be called at a specified time.
     43 + // https://learn.microsoft.com/en-us/windows/win32/api/threadpoollegacyapiset/nf-threadpoollegacyapiset-createtimerqueue
    30 44   let h_timer_queue = unsafe { CreateTimerQueue() };
    31 45   
    32  - let nt_continue = unsafe { GetProcAddress(GetModuleHandleA("ntdll".as_ptr()), "NtContinue".as_ptr()) };
    33  - let sys_func032 = unsafe { GetProcAddress(LoadLibraryA("cryptsp".as_ptr()), "SystemFunction032".as_ptr()) };
    34  - let rtlcont = unsafe { GetProcAddress(GetModuleHandleA("Ntdll".as_ptr()), "RtlCaptureContext".as_ptr()) };
     46 + let nt_continue = unsafe {
     47 + GetProcAddress(
     48 + GetModuleHandleA("ntdll.dll\0".as_ptr()),
     49 + "NtContinue\0".as_ptr(),
     50 + )
     51 + };
     52 + let sys_func032 = unsafe {
     53 + GetProcAddress(
     54 + LoadLibraryA("cryptsp.dll\0".as_ptr()),
     55 + "SystemFunction032\0".as_ptr(),
     56 + )
     57 + };
     58 + let rtlcont = unsafe {
     59 + GetProcAddress(
     60 + GetModuleHandleA("ntdll.dll\0".as_ptr()),
     61 + "RtlCaptureContext\0".as_ptr(),
     62 + )
     63 + };
    35 64   
    36 65   let image_base = unsafe { GetModuleHandleA(null_mut()) };
    37 66   let dos_header = image_base as *mut IMAGE_DOS_HEADER;
    38  - let nt_headesr = unsafe { (dos_header as u64 + (*dos_header).e_lfanew as u64) as *mut IMAGE_NT_HEADERS64 };
     67 + let nt_headesr =
     68 + unsafe { (dos_header as u64 + (*dos_header).e_lfanew as u64) as *mut IMAGE_NT_HEADERS64 };
    39 69   let image_size = unsafe { (*nt_headesr).OptionalHeader.SizeOfImage };
    40 70   
     71 + log::info!("[+] Image Base: {:#x}", image_base as u64);
     72 + log::info!("[+] Image Size: {:#x}", image_size as u64);
     73 + log::info!("[+] NtContinue: {:#x}", nt_continue.unwrap() as u64);
     74 + log::info!("[+] SystemFunction032: {:#x}", sys_func032.unwrap() as u64);
     75 + log::info!("[+] RtlCaptureContext: {:#x}", rtlcont.unwrap() as u64);
     76 + 
    41 77   key.Buffer = key_buf.as_mut_ptr() as *mut u16;
    42  - key.Length = key_buf.len() as _; // 16
    43  - key.MaximumLength = key_buf.len() as _; // 16
     78 + key.Length = key_buf.len() as u16; // 16
     79 + key.MaximumLength = key_buf.len() as u16; // 16
    44 80   
    45 81   img.Buffer = image_base as *mut u16;
     82 + img.Length = image_size as u16;
     83 + img.MaximumLength = image_size as u16;
    46 84   
    47  - let sucesss = unsafe {
    48  - CreateTimerQueueTimer(&mut h_new_timer, h_timer_queue, Some(std::mem::transmute(rtlcont)), &ctx_thread as *const _ as *const _, 0, 0, WT_EXECUTEINTIMERTHREAD)
     85 + log::info!("[+] Calling CreateTimerQueueTimer with ctx_thread");
     86 + // Creates a timer-queue timer. This timer expires at the specified due time, then after every specified period. When the timer expires, the callback function is called.
     87 + // https://learn.microsoft.com/en-us/windows/win32/api/threadpoollegacyapiset/nf-threadpoollegacyapiset-createtimerqueuetimer
     88 + let success = unsafe {
     89 + CreateTimerQueueTimer(
     90 + &mut h_new_timer,
     91 + h_timer_queue,
     92 + std::mem::transmute(rtlcont),
     93 + &ctx_thread as *const _ as *const _,
     94 + 0,
     95 + 0,
     96 + WT_EXECUTEINTIMERTHREAD,
     97 + )
    49 98   };
    50 99   
    51  - if sucesss != 0 {
    52  - 
    53  - unsafe{
     100 + if success != 0 {
     101 + unsafe {
     102 + log::info!("[+] Calling WaitForSingleObject for 0x32 ms");
     103 + // Waits until the specified object is in the signaled state or the time-out interval elapses.
     104 + // https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject
    54 105   WaitForSingleObject(h_event, 0x32);
    55  - 
    56  - copy(&ctx_thread, &mut rop_prot_rw, size_of::<CONTEXT>());
    57  - copy(&ctx_thread, &mut rop_mem_enc, size_of::<CONTEXT>());
    58  - copy(&ctx_thread, &mut rop_delay, size_of::<CONTEXT>());
    59  - copy(&ctx_thread, &mut rop_mem_dec, size_of::<CONTEXT>());
    60  - copy(&ctx_thread, &mut rop_prot_rx, size_of::<CONTEXT>());
    61  - copy(&ctx_thread, &mut rop_set_evt, size_of::<CONTEXT>());
    62 106   }
    63 107   
     108 + log::info!("[+] Copying ctx_thread to rop chains");
     109 + let mut rop_prot_rw = ctx_thread.clone();
     110 + let mut rop_mem_enc = ctx_thread.clone();
     111 + let mut rop_delay = ctx_thread.clone();
     112 + let mut rop_mem_dec = ctx_thread.clone();
     113 + let mut rop_prot_rx = ctx_thread.clone();
     114 + let mut rop_set_evt = ctx_thread.clone();
     115 + 
     116 + log::info!("[+] Building ROP chain");
     117 + // pub unsafe extern "system" fn VirtualProtect(lpaddress: *const c_void, dwsize: usize, flnewprotect: PAGE_PROTECTION_FLAGS, lpfloldprotect: *mut PAGE_PROTECTION_FLAGS) -> BOOL
     118 + // https://docs.rs/windows-sys/latest/windows_sys/Win32/System/Memory/fn.VirtualProtect.html
    64 119   rop_prot_rw.Rsp = -8 as isize as u64;
    65 120   rop_prot_rw.Rip = VirtualProtect as u64;
    66 121   rop_prot_rw.Rcx = image_base as u64;
    67 122   rop_prot_rw.Rdx = image_size as u64;
    68 123   rop_prot_rw.R8 = PAGE_READWRITE as u64;
    69  - rop_prot_rw.R9 = *&old_protect as u64;
     124 + rop_prot_rw.R9 = &mut old_protect as *mut u32 as u64;
    70 125   
     126 + // pub unsafe extern "system" fn SystemFunction036(randombuffer: *mut c_void, randombufferlength: u32) -> BOOLEAN
     127 + // https://docs.rs/windows-sys/latest/windows_sys/Win32/Security/Authentication/Identity/fn.SystemFunction036.html
    71 128   rop_mem_enc.Rsp = -8 as isize as u64;
    72  - rop_mem_enc.Rip = &sys_func032 as *const _ as u64;
    73  - rop_mem_enc.Rcx = &img as *const _ as u64;
    74  - rop_mem_enc.Rdx = &key as *const _ as u64;
     129 + rop_mem_enc.Rip = sys_func032.unwrap() as u64;
     130 + rop_mem_enc.Rcx = &mut img as *mut UNICODE_STRING as *mut c_void as u64;
     131 + rop_mem_enc.Rdx = key.Length as u64;
    75 132   
     133 + // pub unsafe extern "system" fn WaitForSingleObject(hhandle: HANDLE, dwmilliseconds: u32) -> WIN32_ERROR
     134 + // https://docs.rs/windows-sys/latest/windows_sys/Win32/System/Threading/fn.WaitForSingleObject.html
    76 135   rop_delay.Rsp = -8 as isize as u64;
    77 136   rop_delay.Rip = WaitForSingleObject as u64;
    78  - rop_delay.Rcx = -1 as isize as u64;
     137 + rop_delay.Rcx = -1 as isize as u64; // NtCurrentProcess
    79 138   rop_delay.Rdx = sleep_time as u64;
    80 139   
     140 + // pub unsafe extern "system" fn SystemFunction036(randombuffer: *mut c_void, randombufferlength: u32) -> BOOLEAN
     141 + // https://docs.rs/windows-sys/latest/windows_sys/Win32/Security/Authentication/Identity/fn.SystemFunction036.html
    81 142   rop_mem_dec.Rsp = -8 as isize as u64;
    82  - rop_mem_dec.Rip = &sys_func032 as *const _ as u64;
    83  - rop_mem_dec.Rcx = &img as *const _ as u64;
    84  - rop_mem_dec.Rdx = &key as *const _ as u64;
     143 + rop_mem_dec.Rip = sys_func032.unwrap() as u64;
     144 + rop_mem_dec.Rcx = &mut img as *mut UNICODE_STRING as *mut c_void as u64;
     145 + rop_mem_dec.Rdx = key.Length as u64;
    85 146   
     147 + // pub unsafe extern "system" fn VirtualProtect(lpaddress: *const c_void, dwsize: usize, flnewprotect: PAGE_PROTECTION_FLAGS, lpfloldprotect: *mut PAGE_PROTECTION_FLAGS) -> BOOL
     148 + // https://docs.rs/windows-sys/latest/windows_sys/Win32/System/Memory/fn.VirtualProtect.html
    86 149   rop_prot_rx.Rsp = -8 as isize as u64;
    87 150   rop_prot_rx.Rip = VirtualProtect as u64;
    88 151   rop_prot_rx.Rcx = image_base as u64;
    89 152   rop_prot_rx.Rdx = image_size as u64;
    90 153   rop_prot_rx.R8 = PAGE_EXECUTE_READ as u64;
    91  - rop_prot_rx.R9 = *&old_protect as u64;
     154 + rop_prot_rw.R9 = &mut old_protect as *mut u32 as u64;
    92 155   
     156 + // https://docs.rs/windows-sys/latest/windows_sys/Win32/System/Threading/fn.SetEvent.html
     157 + // pub unsafe extern "system" fn SetEvent(hevent: HANDLE) -> BOOL
    93 158   rop_set_evt.Rsp = -8 as isize as u64;
    94 159   rop_set_evt.Rip = SetEvent as u64;
    95 160   rop_set_evt.Rcx = h_event as u64;
    96 161   
    97  - unsafe{
    98  - CreateTimerQueueTimer( &mut h_new_timer, h_timer_queue, Some(std::mem::transmute(nt_continue)), &rop_prot_rw as *const _ as *const _, 100, 0, WT_EXECUTEINTIMERTHREAD );
    99  - CreateTimerQueueTimer( &mut h_new_timer, h_timer_queue, Some(std::mem::transmute(nt_continue)), &rop_mem_enc as *const _ as *const _, 100, 0, WT_EXECUTEINTIMERTHREAD );
    100  - CreateTimerQueueTimer( &mut h_new_timer, h_timer_queue, Some(std::mem::transmute(nt_continue)), &rop_delay as *const _ as *const _, 100, 0, WT_EXECUTEINTIMERTHREAD );
    101  - CreateTimerQueueTimer( &mut h_new_timer, h_timer_queue, Some(std::mem::transmute(nt_continue)), &rop_mem_dec as *const _ as *const _, 100, 0, WT_EXECUTEINTIMERTHREAD );
    102  - CreateTimerQueueTimer( &mut h_new_timer, h_timer_queue, Some(std::mem::transmute(nt_continue)), &rop_prot_rx as *const _ as *const _, 100, 0, WT_EXECUTEINTIMERTHREAD );
    103  - CreateTimerQueueTimer( &mut h_new_timer, h_timer_queue, Some(std::mem::transmute(nt_continue)), &rop_set_evt as *const _ as *const _, 100, 0, WT_EXECUTEINTIMERTHREAD );
    104  - WaitForSingleObject(h_event, 0xFFFFFFFF );
     162 + log::info!("[+] Rop chain built");
     163 + log::info!("[+] Queue timers");
     164 + unsafe {
     165 + let result = CreateTimerQueueTimer(
     166 + &mut h_new_timer,
     167 + h_timer_queue,
     168 + std::mem::transmute(nt_continue),
     169 + &rop_prot_rw as *const _ as *const _,
     170 + 100,
     171 + 0,
     172 + WT_EXECUTEINTIMERTHREAD,
     173 + );
     174 + if result == 0 {
     175 + panic!("[!] Failed calling CreateTimerQueueTimer with rop_prot_rw (VirtualProtect RW) {:#x}", GetLastError());
     176 + }
     177 + 
     178 + let result = CreateTimerQueueTimer(
     179 + &mut h_new_timer,
     180 + h_timer_queue,
     181 + std::mem::transmute(nt_continue),
     182 + &rop_mem_enc as *const _ as *const _,
     183 + 100,
     184 + 0,
     185 + WT_EXECUTEINTIMERTHREAD,
     186 + );
     187 + if result == 0 {
     188 + panic!("[!] Failed calling CreateTimerQueueTimer with rop_mem_enc (SystemFunction036) {:#x}", GetLastError());
     189 + }
     190 + 
     191 + let result = CreateTimerQueueTimer(
     192 + &mut h_new_timer,
     193 + h_timer_queue,
     194 + std::mem::transmute(nt_continue),
     195 + &rop_delay as *const _ as *const _,
     196 + 100,
     197 + 0,
     198 + WT_EXECUTEINTIMERTHREAD,
     199 + );
     200 + if result == 0 {
     201 + panic!("[!] Failed calling CreateTimerQueueTimer with rop_delay (WaitForSingleObject) {:#x}", GetLastError());
     202 + }
     203 + 
     204 + let result = CreateTimerQueueTimer(
     205 + &mut h_new_timer,
     206 + h_timer_queue,
     207 + std::mem::transmute(nt_continue),
     208 + &rop_mem_dec as *const _ as *const _,
     209 + 100,
     210 + 0,
     211 + WT_EXECUTEINTIMERTHREAD,
     212 + );
     213 + if result == 0 {
     214 + panic!("[!] Failed calling CreateTimerQueueTimer with rop_mem_dec (SystemFunction036) {:#x}", GetLastError());
     215 + }
     216 + 
     217 + let result = CreateTimerQueueTimer(
     218 + &mut h_new_timer,
     219 + h_timer_queue,
     220 + std::mem::transmute(nt_continue),
     221 + &rop_prot_rx as *const _ as *const _,
     222 + 100,
     223 + 0,
     224 + WT_EXECUTEINTIMERTHREAD,
     225 + );
     226 + if result == 0 {
     227 + panic!("[!] Failed calling CreateTimerQueueTimer with rop_prot_rx (VirtualProtect RX) {:#x}", GetLastError());
     228 + }
     229 + 
     230 + let result = CreateTimerQueueTimer(
     231 + &mut h_new_timer,
     232 + h_timer_queue,
     233 + std::mem::transmute(nt_continue),
     234 + &rop_set_evt as *const _ as *const _,
     235 + 100,
     236 + 0,
     237 + WT_EXECUTEINTIMERTHREAD,
     238 + );
     239 + if result == 0 {
     240 + panic!("[!] Failed calling CreateTimerQueueTimer with rop_set_evt (SetEvent) {:#x}", GetLastError());
     241 + }
     242 +
     243 + log::info!("[+] Waiting for event...");
     244 + 
     245 + WaitForSingleObject(h_event, INFINITE); //0xFFFFFFFF
     246 + 
     247 + log::info!("[+] Finished waiting for event");
    105 248   }
    106 249   }
    107 250   
    108  - unsafe{DeleteTimerQueue( h_timer_queue );}
     251 + // Deletes a timer queue. Any pending timers in the queue are canceled and deleted.
     252 + // https://learn.microsoft.com/en-us/windows/win32/api/threadpoollegacyapiset/nf-threadpoollegacyapiset-deletetimerqueue
     253 + unsafe {
     254 + DeleteTimerQueue(h_timer_queue);
     255 + }
    109 256  }
     257 + 
     258 +#[allow(dead_code)]
     259 +/// Gets user input from the terminal
     260 +fn get_input() -> std::io::Result<()> {
     261 + let mut buf = String::new();
     262 + std::io::stdin().read_line(&mut buf)?;
     263 + Ok(())
     264 +}
     265 + 
     266 +#[allow(dead_code)]
     267 +/// Used for debugging
     268 +pub fn pause() {
     269 + match get_input() {
     270 + Ok(buffer) => println!("{:?}", buffer),
     271 + Err(error) => println!("error: {}", error),
     272 + };
     273 +}
     274 + 
  • ■ ■ ■ ■ ■ ■
    src/main.rs
    skipped 1 lines
    2 2   
    3 3  fn main() {
    4 4   env_logger::init();
    5  - log::info!("[*] Ekko Sleep Obfuscation by @memN0ps. Full credits to Paul (@C5pider), Austin Hudson (@SecIdiot), Peter Winter-Smith (@peterwintrsmith)");
    6  - ekko::ekko(10000);
     5 + log::info!("[*] Ekko Sleep Obfuscation by @memN0ps and @trickster0. Full credits to Paul (@C5pider), Austin Hudson (@SecIdiot), Peter Winter-Smith (@peterwintrsmith)");
     6 + ekko::ekko(4 * 1000);
    7 7  }
    8 8   
Please wait...
Page is in error, reload to recover