Skip to content

Commit

Permalink
Add event generator script
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexRuiz7 committed Oct 5, 2023
1 parent ec9a963 commit 2fd5259
Show file tree
Hide file tree
Showing 3 changed files with 274 additions and 6 deletions.
2 changes: 2 additions & 0 deletions ecs/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
**/mappings
*.log
generatedData.json
43 changes: 37 additions & 6 deletions ecs/README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
### ECS mappings generator
## ECS mappings generator

This script generates the ECS mappings for the Wazuh indices.

#### Requirements
### Requirements

- ECS repository clone. The script is meant to be launched from the root level of that repository.
- Python 3.6 or higher
- jq

#### Folder structrue
### Folder structrue

There is a folder for each module. Inside each folder, there is a `fields` folder with the required
files to generate the mappings. These are the inputs for the ECS generator.

#### Usage
### Usage

**Copy the `generate.sh` script to the root level of the ECS repository.**

Expand All @@ -36,7 +36,7 @@ ECS version `v8.10.0` and the Wazuh indexer in path `~/wazuh/wazuh-indexer`:
./generate.sh v8.10.0 ~/wazuh/wazuh-indexer vulnerability-detector
```

#### Output
### Output

A new `mappings` folder will be created inside the module folder, containing all the generated files.
The files are versioned using the ECS version, so different versions of the same module can be generated.
Expand All @@ -53,7 +53,20 @@ to make this template compatible with OpenSearch, the following changes are made

The script takes care of these changes automatically, generating the `opensearch-template.json` file as a result.

#### Adding new mappings
### Upload

You can either upload the index template using cURL or the UI (dev tools).

```bash
curl -u admin:admin -k -X PUT "https://indexer:9200/_index_template/wazuh-vulnerability-detector" -H "Content-Type: application/json" -d @opensearch-template.json
```

Notes:
- PUT and POST are interchangable.
- The name of the index template does not matter. Any name can be used.
- Adjust credentials and URL accordingly.

### Adding new mappings

The easiest way to create mappings for a new module is to take a previous one as a base.
Copy a folder and rename it to the new module name. Then, edit the `fields` files to
Expand All @@ -66,6 +79,24 @@ are required.
- `fields/template-settings-legacy.json`: This file contains the legacy template settings for the module.
- `fields/template-settings.json`: This file contains the composable template settings for the module.

### Event generator

For testing purposes, the script `generate_events.py` can be used to generate events for a given module.
Currently, it is only able to generate events for the `vulnerability-detector` module. To support other
modules, please extend of refactor the script.

The script prompts for the required parameters, so it can be launched without arguments:

```bash
./event_generator.py
```

The script will generate a JSON file with the events, and will also ask whether to upload them to the
indexer. If the upload option is selected, the script will ask for the indexer URL and port, credentials,
and index name.

The script uses log file. Check it out for debugging or additonal information.

#### References

- [ECS repository](https://github.com/elastic/ecs)
Expand Down
235 changes: 235 additions & 0 deletions ecs/vulnerability-detector/event-generator/event_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
#!/bin/python3

# This script generates sample events and injects them into the Wazuh Indexer.
# The events follow the Elastic Common Schema (ECS) format, and contains the following fields:
# - ecs
# - base
# - event
# - agent
# - package
# - host
# - vulnerability
#
# This is an ad-hoc script for the vulnearbility module. Extend to support other modules.

import datetime
import random
import json
import requests
import warnings
import logging

# Constants and Configuration
LOG_FILE = 'generate_data.log'
GENERATED_DATA_FILE = 'generatedData.json'
DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"

# Configure logging
logging.basicConfig(filename=LOG_FILE, level=logging.INFO)

# Suppress warnings
warnings.filterwarnings("ignore")


def generate_random_date():
start_date = datetime.datetime.now()
end_date = start_date - datetime.timedelta(days=10)
random_date = start_date + (end_date - start_date) * random.random()
return random_date.strftime(DATE_FORMAT)


def generate_random_agent():
agent = {
'build': {'original': f'build{random.randint(0, 9999)}'},
'id': f'agent{random.randint(0, 99)}',
'name': f'Agent{random.randint(0, 99)}',
'version': f'v{random.randint(0, 9)}-stable',
'ephemeral_id': f'{random.randint(0, 99999)}',
'type': random.choice(['filebeat', 'windows', 'linux', 'macos'])
}
return agent


def generate_random_event():
event = {
'action': random.choice(['login', 'logout', 'create', 'delete', 'modify', 'read', 'write', 'upload', 'download',
'copy', 'paste', 'cut', 'move', 'rename', 'open', 'close', 'execute', 'run', 'install',
'uninstall', 'start', 'stop', 'kill', 'suspend', 'resume', 'sleep', 'wake', 'lock',
'unlock', 'encrypt', 'decrypt', 'compress', 'decompress', 'archive', 'unarchive',
'mount', 'unmount', 'eject', 'connect', 'disconnect', 'send', 'receive']),
'agent_id_status': random.choice(['verified', 'mismatch', 'missing', 'auth_metadata_missing']),
'category': random.choice(['authentication', 'authorization', 'configuration', 'communication', 'file',
'network', 'process', 'registry', 'storage', 'system', 'web']),
'code': f'{random.randint(0, 99999)}',
'created': generate_random_date(),
'dataset': random.choice(['process', 'file', 'registry', 'socket', 'dns', 'http', 'tls', 'alert',
'authentication', 'authorization', 'configuration', 'communication', 'file',
'network', 'process', 'registry', 'storage', 'system', 'web']),
'duration': random.randint(0, 99999),
'end': generate_random_date(),
'hash': str(hash(f'hash{random.randint(0, 99999)}')),
'id': f'{random.randint(0, 99999)}',
'ingested': generate_random_date(),
'kind': random.choice(['alert', 'asset', 'enrichment', 'event', 'metric',
'state', 'pipeline_error', 'signal']),
'module': random.choice(['process', 'file', 'registry', 'socket', 'dns', 'http', 'tls', 'alert',
'authentication', 'authorization', 'configuration', 'communication', 'file',
'network', 'process', 'registry', 'storage', 'system', 'web']),
'original': f'original{random.randint(0, 99999)}',
'outcome': random.choice(['success', 'failure', 'unknown']),
'provider': random.choice(['process', 'file', 'registry', 'socket', 'dns', 'http', 'tls', 'alert',
'authentication', 'authorization', 'configuration', 'communication', 'file',
'network', 'process', 'registry', 'storage', 'system', 'web']),
'reason': f'This event happened due to reason{random.randint(0, 99999)}',
'reference': f'https://system.example.com/event/#{random.randint(0, 99999)}',
'risk_score': round(random.uniform(0, 10), 1),
'risk_score_norm': round(random.uniform(0, 10), 1),
'sequence': random.randint(0, 10),
'severity': random.randint(0, 10),
'start': generate_random_date(),
'timezone': random.choice(['UTC', 'GMT', 'PST', 'EST', 'CST', 'MST', 'PDT', 'EDT', 'CDT', 'MDT']),
'type': random.choice(['access', 'admin', 'allowed', 'change', 'connection', 'creation', 'deletion',
'denied', 'end', 'error', 'group', 'indicator', 'info', 'installation', 'protocol',
'start', 'user']),
'url': f'http://mysystem.example.com/alert/{random.randint(0, 99999)}'
}
return event


def generate_random_host():
family = random.choice(['debian', 'ubuntu', 'macos', 'ios', 'android', 'RHEL'])
version = f'{random.randint(0, 99)}.{random.randint(0, 99)}'
host = {
'os': {
'family': family,
'full': f'{family} {version}',
'kernel': f'{version}kernel{random.randint(0, 99)}',
'name': f'{family} {version}',
'platform': family,
'type': random.choice(['windows', 'linux', 'macos', 'ios', 'android', 'unix']),
'version': version
}
}
return host


def generate_random_labels():
labels = {'label1': f'label{random.randint(0, 99)}', 'label2': f'label{random.randint(0, 99)}'}
return labels


def generate_random_package():
package = {
'architecture': random.choice(['x86', 'x64', 'arm', 'arm64']),
'build_version': f'build{random.randint(0, 9999)}',
'checksum': f'checksum{random.randint(0, 9999)}',
'description': f'description{random.randint(0, 9999)}',
'install_scope': random.choice(['user', 'system']),
'installed': generate_random_date(),
'license': f'license{random.randint(0, 9)}',
'name': f'name{random.randint(0, 99)}',
'path': f'/path/to/package{random.randint(0, 99)}',
'reference': f'package-reference-{random.randint(0, 99)}',
'size': random.randint(0, 99999),
'type': random.choice(['deb', 'rpm', 'msi', 'pkg', 'app', 'apk', 'exe', 'zip', 'tar', 'gz', '7z',
'rar', 'cab', 'iso', 'dmg', 'tar.gz', 'tar.bz2', 'tar.xz', 'tar.Z', 'tar.lz4',
'tar.sz', 'tar.zst']),
'version': f'v{random.randint(0, 9)}-stable'
}
return package


def generate_random_tags():
tags = [f'tag{random.randint(0, 99)}' for _ in range(random.randint(0, 9))]
return tags


def generate_random_vulnerability():
id = random.randint(0, 9999)
vulnerability = {
'category': random.choice(['security', 'config', 'os', 'package', 'custom']),
'classification': [f'classification{random.randint(0, 9999)}'],
'description': f'description{random.randint(0, 9999)}',
'enumeration': 'CVE',
'id': f'CVE-{id}',
'reference': f'https://mycve.test.org/cgi-bin/cvename.cgi?name={id}',
'report_id': f'report-{random.randint(0, 9999)}',
'scanner': {'vendor': f'vendor-{random.randint(0, 9)}'},
'score': {
'base': round(random.uniform(0, 10), 1),
'environmental': round(random.uniform(0, 10), 1),
'temporal': round(random.uniform(0, 10), 1),
'version': round(random.uniform(0, 10), 1)
},
'severity': random.choice(['low', 'medium', 'high', 'critical'])
}
return vulnerability


def generate_random_data(number):
data = []
for _ in range(number):
event_data = {
'@timestamp': generate_random_date(),
'agent': generate_random_agent(),
'ecs': {'version': '1.7.0'},
'event': generate_random_event(),
'host': generate_random_host(),
'labels': generate_random_labels(),
'message': f'message{random.randint(0, 99999)}',
'package': generate_random_package(),
'tags': generate_random_tags(),
'vulnerability': generate_random_vulnerability()
}
data.append(event_data)
return data


def inject_events(ip, port, index, username, password, data):
url = f'https://{ip}:{port}/{index}/_doc'
session = requests.Session()
session.auth = (username, password)
session.verify = False
headers = {'Content-Type': 'application/json'}

try:
for event_data in data:
response = session.post(url, json=event_data, headers=headers)
if response.status_code != 201:
logging.error(f'Error: {response.status_code}')
logging.error(response.text)
break
logging.info('Data injection completed successfully.')
except Exception as e:
logging.error(f'Error: {str(e)}')


def main():
try:
number = int(input("How many events do you want to generate? "))
except ValueError:
logging.error("Invalid input. Please enter a valid number.")
return

logging.info(f"Generating {number} events...")
data = generate_random_data(number)

with open(GENERATED_DATA_FILE, 'a') as outfile:
for event_data in data:
json.dump(event_data, outfile)
outfile.write('\n')

logging.info('Data generation completed.')

inject = input("Do you want to inject the generated data into your indexer? (y/n) ").strip().lower()
if inject == 'y':
ip = input("Enter the IP of your Indexer: ")
port = input("Enter the port of your Indexer: ")
index = input("Enter the index name: ")
username = input("Username: ")
password = input("Password: ")
inject_events(ip, port, index, username, password, data)


if __name__ == "__main__":
main()

0 comments on commit 2fd5259

Please sign in to comment.