Writing
penetration testing
25 articles on this subject.
Writing to __proto__: prototype pollution in Node.js and the lodash defaultsDeep bug
Prototype pollution lets an attacker set a property on every object in a Node process by writing through __proto__. CVE-2019-10744 in lodash is the canonical case. How the merge goes wrong, how to find it, and how it differs from Python class pollution.
9 min read
The cache said yes: web cache deception, cache poisoning, and CVE-2024-46982 in Next.js
Web cache deception stores a victim's private response for anyone to fetch; cache poisoning stores an attacker's response for everyone. Both come from the cache and the origin disagreeing about a URL. CVE-2024-46982 in Next.js is the current, well-documented case.
9 min read
One request, two lengths: HTTP request smuggling and the HAProxy Transfer-Encoding bug
CL.TE and TE.CL desync happen when two servers disagree about where a request ends. CVE-2019-18277 in HAProxy is the clean example: how the disagreement forms, how to find it safely, and how to shut it down.
8 min read
No 'Access-Control-Allow-Origin' header is present on the requested resource: the error is right, and both popular fixes are vulnerabilities
No 'Access-Control-Allow-Origin' header is present on the requested resource. The two fixes developers copy do not fix CORS, they delete it. One is account takeover.
13 min read
Non-human identity: 109 machine accounts per human, and the offboarding process that never runs
Service accounts, API keys, OAuth grants and CI/CD secrets hold broader access and longer-lived credentials than any employee would be permitted to carry, and none of them appear in an access review. Inventory, short-lived tokens, and the IAM calls that give the game away.
8 min read
Indirect prompt injection: EchoLeak, CVE-2025-32711, and the email nobody opened
A crafted email sitting unread in an inbox was enough to make Microsoft 365 Copilot exfiltrate internal data. The injectable surface is every document the model reads, and the control that bounds it is least privilege on tools.
7 min read
Your certificate authority is an authentication system: ESC1, the SID extension, and what changed in 2025
ESC1 turns a low-privileged domain account into a domain admin certificate. Strong certificate mapping was supposed to end that, and since September 2025 it cannot be switched off. Here is what still works, what does not, and how to check your own templates.
8 min read
Race conditions are not hard any more: the single-packet attack and what it broke
Web race conditions were dismissed as theoretical because network jitter made the window unhittable. Then HTTP/2 removed the jitter. The technique, the sub-states nobody models, and how to build code that cannot race.
7 min read
Linux capabilities: root privileges without the SUID bit, and the container escape they enable
Capabilities split root into 40+ pieces, and about a dozen of them are still root. Which ones matter, how CVE-2022-0492 turned a cgroup detail into a container escape, and how to audit a host and an image.
7 min read
Sudo is a shell in disguise: CVE-2025-32463, GTFOBins, and reading sudo -l properly
A 9.3 in sudo itself, plus twenty ways a single sudoers line hands over root. How to read sudo -l like an attacker, why the chroot bug worked, and what a safe sudoers policy looks like.
7 min read
ECB byte-at-a-time: recovering a secret you are never shown
If an application encrypts your input concatenated with a secret, and does it in ECB mode, you can extract that secret one character at a time. A complete walkthrough with runnable code. The clearest demonstration of why ECB is not encryption.
7 min read
Encrypted is not authenticated: padding oracles, ViewState, and why leaked machine keys became a 2025 problem
CBC without a MAC lets an attacker decrypt and forge ciphertext using nothing but the server's error behaviour. The mechanism from first principles, the Telerik and ASP.NET cases, and the one rule that removes the class.
7 min read
SSRF past the metadata endpoint: Docker on 2375, kubelet on 10250, and the 50,000 hosts TeamTNT found
IMDSv2 closed the famous SSRF target. The internal network is still full of unauthenticated control planes that will run a container for anyone who asks, and crypto-mining crews have been scanning for them since 2020.
7 min read
SSRF to cloud takeover: what Capital One taught us, and why half of EC2 still has not learned it
One HTTP request to 169.254.169.254 turned an SSRF into 100 million records. Seven years on, IMDSv2 fixes it and adoption is roughly half. How the attack works, why v2 stops it, and how to enforce it.
7 min read
JWT: alg confusion, kid injection, and the day Java accepted a signature of zero
A JSON Web Token is only as good as the verification code. Five failure modes with concrete examples, including CVE-2022-21449, where an all-zero ECDSA signature validated against any key on Java 15 through 18.
9 min read
Shadow APIs: the v1 endpoint nobody remembered to switch off
Every API inventory is a snapshot of what someone remembered to document. The gap between that list and what your servers actually answer is where breaches live, how to find zombie, shadow and orphaned endpoints, and how to stop creating them.
9 min read
GraphQL has no authorisation layer: aliases, batching, and the resolvers everyone forgets
GraphQL moves authorisation from the route to the resolver, and most teams only notice half of them. Introspection, alias multiplication, batched mutations against rate limits, and the complexity-DoS CVEs, with the fixes that actually hold.
8 min read
BOLA, BFLA, BOPLA: the three authorisation bugs no scanner will find for you
Optus lost roughly 10 million customer records to an API with sequential IDs and no authorisation check. Here is how to tell the three authorisation failures apart, how to test each one, and why automated tools are structurally blind to them.
7 min read
Prototype pollution has a Python cousin: class pollution and the road to __globals__
Python has no prototypes, so it cannot have prototype pollution, except it can. A walkthrough of class pollution: the vulnerable merge function, the four attribute chains that matter, and how to spot it in a code review.
7 min read
Argument injection: how a soft hyphen became CVE-2024-4577 and put 9.8 on the board
You escaped the shell metacharacters and the command still ran. Argument injection is the bug that survives shell-safe APIs, here is the mechanism, the PHP-CGI case that got mass-exploited, and the one-character fix.
7 min read
Pickle is a code format, not a data format: CVE-2025-32434 and the model file you just downloaded
torch.load(weights_only=True) was the recommended safe path, and it was bypassable. A practical look at Python deserialization, pickle, PyYAML and jsonpickle, how one gadget works, and what actually stops it.
12 min read
Server-side template injection: why {{7*7}} is a bad test, and how CVE-2023-22527 got to CVSS 10.0
Most SSTI write-ups teach one Jinja2 payload and stop. Here is the actual bug class, the per-engine probe table, a lab you can build in ten minutes, and the code-review rule that removes it entirely.
9 min read
MFA succeeded and the account still fell: adversary-in-the-middle phishing and session cookie theft
The phishing chain that still works against companies with MFA switched on. A reverse proxy sits in front of the real login page, the victim authenticates for real, and the attacker keeps the session cookie.
7 min read
An LLM is not a security boundary: building a moderation layer, then walking through it
We built a kids' chat app where a language model decides which messages are safe, then attacked the decision. Prompt injection is OWASP's number one LLM risk for the second edition running, and the reason is structural.
8 min read
Active Directory is walked, not exploited: BloodHound edges, Kerberoasting and ADCS ESC1
Domain compromise is almost never a memory-corruption bug. It is a graph traversal across permissions somebody granted on purpose. Here are the three routes that keep working, and what to check tonight.
8 min read