Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions Linux.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# .NET for Linux

.NET is supported on Linux through official Microsoft packages or manual installation. Installation instructions for supported operating systems and package management systems are available through the Microsoft .NET Core site.

General steps are as follows:

1) [Microsoft: Install .NET on Linux](https://docs.microsoft.com/en-us/dotnet/core/install/linux). For example, instructions are listed for [Debian](https://docs.microsoft.com/en-us/dotnet/core/install/linux) as well as other Linux operating systems.

2) Install the SDK, making sure to complete any pre-installation steps such as removing previous versions, adding package management repositories and signing keys, dependencies, etc.

3) Make note of any instructions to install other .NET versions other than the latest versiopn. For example, Microsoft states for [Debian](https://docs.microsoft.com/en-us/dotnet/core/install/linux):

> The packages added to package manager feeds are named in a hackable format, for example: {product}-{type}-{version}.
>
> ...
>
> - version
>
> The version of the SDK or runtime to install. This article will always give the instructions for the latest supported version. Valid options are any released version, such as:
>
> - 5.0
> - 3.1
> - 3.0
> - 2.1
>
> ...
>
> Install the .NET Core 3.1 SDK: `dotnet-sdk-3.1`

4) Check installed .NET versions after installation is completed, for example:

```$``` ```dotnet --list-sdks```

```3.1.420 [/usr/share/dotnet/sdk]```


# Linux Support
The source has been minimally refactored to support Linux paths and file system operations. The tool will also create a backup of the original project file, for example, `Payload.csproj.orig` to avoid inadvertedly globbering it.

The tool now supports the replacing the target framework moniker (TFM) within the templates SDK-style project files and is specifically hardcoded to look for `Payload.csproj`. This is implemented as an optional argument `-targetframework TFM`. By default, it will use `net45` to support the versions in the original template project files (backward compatibility).

**The `net45` version will fail on Linux since it is not a supported .NET version on Linux.**

For Linux, simply use the `-targetframework` command line argument to align with the installed version. This will modify the specified template's `TargetVersion` tag. For example, `-targetframework netcoreapp3.1` will read `Payload.csproj.orig`, replace the string, then create a new version of `Payload.csproj` with the .NET Core 3.1 SDK string `netcoreapp3.1`.

## Example

After installing .NET on Linux then executing `python3 nsgencs.py` with the proper arguments, an error will likely be thrown resembling the following:

```$``` ```python3 NSGenCS.py -file msgbox.cs -method xor -template Thread_Hijack -key 22```
```
...

> Creating encoded shellcode from CS file
> Generating payload
> Cleanup
Microsoft (R) Build Engine version 16.7.2+b60ddb6f4 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

Determining projects to restore...
Restored /home/user/NSGenCS/Thread_Hijack/Payload.csproj (in 92 ms).
/usr/share/dotnet/sdk/3.1.420/Microsoft.Common.CurrentVersion.targets(1177,5): error MSB3644: The reference assemblies for .NETFramework,Version=v4.5 were not found. To resolve this, install the Developer Pack (SDK/Targeting Pack) for this framework version or retarget your application. You can download .NET Framework Developer Packs at https://aka.ms/msbuild/developerpacks [/home/user/NSGenCS/Thread_Hijack/Payload.csproj]
```

In the above example, the .NET Core 3.1 SDK has been installed but `dotnet` is failing due to target version defined in the project file.

To resolve, use the `-targetframework`.

```$``` ```python3 NSGenCS.py -file msgbox.cs -method xor -template Thread_Hijack -key 22 -targetframework netcoreapp3.1```

```
...

> Creating encoded shellcode from CS file
> Generating payload
> Cleanup
Microsoft (R) Build Engine version 16.7.2+b60ddb6f4 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

Determining projects to restore...
Restored /home/user/NSGenCS/Thread_Hijack/Payload.csproj (in 141 ms).
Payload -> /home/user/NSGenCS/Thread_Hijack/bin/Release/netcoreapp3.1/win10-x64/Payload.dll
Payload -> /home/user/NSGenCS/Thread_Hijack/bin/Release/netcoreapp3.1/win10-x64/publish/

If you didn't see a bunch of red lines before this message, you should see payload.exe now :)
```

## Additional Information

More information on the TFMs can be found on Microsoft's site, [Target frameworks in SDK-style projects](https://docs.microsoft.com/en-us/dotnet/standard/frameworks).

More information on the the project file syntax can be found on Microsoft's site, [Target Framework Monikers define build time APIs](https://docs.microsoft.com/en-us/dotnet/core/versions/selection#target-framework-monikers-define-build-time-apis)
115 changes: 84 additions & 31 deletions NSGenCS.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import subprocess
import argparse
import random
import re
import shutil
import string
import sys
import os
Expand All @@ -20,12 +22,12 @@ def generateEncodedShell(encoder, key, file, sdir):
shellcode = open(file, "r")
shelcode_data = shellcode.read()
# Open the original C# Code for the encoder
full_template_path = "{0}/{1}/template".format(base_dir, sdir)
full_template_path = f"{base_dir}{os.sep}{sdir}{os.sep}template"
original = open(full_template_path, "r")
data = original.read()

# Grab the encryption code to place in template
encryption_template = "{0}/{1}/encrypt.txt".format(base_dir, encoder)
encryption_template = f"{base_dir}{os.sep}{encoder}{os.sep}encrypt.txt"
encryptfile = open(encryption_template,"r")
encryptcode = encryptfile.read()

Expand All @@ -38,7 +40,7 @@ def generateEncodedShell(encoder, key, file, sdir):
data = data.replace("KEYHERE", key)

# Open a new file and write the replaced text here
temp_tempate_path = "{0}/{1}/Program.cs".format(base_dir, sdir)
temp_tempate_path = f"{base_dir}{os.sep}{sdir}{os.sep}Program.cs"
template = open(temp_tempate_path, "w+")
template.write(data)

Expand All @@ -50,23 +52,41 @@ def generateEncodedShell(encoder, key, file, sdir):
else:
print("[-] Shellcode path is not exists, please check it!")

def generatePayload(encoder, template_directory, key, sdir):
def generatePayload(encoder, template_directory, key, sdir, target_framework):
# Backup the original project file (Payload.csproj.orig) and replace the active Payload.csproj TargetFramework value
csproj = f"{base_dir}{os.sep}{template_directory}{os.sep}Payload.csproj"
if not os.path.exists(csproj + ".orig"):
with open(csproj + ".orig", "w") as orig:
with open(csproj, "r") as active:
data = active.read()
orig.write(data)
else:
with open(csproj + ".orig", "r") as orig:
data = orig.read()
data = re.sub("<TargetFramework>[^<]+</TargetFramework>", f"<TargetFramework>{target_framework}</TargetFramework>", data)
with open(csproj, "w") as active:
active.write(data)

# Build the encrypted version of the shellcode and save it to output.shellcode
build_command = "cd {0}/{1}/ && dotnet run > output.shellcode".format(base_dir, sdir)
os.system(build_command)
build_command = ["dotnet", "run"]
os.chdir(f"{base_dir}{os.sep}{sdir}")

proc = subprocess.run(build_command, capture_output=True)
with open("output.shellcode", "w") as fp:
fp.write(proc.stdout.decode())

# Open the shellcode
shellcode_path = "{0}/{1}/output.shellcode".format(base_dir, sdir)
shellcode_path = f"{base_dir}{os.sep}{sdir}{os.sep}output.shellcode"
shellcode = open(shellcode_path,"r")
shelcode_data = shellcode.read()

# Open the original C# code for the payload
original_code_path = "{0}/{1}/template".format(base_dir, template_directory)
original_code_path = f"{base_dir}{os.sep}{template_directory}{os.sep}template"
original = open(original_code_path, "r")
data = original.read()

# Grab the decryption code to place in template
decryption_code_path = "{0}/{1}/decrypt.txt".format(base_dir, encoder)
decryption_code_path = f"{base_dir}{os.sep}{encoder}{os.sep}decrypt.txt"
decrypt_file = open(decryption_code_path,"r")
decrypt_code = decrypt_file.read()

Expand All @@ -78,38 +98,53 @@ def generatePayload(encoder, template_directory, key, sdir):
if key != "false":
data = data.replace("KEYHERE", key)
original.close()
tmp_tempate_path = "{0}/{1}/Program.cs".format(base_dir, template_directory)
tmp_tempate_path = f"{base_dir}{os.sep}{template_directory}{os.sep}Program.cs"
template = open(tmp_tempate_path, "w+")
template.write(data)
shellcode.close()
decrypt_file.close()

def cleanUp(encoder,template_directory,outfile,sdir):
def cleanUp(encoder,template_directory,outfile,sdir,target_framework):
#TO DO improve cleaning up of files
compile_command = "cd {0}/{1} && dotnet publish -c Release -r win10-x64".format(base_dir, template_directory)
os.system(compile_command)
compile_command = ["dotnet", "publish", "-c", "Release", "-r", "win10-x64"]
os.chdir(f"{base_dir}{os.sep}{template_directory}")
_ = subprocess.run(compile_command)

copy_command = r"copy {}\{}\bin\Release\net45\win10-x64\payload.exe {}\{} /Y".format(base_dir, template_directory, base_dir, outfile)
os.system(copy_command)
#copy_command
src = f"{base_dir}{os.sep}{template_directory}{os.sep}bin{os.sep}Release{os.sep}{target_framework}{os.sep}win10-x64{os.sep}payload.exe"
dst = f"{base_dir}{os.sep}{outfile}"
if os.path.exists(src):
shutil.copyfile(src, dst)


time.sleep(0.5)

if not args.noclean:
delete_template_command = r"del {}\{}\Program.cs && del {}\{}\Program.cs && rd /s/q {}\{}\bin".format(base_dir,template_directory,base_dir,sdir,base_dir,template_directory)
os.system(delete_template_command)

delete_shellcode_command = "del {}\{}\output.shellcode".format(base_dir, sdir)
os.system(delete_shellcode_command)

delete_bin_directory = r"rd /s/q {}\{}\bin".format(base_dir, sdir)
os.system(delete_bin_directory)

delete_obj_directory_command = r"rd /s/q {}\{}\obj".format(base_dir,sdir)
os.system(delete_obj_directory_command)

delete_template_directory_command = r"rd /s/q {}\{}\obj".format(base_dir, template_directory)
os.system(delete_template_directory_command)
# delete template
os.remove(f"{base_dir}{os.sep}{template_directory}{os.sep}Program.cs")
os.remove(f"{base_dir}{os.sep}{sdir}{os.sep}Program.cs")
# delete shellcode
os.remove(f"{base_dir}{os.sep}{sdir}{os.sep}output.shellcode")

try:
shutil.rmtree(f"{base_dir}{os.sep}{template_directory}{os.sep}bin")
except FileNotFoundError:
pass
try:
# delete bin directory
shutil.rmtree(f"{base_dir}{os.sep}{sdir}{os.sep}bin")
except FileNotFoundError:
pass
try:
# delete obj directory
shutil.rmtree(f"{base_dir}{os.sep}{sdir}{os.sep}obj")
except FileNotFoundError:
pass
try:
# delete template directory
shutil.rmtree(f"{base_dir}{os.sep}{template_directory}{os.sep}obj")
except FileNotFoundError:
pass
else:
print("Files not cleaned as -noclean flag present")

Expand Down Expand Up @@ -172,6 +207,24 @@ def cleanUp(encoder,template_directory,outfile,sdir):
required=False,
)

# https://docs.microsoft.com/en-us/dotnet/standard/frameworks
target_frameworks = ["netcoreapp1.0", "netcoreapp1.1", "netcoreapp2.0", "netcoreapp2.1",
"netcoreapp2.2", "netcoreapp3.0", "netcoreapp3.1", "net5.0", "net6.0",
"netstandard1.0", "netstandard1.1", "netstandard1.2", "netstandard1.3",
"netstandard1.4", "netstandard1.5", "netstandard1.6", "netstandard2.0",
"netstandard2.1", "net11", "net20", "net35", "net40", "net403", "net45",
"net451", "net452", "net46", "net461", "net462", "net47", "net471",
"net472", "net48"]
requiredNamed.add_argument(
"-targetframework",
dest="target_framework",
help="Override the target framework moniker in Payload.csproj project file",
required=False,
metavar="TFM",
choices=target_frameworks,
default="net45"
)

args = parser.parse_args()

#TO DO - check encrypt/decrypt file in method folder for presence of KEYHERE. If present and args.key = false then break and alert.
Expand All @@ -196,8 +249,8 @@ def cleanUp(encoder,template_directory,outfile,sdir):
print("> Creating encoded shellcode from CS file")
generateEncodedShell(args.method,args.key,args.file, args.shelldir)
print("> Generating payload")
generatePayload(args.method,args.templatedir,args.key, args.shelldir)
generatePayload(args.method,args.templatedir,args.key, args.shelldir, args.target_framework)
print("> Cleanup")
cleanUp(args.method, args.templatedir, args.out, args.shelldir)
cleanUp(args.method, args.templatedir, args.out, args.shelldir, args.target_framework)
print("\nIf you didn't see a bunch of red lines before this message, you should see " + args.out + " now :)")
exit()