Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jumper and Jumper_randomized are not sending calls from NTDLL address space #13

Open
philross88 opened this issue Feb 10, 2023 · 6 comments

Comments

@philross88
Copy link

philross88 commented Feb 10, 2023

My understanding is that Jumper and Jumper_randomized would jump into NTDLL space, and use the syscall instructions by jumping into those assembly instructions, however when I trace the program with Frida, I can see that none of calls are jumped or at least Frida script is catching them being called outside of the NTDLL space.

I also enabled the debug and prints for Found Syscalls and I see them getting kicked In. I tried debugging with the debugger and I saw jumps to NTDLL but I am not sure why the calls are still made from outside of the NTDLL address space.

Using the following functions and command to generate the syswhisper files in generic code injector
python3 syswhispers.py -a x64 -c msvc -m jumper_randomized -f NtAllocateVirtualMemory,NtWriteVirtualMemory,NtOpenProcess,NtCreateThreadEx,NtProtectVirtualMemory

I used the following Frida script to trace the syscalls origination:

var modules = Process.enumerateModules()
//send("[+] list of Modules " + modules.toString())
var ntdll = modules[1]
//send("[+] list of Modules " + ntdll.toString())


var ntdllBase = ntdll.base
send("[*] Ntdll base: " + ntdllBase)
var ntdllOffset = ntdllBase.add(ntdll.size)
send("[*] Ntdll end: " + ntdllOffset)

const mainThread = Process.enumerateThreads()[0];
Process.enumerateThreads().map(t => {
Stalker.follow(t.id, {
  events: {
    call: false, // CALL instructions: yes please
    // Other events:
    ret: false, // RET instructions
    exec: false, // all instructions: not recommended as it's
                 //                   a lot of data
    block: false, // block executed: coarse execution trace
    compile: false // block compiled: useful for coverage
  },
  onReceive(events) {    
  },
  transform(iterator){
      let instruction = iterator.next()
      do{
        
        //I think this reduces overhead
        if(instruction.mnemonic == "mov"){
            //Should provide a good filter for syscalls, might need further filtering
            if(instruction.toString() == "mov r10, rcx"){
                iterator.keep() //keep the instruction
                instruction = iterator.next() //next instruction should have the syscall number
								//This helps to clear up some false positives
                if(instruction.toString().split(',')[0] == "mov eax"){
                    var addrInt = instruction.address.toInt32()
                    //If the syscall is coming from somewhere outside the bounds of NTDLL
                    //then it may be malicious
                    if(addrInt < ntdllBase.toInt32() || addrInt > ntdllOffset.toInt32()){
                        send("[+] Found a potentially malicious syscall: " + instruction.toString())
                    }
                }
            }
        }
        
      iterator.keep()
      } while ((instruction = iterator.next()) !== null)
  }
  
 
})
})
@philross88
Copy link
Author

My understanding is that jumper is doing the same thing as this post described -- instead of doing it manually
https://passthehashbrowns.github.io/hiding-your-syscalls

@klezVirus
Copy link
Owner

klezVirus commented Feb 10, 2023

You're intuition about the syscall is right, what it is a bit off is the detection you're presenting.

This Frida script doesn't actually check where the syscall is originating from. It's trying to detect the syscall stub position. What's happening, at least for what I can guess by reading the Frida script, is that when you are calling an "Nt" function, the script will detect the call, trace execution "forward" using an instruction iterator, and detects the syscall stub by looking ahead the stub instructions: mov r10, rcx, and mov eax, <Syscall Address>. As SW3 embeds the syscall stub in the program, it is impossible to fully hide the stub from it, but you can easily bypass this just by adding a "nop" between the two instructions.

@philross88
Copy link
Author

I added bunch of NOPs between those statements, but it didn't help.
The reason is that those instructions are merely to nail down the syscall operations being called out.

The Frida script checks if mov eax is outbounds of the NTDLL address, which it is therefore it declares it malicious direct syscall.
My understanding is that with jumper, we are just calling syscall outside of the main program but we set the registers in the stub. Is it not possible to find the the relevant eax register value as well in NTDLL and then jump to syscall but patching some instructions and reverting them back?

More like ROP chain concept

I am not trying to evade this scanning, but it seems like modern AVs/EDRs are leveraging this along with ETW to direct syscalls and shutsdown the process

@klezVirus
Copy link
Owner

Thanks for pointing it out. On the first glance, this kind of detection seems trivial to bypass, I'll have a play with this script as soon as I can.

Anyway, regardless this specific Firda script, there are several updates currently under development to address stack tracing and detections similar to what you're describing.

However, I'm afraid they won't be ready overnight. I planned to release more things faster but I got delayed and I won't be ready to release anything major before March I guess.

@philross88
Copy link
Author

Hi, I was wondering if you have any updates planned. Seems like a lot of EDRs are now tracking the stack to detect direct syscalls and blocking them -- which becomes a pain in the butt for Red Teaming.
https://medium.com/falconforce/falconfriday-direct-system-calls-and-cobalt-strike-bofs-0xff14-741fa8e1bdd6

@klezVirus
Copy link
Owner

Hey @philross88, thank you very much for the heads up. I do have some plans for this, yes. But I am afraid they won't be announced before August this year 😬

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

No branches or pull requests

2 participants