Like it or not, developing an attack plan for a penetration test requires standing up a rudimentary threat model. Not surprisingly, threat modeling often produces discomfort in the stomach and uncertainty in the heart of many testers, but you’ve got to cowboy up and do it.

You need to determine where you’re going to spend your limited testing time, which tools you want to pull out of your arsenal, and how to prioritize your findings after the test is complete. Testing an application while blind to the context of how it could be abused in a real world environment is a sure-fire recipe for disaster and embarrassment.
Start with an application overview sitting peacefully on your desk. A sudden bang, a quick diversion, a cloud of smoke - and a test plan appears in its place. Fear no more, for the guide to ninja threat modeling is here.
You’ve probably heard of threat models before, especially in traditional security development lifecycle circles. In that context, the threat models are generated in the requirements/design phase, which is much earlier in the process. It has spawned not just one, but two, Visio-driven tool sets from Microsoft and countless data-flow diagrams, attack trees, consulting engagements, and perplexed developers. When performed by a skilled and experienced team member, the model can be used to identify architectural weaknesses, guide default application behavior, and outline functional requirements for the product.
However, by the time you’re in the verification phase of the lifecycle, generating a formal threat model starts to yield diminishing returns. We recommend taking a short-cut with a set of assumptions that have served us well over the years.
Client-side code assumptions
- The attacker will have unfettered access to the client and will instrument all of the functionality (this includes the “secret” admin and debug functions), and all of the client-side controls will be removed.
- If it accesses the network, there’s a man-in-the-middle.
- All input will be malformed and unexpected - even from the trusted end user: who will happily introduce input from untrusted sources.
- The source code is completely exposed - including any secrets stored in the code.
- If the application runs at a privilege level that the attacker desires, he will attempt to abuse it for nefarious purposes.
- Don’t assume the code you’re testing is the attacker’s final destination in his path of exploitation. If the code weakens the system’s security posture in any way, the attacker will abuse it.
Server-side code assumptions
- All of the assumptions for client-side code hold for server-side code as well. This includes the source code disclosure assumption if you’rere shipping product. If you’re running SaaS, it’s more of a question of when, not if.
- The attacker is going to sit on the same network segment as the application. There’s no firewall or filters. There’s a special place in hell reserved for products that require firewalls or filtering to protect themselves against attack.
- The naming service that the product relies upon will be compromised.
- The switching and routing fabrics will be compromised.
- If you have more than one defined user, one user will want to do things as the other user.
- If you have more than one defined role, a user in one role will want to perform functions in the other role.
- An attacker may want to cover their tracks. If he can do it with subtlety, he will take that path if it is easy to do.
- If it is exposed to the Internet, some yahoo will want to make it crash with an asymmetric attack (think packet of death or attack amplification).
- If the application runs on a multi-user system, other users on the system will attempt to subvert the application through any and all resources they can access (such as the file system, shared frameworks, IPC, and others) .
- If the application uses URIs (HTTP/FTP/SMTP/ITMS, whatever), an attacker has compromised the innocent client and has access to the session command channel.
With these assumptions in place, you could go ahead and do some basic data flow analysis. You should be able identify key entry points in the application where data and functionality cross trust boundaries. If you want to stay in step with Microsoft, feel free to pull out the STRIDE* model and apply the threat types to each entry point, keeping in mind the assumptions m= entioned above. With the threat landscape outlined, you’ll find the STRIDE process will go faster than simply working from a blank data flow diagram without context.
However, ninja threat modeling uses data flow analysis as a clean-up task, rather than the opening salvo. We’re impatient and want to know the answer to the question, “What entry points should we really care about?” right away.
* As required by the SDL powers-that-be, I am obligated to mention STRIDE threat types (Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, and Elevation of Privilege) in all communications relating to threat modeling at least once.
Where there’s smoke, there’s fire
A heat-seeking approach to drive your test plan is just what you need. When we blather on about entry points, trust boundaries, and threat vectors, what we’re really thinking is “Where can I do the most damage in the shortest period of time?”
The ninja threat model relies on two techniques: incident and vulnerability history and the commission of deadly sins.
The first technique examines how the product or similar products have been abused in the past. Don’t just look up old advisories or bug reports - look for incident data. Did a product have a vulnerability that allowed malicious code to spread? Was the vulnerability discovered and abused by a 16-year-old to make lots of friends on the social networking platform of the day?
Examine these abuse cases and make sure you can recreate them against the application you’re testing. At the root of each one, there should be a vulnerability, a threat actor, and an impacted asset. If you’re stuck with dry vulnerability advisories, sometimes the researcher will have a good grasp on the impact of the finding and will document it well. In other cases, you have to watch out for advisories with overreaching statements or extreme speculation.
What’s nice about these types of derived threats is a good amount of the test planning is already done for you and can be lifted wholesale. Watch out for tunnel vision, though - time, platform specifics, or other dependencies may have limited the avenues available to the researcher or intruder. Make sure you’rere looking for the pattern that indicates the flaw is present, not just performing a pattern match on yesterday’s exploit.
The second technique focuses on what developers get wrong most of the time - otherwise known as the “Deadly Sins”. The vulnerabilities introduced by these common flaws are mentioned for a reason: they get abused by threat actors on a regular basis.
Every security outfit has a list of their deadly sins, including Microsoft, SANS, OWASP, and Matasano, because lists are just that cool. We think ours is better for one major reason - our Deadly Sins are features rather than defects. As a result, our work fits into ninja threat modeling much cleaner than a laundry list of vulnerabilities.
It’s an issue of perspective and language. Developers don’t roll out of bed in the morning and say, “I’m going to code up a few SQL Injection vulnerabilities today!” Instead, they say, “I’m going to write the Advanced Search module for the Report Engine today!” When you hear this, your utter lack of faith in your development brethren should kick into overdrive and you should be busily entering “Test for SQL Injection here, here, and here” in your test plan.
We’ve appended our list of Deadly Sins for Web Applications for easy reference. Parts of it also apply to other sorts of applications. When you see these features, get out your red pen, add another page to your test plan, and start outlining how many different ways developers get these things wrong and how to test for it.
After wrapping up the heat-seeking exercises, it’s time to sanity check your work with data flow analysis including the selective application of STRIDE mentioned earlier. Hopefully, the overlap should be extensive. If it isn’t, you may be dealing with something that hasn’t been tested extensively and may bear some interesting fruit. On the other hand, you may have been assigned the most boring application known to mankind. Congratulations!
After following this approach, you should have a good definition of the application’s attack surface and the threat landscape which you will be attempting to recreate. As a result, you should be able to produce a meaningful test plan with a threat model that is defensible and sufficient to guide your engagement. You might even get away with doing one without a single Visio diagram.
1. Security
- Encryption
- Hierarchical role/privilege management
- Password storage
- Password reset
2. E-mail functionality
3. Thick Clients: Web Applications In Name Only (WINOs)
4. File Upload/Download
5. Templating and Content-Controlled Code
6. Advanced Search
7. Persistent network sockets that accept arbitrary connections
8. Installers
9. Use of plug-ins or third party software to write directly to the DOM
10. Single Sign-On Authentication Hand-offs