Skip to content

Commit f0df5d4

Browse files
committed
Added rundll32 support
1 parent 2583b2e commit f0df5d4

19 files changed

Lines changed: 650 additions & 68 deletions

CONTRIBUTING.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Contributing
2+
3+
Add your own cmdlets by submitting a pull request.
4+
## Requirement
5+
- Maintain .NET 2.0 compatibility in order to support the broadest range of operating systems
6+
7+
## Instructions
8+
Use the TemplateCommand.cs file in the Commands folder to construct new cmdlets. The TemplateCommand cmdlet is hidden from the list of available cmdlets, but can be called in order to understand its workings. This command looks as follows: `Get-TemplateCommand [-MyFlag] -MyInteger [Int32] -MyString [Value]` and is also accessible via alias `gtc`.
9+
10+
### Example usages
11+
| Action | Command |
12+
| - | - |
13+
| Simply run with default values | `gtc` |
14+
| Run with the -MyFlag parameter which executes the 'else' statement | `gtc -MyFlag` |
15+
| Run with the -MyInteger parameter which changes the number of iterations from its default number of 5 iterations to whatever number is provided | `gtc -MyInteger 10` |
16+
| Run with the -MyString parameter which changes the text that is printed from its default value of 'Hello World' to whatever string is provided | `gtc -MyString "Bye PowerShell"` |
17+
| Combination of parameters | `gtc -MyInteger 10 -MyString "Bye PowerShell"` |
18+
| Combination of parameters - Using fact that MyString is the only mandatory parameter for this command | `gtc -MyInteger 10 "Bye PowerShell"` |
19+
| Command in combination with a couple of data manipulators in the pipe | `gtc "Bye PowerShell" -MyInteger 30 \| ? Attribute2 -Like Line1* \| select Attribute2 \| fl` |
20+
21+
Execute the following steps to implement your own cmdlet:
22+
1. Download Visual Studio Community from https://visualstudio.microsoft.com/downloads/
23+
* In the installer select the **.NET desktop development** component.
24+
* From this component no optional modules are required for developing NoPowerShell modules.
25+
2. Make sure to have the .NET 2 framework installed: OptionalFeatures -> '.NET Framework 3.5 (includes .NET 2.0 and 3.0)'.
26+
3. Clone this repository and create a copy of the **TemplateCommand.cs** file.
27+
* In case you are implementing a native PowerShell command, place it in folder the corresponding to the _Source_ attribute when executing in PowerShell: `Get-Command My-Commandlet`.
28+
* Moreover, use the name of the _Source_ attribute in the command's namespace.
29+
* Example of a native command: `Get-Command Get-Process` -> Source: `Microsoft.PowerShell.Management` -> Place the .cs file in the **Management** subfolder and use `NoPowerShell.Commands.Management` namespace.
30+
* In case it is a non-native command, place it in the **Additional** folder and use the `NoPowerShell.Commands.Additional` namespace.
31+
4. Update the `TemplateCommand` classname and its constructor name.
32+
5. Update the static **Aliases** variable to the command and aliases you want to use to call this cmdlet. For native PowerShell commands you can lookup the aliases using `Get-Alias | ? ResolvedCommandName -EQ My-Commandlet` to obtain the list of aliases. Always make sure the full command is the first "alias", for example: `Get-Alias | ? ResolvedCommandName -EQ Get-Process` -> Aliases are: `Get-Process`, `gps`, `ps`
33+
6. Update the static **Synopsis** variable to a small text that describes the command. This will be shown in the help.
34+
7. Update the arguments supported by the command by adding _StringArguments_, _BoolArguments_ and _IntegerArguments_ to the static **SupportedArguments** variable.
35+
8. In the Execute function:
36+
1. Fetch the values of the _StringArguments_, _BoolArguments_ and _IntegerArguments_ as shown in the examples;
37+
2. Based on the parameters provided by the user, perform your actions;
38+
3. Make sure all results are stored in the `_results` variable.
39+
9. Remove all of the template sample code and comments from the file to keep the source tidy.

Pictures/NoPowerShellDll.png

31.6 KB
Loading

README.md

Lines changed: 15 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
# NoPowerShell
2-
NoPowerShell is a tool implemented in C# which supports executing PowerShell-like commands while remaining invisible to any PowerShell logging mechanisms. This .NET Framework 2 compatible binary can be loaded in Cobalt Strike to execute commands in-memory. No `System.Management.Automation.dll` is used; only native .NET libraries.
2+
NoPowerShell is a tool implemented in C# which supports executing PowerShell-like commands while remaining invisible to any PowerShell logging mechanisms. This .NET Framework 2 compatible binary can be loaded in Cobalt Strike to execute commands in-memory. No `System.Management.Automation.dll` is used; only native .NET libraries. An alternative usecase for NoPowerShell is to launch it as a DLL via rundll32.exe: `rundll32 NoPowerShell.dll,main`.
33

44
Moreover, this project makes it easy for everyone to extend its functionality using only a few lines of C# code.
55

6-
Latest binary available from the [Releases](https://github.com/bitsadmin/nopowershell/releases) page.
6+
Latest binaries available from the [Releases](https://github.com/bitsadmin/nopowershell/releases) page.
77

88
# Screenshots
99
## Running in Cobalt Strike
1010
![NoPowerShell supported commands](https://raw.githubusercontent.com/bitsadmin/nopowershell/master/Pictures/CurrentlySupportedCommands.png "NoPowerShell in Cobalt Strike")
1111
## Sample execution of commands
1212
![NoPowerShell sample commands](https://raw.githubusercontent.com/bitsadmin/nopowershell/master/Pictures/SampleCommands.png "NoPowerShell in Cobalt Strike")
13+
## Rundll32 version
14+
![NoPowerShellDll via rundll32](https://raw.githubusercontent.com/bitsadmin/nopowershell/master/Pictures/NoPowerShellDll.png "NoPowerShellDll via rundll32")
1315

1416
# Usage
1517
## Note
@@ -23,7 +25,7 @@ When using NoPowerShell from cmd.exe or PowerShell, you need to escape the pipe
2325
| - | - | - |
2426
| List all commands supported by NoPowerShell | `Get-Command` | |
2527
| Get help for a command | `Get-Help -Name Get-Process` | Alternative: `man ps` |
26-
| Show current user | `NoPowerShell.exe whoami` | Unofficial command |
28+
| Show current user | `whoami` | Unofficial command |
2729
| List SMB shares of MyServer | `Get-RemoteSmbShare \\MyServer` | Unofficial command |
2830
| List all user groups in domain | `Get-ADGroup -Filter *` | |
2931
| List all administrative groups in domain | `Get-ADGroup -LDAPFilter "(admincount=1)" \| select Name` | |
@@ -53,7 +55,7 @@ When using NoPowerShell from cmd.exe or PowerShell, you need to escape the pipe
5355
| List processes on remote host | `Get-Process -ComputerName dc01.corp.local -Username Administrator -Password P4ssw0rd!` | |
5456
| Gracefully stop processes | `Stop-Process -Id 4512,7241` | |
5557
| Kill process | `Stop-Process -Force -Id 4512` | |
56-
| Kill all cmd.exe processes | `Get-Process cmd | Stop-Process -Force` | |
58+
| Kill all cmd.exe processes | `Get-Process cmd \| Stop-Process -Force` | |
5759
| Obtain data of Win32_Process class from a remote system and apply a filter on the output | `gwmi "Select ProcessId,Name,CommandLine From Win32_Process" -ComputerName dc01.corp.local \| ? Name -Like *PowerShell* \| select ProcessId,CommandLine` | Explicit credentials can be specified using the `-Username` and `-Password` parameters |
5860
| View details about a certain service | `Get-WmiObject -Class Win32_Service -Filter "Name = 'WinRM'"` | |
5961
| Launch process using WMI | `Invoke-WmiMethod -Class Win32_Process -Name Create "cmd /c calc.exe"` | This can also be done on a remote system |
@@ -63,7 +65,7 @@ When using NoPowerShell from cmd.exe or PowerShell, you need to escape the pipe
6365
| Show the IP routing table | `Get-NetRoute` | |
6466
| Send 2 ICMP requests to IP address 1.1.1.1 with half a second of timeout | `Test-NetConnection -Count 2 -Timeout 500 1.1.1.1` | |
6567
| Perform a traceroute with a timeout of 1 second and a maximum of 20 hops | `Test-NetConnection -TraceRoute -Timeout 1000 -Hops 20 google.com` | |
66-
| List network shares on the local machine that are exposed to the network | `Get-NetSmbMapping` | |
68+
| List network shares on the local machine that are exposed to the network | `Get-SmbMapping` | |
6769
| Format output as a list | `Get-LocalUser \| fl` | |
6870
| Format output as a list showing only specific attributes | `Get-LocalUser \| fl Name,Description` | |
6971
| Format output as a table | `Get-Process \| ft` | |
@@ -86,6 +88,12 @@ When using NoPowerShell from cmd.exe or PowerShell, you need to escape the pipe
8688
2. Launch Cobalt Strike and load the .cna script in the Script Manager
8789
3. Interact with a beacon and execute commands using the `nps` command
8890

91+
## Launch via rundll32
92+
1. Create a new shortcut to `NoPowerShell.dll` file (drag using right click -> Create shortcuts here)
93+
2. Update the shortcut prefixing the filename with `rundll32` and appending `,main`
94+
3. The shortcut will now look like `rundll32 C:\Path\to\NoPowerShell.dll,main`
95+
4. Double click the shortcut
96+
8997
# Known issues
9098
- Pipeline characters need to surrounded by spaces
9199
- TLS 1.1+ is not supported by .NET Framework 2, so any site enforcing it will result in a connection error
@@ -98,43 +106,7 @@ When using NoPowerShell from cmd.exe or PowerShell, you need to escape the pipe
98106
- Add support for .NET code in commandline, i.e.: `[System.Security.Principal.WindowsIdentity]::GetCurrent().Name`
99107

100108
# Contributing
101-
Add your own cmdlets by submitting a pull request.
102-
## Requirement
103-
- Maintain .NET 2.0 compatibility in order to support the broadest range of operating systems
104-
105-
## Instructions
106-
Use the TemplateCommand.cs file in the Commands folder to construct new cmdlets. The TemplateCommand cmdlet is hidden from the list of available cmdlets, but can be called in order to understand its workings. This command looks as follows: `Get-TemplateCommand [-MyFlag] -MyInteger [Int32] -MyString [Value]` and is also accessible via alias `gtc`.
107-
108-
### Example usages
109-
| Action | Command |
110-
| - | - |
111-
| Simply run with default values | `gtc` |
112-
| Run with the -MyFlag parameter which executes the 'else' statement | `gtc -MyFlag` |
113-
| Run with the -MyInteger parameter which changes the number of iterations from its default number of 5 iterations to whatever number is provided | `gtc -MyInteger 10` |
114-
| Run with the -MyString parameter which changes the text that is printed from its default value of 'Hello World' to whatever string is provided | `gtc -MyString "Bye PowerShell"` |
115-
| Combination of parameters | `gtc -MyInteger 10 -MyString "Bye PowerShell"` |
116-
| Combination of parameters - Using fact that MyString is the only mandatory parameter for this command | `gtc -MyInteger 10 "Bye PowerShell"` |
117-
| Command in combination with a couple of data manipulators in the pipe | `gtc "Bye PowerShell" -MyInteger 30 \| ? Attribute2 -Like Line1* \| select Attribute2 \| fl` |
118-
119-
Execute the following steps to implement your own cmdlet:
120-
1. Download Visual Studio Community from https://visualstudio.microsoft.com/downloads/
121-
* In the installer select the **.NET desktop development** component.
122-
* From this component no optional modules are required for developing NoPowerShell modules.
123-
2. Make sure to have the .NET 2 framework installed: OptionalFeatures -> '.NET Framework 3.5 (includes .NET 2.0 and 3.0)'.
124-
3. Clone this repository and create a copy of the **TemplateCommand.cs** file.
125-
* In case you are implementing a native PowerShell command, place it in folder the corresponding to the _Source_ attribute when executing in PowerShell: `Get-Command My-Commandlet`.
126-
* Moreover, use the name of the _Source_ attribute in the command's namespace.
127-
* Example of a native command: `Get-Command Get-Process` -> Source: `Microsoft.PowerShell.Management` -> Place the .cs file in the **Management** subfolder and use `NoPowerShell.Commands.Management` namespace.
128-
* In case it is a non-native command, place it in the **Additional** folder and use the `NoPowerShell.Commands.Additional` namespace.
129-
4. Update the `TemplateCommand` classname and its constructor name.
130-
5. Update the static **Aliases** variable to the command and aliases you want to use to call this cmdlet. For native PowerShell commands you can lookup the aliases using `Get-Alias | ? ResolvedCommandName -EQ My-Commandlet` to obtain the list of aliases. Always make sure the full command is the first "alias", for example: `Get-Alias | ? ResolvedCommandName -EQ Get-Process` -> Aliases are: `Get-Process`, `gps`, `ps`
131-
6. Update the static **Synopsis** variable to a small text that describes the command. This will be shown in the help.
132-
7. Update the arguments supported by the command by adding _StringArguments_, _BoolArguments_ and _IntegerArguments_ to the static **SupportedArguments** variable.
133-
8. In the Execute function:
134-
1. Fetch the values of the _StringArguments_, _BoolArguments_ and _IntegerArguments_ as shown in the examples;
135-
2. Based on the parameters provided by the user, perform your actions;
136-
3. Make sure all results are stored in the `_results` variable.
137-
9. Remove all of the template sample code and comments from the file to keep the source tidy.
109+
See [CONTRIBUTING.md](https://github.com/bitsadmin/nopowershell/blob/master/CONTRIBUTING.md).
138110

139111
# Requested NoPowerShell cmdlets
140112
| Cmdlet | Description |
@@ -191,4 +163,4 @@ Authors of additional NoPowerShell cmdlets are added to the table below. Moreove
191163
| Measure-Object | Utility |
192164
| Select-Object | Utility |
193165

194-
**Authored by Arris Huijgen ([@bitsadmin](https://twitter.com/bitsadmin/) - https://github.com/bitsadmin/)**
166+
**Authored by Arris Huijgen ([@bitsadmin](https://twitter.com/bitsadmin/) - https://github.com/bitsadmin/)**

Source/NoPowerShell.sln

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,55 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 15
4-
VisualStudioVersion = 15.0.26228.4
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.28803.156
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoPowerShell", "NoPowerShell\NoPowerShell.csproj", "{555AD0AC-1FDB-4016-8257-170A74CB2F55}"
77
EndProject
88
Global
99
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug DLL|Any CPU = Debug DLL|Any CPU
11+
Debug DLL|x64 = Debug DLL|x64
12+
Debug DLL|x86 = Debug DLL|x86
1013
Debug|Any CPU = Debug|Any CPU
14+
Debug|x64 = Debug|x64
15+
Debug|x86 = Debug|x86
16+
Release DLL|Any CPU = Release DLL|Any CPU
17+
Release DLL|x64 = Release DLL|x64
18+
Release DLL|x86 = Release DLL|x86
1119
Release|Any CPU = Release|Any CPU
20+
Release|x64 = Release|x64
21+
Release|x86 = Release|x86
1222
EndGlobalSection
1323
GlobalSection(ProjectConfigurationPlatforms) = postSolution
24+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Debug DLL|Any CPU.ActiveCfg = Debug DLL|Any CPU
25+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Debug DLL|Any CPU.Build.0 = Debug DLL|Any CPU
26+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Debug DLL|x64.ActiveCfg = Debug DLL|x64
27+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Debug DLL|x64.Build.0 = Debug DLL|x64
28+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Debug DLL|x86.ActiveCfg = Debug DLL|x86
29+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Debug DLL|x86.Build.0 = Debug DLL|x86
1430
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1531
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Debug|Any CPU.Build.0 = Debug|Any CPU
32+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Debug|x64.ActiveCfg = Debug|x64
33+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Debug|x64.Build.0 = Debug|x64
34+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Debug|x86.ActiveCfg = Debug|x86
35+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Debug|x86.Build.0 = Debug|x86
36+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Release DLL|Any CPU.ActiveCfg = Release DLL|Any CPU
37+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Release DLL|Any CPU.Build.0 = Release DLL|Any CPU
38+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Release DLL|x64.ActiveCfg = Release DLL|x64
39+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Release DLL|x64.Build.0 = Release DLL|x64
40+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Release DLL|x86.ActiveCfg = Release DLL|x86
41+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Release DLL|x86.Build.0 = Release DLL|x86
1642
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Release|Any CPU.ActiveCfg = Release|Any CPU
1743
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Release|Any CPU.Build.0 = Release|Any CPU
44+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Release|x64.ActiveCfg = Release|x64
45+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Release|x64.Build.0 = Release|x64
46+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Release|x86.ActiveCfg = Release|x86
47+
{555AD0AC-1FDB-4016-8257-170A74CB2F55}.Release|x86.Build.0 = Release|x86
1848
EndGlobalSection
1949
GlobalSection(SolutionProperties) = preSolution
2050
HideSolutionNode = FALSE
2151
EndGlobalSection
52+
GlobalSection(ExtensibilityGlobals) = postSolution
53+
SolutionGuid = {B3B5B4D8-74DD-4715-801C-1DC6DC1E2091}
54+
EndGlobalSection
2255
EndGlobal

Source/NoPowerShell/Commands/Core/GetCommandCommand.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,14 @@ public static string GetArguments(ArgumentList arguments)
106106

107107
public static new CaseInsensitiveList Aliases
108108
{
109-
get { return new CaseInsensitiveList() { "Get-Command" }; }
109+
get
110+
{
111+
return new CaseInsensitiveList()
112+
{
113+
"Get-Command",
114+
"help" // Unofficial
115+
};
116+
}
110117
}
111118

112119
public static new ArgumentList SupportedArguments

Source/NoPowerShell/Commands/Core/WhereObjectCommand.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public override CommandResult Execute(CommandResult pipeIn)
2020
// Obtain parameters
2121
string property = _arguments.Get<StringArgument>("Property").Value;
2222
bool eq = _arguments.Get<BoolArgument>("EQ").Value;
23+
bool ne = _arguments.Get<BoolArgument>("NE").Value;
2324
bool like = _arguments.Get<BoolArgument>("Like").Value;
2425
string value = _arguments.Get<StringArgument>("Value").Value;
2526

@@ -36,6 +37,10 @@ public override CommandResult Execute(CommandResult pipeIn)
3637
{
3738
foundValue = (tablevalue == checkvalue);
3839
}
40+
else if (ne)
41+
{
42+
foundValue = (tablevalue != checkvalue);
43+
}
3944
// Name -like "value"
4045
else if (like)
4146
{
@@ -79,6 +84,7 @@ public override CommandResult Execute(CommandResult pipeIn)
7984
{
8085
new StringArgument("Property"),
8186
new BoolArgument("EQ"),
87+
new BoolArgument("NE"),
8288
new BoolArgument("Like"),
8389
new StringArgument("Value")
8490
};

Source/NoPowerShell/Commands/DnsClient/ResolveDnsNameCommand.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,9 @@ public override CommandResult Execute(CommandResult pipeIn)
7777

7878
public static new string Synopsis
7979
{
80-
get {
81-
string[] recordTypes = new string[Dns.RecordTypes.Count];
82-
Dns.RecordTypes.Keys.CopyTo(recordTypes, 0);
83-
return string.Format("Resolve DNS name.\r\n Supported query types: {0}", string.Join(",", recordTypes));
80+
get
81+
{
82+
return string.Format("Resolve DNS name.");
8483
}
8584
}
8685

@@ -145,7 +144,14 @@ public static CommandResult GetRecords(string domain, string type)
145144
}
146145

147146
CommandResult results = new CommandResult();
148-
DnsRecordTypes queryType = (DnsRecordTypes)RecordTypes[type];
147+
object foundType = RecordTypes[type];
148+
if (foundType == null)
149+
{
150+
string[] types = new string[RecordTypes.Count];
151+
RecordTypes.Keys.CopyTo(types, 0);
152+
throw new NoPowerShellException(string.Format("Invalid type specified. Specify one of the following: {0}.", string.Join(",", types)));
153+
}
154+
DnsRecordTypes queryType = (DnsRecordTypes)foundType;
149155

150156
var recordsArray = IntPtr.Zero;
151157
try

0 commit comments

Comments
 (0)