Adam Caudill

Security Leader, Researcher, Developer, Writer, & Photographer

Threat Modeling for Applications

Whether you are running a bug bounty, or just want a useful way to classify the severity of security issues, it’s important to have a threat-model for your application. There are many different types of attackers, with different capabilities. If you haven’t defined the attackers you are concerned about, and how you deal with them – you can’t accurately define just how critical an issue is.

There are many different views on threat models; I’m going to talk about a simple form that’s quick and easy to define. There are books, tools, and countless threat modeling techniques – it can easily be overwhelming. The goal here is to present something that can add value, while being easy to document.

The Threat Actors #

To define the threat model, you must define the different types of attackers that your application may face; some of these apply more to some applications than others. In some cases, it may be perfectly reasonable to say that you don’t protect against some of these – what’s important is to clearly document that fact.

It’s also important to think about what attackers want to achieve – are they trying to steal sensitive data such as credit card numbers or user credentials, tamper with existing data – from business plans to student grades, disrupt services (which can come at great cost), or attacking for some other purpose. There are countless reasons for attackers to target an application, some are important to understand (such as stealing credit card numbers) to prioritize protection mechanisms – in some cases, such as those that attack just because they can, it’s impossible to prioritize to protect against them, as there’s no way to predict how they’ll attack.

Passive Attackers #

Passive attackers are in a position that they are able to see communications, but can’t (or won’t, to avoid detection) modify those communications. The solution to most of the passive attackers is to encrypt everything – this not only eliminates most of the passive attacks, it eliminates or greatly complicates many active attacks.

Local (User) #

One of the most common examples of a local passive attacker is an evil barista – you are working from a coffee shop and on their open WiFi. At that point, they (and many others) can easily see your network traffic. This also applies to corporate networks and many other environments. What information can a Passive-Local attacker gain to perform attackers later?

Local (Server) #

Users aren’t the only ones that have to worry about local passive threats, an attacker monitoring the traffic going to and from a server can be quite effective (rogue monitor port on a local switch perhaps). By watching this traffic, an attacker can gain a great deal of information for future use, and perform some very effective attacks.

One example is a password reset email – if not encrypted, an attacker could capture the reset link, and change a user’s password. This is especially effective if the attacker triggered the password reset themselves. This is made worse, due to the common user training to ignore password reset emails that they didn’t request. As an example of this, let’s look at the email that Twitter sends when you request a password reset:


The all too common verbiage “If you didn’t make this request, please ignore this email” is present – an attacker requests a password reset, and waits for the email to go through. If the connection between the application and the remote email service isn’t encrypted, an attacker could easily take over accounts.

Upstream #

Upstream providers, from the server or client end, are in a position where they have easy access to information – they are also a target for those that want that information; and they will provide it, sometimes willingly, sometimes by force. These positions of power allow for transparently monitoring all traffic to and from a device – giving no indication that an attack is ongoing.

As with passive local attackers, any unencrypted communication could be captured, and used real-time, or stored for later use to enable attacks.

Global #

A special, and thankfully rare, type of passive attacker is those that have data feeds coming from many locations, and are able to analyze both ends of a communication. This type of attacker is a challenge that only the most sensitive systems need to be concerned with. Perhaps the most public example of an application that is especially vulnerable to this type of attacker is Tor, thanks to traffic correlation.

Active Attackers #

Active attackers run the gamut from normal users looking to get around restrictions to professional hackers – with motivations running from boredom to profit. With passive attackers, there’s generally no way to detect an attack, as nothing is being changed. With active attackers, there are often clear signs of their presence – though too often they are missed due to a flood of false positives.

Malicious User #

The malicious user is a legitimate user of your application – could be an employee or someone who signed-up from the internet – that seeks to gain additional privileges, gain access to restricted data, or otherwise make your application do whatever they want. In some of the simplest cases, it can be an employee looking to gain extra privileges to bypass required work, or it can be an attacker that is looking to steal data from the application’s database.

Most often, this type of attacker will look for easy wins, may try to understand the application by manually studying the application, or may just use one of the many tools available to see if there’s an easy way in. Most of the so-called “script kiddies” fit into this category.

Malicious Device #

Not only is there a risk of a malicious user, but that a legitimate well-intended user has malware or had their device otherwise been compromised. Attackers can leverage a user’s device to perform attacks as them, or capture information. When a user’s device is compromised, an whole range of attacks are enabled – and allows attacks against many types of applications. It has been well documented that software that allows simple man-in-the-middle attacks can be pre-installed on new PCs – and it can be easily installed via other malware. By performing a local man-in-the-middle attack, an attacker could not only capture information, but inject malicious traffic.

In many cases, there is little to nothing that can be done in these situations, as there’s no reliable way to ensure that a device isn’t compromised.

Advanced / Persistent Attacker #

From script kiddies with a lot of time on their hands, to professional hackers, this class of attacker will use similar tactics to the Malicious User, but will move on to more advanced attacks and better tools to achieve their goal. From using effective Cross-Site Scripting (beyond proof-of-concepts like alert('xss')), to SQL Injection, and so many more attack types, these attackers are the ones that are most likely to find real issues. When building a threat model, this class of attacker is likely the most important to consider, as they are the greatest and most likely threat.

IT / SysAdmin #

One threat that is too often overlooked is the people running the servers – they have access to the logs, can see the server setup, and in some cases can see the application source code and configuration files. This type of attacker has far more insight than most attackers do – in some cases, it’s assumed that those that run the servers simply must be trusted, as there’s no way to fully protect against them.

This is a case where detailed logging, change approval, and separation of duties – mostly process controls – can help, though are unlikely to prevent someone with this degree of knowledge from taking what they want. When defining a threat model, it’s important to address this issue – identify the technical and procedural controls that are in place, and determine how, or if, you will address the situations where such an attacker could be effective.

Developers / DBA #

Perhaps the hardest to address are those that build and maintain the application itself. How do you prevent them from inserting a backdoor? Code reviews are normally the answer, but there are real flaws to relying on them. While there may be policies that prevent developers from accessing the production database or credentials – a simple “mistake” made that displays a detailed error message, or allows SQL or code to be injected can quickly render those policies worthless.

A malicious developer is an extremely difficult attacker to address – because they have so much control, and such intimate knowledge of how their applications work. When defining a threat model, you have to account for a member of the development or DBA staff going rogue, ignoring all policies that get into their way, and inserting backdoors or otherwise opening a door that completely defeats the security of the application

Host / Service Provider #

When a host or service provider becomes malicious, it can be difficult to impossible to maintain security. As fewer and fewer applications are hosted within corporate walls, it’s important to understand that there is now an additional team of system administrators, networking and IT support that are suddenly involved. They could modify network traffic, add, remove, or replace hardware, clone drives, etc. – this realization has held many organizations back from moving to the cloud, as the fear of breaches and compliance nightmares is too great.

Another issue often overlooked, is the issue of other users of the same host, and this is especially true when using virtual servers; from side channels that allow encryption keys to be stolen, to breaking out the the hypervisor to attack other servers on the same pyhsical hardware.

For high security applications, building an application that can withstand an assault by its hosting provider is a sizeable challenge, though a challenge that must at least be acknowledged.

Local & Upstream Network Providers #

If the local or upstream network providers are used to attack, in one way or another, maintaining security can come with a surprising number of challenges. The key to defeating QUANTUM INSERT is as simple as using HTTPS – but there are others that are more challenging.

I recently reviewed an application that verified that the packages it installed were authentic by checking PGP signatures – unfortunately the PGP public key wasn’t securely delivered. Installing that software when under an active network attack would have allowed the attacker to run their own code as root.

Building A Threat Model #

A threat model doesn’t have to be some compliance laden document that means more to auditors than developers and security engineers, it can be a simple listing of threat actors and what the defense against them is – or if a given actor isn’t deemed to be a threat, document that. Having a very simple list-based document can provide guidance to the development team, to those classifying security reports, and to those submitting those reports.

As is true with most things in security, simplicity is key.

A Sample #

To get you started, I’ve defined a sample, so you can see how easy this can be to setup for your application (this will also save you a bit of time when setting up a bug bounty or pentest). This is an extremely simple, but useful model – it provides critical information and insight.

Application Name: Summus
Application Type: Web
Public Facing: Yes
Language / Platform: C# / ASP.NET MVC
TLS In Use: Yes

Application Description:

Web-based data capture application with four user roles (user, manager, QA, admin); users are able to read scripting and enter data from phone calls and back office transactions. Managers are able to review captured data, flag transactions for QA review, edit data, and approve transactions. QA is able to review captured data, approve transactions, and mark transactions as reviewed, flag for escalation, and assign a quality score. Administrators are able to setup new campaigns (scripting, data capture, etc.), update scripting, update data capture, change settings (approx. 200 options – some global, some campaign specific).

Application is written in C#, and uses the ASP.NET MVC framework; data is stored in SQL Server, and data access is performed via LINQ to SQL. Application is hosted on a cluster of eight IIS 8.5 servers. Application is hosted behind a CDN with a Web Application Firewall. Data capture fields may be flagged as sensitive, and encrypted using AES-256-GCM when stored in the database.


Note: Violations of any of the assumptions listed below, or defects in the processes used are considered to be a vulnerability.

Passive – Local – User: TLS is required; both via redirect from HTTP to HTTPS, and via the Strict-Transport-Security header. Due to these mitigations, it is believed that an attacker monitoring a user’s traffic is not able to gain useful information.

Passive – Local – Server: The server is hosting in a secure, PCI-compliant facility. Firewall rules and switch configurations are audited on a quarterly basis; traffic flows are monitored in real-time by Network Administration; switch ports are set to connect to a single MAC only; unused switch ports are disabled; network access & event logs are monitored in real-time by Network Administration.

Passive – Upstream: Due to the use of TLS, it is believed that an upstream provider is unable to access useful information.

Passive – Global: Due to the use of TLS, it is believed that an upstream provider is unable to access useful information.

Active – Malicious User: The application takes advantage of Cross-Site Scripting and Cross-Site Request Forgery protections provided by the ASP.NET MVC framework; data access is performed via an ORM to eliminate SQL Injection attacks; each page includes permission checks to prevent vertical privilege escalation; a Campaign ID and User ID are stored server-side as part of the session to prevent horizontal privilege escalation.

Active – Malicious Device: As there is no ability to remotely detect if a user’s device has been compromised, this is considered out of scope.

Active – Advanced: In addition to the protections mentioned in the Active – Malicious User section, the application is scanned on a regular basis by commercial scanning tools, and a quarterly pentest is conducted to identify vulnerabilities.

Active – IT / SysAdmin: This scenario is considered to be out of scope for this document. IT controls are documented in a seperate document.

Active – Developer / DBA: All code changes are required to pass through a peer code review prior to being merged into the master branch of source control; developers do not have access to production servers; static code analysis and vulnerability scan is performed prior to deployments; deployments are performed by System Administrators after approval from Change Control group; database server access & event logs are monitored in Real-Time by System Administrators.

Active – Host: Due to other controls, this scenario is considered to be out of scope.

Active – Network Providers: Due to the use of TLS, it is believed that an upstream provider is unable to access useful information, or perform meaningful alteration to traffic.

In closing… #

I hope that this will help you define not only a useful threat model for your application, but also come to a better understanding of what threats your application faces and how to address them.

Adam Caudill

Related Posts

  • Developers, Developers, Developers

    Note: This was written in 2012, but not published at the time. The point is still valid, perhaps moreso than ever and deserves to be made publicly. The content has been updated as appropriate, though the core of this article remains intact from the 2012 draft. I would like to note that this doesn’t apply to every environment, there are some where developers are very knowledgeable about security, and write code with minimal issues – my current employer happens to be one of those rare & exciting places.

  • phpMyID: Fixing Abandoned OSS Software

    phpMyID is a simple solution for those that want to run their own OpenID endpoint – the problem is that its author stopped maintaining the project in 2008. Despite this, there’s still quite a few people that use it, because it’s the easiest single-user OpenID option available. Unfortunately, the author didn’t follow best practices when building the software, and as a result multiple security flaws were introduced. In 2008, a XSS was identified and never fixed (CVE-2008-4730), in the years since then it seems the software has been below the radar.

  • First, Do No Harm: Developers & Bad APIs

    Primum non nocere (first, do no harm) – an iconic phrase in modern medicine, yet also applicable to many other fields. This is something I wish more people would think about, developers especially – and primarily when writing new APIs. In general, developers don’t have an impressive history with security – quite frankly, developers suck. Seeing as I consider myself a developer, that’s painful to admit. Chris Andrè Dale posted an interesting article some time ago that got me thinking: Why it’s easy being a hacker: A SQL injection case study – Chris pointed out the problems with educational material that developers are using, and just how bad the examples are.

  • Ruby + GCM Nonce Reuse: When your language sets you up to fail…

    A couple hours ago, Mike Santillana posted to oss-security about a rather interesting find in Ruby’s OpenSSL library; in this case, the flaw is subtle – so much so that it’s unlikely that anyone would notice it, and it’s a matter of a seemingly insignificant choice that determines if your code is affected. When performing AES-GCM encryption, if you set the key first, then the IV, and you are fine – set the IV first, you’re in trouble.

  • Developers: Placing Trust in Strangers

    Much has been said, especially recently, about that mess of dependencies that modern applications have – and for those of us working in application security, there is good reason to be concerned about how these dependencies are being handled. While working on YAWAST, I was adding a new feature, and as a result, I needed a new dependency – ssllabs.rb. While most Ruby dependencies are delivered via Gems, ssllabs.rb is a little different – it pulls directly from Github: