Understanding the Architecture of a System
The question that needs answering in order to factor the architecture properly for attack surfaces is at what level of specificity can components be treated as atomic? In other words, how deep should the analysis decompose an architecture? What constitutes meaningless detail that confuses the picture?
Size Really Does Matter
As mentioned above, any executable package that is joined to a running process after it’s been launched is a point of attack to the executable, perhaps to the operating system. This is particularly true where the attack target is the machine or virtual machine itself. Remember that some cyber criminals make their living by renting “botnets,” networks of attacker-controlled machines. For this attack goal, the compromise of a machine has attacker value in and of itself (without promulgating some further attack, like keystroke logging or capturing a user session). In the world of Advanced Persistent Threats (APT), the attacker may wish to control internal servers as a beachhead, an internal
machine from which to launch further attacks. Depending upon the architecture of intrusion detection services (IDS), if attacks come from an internal machine, these internally originating attacks may be ignored. Like botnet compromise, APT attackers are interested in gaining the underlying computer operating environment and subverting the OS to their purposes.
Probing a typical computer operating system’s privilege levels can help us delve into the factoring problem. When protecting an operating environment, such as a user’s laptop or mobile phone, we must decompose down to executable and/or process boundaries. The presence of a vulnerability, particularly an overflow or boundary condition vulnerability that allows the attacker to execute code of her or his choosing, means that one process may be used against all the others, especially if that process is implicitly trusted.
As an example, imagine the user interface (UI) to an anti-virus engine (AV). Figure 3.4 could represent an architecture that an AV engine might employ. We could add an additional process running in user space, the AV engine. Figure 3.8 depicts this change to the architecture that we examined in Figure 3.4. Many AV engines employ system drivers in order to capture file and network traffic transparently. In Figure 3.8, we have a generalized anti-virus or anti-malware endpoint architecture.
The AV runs in a separate process space; it receives commands from the UI, which also runs in a separate process. Despite what you may believe, quite often, AV engines do not run at high privilege. This is purposive. But, AV engines typically communicate or receive communications from higher privilege components, such as system drivers and the like. The UI will be running at the privilege level of the user (unless the security architect has made a big mistake!).
In this situation, a takeover of the UI process would allow the attacker to send commands to the AV engine. This could result in a simple denial of service (DOS) through overloading the engine with commands. But perhaps the UI can turn off the engine? Perhaps the UI can tell the engine to ignore malicious code of the attacker’s choosing? These scenarios suggest that the communication channel from UI to AV needs some protection. Generally, the AV engine should be reasonably suspicious of all communications, even from the UI.
Still, if the AV engine does not confirm that the UI is, indeed, the one true UI component shipped with the product, the AV engine presents a much bigger and more dangerous attack surface. In this case, with no authentication and validation of the UI process, an attacker no longer needs to compromise the UI! Why go to all the trouble of reverse-engineering the UI, hunting for possible overflow conditions, and then building an exploit for the vulnerability? That’s quite a bit of work compared to simply supplying the attacker’s very own UI. By studying the calls and communications between the UI and the AV engine, the attacker can craft her or his own UI component that has the same level of control as the product’s UI component. This is a lot less work than reverse engineering the product’s UI component. This attack is made possible when the AV engine assumes the validity of the UI without verification. If you will, there is a trust relationship between the AV engine and the UI process. The AV process must establish trust of the UI. Failure to do so allows the attacker to send commands to the AV engine, possibly including, “Stop checking for malware.”
The foregoing details why most anti-virus and malware programs employ digital signatures rendered over executable binary files. The digital signature can be validated by each process before communications commence. Each process will verify that, indeed, the process attempting to communicate is the intended process. Although not entirely foolproof, binary signature validation can provide a significant barrier to an attack to a more trusted process from a less than trusted source.
Abstracting the decomposition problem from the anti-virus engine example, one must factor an independently running endpoint architecture (or subcomponent) down to the granularity of each process space in order to establish trust boundaries, attack surfaces, and defensible perimeters. As we have seen, such granular depth may be unnecessary in other scenarios. If you recall, we were able to generally treat the user’s browser atomically simply because the whole endpoint is untrusted. I’ll stress again: It is the context of the architecture that determines whether or not a particular component will need to be factored further.
For the general case of an operating system without the presence of significant, additional, exterior protections, the system under analysis can be broken down into executable processes and dynamically loaded libraries. A useful guideline is to decompose the architecture to the level of executable binary packages. Obviously, a loadable “program,” which when executed by the operating system will be placed into whatever runtime space is normally given to an executable binary package, can be considered an atomic unit. Communications with the operating system and with other executable processes can then be examined as likely attack vectors.