Almost a year ago, while describing our company mission and the limitations of available solutions for detecting firmware threats, we discussed our initial vision around binary code inspection for detecting firmware threats and vulnerabilities (See: Why Firmware Integrity Is Insufficient For Effective Threat Detection And Hunting).
Since then, our approach has transformed based on the evolution of Binarly’s technology stack and valuable customer feedback. From the beginning, we knew that straightforward pattern matching (signature-based approach) doesn’t fit well for vulnerability and threat detection at scale without producing a massive number of false positives or false negatives. The effectiveness of Binarly’s approach has been confirmed by multiple major discoveries of repeatable failures of the firmware supply chain as well as failures in terms of firmware-specific threat detection by endpoint solutions (See: A Deeper UEFI Dive Into MoonBounce).
Recently, we presented research at UEFI Forum and Black Hat Asia (“The Firmware Supply-Chain Security Is Broken: Can We Fix It?”) highlighting the major firmware supply chain gaps and ways to fix it together with our partners Insyde, Immune, and LVFS.
In addition, the Binarly efiXplorer team demonstrated the first publicly known ROP attack causing arbitrary code execution in System Management Mode (SMM). Our research in firmware security continues to lead the field, and we plan to present new results at the Black Hat conference this summer ("Breaking Firmware Trust From Pre-EFI: Exploiting Early Boot Phases").
Binarly’s research work has conclusively proved that existing approaches to detecting firmware-related security problems do not work based on the numerous vulnerabilities we have disclosed across the entire industry: - Silicon Vendors Reference Code: Intel, AMD - Independent BIOS Vendors (IBV) Reference Code: Insyde, AMI - Original Design Manufacturing (ODM) Firmware Code: Atos, Intel, Dell, HP, Lenovo, Fujitsu.
The Binarly efiXplorer team publicly disclosed 48 high-impact vulnerabilities (46 CVEs assigned) which can cause arbitrary code execution in highly privileged firmware components. While this list of vendors only includes vulnerabilities disclosed directly to them, some were found in reference code and had a significant impact across the entire industry. The full details of all of these disclosures are available on the Binarly Advisories website.
These massive repeatable failures in critical firmware components can lead to multiple security failures, including long-term persistence of threat actors -- something that remains invisible to most endpoint solutions.
The complexity of the firmware supply chain leads to an almost infinite source of vulnerabilities. One of the best examples of this is the five-year-old AMI UsbRt vulnerability (INTEL-SA-00057) that is still present in newer hardware devices. The UsbRt vulnerability was first discovered in 2016. However, due to the complexity of the code, multiple variants of the bug were subsequently discovered. As we highlighted in our previous research (“Repeatable Failures: AMI UsbRt - Six Years Later, Firmware Attack Vector Still Affect Millions Of Enterprise Devices”), UsbRt bugs have an almost-six-year-old history of successful and repeatable successful exploitation. Even today, the Binarly Platform continues to detect vulnerable versions of AMI UsbRt at scale in enterprise infrastructure, and the number of impacted devices is frighteningly high.
We knew from the beginning that we had to find a better way to detect and scale detections for known vulnerabilities and threats. After experimenting with multiple approaches, we did not find an appropriate solution, so we developed the Firmware Hunt (FwHunt) technology that is already having a significant impact by detecting known vulnerabilities and threats at scale. The FwHunt Community Scanner has been integrated into LVFS and is recommended by CERT/CC for detecting known firmware vulnerabilities at scale.
We are fortunate to have all of these community efforts that enable us to collect valuable feedback from industry and to continue to develop the FwHunt technology stack to help industry recover from the repeatable failures of the firmware supply chain.
Today, we are introducing FwHunt 2.0, the next iteration of the FwHunt technology.
In contrast to YARA, FwHunt uses semantic properties as a filtering criteria during the binary code search to improve detection accuracy and reduce the number of false positives. The semantics-based approach taken in FwHunt overcomes many of the problems faced when using lightweight syntactic scanning rules as in YARA and other pattern or signature-based approaches.
We previously discussed the reasoning for developing the FwHunt rule format and the advantages of this approach over a YARA-based approach (Detecting Firmware vulnerabilities at scale: Intel BSSA DFT case study).
From the beginning we chose a YAML-based format to develop FwHunt rules. The rules now have more machine-readable format context to facilitate parsing. Below is YAML example for protocols detection and its JSON scheme:
YAML rule example:
protocols:
- name: EFI_HII_DATABASE_PROTOCOL_GUID
value: EF9FC172-A1B2-4693-B3276D32FC416042
service:
name: LocateProtocol
- name: EFI_HII_DATABASE_PROTOCOL_GUID
value: EF9FC172-A1B2-4693-B3276D32FC416042
service:
name: HandleProtocol
JSON rule representation:
{
"protocols": [
{
"name": "EFI_HII_DATABASE_PROTOCOL_GUID",
"value": "EF9FC172-A1B2-4693-B3276D32FC416042",
"service": {
"name": "LocateProtocol"
}
},
{
"name": "EFI_HII_DATABASE_PROTOCOL_GUID",
"value": "EF9FC172-A1B2-4693-B3276D32FC416042",
"service": {
"name": "HandleProtocol"
}
}
]
}
From the previous listing, we can see how the UEFI protocol information extracted from DXE drivers provides additional semantic context that can be leveraged in the FwHunt rule. These UEFI firmware specific semantic annotations provide context to navigate through the code pattern and create data flow connections between the basic-blocks or functions. Metadata provided by annotations helps reduce the rule's size without sacrificing accuracy or increasing false positives. Furthermore, additional semantic context helps us greatly improve the detection process from a performance perspective.
FwHunt v2.0 adds logical operators to the rule specification in order to improve detection efficiency.
Added types of matches for each component of the rule
For each component of the rule we added following logical operators:
Let’s dive into an example where we apply the logical OR and AND operators to create additional detection context.
protocols:
or:
- name: EFI_HII_DATABASE_PROTOCOL_GUID
value: EF9FC172-A1B2-4693-B3276D32FC416042
service:
name: LocateProtocol
- name: EFI_HII_DATABASE_PROTOCOL_GUID
value: EF9FC172-A1B2-4693-B3276D32FC416042
service:
name: HandleProtocol
and:
- name: EFI_SMM_END_OF_DXE_PROTOCOL_GUID
value: 24E70042-D5C5-4260-8C39-0AD3AA32E93D
service:
name: LocateProtocol
Now let's look at the details of what is happening and dig into the logic of the previous rule. The rule is looking only for the following UEFI services calls into the DXE module:
LocateProtocol(&EFI_SMM_END_OF_DXE_PROTOCOL_GUID, ...) AND
(LocateProtocol(&EFI_HII_DATABASE_PROTOCOL_GUID, ...) OR
HandleProtocol(..., &EFI_HII_DATABASE_PROTOCOL_GUID, ...))
Such logic helps to create additional context in the assembly code guided by semantic-based annotations.
Another example illustrates how semantic-based annotations can be used with other UEFI-specific data types or metadata, such as NVRAMs, PPIs, GUIDs, or just string patterns.
nvram:
not-any:
- name: NetworkStackVar
guid: D1405D16-7AFC-4695-BB12-41459D3695A2
service:
name: GetVariable
- name: NetworkStackVar
guid: D1405D16-7AFC-4695-BB12-41459D3695A2
service:
name: SetVariable
The best way to learn about all supported semantic annotation features is to review the FwHunt rule format specification which has been open-sourced by the Binarly team. FwHunt v2.0 also includes a feature that allows multiple variants of a detection trigger to constitute in a single rule.
Added the ability to handle multiple rule variants
To handle multiple rule variants that would otherwise be difficult or clearly impossible to express the context of detection trigger by using and/or/not-any/not-all logical combinators for individual match logic patterns. By adding the variant abstraction, multiple distinct rule bodies can be specified in a single rule. Let's look at one of the examples to see the value:
BRLY-2021-011:
meta:
author: Binarly (https://github.com/binarly-io/FwHunt)
license: CC0-1.0
name: BRLY-2021-011
namespace: vulnerabilities
CVE number: CVE-2021-33627
advisory: https://binarly.io/advisories/BRLY-2021-011/index.html
description: SMM memory corruption vulnerability in combined DXE/SMM driver (SMRAM write)
volume guids:
- 74D936FA-D8BD-4633-B64D-6424BDD23D24
variants:
variant1:
code:
and:
- pattern: 488b5310498d48204d8b4018e8....0000
place: child_sw_smi_handlers
- pattern: 4981392010000075
place: child_sw_smi_handlers
variant2:
code:
- pattern: 488b5310498d40204c8bc948894424..4533c033c9e8
place: child_sw_smi_handlers
In the first iteration of the FwHunt specification, we had to develop two different rules to cover both variants of the same vulnerability. But after the introduction of the new detection logic with variants in FwHunt v2.0, we can cover all the known variants by a single rule. Each variant is annotated with a unique identifier, which makes it possible to distinguish between which rule variant matches what and create separation in the detection process.
To learn more about FwHunt v2.0, please dive into the rule format specification. We are always open to feedback, feel free to create an issue in the specification github repository.
Let's address why YARA will not provide the same detection quality for hunting vulnerable code patterns and why it will often create a large number of false positives/negatives. The YARA rule specification does not support the semantic-annotation feature of data-flow extraction that reduces the specifics of UEFI and control-flow or function boundary information. Let’s look at specific examples that will provide additional context of the problem we are solving through FwHunt rules.
Example 1. By using a YARA rule, we can define the EFI protocol or PEI PPI interfaces by specifying in the rule GUID format by 16 bytes value. But having only encoded GUID value in bytes will not contain any additional context to specify which service of this protocol or interface is called. In this case, FwHunt’s approach can keep additional metadata by transition call-site semantic annotation and applying this metadata to actual EFI service-context which helps to create a metadata transition of the bytestring with the GUID and actual context how it gets used in the code.
Example 2. Let’s assume there is a need to determine if a specific NVRAM variable is set. In other words, the SetVariable() (but not GetVariable()) service should be called with a specific argument. With some level of accuracy, we can use the YARA rule and it can detect that the SetVariable() EFI service is used in the module (by detecting the GUID bytestring) and there is a string with the variable name. But the major limitation of the YARA approach is revealed in explaining the context of how SetVariable() will be used in the code-flow. The YARA approach doesn’t provide an ability to explain that this particular string is the argument of the SetVariable service.
Example 3. Another example where we need to detect code-based patterns inside the SMI handler (this is a very common case in practice) but YARA doesn’t have an ability to parse and recognize the SMI handlers context in the code pattern. The YARA rule can only detect the specific code pattern, but it cannot guarantee or provide the context inside the rule where it’s possible to explain the context the detection triggers inside the SMI handler.
Curious readers can pose the question: Why can't we develop the additional YARA module to detect such code-specific context like SMI calls? We can technically do that, but the answer is a little bit more complicated. Is it possible to develop the additional YARA module to detect code-specific context like SMI calls? Here's an example:
import "uefi"
rule BRLY-2021-042:
{
strings:
$pattern1 = { 49 8B D8 4D 85 C0 74 ?? 4D 85 C9 74 ?? 41 81 38 48 43 46 44 }
$pattern2 = { 48 8B 05 ?? ?? 00 00 48 8B 4B 10 4C 69 C0 AF 00 00 00 48 89 43 08 }
condition:
uefi.child_sw_smi_handlers.contains($pattern*)
}
Technically, we can do that, but the solution is a bit more complicated. Firstly, it is preferable to abstract code analysis, which is responsible for control-flow and semantic extraction, from the scanner, which uses the extracted metadata for detection context. Secondly, this way imposes restrictions on the extensibility of the detection logic which we created in the FwHunt specification.
Additionally, YARA cannot recover control-flow and or/function boundaries, so it treats all the regions within a module similarly in the detection logic. The recovery of program structure and semantics (e.g., data cross-references) using FwHunt allows us to better restrict lightweight syntactic scanning rules to regions with certain properties, which reduces the risk of false positives and positively impacts the performance in general.
The fwhunt-scan tool uses Rizin as the reverse engineering framework for its analysis engine. Rizin is used for disassembly and code analysis to extract semantic annotations and provide context for FwHunt detection. The fwhunt-scan is already widely used by the firmware security industry to detect supply chain failures in the UEFI firmware ecosystem. In our previous blog, we discussed how fwhunt-scan found new variants of UsbRt vulnerabilities in Dell systems using integrated query search with the LVFS dataset of firmware images.
We created a docker container image with all of the necessary setup to perform the FwHunt scans in private environments (on premises) to simplify technology adoption. Detailed installation instructions, API usage examples, and dependencies are all listed in the readme file in the github repository. In addition, we also provide ready-to-use docker images.
$ python3 fwhunt_scan_docker.py pull # pull binarly-io/fwhunt-scan docker image
$ python3 fwhunt_scan_docker.py scan --rule {path to rule} {path to module} # scan specified module with specified rule
In the new version to address the needs to support FwHunt v2.0 we have also added support for multi-rule scanning, allowing multiple rules to be checked in one scanner invocation. This feature can be used from the fwhunt-scan
API via the UefiScanner
class.
Our fwhunt-ida plugin simplifies the rule development process for FwHunt technology. We have already added support for FwHunt v2.0 in the plugin. A live demonstration of the fwhunt-ida plugin shows the advantages of the FwHunt rule development process in the IDA environment.
In addition to today's announcement, we are excited to announce our new community project to scan all publicly available FwHunt rules in one place. FwHunt.run was created to satisfy the community's need to quickly scan UEFI firmware images against multiple disclosures Binarly's team have made and documented in-the-wild threats. In the past few months after the publication of our blog “The Firmware Supply Chain Security is broken Can we fix it”, there is a growing need for such a tool to help address growing security gaps in the firmware supply chain ecosystem. Also, the U.S. government recently raised awareness of major weaknesses in the firmware supply chain in the “Assessment of the Critical Supply Chains Supporting the U.S. ICT Industry” report.
In many cases firmware is a single point of failure between all the layers of the supply chain and the endpoint customer device.
We received a lot of requests to provide such service and made the decision to commit to firmware supply chain security by creating FwHunt.run as a free service.
This is an absolutely simple way of scanning all publicly available FwHunt rules in one place. After you add your email to receive a report, it will show a screen to simply drag and drop the firmware image for further analysis.
FwHunt.run uses the rules from Binarly's public github repository. In order to address firmware supply chain security risks, Binarly advisories have matching FwHunt rules to enable detection at scale of disclosed vulnerabilities. Here’s an example of the report that will be provided at the end of the firmware image analysis process.
FwHunt.run is a great tool to quickly test your firmware image against the recently disclosed security issues to ensure nothing is left unpatched. FwHunt.run is leveraging a static analysis framework based on Rust programming language. Binarly static analysis framework translates assembly code to intermediate representation (IR), preserving all semantic metadata from assembly code and allowing more advanced analysis techniques to be used. The figure below compares different Binarly technology stacks that use FwHunt rules.
Interested in learning more about Binarly FwHunt or Binarly SaaS Platform? Don't hesitate to contact us at [email protected].
The Binarly team is constantly working to protect the firmware supply chain and reduce the attack surfaces of our customers industry-wide by delivering innovative technologies to the market. Based on our experience we understand that fixing the vulnerability for a single vendor is not enough. As a result of the complexity of the firmware supply chain, there are gaps that are difficult to close on the manufacturing end since it involves issues beyond the control of the device vendors. That’s one of the main reasons why tools like FwHunt.run and fwhunt-scan are important to help protect the firmware supply chain from the repeatable failures we can see in-the-wild. Providing such free tools to the security community is our commitment to the industry-wide firmware supply chain security and helps companies recover from massive numbers of repeatable failures happening in their devices.