Mini Shell
#!/opt/imh-python/bin/python3
"""Module/Script for creating yara
based ClamAV definitions"""
import re
import sys
import os
import time
import shutil
import getpass
import argparse
class MkDef:
"""Main class for definition maker"""
def __init__(self, args):
"""sets user's database file name based on executing user"""
self.args = args
if self.args.all:
self.condition = 'any of them'
else:
self.condition = 'PHP and any of them'
# writes to tmp which should clear on boot, allowing instant definitions
self.def_file = '/opt/imh-scan/sigs/new/new.yara'
self.def_dir = os.path.split(self.def_file)[0]
self.running_user = getpass.getuser()
if self.running_user != 'root':
print('mkdef only can be ran as root')
sys.exit(1)
self.prefix = f'$new_{self.running_user}'
self.time = int(time.time())
def check_dir(self):
"""Makes NDB file if it doesnt exist"""
if not os.path.exists(self.def_dir):
os.makedirs(self.def_dir)
def definition_exists(self, definition):
"""Checks if the definition already exists in any of the dbs"""
if not os.path.exists(self.def_file):
print('making new.yara file')
return None
definition = re.escape(definition)
with open(self.def_file, encoding='utf-8') as file:
searcher = (
fr'''=\s*("|'){definition}("|')'''
fr'.*condition:\s*{self.condition}'
)
# searcher = 'strings.*condition'
file_data = file.read().replace('\n', '')
if re.search(searcher, file_data):
print('Definition already in database, skipping')
return 1
return None
def make_definition(self):
"""The function that actually writes the definition"""
e_sig = self.args.sig.replace('\n', r'\n')
if self.definition_exists(e_sig):
return
sig_line = f' $new_imh_{self.time} = "{e_sig}"\n'
default = [
f'rule imh_new_{self.time}\n',
'{\n',
' strings:\n',
f'{sig_line}\n',
' condition:\n',
f' {self.condition}\n',
'}',
]
def_dir = os.path.split(self.def_file)[0]
if not os.path.exists(def_dir):
os.makedirs(def_dir)
if not os.path.exists(self.def_file):
with open(self.def_file, 'w', encoding='utf-8') as file:
print(''.join(default), file=file)
return
insert_index, insert_legit, index = 0, 0, 0
tmp_list = []
with open(self.def_file, encoding='utf-8') as file:
lines = file.readlines()
for line in lines:
if re.search(r'\s* strings:\s*\n', line) and not insert_legit:
tmp_list += [line, sig_line]
index += 1
insert_index = index
continue
if (
insert_index
and not insert_legit
and re.search(r'\s* condition:\s*\n', line)
):
cond_index = index + 1
print(lines[cond_index])
if self.condition != lines[cond_index].strip():
print('not the right condition, deleting from tmp_list')
del tmp_list[insert_index]
else:
print('cond is good, writing')
insert_legit = 1
tmp_list.append(line)
index += 1
if not insert_legit:
print(f'adding default block to new.yara:\n{default}')
tmp_list += default
else:
print(
f'inserting line to existing cond: '
f'{self.condition}\n{sig_line}'
)
tmp_fn = f'/tmp/mkdef_{self.time}'
tmp_str = ''.join(tmp_list)
with open(tmp_fn, 'w', encoding='utf-8') as file:
print(tmp_str, file=file)
shutil.move(tmp_fn, self.def_file)
def parse_args():
"""argparse function"""
parser = argparse.ArgumentParser()
# fmt: off
parser.add_argument(
'-a', '--all', action='store_true', default=False,
help='allows the definition to match non php',
)
parser.add_argument(
'sig', type=str, nargs=1, help='signature to make a definition for'
)
# fmt: on
args = parser.parse_args()
args.sig = ''.join(args.sig)
return args
def main():
"""Main function executions"""
args = parse_args()
mkdef = MkDef(args)
mkdef.check_dir()
mkdef.make_definition()
if __name__ == '__main__':
main()
Zerion Mini Shell 1.0