Monitoring AWS environments with Wazuh

| by | Wazuh 3.7
Post icon

AWS integration has been improved greatly in our latest release, Wazuh v3.6.1. We have both enhanced logs retrieved from S3 buckets as well as added support for additional AWS security services such as GuardDuty, Macie, and VPCFlow. Combining the information from these additional sources, Wazuh’s ability to monitor an AWS environment is stronger than ever before.

In this blog post, we will discuss how AWS provides useful tools to monitor a cloud environment and how Wazuh can incorporate all of the logs and findings (generated by Amazon GuardDuty when it detects a potentially malicious activity) from these tools directly into Wazuh alerts.  As part of this improved integration, we are demonstrating a sample of new dashboards dedicated to the new AWS information being ingested, analyzed, and acted upon. These dashboards can be found in the AWSDetonationLab repository made by Ryan Nolette.

Lastly, we will create an integration example aimed to automate a response to a typical brute force attack reported by GuardDuty.

New and Improved Dashboarding

One of the biggest additions to the AWS integration is the new collection of dashboards. They contain information about GuardDuty findings, CloudTrail events, associated instances IDs, and relevant network information provided by VPCFlow.

Amazon Web Services tab on the Wazuh app. Screenshot.

Some examples of interesting information shown in these dashboards are:

  • Almost half of the attacks detected by GuardDuty are against the same instance.
  • Most of the attacks detected by GuardDuty come from the same geographic region.
  • Almost half of the communication in the environment is made through a single network interface.
  • The last GuardDuty finding shows the instance is performing a malicious activity.
  • One-third of the CloudTrail events are StopInstance.

What actions would you take to better secure your workloads based on this information?

Blocking unprotected port attacks

One of the most common GuardDuty findings is EC2 instance has an unprotected port which is being probed by a known malicious host. This alert includes the following information:

  • Which port is being probed.
  • The remote Host performing the probing action.
  • Which instance (and its IP) is being probed.

Using this information, Wazuh can configure the instance to deny all traffic to and from that remote IP. To do so, a simple integration will be developed. That integration will read the finding’s data and create a network ACL entry to block traffic from the remote host.

Doing a custom integration with Wazuh

Wazuh can be integrated with any software to execute certain actions when an event takes place. For example, our Virustotal integration sends a file’s checksum to the Virustotal API everytime a FIM alert is raised. Integrations are similar to an Active Response.  The main difference is that integrations allow more complex actions than Active Responses since they get the full alert in JSON format while the Active Response can only receive the SrcIP field and a limited number of arguments. Integrations are defined in a specific XML section of the configuration file. When a defined condition (rule ID, rule group, rule level…) is met by a particular finding, then the daemon Integratord will call the scripts.

The following is an example of a custom integration. The configuration options needed for this use case are:

  • Name of the script the integration uses.
  • Alert rule ID that will be used as the condition for the integration.
  • Log format.


Let’s start with the simplest part: the rule. As mentioned above, the integration will be executed when a finding with a specific rule ID is triggered. The rule ID 80301 is the default rule ID for all GuardDuty findings with low severity. That rule could be used, but it’s better to define a custom rule. This way the integration will only be executed when an alert with the specific finding is raised:

<group name="amazon,">
  <rule id="100002" level="4">
    <field name="aws.description">EC2 instance has an unprotected port which is being probed by a known malicious host.</field>
    <description>Guard Duty custom rule: $(aws.description)</description>


Once the rule is defined, the following step is taken to define the script the integration will execute. For this example, I’ve developed a simple Python script which creates a new Network ACL in AWS called Wazuh and adds entries with each detected IP. The script will:

  • Be stored in/var/ossec/integrations/custom-aws_block_host
  • Have 750 permissions.
  • Have the owner root:ossec.

An integration script must have two parts: reading the alert and processing it.

Integratord writes the JSON alert in a temporary file, the path of that file is sent to the integration’s script as its first argument. To read the alert, the only requirement is to read it from argv[1] and parse it to JSON. It’s just as simple as this:

with open(sys.argv[1]) as alert_file:
    json_alert = json.loads(json.load(alert_file)['full_log'])

Once read, the next step is to process the alert.  In this example, that consists of adding the alert’s remote IP field to a Network ACL in AWS.

First, we would retrieve the necessary parameters from the alert, which in this case is a remote alert, and the region.

    ip_to_block = json_alert['aws']['service']['action']\
    region = json_alert['aws']['region']
except KeyError as e:
    raise Exception("Remote IP not found in alert")

Once the necessary information is obtained, the following step is to retrieve the Network ACL ID or create it if it doesn’t exist. For testing purposes, we’re doing everything in a new and separated Network ACL with no associated subnets, which is created using create_network_acl function, but if you want to actually block that IP, you must add the rules to the Network ACL associated with your instance.

ec2_client = boto3.client('ec2', region_name=region,
response = ec2_client.describe_network_acls(Filters=[

if len(response['NetworkAcls']) == 0:
    network_acl_id = create_network_acl(ec2_client)
    network_acl_id = response['NetworkAcls'][0]['NetworkAclId']

Once the Network ACL ID is retrieved, it is necessary to create an EC2 resource object to list which IPs are already blocked.

ec2 = boto3.resource('ec2', region_name=region,

network_acl = ec2.NetworkAcl(network_acl_id)
already_blocked_ips = set(map(operator.itemgetter('CidrBlock'),

And the last step is to actually block the IP if it hasn’t been blocked yet.

if ip_to_block+'/32' not in already_blocked_ips:
        data = ec2_client.create_network_acl_entry(
                  Egress=False #Creates inbound traffic rule

        data = ec2_client.create_network_acl_entry(
                  Egress=True #Creates outbound traffic rule
    except ClientError as e:

Since Integratord runs as ossecm, the script will as well. This can be an authentication problem if AWS keys are configured for a specific user.

Putting everything together

Finally, we simply add an integration command to ossec.conf. using the following block:


Before restarting Wazuh, it is necessary to enable the integrator daemon:

# /var/ossec/bin/ossec-control enable integrator
# systemctl restart wazuh-manager


After getting the alert, a new Network ACL is created with a rule to block traffic from the attacker’s IP.

Generated AWS event shown on the Wazuh app
Creating Network ACL and definition of rules. Screenshot.

We hope this simple example helps the community to develop its own integrations. We are willing to hear and discuss your ideas. Remember we have a Slack channel you can join, a public mailing list and GitHub repositories.