dreamkinn / go-ThreadlessInject

Golang implementation of @CCob's C# ThreadlessInject
30 stars 1 forks source link

doesnt work with different shellcode #1

Closed Beykir closed 2 months ago

Beykir commented 2 months ago

Hello,

i have tried it with a generated calc shellcode from donut but it doesnt execute. With the default shellcode it works, why is that?

dreamkinn commented 2 months ago

Hi, could you provide more info for debug ? Many things can lead to the payload not executing. How exactly did you generate the shellcode ? Are you sure it is x64 ? is the shellcode raw or encoded (for example SGN would require RWX but tinject only uses RX) ? Is it the same hooked function/are you sure it triggered properly ? ....

Beykir commented 2 months ago

Yes of course. Im using the latest win 10 64 bit and the latest donut version. Im building the shellcode with "donut -i calc.exe" its for sure x64 and the shellcode is not encoded. Command im using "tinject.exe -dll ntdll.dll -fct NtClose -pid 9528" the pid is from msedge.exe.

This one is working with the donut generated shellcode, its also based on the original threadlessinject from CCob. Im no C expert but he changed a few things, maybe you can have a look? https://github.com/caueb/ThreadlessC/tree/main

More precisely in the code from caueb i dont see a ReadProcessMemory and thats exactly where tinject is crashing for me with "Only part of a ReadProcessMemory or WriteProcessMemory request was completed."

Log:

[+] DEBUG - Export address: 7ffe2d48d1b0
[+] Trying address : @7ffdbd400000
[+] Successfully allocated : @7ffdbd400000
[+] DEBUG - Original bytes: 0x4c8bd1b80f000000
[+] DEBUG - Loader : 584883e805505152415041514152415348b94c8bd1b80f0000004889084883ec40e8110000004883c440415b415a415941585a5958ffe090
[+] DEBUG - Relative loader address: 8ff72e4b
[+] DEBUG - callOpCode : 0xe84b2ef78f
[+] DEBUG - newBytes : b0d1482dfe7f0000
[+] Shellcode injected, waiting 60s for the hook to be called...
2024/05/10 00:22:19 Error monitoring function :Only part of a ReadProcessMemory or WriteProcessMemory request was completed.
exit status 1

Thanks

dreamkinn commented 2 months ago

Thank you for the details. Out of curiosity does the shellcode run when using the example function+process (ie NtOpenFile with notepad) ? I'll try to look into the specific error

Beykir commented 2 months ago

I have tried it right now and its the same. With the default calc shellcode it works, but with the self generated calc shellcode from donut it does not. Same error message... notepad just closes and calc doesnt popping up.

dreamkinn commented 2 months ago

After reading the code from caueb here https://github.com/caueb/ThreadlessC/blob/main/ThreadlessCaue/Threadless.c I only see that caueb is writing the shellcode and hook and just not monitoring & cleaning up (hence no ReadProcessMemory). So I don't think their code is doing much more.

By trying coeb's project : my msedge crashes and the calc is executed.

./donut --arch:amd64 --input:calc.exe --level:none -o calc.bin

My best guess is the following : the initial shellcode (provided by CCob) restores the stack and ret to the original function so that the injected process can still function normally. On the other hand, donut by default uses exit thread after executing the shellcode, this exits the thread and potentially crashes the host process (notepad or whatever process you are injecting).

For me the "issue" is inherent to the technique : if you do not restore the stack & ret, then you are crashing a thread and potentially the process.

Beykir commented 2 months ago

Well, that makes sense. But why calc is getting executed, when using the project from caueb (even when msedge is crashing) but not with tinject? If the only difference is the monitoring and the cleaning stuff donut calc should pop up if i comment it out right?

dreamkinn commented 2 months ago

Yes that's what happens on my side. I didn't comment the cleaning part but just the shitty error check. Calc pops & edge crashes "as expected".

Edit : as we are crashing rather blindly some threads (msedge also has many different processes), to me it is not a surprise that we get inconsistent results when not restoring the context. image

Beykir commented 2 months ago

Weird, that its working for you. I have also removed the error check, but with the donut calc shellcode the process is crashing as expected like you said, but my calc is not getting executed.

As soon as NtOpenFile gets triggered in Notepad.exe it crashes and instead of reading the actual bytes it just reads "[+] Read bytes: 0000000000000000" and the hook is never being called. But what suprises me is, that the code from caueb is also crashing the process, but also executing the shellcode.

[+] Monitoring...
[+] Read bytes: e8cb29f78f000000
[+] Original bytes: 4c8bd1b833000000
[+] Monitoring...
[+] Read bytes: e8cb29f78f000000
[+] Original bytes: 4c8bd1b833000000
[+] Monitoring...
[+] Read bytes: e8cb29f78f000000
[+] Original bytes: 4c8bd1b833000000
PROCESS IS CRASHING HERE
[+] Monitoring...
[+] Read bytes: 0000000000000000
[+] Original bytes: 4c8bd1b833000000
[+] Monitoring...
[+] Read bytes: 0000000000000000
[+] Original bytes: 4c8bd1b833000000
[+] Monitoring...
[+] Read bytes: 0000000000000000
[+] Original bytes: 4c8bd1b833000000
SHELLCODE DOESNT GET EXECUTED
[+] Done
dreamkinn commented 2 months ago

Ok I just pushed a version on no-check branch that you can try https://github.com/dreamkinn/go-ThreadlessInject/blob/no_check/threadlessinject.go :

./donut --arch:amd64 --input:calc.exe --level:none -o calc.bin

# Format your shellcode to go buffer and put it at SHELLCODE_REPLACE position in the code

# On notepad.exe. Don't use the GUI "Open File" but rather hit CTRL+o 
.\tinject.exe -pid 123 -fct NtOpenFile -dll ntdll.dll

On my side this gives the same behaviour as ThreadlessC : crash notepad + Calc pops (somehow works better when hitting ctrl+o instead of gui idk why honestly)

Beykir commented 2 months ago

Now it works on my side too, thank you