Cloud Reputation Lookup (VirusTotal via MalwareBazaar Interface)

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 MBResult structure (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:

  1. Open the file using fopen(path, "rb")
  2. Create a digest context (EVP_MD_CTX_new())
  3. Initialize SHA256 (EVP_DigestInit_ex(..., EVP_sha256(), ...))
  4. Read the file in 8KB chunks and update digest (EVP_DigestUpdate())
  5. Finalize digest (EVP_DigestFinal_ex())
  6. 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 a std::string
  • The callback is registered via CURLOPT_WRITEFUNCTION and CURLOPT_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 error object, the error code is logged and the function returns an empty/default result
  • If data is 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:
    • malicious
    • suspicious
    • undetected
  • 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:

  1. If popular_threat_name is present and is a string, use that value
  2. Else if malicious > 0, use VirusTotal.Detected
  3. 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.