1. Overview
The Cloud Reputation Lookup module provides a lightweight integration between BastionGuard and the VirusTotal v3 API, exposing hash-based reputation queries through an interface named MalwareBazaar.
The module supports:
- Loading an API key from a user configuration file (
~/.config/BastionGuard/cloud.conf) - Computing a file SHA256 locally using OpenSSL EVP APIs
- Querying VirusTotal by SHA256 (
/api/v3/files/<sha256>) - Parsing JSON responses using
nlohmann::json - Returning a normalized
MBResultstructure (found/name/type/tags)
Although the interface is named MalwareBazaar, the current implementation targets VirusTotal and uses a VirusTotal API key header (x-apikey).
2. Components and Dependencies
The module integrates the following libraries and subsystems:
- libcurl – HTTPS requests to the VirusTotal REST API
- nlohmann::json – JSON parsing and field extraction
- OpenSSL EVP – streaming SHA256 computation for local files
- glibmm – home directory resolution (
Glib::get_home_dir()) - glib/gi18n – i18n message macros (logging strings wrapped in
_())
3. API Key Management
3.1 Global API Key Storage
The API key is stored in a process-global variable:
std::string globalMalwareBazaarApiKey = "";
This key is required for any remote lookup operation. If unset, lookups fail fast and return an empty/default result.
3.2 Loading API Key from cloud.conf
The module loads the key from:
~/.config/BastionGuard/cloud.conf
Key loading is performed by loadCloudApiKeys(), which:
- Opens the configuration file under the user home directory
- Reads the file line-by-line
- Strips whitespace from each line
- Matches the prefix
malware_bazaar_api_key= - Extracts and stores the remainder as the API key value
If the file is missing, a diagnostic message is logged and the function returns without modifying the key.
3.3 Key Naming and Provider Note
Despite using the prefix malware_bazaar_api_key=, the key is currently used as a VirusTotal API key (header x-apikey and VirusTotal endpoint). This naming is maintained for compatibility with existing configuration and interfaces.
4. Local SHA256 Computation
4.1 File Hashing via OpenSSL EVP
Local SHA256 computation is implemented in MalwareBazaar::sha256_file(path) using OpenSSL EVP APIs.
Operational flow:
- Open the file using
fopen(path, "rb") - Create a digest context (
EVP_MD_CTX_new()) - Initialize SHA256 (
EVP_DigestInit_ex(..., EVP_sha256(), ...)) - Read the file in 8KB chunks and update digest (
EVP_DigestUpdate()) - Finalize digest (
EVP_DigestFinal_ex()) - Convert the binary digest to lowercase hex and return it as a string
If any step fails, the function returns an empty string and ensures resources are released (digest context and file handle).
4.2 Output Format
The returned SHA256 is formatted as:
- Lowercase hexadecimal
- Zero-padded bytes (
std::setw(2),std::setfill('0'))
5. Network Query Implementation
5.1 Lookup Entry Point
Remote lookups are performed by:
MBResult MalwareBazaar::lookupHash(const std::string& sha256)
The function returns an MBResult object, populated from VirusTotal response data when available.
5.2 Precondition: API Key Required
If globalMalwareBazaarApiKey is empty, the function logs a message and returns an empty/default MBResult without making any network requests.
5.3 VirusTotal Endpoint and Headers
The module queries the VirusTotal v3 endpoint:
https://www.virustotal.com/api/v3/files/<sha256>
Authentication is provided via a request header:
x-apikey: <API_KEY>
The request also sets a custom user agent:
BastionGuard-VT-Agent
5.4 libcurl Response Collection
HTTP response data is captured using a write callback:
write_callback(contents, size, nmemb, out)appends received bytes into astd::string- The callback is registered via
CURLOPT_WRITEFUNCTIONandCURLOPT_WRITEDATA
On network or transfer errors (curl_easy_perform != CURLE_OK), the function logs an error and returns an empty/default result.
6. JSON Parsing and Result Normalization
6.1 JSON Parsing Strategy
The module parses the response using nlohmann::json::parse with error suppression:
- If parsing fails,
j.is_discarded()is true and the function returns an empty/default result - If the response includes an
errorobject, the error code is logged and the function returns an empty/default result - If
datais absent, the sample is treated as not found
6.2 Extracted Fields
When data is present, the module reads from:
j["data"]["attributes"]
The following fields are extracted and mapped into MBResult:
- Detection statistics from
last_analysis_stats:malicioussuspiciousundetected
- Threat name from
popular_threat_name(if present and string) - File type from
type_description(fallback:Unknown) - Tags from
tags[]concatenated into a space-separated string
6.3 Found/Detected Flag
The MBResult::found flag is set to true when:
malicious > 0 || suspicious > 0
This provides a normalized decision signal independent of the threat naming logic.
6.4 Malware Name Fallback Strategy
The malware name returned in MBResult::malwareName is determined as follows:
- If
popular_threat_nameis present and is a string, use that value - Else if
malicious > 0, useVirusTotal.Detected - Else, use
Clean
7. Runtime and Security Considerations
- Key storage scope: the API key is stored in a global variable; it should be treated as sensitive and should not be logged or exposed in UI
- Config hardening: configuration parsing strips whitespace and only accepts a fixed prefix (
malware_bazaar_api_key=) - Network privacy: the lookup transmits the SHA256 to a third-party service (VirusTotal), which may be subject to external retention policies
- Fail-closed behavior: missing key, network errors, invalid JSON, or API errors return an empty/default result without crashing the process
- Cryptographic implementation: hashing uses the OpenSSL EVP streaming interface, avoiding full-file memory loading
- Localization layer: log messages are wrapped in
_()for translation; operationally critical parsing prefixes should remain stable to avoid locale-dependent breakage
8. Integration Notes
This module is intended to be used as a backend utility for cloud reputation checks within BastionGuard workflows such as:
- Post-scan enrichment of detections (hash reputation)
- Quarantine review actions (verify sample reputation)
- Manual inspection utilities (operator-driven hash lookup)
The current implementation is designed for VirusTotal while preserving an interface name compatible with MalwareBazaar-style workflows. Provider abstraction can be extended by introducing additional endpoints and configuration keys while keeping the same MBResult contract.