In March of 2020, the National Institute of Standards and Technology (NIST) updated their Security and Privacy Controls for Federal Information Systems and Organizations. These guidelines recommend the use of Runtime Application Self Protection (RASP) technology.
So, what exactly is RASP, and what does it mean for the future of cybersecurity?
Definition of a Cyberattack
Before explaining what RASP is and why it's such an important element of cybersecurity, we need to define a few terms.
First, what is a successful cyberattack against an application?
A successful cyberattack is an action or actions by an adversary that compromise either the confidentiality or integrity of data manipulated by the application. When an attacker violates the confidentiality of data, they are able to observe values used by, but not intended to be exposed by, the application (e.g. personally identifiable information).
A violation of integrity means that the attacker is able to modify data used, or produced, by the application.This means either an incorrect result is produced by the application (e.g. an incorrect lat/long coordinate), or that an incorrect action, based on incorrect data, is taken by the application (e.g. shutting down, releasing payload).
Almost all cyberattacks depend on errors in software logic (aka bugs). And as we know, all complex software contains up to 50 bugs per thousand lines of code. Once an attacker is able to identify a vulnerability, they are able to easily exploit it to take over the system. So how do you defend against cyberattacks?
There are a couple of options for defense, across the different lifecycle stages of a software vulnerability. First, there are static analysis and testing tools that can be used in the software development process to catch and fix vulnerabilities before the software is ever even deployed. Unfortunately, despite valiant efforts, however, many exploitable flaws avoid detection during this phase and are released into the wild.
Other options are virus scanning solutions and firewalls. A mainstay of cybersecurity protection, these tools rely on detecting attacks after the attacks have successfully caused damage, and then attempt to block additional attacks by scanning files and network packets for data that matches the signature of a previously identified attack. Sadly, these solutions are sorely lacking because they are reactive and retrospective approaches. They cannot protect systems against unknown, or zero-day attacks, which are being discovered every day. In 2020 alone, over 17,000 vulnerabilities were recorded by the US Computer Emergency Readiness Team. And on top of that, software-based security tools are just that: based in software and thus can contain vulnerabilities exploitable by bad actors. In 2019, a vulnerability in a popular anti-virus tool allowed an attacker to bypass authentication and log onto an affected product’s management console as a root user.
Relying on scanning incoming data for known malware signatures is clearly not enough to protect vulnerable software from attack. This is where RASP comes in.
RASP monitors an application as it executes, looking for behavior that indicates the application is under attack or about to violate confidentiality or integrity. Unfortunately, there is a wide range of security products describing themselves as RASP, representing an equally wide range of effectiveness against cyberattacks.
Types of Runtime Application Self Protection (RASP) Solutions
RASP products define a set of events that can be monitored, as well as data for each event. This combination of events and associated data define what level of monitoring a given RASP product can perform. At a high level, RASP products fall into four categories:
Interpreted Language Reflection
One the positive side, because the language is both reflective—can observe and modify its own state—and interpreted, there is no recompile/relink step involved in adding monitoring code to an existing application. However, the monitoring code does add significant overhead and thus reduces performance. Because of this, dynamic interpreted instrumentation is only practical in contexts without tight performance requirements, such as web applications.
Code or Binary Instrumentation
For languages that do not support dynamic code instrumentation, there are tools available that can rewrite an application and insert additional checks at critical points in the code. Sometimes called Inline Reference Monitors, this general technique has the same net effect as the Interpreted Language Reflection approach above, but it requires applications to be recompiled and/or relinked—making adoption and deployment more complicated.
Uses of instrumentation vary widely and include checking pointer dereferences to providing memory safety, adding data sanitization to prevent cyberattacks that utilize SQL injection, and examining call stacks at system calls for suspicious callers. The instrumentation can be added to the original application source code (e.g. using Valgrind) before compilation, during compilation (e.g. LLVM ASan) or to the binary output (e.g. java bytecode rewriting or ELF binary rewriting tools). There are also dynamic binary instrumentation tools (e.g. DynamoRIO) that rewrite executables as they are executed. Unfortunately, this approach also adds overhead and reduces performance. Therefore, they are typically only used during debugging and not in deployed applications.
Runtime Hooking & Tracing
Many runtimes (operating systems, runtime libraries) expose events via a logging infrastructure—DTRACE is a well known example. While the set of events logged is predefined, tracing approaches to application monitoring typically have low overhead compared to Interpreted Reflection or Instrumentation, described above, which directly add additional logic to the running application. While tracing frameworks can detect misbehavior at fine granularity with low overhead, the ability to actually prevent damage is more limited. Typical uses of monitoring a runtime trace include looking for suspicious sequences of system calls or IO activity on the disk or network, and looking for anomalous process creation or memory allocation.
The last type of RASP technology is hardware tracing. Most modern processor designs include a low-level hardware trace facility, enabling attached hardware to monitor each instruction and data reference performed by a processor. This feature gives extremely fine-grained visibility, but is typically only used for in-house debugging before deployment, because of the requirement of attached hardware. However, Dover has figured out a way to deliver a hardware tracing-based RASP solution post-deployment. Dover’s CoreGuard® technology includes hardware logic that is integrated directly into the SoC fabric and consumes the trace output, checking every instruction executed by the host processor for correctness in real-time.
How RASP Works
All RASP products consist of a runtime monitor that receives a continuous stream of events, with associated data. Each RASP system has its own set list of security rules that it uses to crosscheck the events and associated data to ensure only allowed events are able to execute. The level of protection provided by each RASP technology depends on the depth of the security rules, as well as the type of implementation.
For example, the RASP system may be presented with the event:
call to “encrypt” with arguments val = 42 and key = 0xffe01234
How does a RASP system determine whether this call should be allowed? Well, it depends.
There are some RASP products which allow for basic “data sanitization” rules. Such rules check against things like executable code within string arguments. However, there are many security aspects that cannot be adequately monitored by simply examining a sequence of function calls and the data sent to those calls, in isolation.
In order to implement more sophisticated levels of RASP security, two elements are needed in your RASP solution: dynamic context, and metadata.
The first element, dynamic context, refers to nesting and sequencing of events. For example, a RASP rule could be designed to ensure a call to “open_file” within the scope of a call to “check_credentials” is allowed but not within a call to “factorial”. Further, another rule could establish that a call to “free_memory” can occur only after a call to “allocate_memory”, and that the argument to free_memory matches the value returned from allocate_memory. Dynamic context refers, in general, to temporal conditions such as “A follows B”, “C must occur within a call to D”, and so forth.
Some RASP tools maintain simple representations of dynamic context, such as counting the number of times a function is called, or keeping a shallow representation of the call stack. More powerful RASP tools, like Dover’s CoreGuard solution, support some variant of temporal logic, which allows for the definition of complex checks involving temporal operators such as “before”, “while”, and “after”.
The second element, metadata, refers to “data about data”. For example, in a call to “encrypt”, the RASP tool can check that the value for “key” was emitted by a trusted key store and was not modified between then and consumption by the “encrypt” function. And when reading a value from memory via a pointer, RASP can check whether that pointer is allowed to reference that memory. Further, when writing a value to a network port, the RASP system could check whether or not the value is private and thus prevent exposure over a network. And so forth.
Direct vs. Indirect RASP Cybersecurity Checks
In order to determine which method of RASP implementation is best suited for a particular system, let’s revisit our original definition of a successful cybersecurity attack: either exfiltration of private data (confidentiality) or erroneous output or behavior (integrity).
When it comes to ensuring confidentiality, a cybersecurity tool is not able to prevent the exfiltration of private data if it cannot distinguish between confidential and public information. Likewise, a cybersecurity tool will not be able to detect incorrect output or behavior if it has no notion of correct operation.
Thus, while a RASP technology may provide fine-grained visibility into the operation of a software application while it's running, the challenge remains how to distinguish correct versus incorrect behavior.
That is the fundamental challenge of cybersecurity.
That brings us to two different approaches to defining RASP-based cybersecurity checks for Confidentiality and Integrity: direct, and indirect. Both of which are employed by Dover’s CoreGuard solution.
Direct Specification of Confidentiality & Integrity
Since the definition of cybersecurity is the protection of confidentiality and integrity, when elements of confidentiality and integrity can be precisely defined, they should. However, the definitions of confidentiality and integrity tend to be application-specific and so invariably require some additional specification on the part of software or security engineers. In this case, it's the job of the cybersecurity tool to minimize the amount of additional effort required to indicate confidential data and integrity constraints.
Defining confidentiality sufficiently for a RASP tool to enforce it requires defining a few different elements.
First, how or where confidential data originates must be defined. For example, certain fields in a database table may be deemed confidential, and when the values are retrieved from the database, the values are labeled confidential. Or, all data entering a system via a particular IO channel may be considered confidential. The CoreGuard technology can track those confidential distinctions with its metadata.
Next, how confidential data propagates must be specified. As confidential data flows through an application and is combined with other data, how does that affect the confidentiality of derived values? Typically, if a value is directly influenced by a confidential value (e.g. the sum of a confidential number and a non-confidential number) the result is considered confidential.
For example, let’s consider someone’s age to be confidential, and suppose a birthdate is included in the individual’s personnel file. Today’s date is, of course, public data and not considered confidential. So how do we prevent a combination of these two pieces of data to determine someone’s age. CoreGuard’s silicon IP uses a mechanism called taint. When the system received an instruction to subtract today’s data (public) from the person’s birthdate (confidential), it enforces a micropolicy rule stating that if an instruction performs computation of both confidential and non-confidential data, then the result (age in this case) is “tainted” and therefore also marked as confidential.
In addition, how confidential data is declassified should be considered. In practice, the results of computing over confidential data must be communicated to the outside world. To do this requires declassification of the otherwise confidential results. Often this involves encryption of data. Which functions or sequences of events are sufficient and privileged to declassify data is one element of defining confidentiality.
Finally, the definition of confidentiality must include specification of where confidential data is allowed, or not allowed, to propagate. Confidential data should not be allowed to be sent to “publicly observable” IO outputs. Other output channels in a system may be considered “private” and so can receive confidential information (such as an encrypted filesystem, or an HSM).
For example, the CoreGuard solution tracks locations in memory that correspond to writing data to the network (memory-mapped IO), and then prohibits writing tainted data to those locations. To prepare tainted data to be exported, the CoreGuard solution uses the system’s existing encryption routine to remove the taint from the data. Only that trusted routine can remove taint, thus ensuring that only authorized individuals who possess the appropriate decryption key can see the confidential data.
Where confidentiality is concerned with “where sensitive data may flow”, integrity is concerned with “where data came from”, or, even more generally, whether or not the data can be trusted.
Enforcing confidentiality involves labeling data as it's created or ingested as confidential, tracking that data as it flows through computation, and disallowing the flow of confidential data to public outputs.
Enforcing integrity, on the other hand, involves checking data when it's used in order to determine whether the data has been appropriately shepherded from origin to consumption. Thus, defining integrity sufficiently for a RASP tool to enforce it requires defining what are inputs into the system, or places where values are created, that are considered “trusted” or “high integrity”? This may involve trusted input channels, or may require that data has successfully passed through trusted “sanitization” functions before the data is considered trusted. As with confidentiality, how trusted data propagates must be defined, as well as where the trusted data is required.
Before values are used in some functions (typically “control” functions that have an external effect), the integrity of the incoming values must be checked. For example, before a value is sent to a SQL engine, there may be an integrity constraint that the value originated from a particular input channel, that the value has passed through a trusted sanitization routine, and the value has not been modified since then.
CoreGuard’s Information Flow Control (IFC) policies track how information propagates through a program during execution to ensure the information is handled securely. It tracks the provenances of data and answers the question: Where did this information come from? For example, metadata for an IFC policy may indicate that the data came from the internet—and the rule may dictate that the system cannot write anything to external memory if it came from the internet.
Specifying all of the above details for all confidentiality and integrity requirements of a system may seem like a lot of work. As noted earlier, it's the job of a cybersecurity tool with RASP to minimize the amount of explicit annotation required to accomplish sufficient enforcement of confidentiality and integrity. There are two ways in which, in practice, many confidentiality and integrity properties can be enforced without additional information from an application developer: runtime-specific policies, and approximate policies.
Indirect Confidentiality & Integrity
Most applications are built on top of a large runtime, which consists of the operating system and various runtime libraries. Because the OS and libraries are used for many applications, any confidentiality and integrity definitions for these runtime components can be reused by all applications written on top of them. For example, a runtime library that provides an interface to a SQL database may include the integrity requirements that incoming queries must have been filtered through a sanitization function.
Runtime-specific policies, such as requiring a particular sanitization function before sending data to a database, are precise. It's also possible to define behavior in terms of a runtime that is a strong indicator of violations of confidentiality and integrity.
The CoreGuard IP’s most common, and precise, such policy is Heap. This micropolicy prevents buffer overflow attacks and protects heap memory by assigning a color to the buffer in which data resides, as well as the pointer to the buffer. A runtime function (often called malloc) is called by application code when the application needs storage for runtime data. Malloc returns a pointer to memory that has been allocated for use by the application. Per the policy, the returned pointer can only be used to access memory within the range allocated (color). If a pointer is used to write a value outside of the intended range, that is a violation of integrity. If a pointer is used to read a value outside of the intended range, it's a violation of confidentiality. Both violations are flagged due to a color mismatch.
Because confidentiality and integrity constraints with respect to a particular runtime (malloc and other memory management functions) need only be written once per runtime, often Heap memory safety micropolicy is a reasonable substitute for a large part of the integrity and confidentiality requirements of an application.
CoreGuard: A Robust RASP Solution
A RASP solution, like Dover’s CoreGuard technology, that is able to maintain base and bounds information for each pointer in a system (after returned by malloc) can then check every use of the pointer to ensure that reads and writes from and to, respectively, memory are adhering to the low level confidentiality and integrity constraints.
Because it maintains both rich runtime context and rich metadata for every word in memory, the values (often called “labels” in this context) maintained as metadata by CoreGuard technology are a function of the micropolicies installed on a system, and can include everything from fine-grained read-write-execute access control to multi-level confidentiality and integrity labels.
The NIST adoption of RASP can be seen as a turning point in the cybersecurity industry. We now know that in the face of an evolving cyberattack landscape riddled with exploitable software vulnerabilities, the perimeter defenses that have been relied on for decades are no longer sufficient. This doesn’t mean that firewalls and virus scanning software become a relic of the past—they are crucial components of a defense-in-depth approach to cybersecurity. However, systems of today demand a greater level of protection, one that is offered by RASP products such as Dover’s CoreGuard solution.