Hello 26ayush,
JSON decoder should work well for this case. However, your event is too large. The standard input in the case of a console is limited by N_TTY_BUF_SIZE (65538 bytes). This is the reason your event is not decoded correctly using the logtest tool.
To troubleshoot this issue you should store your event in a file and use it as input:
/var/ossec/bin/wazuh-logtest < event
At this point, you can create a basic alert like the following and construct your Akamai ruleset using this as a parent.
<rule id="101001" level="0">
<decoded_as>json</decoded_as>
<field name="type">akamai_siem</field>
<description>Akamai SIEM event</description>
</rule>
Using wazuh-logtest
. . .
**Phase 3: Completed filtering (rules).
id: '101001'
level: '0'
description: 'Akamai SIEM event'
If you have any doubt do not hesitate to ask.
Hello Ayush, and sorry for the late reply.
In order to parse those logs we need to follow these steps:
Configure rsyslog to store your undecoded Akamai logs into a file, for example, umparsed_file.json.
Create a custom script, or use an external tool, to decode those logs every time a new event is recorded.
The following script will decode the json data attackData of your events based on the example you shared. For the rest of the fields, you will need to edit the script.
#!/usr/bin/python
import os
import json
import base64
import re
import time
import argparse
encoded_fields = ['rules', 'ruleVersions', 'ruleMessages', 'ruleTags', 'ruleData', 'ruleSelectors', 'ruleActions']
def decode_base64(data, altchars=b'+/'):
data = re.sub(rb'[^a-zA-Z0-9%s]+' % altchars, b'', data)
missing_padding = len(data) % 4
if missing_padding:
data += b'='* (4 - missing_padding)
return base64.b64decode(data, altchars)
def decode_attackData(dct):
decoded_dict = dct
if "attackData" in dct:
for key,value in dct['attackData'].items():
if key in encoded_fields:
encoded_list = [value]
encoded_value = value
decoded_list = []
if '%3d%3b' in encoded_value:
encoded_list = encoded_value.split('%3d%3b')
if '%3d%3d%3b' in encoded_value:
encoded_list = encoded_value.split('%3d%3d%3b')
for i in encoded_list:
element = i.replace('%3b', '')
element = element.replace('%3d', '')
decoded_valued = decode_base64(element.encode())
decoded_list.append(decoded_valued.decode('utf-8'))
decoded_dict['attackData'][key] = decoded_list
return decoded_dict
def tail_file(filename):
with open(filename) as file:
file.seek(0, os.SEEK_END)
while True:
line = file.readline()
if not line:
time.sleep(1)
continue
yield line
def main():
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('-i', '--input-file', metavar='<input-file>', type=str, required=True, help='Input undecoded file', dest='input_file')
arg_parser.add_argument('-o', '--output-file', metavar='<output-file>', type=str, required=True, help='Output file', dest='output_file')
args = arg_parser.parse_args()
logs_lines = tail_file(args.input_file)
for line in logs_lines:
try:
json_log = json.loads(line, object_hook=decode_attackData)
with open(args.output_file, 'a') as o:
o.write(json.dumps(json_log))
o.write('\n')
except Exception:
print(f"ERROR parsing {line}")
if __name__ == "__main__":
main()
You can use it as follows:
python3 parser_decode64.py —i <input_file> -o <output_file>
Where
input_file: Path where akamai undecoded events are stored
output_file: Path where akamai decoded events will be placed. This is the path you need to monitor with the Wazuh agent/manager
Take in mind that this script is a proof of concept, and is probably unstable and not suited for the production environment. Please edit it according to your needs.
If we use logtest as specified in my last message we get all the decoded fields:
**Phase 2: Completed decoding.
name: 'json'
attackData.clientIP: '122.161.48.222'
attackData.configId: '55210'
attackData.policyId: 'qik1_98229'
attackData.ruleActions: 'alert,alert,'
attackData.ruleData: '15108,Vector Score: 1000, Group Threshold: 9, Triggered Rules: 3000180, Triggered Scores: 1000, Triggered Selector: REQUEST_HEADERS:Content-Length, Mitigated Rules: , Last Matched Message: '
attackData.ruleMessages: 'Partial Request Body Inspection Warning - Request Body is larger than the configured inspection limit,Anomaly Score Exceeded for Web POLICY Violation,'
attackData.ruleSelectors: 'REQUEST_HEADERS:Content-LengthREQUEST_HEADERS:Content-Length'
attackData.ruleTags: 'ASE/WEB_ATTACK/POLICYASE/WEB_ATTACK/POLICY'
attackData.ruleVersions: '1,1,'
attackData.rules: '3000180,POLICY-ANOMALY'
Yes, that is the expected behavior of the script.
The script will gather every message that is placed into the input file in real-time and it will decode it into the output file.
To test this script do the following:
- Run the script in the background
python3 script.py -i input.txt -o output.txt &
- Place a testing event log into a file with the name example.txt (for example, the event specified in this message https://groups.google.com/g/wazuh/c/iJTxeRrY2Us/m/prX0CpbKBAAJ)
- Run
cat event.txt >> input.txt
You will see the same message decoded in the output file
Remind that this is only a proof of concept and I recommend improving this script for production use.