Sitemap

IIS User Access Logging (UAL)

4 min readNov 2, 2025

--

Every now and then, I sign up for different offensive and defensive labs to level up my skills and try out tools I’m not familiar with. I like to ask tough questions and usually end up turning what I learn into scripts or automations.

What pushed me to finally write this post after a long quiet period was XintraLabs and the new WSUS (CVE-2025–59287) vulnerability.

In a perfect cybersecurity world — where SOC analysts have every Windows event log turned on, every EDR sensor working, no alert fatigue, perfect AI tools, and zero backend issues — some Windows artefacts wouldn’t really matter. But let’s be real… that world doesn’t exist (and probably won’t anytime soon).

How it started

Usually in lab exercises everything you need to solve the challenge is already there and pre-parsed, so you don’t have to do much work. Real-world cases are different: you need extra steps to collect and parse data, figure out what it means, and ask the right questions before you can solve the problem. When I read about the “Brickstorm” espionage campaign, Google Mandiant highlighted how useful Windows User Access Logging (UAL) was in their investigation. My immediate thought was: “How can I use those logs in my lab exercises — or in real incidents that involve WSUS exploitation?” While reading Microsoft’s User Access Logging article and the practical examples, I noticed that:

Press enter or click to view image in full size
https://learn.microsoft.com/en-us/iis/manage/configuring-security/analyzing-client-usage-data-with-iis-user-access-logging

That was my “A-ha” moment but then more questions popped up “what’s iisual.exe? Is it installed by default on every IIS server?” I looked in my lab and it wasn’t in %SystemRoot%\System32\inetsrv

They say devil is on details! At the bottom of the article “Analyzing Client Usage Data with IIS User Access Logging” there is another documentation about “Troubleshoot IIS performance issues or application errors using LogParser”

How it ended

Okay, I’m starting to see light at the end of the tunnel. The light, in my case, is Microsoft’s LogParser. I had the logs and I knew which tool to use, I just didn’t know how to use it :)

Get Tasos Chatziefstratiou’s stories in your inbox

Join Medium for free to get updates from this writer.

Reading the LogParser docs made me want a holiday, but ChatGPT/Copilot/Claude came to the rescue. After a few back-and-forth chats, I ended up with a PowerShell script that runs LogParser.exe and exports the results to CSV.

$src = 'C:\inetpub\logs\LogFiles\W3SVC1\*.log'
$out = 'C:\inetpub\logs\LogFiles\W3SVC1\iis_csv'
New-Item -ItemType Directory -Force -Path $out | Out-Null

Get-ChildItem $src | ForEach-Object {
$name = [System.IO.Path]::GetFileNameWithoutExtension($_.Name)
& "C:\Path\To\LogParser.exe" `
"SELECT date, time,[s-ip], [cs-method], [cs-version], [cs-username], [cs-uri-stem], [cs-uri-query],
[c-ip], [cs(User-Agent)], [cs(cookie)], [cs-host], [sc-status], [sc-substatus], [sc-win32-status], [cs-bytes], [sc-bytes], [time-taken]
INTO '$out\$name.csv'
FROM '$($_.FullName)'" `
-i:IISW3C -o:CSV -q:ON -headers:ON | Out-Null
}
# Merge all CSVs
Get-ChildItem "$out" -Filter *.csv | ForEach-Object {Import-Csv -Path $_.FullName} | Export-Csv "$out\merged.csv" -NoTypeInformation

Note: After installing LogParser, copy the executable in the same directory with the IIS logs.

Press enter or click to view image in full size
Log files parsed and exported as CSVs
Press enter or click to view image in full size
Examining merged.csv with Timeline Explorer

The main reason that I wanted to parse the IIS logs and save them as CSV was the visual reporting options this file format can provide if it’s combined with the right tooling. My number one preference when I work with data is the Pandas library.

import pandas as pd

df = pd.read_csv("merged.csv")
df["dt"] = pd.to_datetime(df["date"] + " " + df["time"], errors="coerce", utc=False)
df.columns = (
df.columns
.str.replace('-', '_', regex=False)
.str.replace('(', '', regex=False)
.str.replace(')', '', regex=False)
)

df["sc_status"] = pd.to_numeric(df["sc_status"], errors="coerce")
df["sc_substatus"] = pd.to_numeric(df["sc_substatus"], errors="coerce")
df["sc_win32_status"] = pd.to_numeric(df["sc_win32_status"], errors="coerce")
df["time_taken"] = pd.to_numeric(df["time_taken"], errors="coerce")

Closure

This ugly PowerShell script is far from perfect but can do the job quite well. In the future, I’ll focus on building meaningful visual reports that highlight anomalies and make complex insights easier to understand.

Thank you! :)

References

--

--

Tasos Chatziefstratiou
Tasos Chatziefstratiou

Written by Tasos Chatziefstratiou

We are slaves to the machines we build for ourselves.