September 19, 2026
Beating a Filter That Only Checks Once: Traversal Sequences Stripped Non-Recursively
Lab ten in my Web Security Academy series, and the third path traversal lab in a row, each one built on top of the last

By Kaustubh Asthana
3 min read
The Pattern So Far
Lab eight had no filter at all. Lab nine blocked traversal sequences, but an absolute path slipped past it untouched. This lab blocks traversal sequences too, and this time absolute paths don't work either. The developers closed both of the earlier holes. There's still a third one.
What Non-Recursive Stripping Means
The filter here removes traversal sequences from the input. That sounds like a real fix, and against a simple payload, it is one. The catch is how the removal happens: it runs once, a single pass over the string, not repeatedly until nothing is left to remove.
That single pass is the whole vulnerability. If a payload is built so that removing one sequence from the middle of it leaves a new, working sequence behind, the filter never gets a second look. It already did its one job and moved on.
Ruling Things Out First
I found the same kind of endpoint as the last two labs, an image loaded through a filename parameter, using Burp's filtered history to skip past the clutter.
From there I tested the two techniques that had worked in earlier labs. A plain traversal payload, climbing back toward the root, came back as a bad request with a not found message. The filter caught it.
Next, the absolute path trick that had bypassed the previous lab's filter entirely. Same result. Rejected.
Two known techniques, two dead ends. That ruled out an unfiltered app and ruled out a filter that only checks for relative sequences while ignoring absolute ones. Something else was going on.
The Payload That Worked
If the filter runs once and then trusts its own output, the fix is to feed it something that isn't finished being dangerous after just one pass. A sequence like four dots followed by two slashes contains a working traversal sequence buried inside it. Strip that inner piece out, and what's left over reassembles into the exact traversal sequence the filter was trying to block in the first place.
Chaining three of those together, then landing on the target file, went straight through. The response came back with the full file listing.
The lab marked itself solved.
Why This Matters
A filter that removes a bad pattern is only as strong as its assumption that removal is final. Here, removal created something new, because the "bad" substring and the surrounding characters were chosen specifically to interact that way. The fix isn't wrong in what it removes. It's wrong in stopping after doing it once.
The durable version of this defense re-checks its own output. Strip the pattern, then check again, and keep checking until a full pass finds nothing left to remove. Anything short of that leaves the same kind of gap this lab is built around.
Takeaways From Lab Ten
- A filter's first pass is not automatically its last word. Test what its own output looks like.
- Ruling out known techniques first, plain traversal, then absolute paths, is useful groundwork, not wasted effort. It narrows down which specific weakness you're dealing with.
- Removing a harmful pattern only works if removal is applied until none of the pattern remains, not just once.