first commit
This commit is contained in:
282
Invoke-Stealth/Resources/PyFuscation/PyFuscation.py
Normal file
282
Invoke-Stealth/Resources/PyFuscation/PyFuscation.py
Normal file
@@ -0,0 +1,282 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
'''
|
||||
|
||||
PyFuscation.py
|
||||
This python3 script obfuscates powershell function, variable and parameters in an attempt to bypass AV blacklists
|
||||
|
||||
'''
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import ast
|
||||
import time
|
||||
import shutil
|
||||
import random
|
||||
import subprocess
|
||||
import shlex
|
||||
import string
|
||||
from argparse import ArgumentParser
|
||||
import configparser
|
||||
import fileinput
|
||||
|
||||
def printR(out): print("\033[91m{}\033[00m" .format("[!] " + out))
|
||||
def printG(out): print("\033[92m{}\033[00m" .format("[*] " + out))
|
||||
def printY(out): print("\033[93m{}\033[00m" .format("[+] " + out))
|
||||
def printP(out): print("\033[95m{}\033[00m" .format("[-] " + out))
|
||||
|
||||
def realTimeMuxER(command):
|
||||
p = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
|
||||
while True:
|
||||
output = p.stdout.readline().decode()
|
||||
if output == '' and p.poll() is not None:
|
||||
break
|
||||
if output:
|
||||
print(output.strip())
|
||||
rc = p.poll()
|
||||
|
||||
def removeJunk(oF):
|
||||
# general cleanup
|
||||
cmd = "sed -i -e \'/<#/,/#>/c\\\\\' " + "'" + oF + "'"
|
||||
realTimeMuxER(cmd)
|
||||
cmd = "sed -i -e \'s/^[[:space:]]*#.*$//g\' " + "'" + oF + "'"
|
||||
realTimeMuxER(cmd)
|
||||
cmd = "sed -i \'/^$/d\' " + "'" + oF + "'"
|
||||
realTimeMuxER(cmd)
|
||||
|
||||
def useSED(DICT, oF):
|
||||
for var in DICT:
|
||||
new = str(DICT.get(var))
|
||||
cmd = "sed -i -e \'s/" + var +'\\b' + "/" + new + "/g\' " + "'" + oF + "'"
|
||||
realTimeMuxER(cmd)
|
||||
|
||||
def THEreplacER(DICT, iF, oF):
|
||||
iFHandle = open(iF, 'r')
|
||||
ofHandle = open(oF, 'w')
|
||||
regex = r'(\$\w{3,})'
|
||||
lower_DICT = list(map(lambda x:x.lower(),DICT))
|
||||
# For var replace with Dictionary value
|
||||
for line in iFHandle:
|
||||
v = re.findall(regex,line)
|
||||
if not v:
|
||||
#print("Not: " + line)
|
||||
ofHandle.write(line + "\n")
|
||||
ofHandle.flush()
|
||||
else:
|
||||
for var in v:
|
||||
if var.lower() in lower_DICT:
|
||||
new = str(DICT.get(var))
|
||||
#print("Replacing " + var + " with " + new)
|
||||
ofHandle.write(line.replace(var, new) + "\n")
|
||||
ofHandle.flush()
|
||||
else:
|
||||
#print(var)
|
||||
ofHandle.write(line + "\n")
|
||||
ofHandle.flush()
|
||||
|
||||
iFHandle.close()
|
||||
ofHandle.close()
|
||||
|
||||
def findCustomParams(iFile,oFile,VARs):
|
||||
PARAMs = {}
|
||||
READ = False
|
||||
start = 0
|
||||
end = 0
|
||||
regex = r'([\$-]\w{4,})'
|
||||
ofHandle = open(oFile, 'w')
|
||||
|
||||
with open(iFile, "r") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
|
||||
if re.search(r'\bparam\b', line, re.I):
|
||||
# Ok we are at the begining of a custum parameter
|
||||
READ = True
|
||||
|
||||
# The open paren is on another line so move until we find it
|
||||
start = start + line.count('(')
|
||||
if start == 0:
|
||||
continue
|
||||
|
||||
end = end + line.count(')')
|
||||
|
||||
v = re.findall(regex,line)
|
||||
for i in v:
|
||||
if i.lower() not in lower_Reserverd and i not in PARAMs:
|
||||
# Lets check to see if this has been replaced already
|
||||
new = VARs.get(i)
|
||||
if not new:
|
||||
continue
|
||||
new = " -" + new[1:]
|
||||
old = " -" + i[1:]
|
||||
PARAMs[old] = new
|
||||
ofHandle.write("Replacing: " + old + " with: " + new + "\n")
|
||||
|
||||
# If the params are all on one line were done here
|
||||
if start != 0 and start == end:
|
||||
start = 0
|
||||
end = 0
|
||||
READ = False
|
||||
continue
|
||||
|
||||
# These are the custom parameters
|
||||
elif READ:
|
||||
v = re.findall(regex,line)
|
||||
for i in v:
|
||||
if i.lower() not in lower_Reserverd and i not in PARAMs:
|
||||
new = VARs.get(i)
|
||||
if not new:
|
||||
continue
|
||||
new = " -" + new[1:]
|
||||
old = " -" + i[1:]
|
||||
PARAMs[old] = new
|
||||
ofHandle.write("Replacing: " + old + " with: " + new + "\n")
|
||||
|
||||
start = start + line.count('(')
|
||||
end = end + line.count(')')
|
||||
if start != 0 and start == end:
|
||||
start = 0
|
||||
end = 0
|
||||
READ = False
|
||||
|
||||
# Keep moving until we have work
|
||||
else:
|
||||
continue
|
||||
|
||||
printY("Parameters Replaced : " + str(len(PARAMs)))
|
||||
|
||||
return PARAMs
|
||||
|
||||
def findVARs(iFile,lFile):
|
||||
VARs = {}
|
||||
vNum = 9999
|
||||
regex = r'(\$\w{6,})'
|
||||
ofHandle = open(lFile, 'w')
|
||||
|
||||
with open(iFile, "r") as f:
|
||||
for line in f:
|
||||
v = re.findall(regex,line)
|
||||
for i in v:
|
||||
if i in VARs:
|
||||
continue
|
||||
elif i.lower() not in lower_Reserverd:
|
||||
# Powershell vars are case insensitive
|
||||
lowerVARS = {k.lower(): v for k, v in VARs.items()}
|
||||
if i.lower() in lowerVARS:
|
||||
new = lowerVARS.get(i.lower())
|
||||
ofHandle.write("Replacing: " + i + " with: " + new + "\n")
|
||||
VARs[i] = new
|
||||
else:
|
||||
vNum = 99
|
||||
new = "$" + ''.join([random.choice(string.ascii_letters) for n in range(8)])
|
||||
VARs[i] = new + str(vNum)
|
||||
ofHandle.write("Replacing: " + i + " with: " + new + "\n")
|
||||
vNum += 1
|
||||
|
||||
# return dict of variable and their replacements
|
||||
printY("Variables Replaced : " + str(len(VARs)))
|
||||
return VARs
|
||||
|
||||
def findFUNCs(iFile,lFile):
|
||||
|
||||
FUNCs = {}
|
||||
ofHandle = open(lFile, 'w')
|
||||
with open(iFile, "r") as f:
|
||||
for line in f:
|
||||
funcMatch = re.search(r'^\s*Function ([a-zA-Z0-9_-]{6,})[\s\{]+$',line, re.IGNORECASE)
|
||||
if funcMatch and funcMatch.group(1) not in FUNCs:
|
||||
if funcMatch.group(1) == "main":
|
||||
continue
|
||||
vNum = 9999
|
||||
new = randomString(wordList)
|
||||
FUNCs[funcMatch.group(1)] = new
|
||||
ofHandle.write("Replacing: " + funcMatch.group(1) + " with: " + str(new) + "\n")
|
||||
vNum += 1
|
||||
# return dict of variable and their replacements
|
||||
printY("Functions Replaced : " + str(len(FUNCs)))
|
||||
return FUNCs
|
||||
|
||||
def randomString(iFile):
|
||||
with open(iFile, "r") as f:
|
||||
line = next(f)
|
||||
for num, aline in enumerate(f, 2):
|
||||
if random.randrange(num): continue
|
||||
line = aline
|
||||
string = ''.join(e for e in line if e.isalnum())
|
||||
return string
|
||||
|
||||
def main():
|
||||
iFile = args.script
|
||||
|
||||
printR("Obfuscating: " + iFile)
|
||||
oDir = os.getcwd() + "/Resources/PyFuscation/tmp"
|
||||
os.mkdir( oDir );
|
||||
oFile = oDir + "/" + "script.ps1"
|
||||
vFile = oDir + "/" + "vFile.ps1"
|
||||
fFile = oDir + "/" + "fFile.ps1"
|
||||
pFile = oDir + "/" + "pFile.ps1"
|
||||
shutil.copy(args.script, oFile)
|
||||
|
||||
obfuVAR = dict()
|
||||
obfuPARMS = dict()
|
||||
obfuFUNCs = dict()
|
||||
|
||||
# Remove White space and comments
|
||||
removeJunk(oFile)
|
||||
|
||||
# Obfuscate Variables
|
||||
if (args.var):
|
||||
obfuVAR = findVARs(iFile,vFile)
|
||||
useSED(obfuVAR, oFile)
|
||||
|
||||
# Obfuscate custom parameters
|
||||
if (args.par):
|
||||
obfuPARMS = findCustomParams(iFile, pFile, obfuVAR)
|
||||
useSED(obfuPARMS, oFile)
|
||||
|
||||
# Obfuscate Functions
|
||||
if (args.func):
|
||||
obfuFUNCs = findFUNCs(iFile, fFile)
|
||||
useSED(obfuFUNCs, oFile)
|
||||
|
||||
# Print the Functions
|
||||
print("")
|
||||
printP("Obfuscated Functions located : " + fFile)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
if sys.version_info <= (3, 0):
|
||||
sys.stdout.write("This script requires Python 3.x\n")
|
||||
sys.exit(1)
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument("-f", dest="func", help="Obfuscate functions", action="store_true")
|
||||
parser.add_argument("-v", dest="var", help="Obfuscate variables", action="store_true")
|
||||
parser.add_argument("-p", dest="par", help="Obfuscate parameters", action="store_true")
|
||||
parser.add_argument("--ps", dest="script", help="Obfuscate powershell")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Powershell script
|
||||
if (args.script is None):
|
||||
parser.print_help()
|
||||
exit()
|
||||
else:
|
||||
# Check if the input file is valid:
|
||||
if not os.path.isfile(args.script):
|
||||
printR("Check File: " + args.script)
|
||||
exit()
|
||||
else:
|
||||
PSconfigFile = os.path.abspath(os.path.dirname(__file__)) + "/PSconfig.ini"
|
||||
config.read(PSconfigFile)
|
||||
Reseverd = ast.literal_eval(config.get("PS_Reserverd", "f"))
|
||||
lower_Reserverd = list(map(lambda x:x.lower(),Reseverd))
|
||||
|
||||
wordList = os.path.abspath(os.path.dirname(__file__)) + "/Wordlist.txt"
|
||||
if not os.path.isfile(wordList):
|
||||
printR("Check wordList: " + wordList)
|
||||
exit()
|
||||
|
||||
main()
|
||||
Reference in New Issue
Block a user