MalwareBazaar Feed

Use the MalwareBazaar Feed integration to get the list of malware samples added to MalwareBazaar within the last 60 minutes.

Data Enrichment & Threat Intelligence · MalwareBazaar Feed · Feed

Details

IDMalwareBazaar Feed
ProviderAbuseCh
CategoryData Enrichment & Threat Intelligence
From Version6.0.0
Docker Imagedemisto/py3-tools:1.0.0.10120494
Supported ModulesAgentix XSIAM

README

The MalwareBazaar Feed integration leverages abuse.ch to get the list of malware samples added to MalwareBazaar within the last 60 minutes.

Configure MalwareBazaar Feed in Cortex

Parameter Description Required
Server URL   True
Fetch indicators   False
Auth Key Auth Key for authentication with abuser.ch True
Indicator Reputation Indicators from this integration instance will be marked with this reputation. False
Source Reliability Reliability of the source providing the intelligence data. True
Traffic Light Protocol Color The Traffic Light Protocol (TLP) designation to apply to indicators fetched from the feed. False
Feed Fetch Interval   False
Bypass exclusion list When selected, the exclusion list is ignored for indicators from this feed. This means that if an indicator from this feed is on the exclusion list, the indicator might still be added to the system. False
Tags Supports CSV values. False
Create relationships   False
Trust any certificate (not secure)   False
Use system proxy settings   False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

malwarebazzar-get-indicators


Gets the feed indicators.

Base Command

malwarebazzar-get-indicators

Input

Argument Name Description Required
limit The maximum number of results to return. The default value is 50. Default is 50. Optional

Context Output

There is no context output for this command.

Command Example

!malwarebazzar-get-indicators limit=3

Human Readable Output

Indicators

value type rawJSON
2fd9985380a92fa918df25189e1023c27052355dc1c7f8a2d11339be7ddfc7d3 File sha256_hash: 2fd9985380a92fa918df25189e1023c27052355dc1c7f8a2d11339be7ddfc7d3
sha3_384_hash: be865100f4cb963d9dbe6cd10b453cdfc7f5ee7f22975e7c7c8120a9631b797880f58ae842d02761f074429aeb160057
sha1_hash: 4068065df0d806c05b1bd6138bdb32f483c816a2
md5_hash: a76e2cdba762de201e0668178b03bec4
first_seen: 2021-10-04 11:26:00
last_seen: null
file_name: a76e2cdba762de201e0668178b03bec4.exe
file_size: 119016
file_type_mime: application/x-dosexec
file_type: exe
reporter: abuse_ch
origin_country: FR
anonymous: 0
signature: RedLineStealer
imphash: f34d5f2d4577ed6d9ceec516c1f5a744
tlsh: T1BBC33B3553DC8A28EBFE1F75B071120047F0D1CB6512EB5B9EC26CEA5E73B825A512E2
telfhash: null
ssdeep: 1536:5e1zHb1iaNk5mXKSr7y9zjbuRNdsnhuyq/dMV1dbknEjG6qTaoigNK:0S0X7y978snYyOdM9kEW9K
dhash_icon: null
tags: exe,
RedLineStealer
code_sign:
intelligence: {“clamav”: null, “downloads”: “1”, “uploads”: “1”, “mail”: null}
e9dc15fdcf77dd8dde52392b63a79e43e39d85431434198f5103ced790f9034a File sha256_hash: e9dc15fdcf77dd8dde52392b63a79e43e39d85431434198f5103ced790f9034a
sha3_384_hash: 362f8c4a885bacc2158dd1b1f3f96848b982ac94df8e67b930b0dc766369d08bdbb0c97b713a9a561d3f72be4c65f27c
sha1_hash: f59a1e433565255d52720f49ce0f4e52c29b78bf
md5_hash: f0e38ba220616f3b94a5f71712d52817
first_seen: 2021-10-04 11:13:59
last_seen: 2021-10-04 11:14:29
file_name: swift art.pdf.exe
file_size: 422912
file_type_mime: application/x-dosexec
file_type: exe
reporter: cocaman
origin_country: FR
anonymous: 0
signature: AgentTesla
imphash: f34d5f2d4577ed6d9ceec516c1f5a744
tlsh: T1CB94026823A7D314DE3483F43C18A69127FA742B151DD37C5E89E4ED3E52B748AE0A87
telfhash: null
ssdeep: 6144:6Axt05ERQ+3HwO0adBqsB9+U1eqjRISdJOyYge708qlv5/Mf76oAeEHm9E0:iESoZ4C9AWIzJgeCvlY5AeEKh
dhash_icon: null
tags: AgentTesla,
exe,
SWIFT
code_sign:
intelligence: {“clamav”: null, “downloads”: “30”, “uploads”: “2”, “mail”: null}
3e1a48cb4283d83c729670dbdc69d6a8d5290a50bebc621baf7e0a72cd56ffb6 File sha256_hash: 3e1a48cb4283d83c729670dbdc69d6a8d5290a50bebc621baf7e0a72cd56ffb6
sha3_384_hash: 1104c8634247f86d15f3e61faa68d7232f797936e3cf838b21052d4a9ebf52a6a523462e9e1ce3bdb9a13f5e51a5da86
sha1_hash: a5035ff9bd7264431a254a0b973864febfb19c7f
md5_hash: ca13a97b94b0e17f5d863a2290f10931
first_seen: 2021-10-04 11:13:49
last_seen: 2021-10-04 11:14:20
file_name: swift art.pdf.zip
file_size: 372101
file_type_mime: application/zip
file_type: zip
reporter: cocaman
origin_country: US
anonymous: 0
signature: AgentTesla
imphash: null
tlsh: T15A842305488BE237E7433679D60D0A4173D9399B0F15F6FAEC1B9B67AE811108EF49CA
telfhash: null
ssdeep: 6144:aI84xUdNqsB9+U1eq7RUSdROy8g27o8qpv5hM3N6sAIEPmh7hF8:Z84xyUA9gwUNng2avz2ZAImA/8
dhash_icon: null
tags: AgentTesla,
SWIFT,
zip
code_sign:
intelligence: {“clamav”: null, “downloads”: “25”, “uploads”: “2”, “mail”: null}

Create an Auth Key

for abuse.ch

  1. Sign up for an abuse.ch account by using an existing account that you may already have on X, LinkedIn, Google or Github. Just log in with the authentication provider of your choice here: https://auth.abuse.ch/
  2. Once you’ve logged in to abuse.ch, add at least one more way to log in. This helps ensure you can always access our platforms, even if one of your login methods stops working.
  3. Click the Save profile button. In the Optional section, you can now create an Auth-Key. This is your personal authentication key that you can use to query any abuse.ch APIs.

If you already have a profile, you only need to follow step 3. There’s nothing further to do for your authentication set up.

Configuration parameters

  • url — Server URL (required)
  • credentials
  • feed — Fetch indicators
  • feedReputation — Indicator Reputation
  • feedReliability — Source Reliability (required)
  • tlp_color — Traffic Light Protocol Color
  • feedExpirationPolicy
  • feedExpirationInterval
  • feedFetchInterval — Feed Fetch Interval
  • feedBypassExclusionList — Bypass exclusion list
  • feedTags — Tags
  • create_relationships — Create relationships
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (1)

  • malwarebazzar-get-indicators

    Gets the feed indicators.

import demistomock as demisto
import pytest
from pytest_mock import MockerFixture
from requests import HTTPError


def test_fetch_indicators_main(mocker: MockerFixture):
    """
    Given
    - indicators response from malware bazzar feed

    When
    - Running main flow for fetching indicators command

    Then
    - Ensure that all indicators values exist and are not 'None'
    """
    from JSONFeedApiModule import Client
    from MalwareBazaarFeed import main

    mocker.patch.object(
        demisto,
        "params",
        return_value={
            "feed": True,
            "feedBypassExclusionList": False,
            "feedExpirationInterval": "20160",
            "feedExpirationPolicy": "suddenDeath",
            "feedFetchInterval": 1,
            "feedReliability": "A - Completely reliable",
            "feedReputation": "None",
            "feedTags": None,
            "insecure": True,
            "ip_ranges": "All available Google IP ranges",
            "proxy": False,
            "tlp_color": None,
            "url": "https://mb-api.abuse.ch",
            "credentials": {"password": "test"},
        },
    )
    mocker.patch.object(demisto, "command", return_value="fetch-indicators")
    create_indicators_mocker = mocker.patch.object(demisto, "createIndicators")

    mocker.patch.object(
        Client,
        "build_iterator",
        side_effect=[([{"sha256_hash": "1234"}, {"sha256_hash": "12345"}, {"sha256_hash": "123456"}], True), ([], True)],
    )

    main()

    assert create_indicators_mocker.call_args.args[0] == [
        {
            "type": "File",
            "fields": {"tags": [], "downloadurl": "https://bazaar.abuse.ch/sample/1234/"},
            "value": "1234",
            "rawJSON": {"sha256_hash": "1234"},
        },
        {
            "type": "File",
            "fields": {"tags": [], "downloadurl": "https://bazaar.abuse.ch/sample/12345/"},
            "value": "12345",
            "rawJSON": {"sha256_hash": "12345"},
        },
        {
            "type": "File",
            "fields": {"tags": [], "downloadurl": "https://bazaar.abuse.ch/sample/123456/"},
            "value": "123456",
            "rawJSON": {"sha256_hash": "123456"},
        },
    ]


def test_custom_build_relationships():
    """
    Given
    - no feed config

    When
    - Running custom_build_relationships function

    Then
    - Ensure the relationship list is empty
    """
    from MalwareBazaarFeed import custom_build_relationships

    assert custom_build_relationships(feed_config={}, _mapping={}, indicator_data={}) == []


def test_custom_mapping_function():
    """
    Given
    - mapping, attributes and indicators

    When
    - Running custom_mapping_function function

    Then
    - Ensure indicator dict is updated
    """
    from MalwareBazaarFeed import custom_mapping_function

    mapping = {"a": "a.b", "c": "c", "d": "d"}
    attributes = {"a": "a", "b": "b", "c": "c"}
    indicator = {"fields": {"a": [{"a": "a"}], "b": "b"}}

    custom_mapping_function(mapping, indicator, attributes)

    assert indicator == {"fields": {"a": [{"a": "a", "b": "a"}], "b": "b", "c": "c"}}


@pytest.mark.parametrize(
    "err_msg",
    [
        pytest.param("502 Server Error: Bad Gateway for url: https://test/v1/", id="HTTPError 502"),
        pytest.param("Connection broken: IncompleteRead(3923 bytes read, 4269 more expected", id="Connection broken"),
        pytest.param("503 Server Error: Service Unavailable for url: https://test/v1/", id="HTTPError 503"),
    ],
)
def test_exceptions_handle_known_exception(mocker: MockerFixture, err_msg: str):
    """
    In the main function of the MalwareBazaar Feed we handle exceptions as follows:
        - Catch the exceptions occurred during the run of the 'feed_main' function.
        - Check if the exception is a known error.
        - If yes - wait 10 seconds and start another retry of the 'feed_main' function.
        (number or retries is 3).
    This test tests this logic.

    Given
        - A mock response of the 'feed_main' function that raises a known exception.
        1. The exception is a known 502 server error.
        2. The exception is a known connection error.
        3. The exception is a known 503 server error.

    When
        - Running main flow for the fetching indicators command.

    Then
        - Verify that the exception is caught and the 'feed_main' function is called 4 times in total.
        - Verify that the error logs are as expected.
    """
    from MalwareBazaarFeed import main

    mocker.patch.object(demisto, "params", return_value={"credentials": {"password": "test"}})
    mocker.patch.object(demisto, "command", return_value="fetch-indicators")
    error_log_mock = mocker.patch.object(demisto, "error")

    def mock_feed_main(params, feed_name, prefix):
        raise HTTPError(err_msg)

    feed_main_mock = mocker.patch("MalwareBazaarFeed.feed_main", side_effect=mock_feed_main)

    with pytest.raises(Exception):
        main()
    assert "An Error Occurred during the run of the 'fetch-indicators' command" in error_log_mock.call_args_list[0][0][0]
    assert err_msg in error_log_mock.call_args_list[0][0][0]
    assert "Retrying in 10 seconds..." in error_log_mock.call_args_list[1][0][0]
    assert "This is attempt number: 1" in error_log_mock.call_args_list[1][0][0]
    assert "This is attempt number: 2" in error_log_mock.call_args_list[3][0][0]
    assert "This is attempt number: 3" in error_log_mock.call_args_list[5][0][0]
    assert feed_main_mock.call_count == 4


def test_exceptions_handle_unknown_exception(mocker):
    """
    In the main function of the MalwareBazaar Feed we handle exceptions as follows:
        - Catch the exceptions occurred during the run of the 'feed_main' function.
        - Check if the exception is a known error.
        - If yes - wait 10 seconds and start another retry of the 'feed_main' function.
        (number or retries is 3).
        - If no - raises an error.
    This test tests this logic.

    Given
        - A mock response of the 'feed_main' function that raises an un-known exception.

    When
        - Running main flow for the fetching indicators command.

    Then
        - Verify that the exception is caught but the 'feed_main' function called only once.
        - Verify that the original error is raised.
    """
    from MalwareBazaarFeed import main

    mocker.patch.object(demisto, "params", return_value={"credentials": {"password": "test"}})
    mocker.patch.object(demisto, "command", return_value="fetch-indicators")
    error_log_mock = mocker.patch.object(demisto, "error")

    def mock_feed_main(params, feed_name, prefix):
        raise HTTPError(
            "[429] Failed with error: [parent] Data too large, data for [<http_request>] "
            "would be [1033445328/985.5mb], which is larger than the limit of [1020054732/972.7mb]"
        )

    feed_main_mock = mocker.patch("MalwareBazaarFeed.feed_main", side_effect=mock_feed_main)

    with pytest.raises(Exception) as e:
        main()
    assert "[429] Failed with error: [parent] Data too large, data for [<http_request>]" in str(e)
    assert len(error_log_mock.call_args_list) == 0
    assert feed_main_mock.call_count == 1


def test_exception_thrown_when_no_auth_key_param(mocker):
    from MalwareBazaarFeed import main

    mocker.patch.object(demisto, "params", return_value={})
    mocker.patch.object(demisto, "command", return_value="fetch-indicators")
    error_log_mock = mocker.patch.object(demisto, "error")

    def mock_feed_main(params, feed_name, prefix):
        pass

    feed_main_mock = mocker.patch("MalwareBazaarFeed.feed_main", side_effect=mock_feed_main)

    with pytest.raises(Exception) as e:
        main()

    assert "Missing required parameter Auth Key. Please set this parameter in the instance configuration." in str(e)
    assert len(error_log_mock.call_args_list) == 0
    assert feed_main_mock.call_count == 0


def test_no_exception_thrown_when_no_auth_key_param(mocker):
    from MalwareBazaarFeed import main

    mocker.patch.object(demisto, "params", return_value={"credentials": {"password": "test"}})
    mocker.patch.object(demisto, "command", return_value="fetch-indicators")
    error_log_mock = mocker.patch.object(demisto, "error")

    def mock_feed_main(params, feed_name, prefix):
        return None

    feed_main_mock = mocker.patch("MalwareBazaarFeed.feed_main", side_effect=mock_feed_main)

    main()
    assert len(error_log_mock.call_args_list) == 0
    assert feed_main_mock.call_count == 1


@pytest.mark.parametrize(
    "feed_tags, api_tags, expected_tags",
    [
        pytest.param(
            ["source_mbazaar"],
            ["SilverFox", "ValleyRAT", "zip"],
            ["source_mbazaar", "SilverFox", "ValleyRAT", "zip"],
            id="merges_feed_tags_with_api_tags",
        ),
        pytest.param(
            ["source_mbazaar"],
            None,
            ["source_mbazaar"],
            id="preserves_feed_tags_when_api_tags_is_none",
        ),
        pytest.param(
            ["source_mbazaar"],
            [],
            ["source_mbazaar"],
            id="preserves_feed_tags_when_api_tags_is_empty",
        ),
        pytest.param(
            ["source_mbazaar", "SilverFox"],
            ["SilverFox", "ValleyRAT"],
            ["source_mbazaar", "SilverFox", "ValleyRAT"],
            id="deduplicates_overlapping_tags",
        ),
    ],
)
def test_custom_mapping_function_tags_merge(feed_tags: list, api_tags: list | None, expected_tags: list):
    """
    Regression test for XSUP-68506:
    The 'tags' mapping entry in MalwareBazaarFeed must MERGE the API response tags
    with the existing feedTags (set by JSONFeedApiModule.handle_indicator) rather
    than overwriting them.

    Given
    - An indicator whose fields["tags"] has already been populated with feedTags
      by handle_indicator.
    - A mapping that maps the API "tags" field to the indicator "tags" field.
    - Attributes from the API response with various tag values (list, None, empty).

    When
    - custom_mapping_function is called.

    Then
    - The resulting indicator["fields"]["tags"] matches the expected merged/deduplicated list.
    - The configured feedTags value is NOT lost.
    """
    from MalwareBazaarFeed import custom_mapping_function

    mapping = {"tags": "tags"}
    attributes = {"tags": api_tags}
    indicator = {"fields": {"tags": feed_tags.copy()}, "value": "abc123"}

    custom_mapping_function(mapping, indicator, attributes)

    assert sorted(indicator["fields"]["tags"]) == sorted(expected_tags)


def test_exceptions_handler_no_exception(mocker):
    """
    In the main function of the MalwareBazaar Feed we handle exceptions as follows:
        - Catch the exceptions occurred during the run of the 'feed_main' function.
        - Check if the exception is a known error.
        - If yes - wait 10 seconds and start another retry of the 'feed_main' function.
        (number or retries is 3).
        - If no - raises an error.
        - If no error occurred the function is ended (without any recursive calls).
    This test tests this logic in case of a success (when no errors were detected in the feed_main function).

    Given
        - A mock response of the 'feed_main' function that returns None.

    When
        - Running main flow for the fetching indicators command.

    Then
        - Verify that the 'feed_main' function called only once.
    """
    from MalwareBazaarFeed import main

    mocker.patch.object(demisto, "params", return_value={"credentials": {"password": "test"}})
    mocker.patch.object(demisto, "command", return_value="fetch-indicators")
    error_log_mock = mocker.patch.object(demisto, "error")

    def mock_feed_main(params, feed_name, prefix):
        return None

    feed_main_mock = mocker.patch("MalwareBazaarFeed.feed_main", side_effect=mock_feed_main)

    main()
    assert len(error_log_mock.call_args_list) == 0
    assert feed_main_mock.call_count == 1