Intro
Did you know you can easily find all your internet downloaded files on Windows and the website links they came from even if you cleared your browser history?
Windows uses “tag” attributes called Zone Identifiers, which are a feature in Windows that assigns values between 0-4 (by default) to files in order to track their origin. These values are then stored in Alternate Data Streams (ADS) and are used by the operating system to determine the appropriate level of security restrictions for the file.
Before we can dive into how to find all internet downloaded files, two topics need to be explained in some detail: Alternate Data Streams and URL Security Zones.
Alternate Data Streams
So what is an Alternate Data Stream (ADS)? Alternate Data Streams are a feature of the NTFS file system used by Windows OS that allow multiple data streams to be associated with a single file. An existing file is referred to as the ‘Main Stream‘.
Allow me to explain what this means in simpler terms. Take, for instance, these 2 files:


Let’s take a note of their hash values:

Using the built-in Windows Expand utility, we can write the contents of ‘Hidden_Data.txt’ as an Alternate Data Stream to the existing/main file stream for ‘Normal_File.txt’ with a new name of ‘not_suspicious_at_all’.

With the content copied over, removing ‘Hidden_Data.txt’ has no affect on the data copied as the contents are written into the ADS of ‘Normal_File.txt’ now. What is more interesting, despite having content written to the ADS, the hash of ‘Normal_File.txt’ does not change!


Notice above that there are 2 identified streams: $DATA and our new stream ‘not_suspicious_at_all’. $DATA is a stream type that is used in NTFS file systems to store data in a file. The NTFS file system uses streams to store different types of data in a single file, such as the file’s main data and its attributes. However, we want to dive into the content of the newly created stream.

Voila! We can see the “hidden” content! For those that aren’t familiar, the content is simply base64 encoded by my own doing and has nothing to do with the way Windows handles stream content.
So…How does this exactly relate to internet downloads? Here is the takeaway.
There is another stream that can be found in files called ‘Zone.Identifier’, similar to how $DATA was previously shown. In the context of Zone Identifiers, files that are downloaded from the Internet are usually tagged with a Zone Identifier that indicates their origin, such as the Internet. This tag is separate and distinct from the file’s $DATA stream, which contains the file’s main data. Let’s specifically highlight the significance of the “Internet Zone” in the context of the term commonly known as the “Mark of the Web”, which is represented by Zone Identifier value of 3.

The values of the Zone ID are the cause for Microsoft Office to occasionally open files in “Protected View.”

URL Security zones
Now that we covered what an Alternate Data Stream is, we can go over the default URL Security Zones. It is important to note that incident response teams can extract properties from Alternate Data Streams (ADS) to gather relevant information, such as the ZoneId, to assist in investigations. These details can prove to be very valuable.
Default URL Security Zones | Zone Identifier Value | Description |
Local Machine Zone | 0 | The Local Machine zone is an implicit zone for content that exists on the local computer. The content found on the user’s computer (except for content that Internet Explorer caches on the local system) is treated with a high level of trust. |
Local Intranet Zone | 1 | Use the Local Intranet zone for content located on an organization’s intranet. Because the servers and information are within an organization’s firewall, it is reasonable to assign a higher trust level to content on the intranet. |
Trusted Sites Zone | 2 | Use the Trusted Sites zone for content located on Web sites that are considered more reputable or trustworthy than other sites on the Internet. Assigning a higher trust level to these sites minimizes the number of authentication requests. The user adds the URLs of these trusted Web sites to this zone. |
Internet Zone | 3 | Use the Internet zone for Web sites on the Internet that do not belong to another zone. This default setting causes Windows Internet Explorer to prompt the user whenever potentially unsafe content is about to download. Note: Web sites that are not mapped into other zones automatically fall into this zone. |
Restricted Sites Zone | 4 | Use the Restricted Sites zone for Web sites that contain content that can cause (or have previously caused) problems when downloaded. Use this zone to cause Internet Explorer to alert that potentially-unsafe content is about to download, or to prevent that content from downloading. The user adds the URLs of these untrusted Web sites to this zone. |
Putting it all together
Now for the best part. Proof of concept. Let’s use PowerShell to iterate over every single file on the system, find internet-sourced files, and then collect as much information about those files as we can.
PowerShell is highly effective in how it can interact with and do almost any task in Windows. For this reason, it is quite useful to use to extract Zone.Identifier
and file property information into a CSV file to examine further.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# output file
$csvFile = 'C:\temp\found_internet_downloaded_files.csv'
# function to make file size calculation pretty
function Get-FormattedByteSize {
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline=$true)]
[double]$ByteSize
)
$SizeUnits = @("bytes", "KB", "MB", "GB", "TB", "PB")
$ByteSize | ForEach-Object {
$UnitIndex = 0
$Size = [math]::Round($_, 2)
while ($Size -ge 1KB) {
$Size = $Size / 1KB
$UnitIndex++
}
"{0:N2} {1}" -f $Size, $SizeUnits[$UnitIndex]
}
}
# array to hold findings
$results = @()
# initial query
Get-ChildItem C:\ -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object {
$content = Get-Content -Path $_.FullName -Stream Zone.Identifier -ErrorAction SilentlyContinue
if ($content) {
$properties = @{
ZoneId = ''
Path = ''
HostUrl = ''
ReferrerUrl = ''
FileSHA256 = ''
LastCreateTime = ''
LastWriteTime = ''
LastAccessTime = ''
FileSize = ''
}
$zoneId3Found = $false
foreach ($line in $content) {
if ($line -match '^ZoneId=(\d+)') {
$properties['ZoneId'] = $matches[1]
if ($properties['ZoneId'] -eq 3) {
$zoneId3Found = $true
}
}
if ($zoneId3Found) {
if ($line -match '^ReferrerUrl=(.+)') {
$properties['ReferrerUrl'] = $matches[1]
}
if ($line -match '^HostUrl=(.+)') {
$properties['HostUrl'] = $matches[1]
}
}
}
if ($zoneId3Found) {
$properties['Path'] = $_.FullName
$properties['LastCreateTime'] = $_.CreationTime
$properties['LastWriteTime'] = $_.LastWriteTime
$properties['LastAccessTime'] = $_.LastAccessTime
$properties['FileSize'] = (Get-Item -Path $_.FullName).Length
$fileHash = Get-FileHash -Path $properties['Path'] -Algorithm SHA256
$properties['FileSHA256'] = $fileHash.Hash
$results += New-Object -TypeName PSObject -Property @{
Path = $properties['Path']
ZoneId = $properties['ZoneId']
ReferrerUrl = $properties['ReferrerUrl']
HostUrl = $properties['HostUrl']
FileSHA256 = $properties['FileSHA256']
LastCreateTime = $properties['LastCreateTime']
LastWriteTime = $properties['LastWriteTime']
LastAccessTime = $properties['LastAccessTime']
FileSize = Get-FormattedByteSize -ByteSize (Get-Item -Path $_.FullName).Length
}
}
}
}
# output results
$results | Select-Object ZoneId, FileSize, Path, ReferrerUrl, HostUrl, LastWriteTime, LastCreateTime, FileSHA256, LastAccessTime | Export-Csv -Path $csvFile -NoTypeInformation
This will create a CSV file at ‘C:\temp\found_internet_downloaded_files.csv
‘ that includes file property timestamps, current file locations, file hashes, file sizes, download links, site referrals, and the Zone Identifier value for reference. It could take some time to pull this information, but this typically averages about 5-10 minutes for a typical workstation and proves invaluable.
ZoneId | FileSize | Path | ReferrerUrl | HostUrl | LastWriteTime | LastCreateTime | FileSHA256 | LastAccessTime |
3 | 2.92 GB | C:\Users\testuser\VMs\kali-linux-2021.3-installer-amd64.iso | https://www.kali.org/ | https://kali.download/base-images/kali-2021.3/kali-linux-2021.3-installer-amd64.iso | < output > | < output > | < output > | < output > |
3 | 1.91 MB | C:\temp\malware.js | https://mail.google.com/ | https://mail-attachment.googleusercontent.com/attachment/u … | < output > | < output > | < output > | < output > |
Conclusion
Zone Identifiers are a crucial part of the incident response process and help teams quickly identify necessary information when it comes to malware and identifying source attributions.
Alternate data streams are often abused by cyber criminals as attacks involving ADS can be difficult to detect. Having the ability to mask binaries, hidden data, scripts, or command content allows a wide range of potential for abuse. Using ADS makes for an interesting number of methods that adversaries could use to bypass many detections and stay under the radar.