In my last post, I listed off some of the elements that make up a threat model. Now that we have a common vocabulary that can be used to describe the names and types of the elements, let's see what we can do with it.
For this series, I'm going to use an API that's near and dear to my heart: PlaySound.
The nice thing about PlaySound is that it's a relatively simple API, but it's complex enough that it can demonstrate many of the characteristics of threat modeling.
So how do we go about drawing the whiteboard diagram for PlaySound?
First off, you need to characterize the data flows associated with the API. I often find it helps to describe the design of your component to someone - for some reason, it helps me understand the data flow if I explain it to someone (I can't explain why, but I know it does).
For PlaySound, my description was something like:
"The PlaySound API takes as input a string which represents either a WAV filename or an alias. If the input is an alias, the PlaySound API retrieves data from the registry under HKCU to convert the alias into a filename. Once the filename is determined, the PlaySound API opens the WAV file specified and reads the two relevant pieces from the file: the WAVEFORMATEX that defines the type of data in the file and the actual audio data. It then hands that data to the audio rendering APIs."
Given that description, what elements are going to appear in the diagram? Well, obviously the PlaySound API itself. In addition, you're going to have the .WAV file, HKCU, the audio playback APIs. You'll also need one other element that's not immediately obvious - the application that invokes PlaySound.
Here's what it looks like (I drew this in Visio, obviously you could use any tool to draw it (I know one group that literally draws their diagrams on the whiteboard then takes a picture of it with a cell phone camera and then pastes the picture into a Word document)):
All the elements I called out in my description above are present. As I said above, the application calling the API is modeled as an external element (because it is outside your control). Similarly, the audio playback APIs (and there may be more than one of them - it doesn't matter to this threat model) are modeled as external elements because you don't control them either.
The WAV file and the registry are separate data stores, and PlaySound is a process sitting in the middle of it. Remember that when you're drawing your diagram, a "process" isn't the same thing as a WIn32 process, instead it's a piece of code that processes data. Once you've got your elements, you just need to add the data flows and trust boundaries and you're done with the diagram.
It's not obvious from the picture, but the dataflows between "WAV file" and "PlaySound" all flow from "WAV file" to "PlaySound" - that's because we don't ever write data into the file, we only read it. Similarly, since we don't write data into HKCU, there's no data flow into HKCU.
One key thing to notice is that this diagram is significantly simpler than the actual implementation of PlaySound. It doesn't include lots of the options that PlaySound supports (like SND_ASYNC, SND_MEMORY or SND_RESOURCE). This is quite intentional because those options don't change the results of the threat modeling (I'll spend a bit of time talking about this later).
You'll note that I've chosen to inserted trust boundaries between the WAV file, the registry and the application, but I don't have a trust boundary between PlaySound and the audio playback APIs.
The reasons for this are:
- For the Application, you have no way of knowing where the data contained in the "PlaySound Command" comes from. It might have been read from the internet or other untrusted source, so you have to assume that that data is untrusted.
- For WAV file, the same rules apply - you have no way of knowing where the contents of that file came from, so you need to treat the contents as untrusted.
- The trust boundary between "HKCU Sound Aliases" and "PlaySound" exists because (a) as a matter of general purpose, you shouldn't trust data from a data store unless you have some way of assuming it's trusted (if the ACL on the data store prevents anyone but an admin from writing to the store, it's probably safe to trust it, for example), and (b) because PlaySound is such a common API, it has some use scenarios that involve it being called in an elevated context.
- I don't have a trust boundary between PlaySound and the playback APIs because the playback APIs execute at the same trust level as PlaySound. It's possible that the playback APIs might invoke elevated components but that's an issue for the playback APIs threat model, not the PlaySound threat model.
That last point is important. When I'm doing a threat for a component, I usually don't bother to put a trust boundary between my component and data flowing out of my component. That's because I trust that my code produces correct data. On the other hand, the downstream component often can't trust the upstream component (so the Audio Playback APIs can't trust the WAV file Data that is provided by PlaySound).