로그 기반 아티팩트 조사
지금까지 Python을 사용하여 Windows에서 아티팩트를 얻는 방법을 살펴 보았습니다. 이 장에서는 Python을 사용한 로그 기반 아티팩트 조사에 대해 알아 보겠습니다.
소개
로그 기반 인공물은 디지털 포렌식 전문가에게 매우 유용 할 수있는 정보의보고입니다. 정보 수집을위한 다양한 모니터링 소프트웨어가 있지만 유용한 정보를 파싱하는 주요 문제는 많은 데이터가 필요하다는 것입니다.
다양한 로그 기반 아티팩트 및 Python에서 조사
이 섹션에서는 다양한 로그 기반 아티팩트와 Python에서의 조사에 대해 설명합니다.
타임 스탬프
타임 스탬프는 로그에서 활동의 데이터와 시간을 전달합니다. 모든 로그 파일의 중요한 요소 중 하나입니다. 이러한 데이터 및 시간 값은 다양한 형식으로 제공 될 수 있습니다.
아래 표시된 Python 스크립트는 원시 날짜-시간을 입력으로 사용하고 형식이 지정된 타임 스탬프를 출력으로 제공합니다.
이 스크립트의 경우 다음 단계를 따라야합니다.
먼저 데이터 소스 및 데이터 유형과 함께 원시 데이터 값을 사용할 인수를 설정합니다.
이제 다양한 날짜 형식의 데이터에 대한 공통 인터페이스를 제공하는 클래스를 제공합니다.
파이썬 코드
이 목적으로 파이썬 코드를 사용하는 방법을 살펴 보겠습니다.
먼저 다음 Python 모듈을 가져옵니다.
from __future__ import print_function
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from datetime import datetime as dt
from datetime import timedelta
이제 평소와 같이 명령 줄 처리기에 대한 인수를 제공해야합니다. 여기서는 세 개의 인수를받습니다. 첫 번째는 처리 할 날짜 값, 두 번째는 해당 날짜 값의 소스, 세 번째는 해당 유형입니다.
if __name__ == '__main__':
parser = ArgumentParser('Timestamp Log-based artifact')
parser.add_argument("date_value", help="Raw date value to parse")
parser.add_argument(
"source", help = "Source format of date",choices = ParseDate.get_supported_formats())
parser.add_argument(
"type", help = "Data type of input value",choices = ('number', 'hex'), default = 'int')
args = parser.parse_args()
date_parser = ParseDate(args.date_value, args.source, args.type)
date_parser.run()
print(date_parser.timestamp)
이제 날짜 값, 날짜 소스 및 값 유형에 대한 인수를 허용하는 클래스를 정의해야합니다.
class ParseDate(object):
def __init__(self, date_value, source, data_type):
self.date_value = date_value
self.source = source
self.data_type = data_type
self.timestamp = None
이제 main () 메서드처럼 컨트롤러처럼 작동하는 메서드를 정의합니다.
def run(self):
if self.source == 'unix-epoch':
self.parse_unix_epoch()
elif self.source == 'unix-epoch-ms':
self.parse_unix_epoch(True)
elif self.source == 'windows-filetime':
self.parse_windows_filetime()
@classmethod
def get_supported_formats(cls):
return ['unix-epoch', 'unix-epoch-ms', 'windows-filetime']
이제 Unix epoch time과 FILETIME을 각각 처리 할 두 가지 방법을 정의해야합니다.
def parse_unix_epoch(self, milliseconds=False):
if self.data_type == 'hex':
conv_value = int(self.date_value)
if milliseconds:
conv_value = conv_value / 1000.0
elif self.data_type == 'number':
conv_value = float(self.date_value)
if milliseconds:
conv_value = conv_value / 1000.0
else:
print("Unsupported data type '{}' provided".format(self.data_type))
sys.exit('1')
ts = dt.fromtimestamp(conv_value)
self.timestamp = ts.strftime('%Y-%m-%d %H:%M:%S.%f')
def parse_windows_filetime(self):
if self.data_type == 'hex':
microseconds = int(self.date_value, 16) / 10.0
elif self.data_type == 'number':
microseconds = float(self.date_value) / 10
else:
print("Unsupported data type '{}' provided".format(self.data_type))
sys.exit('1')
ts = dt(1601, 1, 1) + timedelta(microseconds=microseconds)
self.timestamp = ts.strftime('%Y-%m-%d %H:%M:%S.%f')
위의 스크립트를 실행 한 후 타임 스탬프를 제공하면 읽기 쉬운 형식으로 변환 된 값을 얻을 수 있습니다.
웹 서버 로그
디지털 포렌식 전문가의 관점에서 웹 서버 로그는 사용자 및 지리적 위치에 대한 정보와 함께 유용한 사용자 통계를 얻을 수 있기 때문에 또 다른 중요한 아티팩트입니다. 다음은 정보를 쉽게 분석하기 위해 웹 서버 로그를 처리 한 후 스프레드 시트를 만드는 Python 스크립트입니다.
먼저 다음 Python 모듈을 가져와야합니다.
from __future__ import print_function
from argparse import ArgumentParser, FileType
import re
import shlex
import logging
import sys
import csv
logger = logging.getLogger(__file__)
이제 로그에서 구문 분석 할 패턴을 정의해야합니다.
iis_log_format = [
("date", re.compile(r"\d{4}-\d{2}-\d{2}")),
("time", re.compile(r"\d\d:\d\d:\d\d")),
("s-ip", re.compile(
r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}")),
("cs-method", re.compile(
r"(GET)|(POST)|(PUT)|(DELETE)|(OPTIONS)|(HEAD)|(CONNECT)")),
("cs-uri-stem", re.compile(r"([A-Za-z0-1/\.-]*)")),
("cs-uri-query", re.compile(r"([A-Za-z0-1/\.-]*)")),
("s-port", re.compile(r"\d*")),
("cs-username", re.compile(r"([A-Za-z0-1/\.-]*)")),
("c-ip", re.compile(
r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}")),
("cs(User-Agent)", re.compile(r".*")),
("sc-status", re.compile(r"\d*")),
("sc-substatus", re.compile(r"\d*")),
("sc-win32-status", re.compile(r"\d*")),
("time-taken", re.compile(r"\d*"))]
이제 명령 줄 처리기에 대한 인수를 제공합니다. 여기서 두 개의 인수를받습니다. 첫 번째는 처리 할 IIS 로그이고 두 번째는 원하는 CSV 파일 경로입니다.
if __name__ == '__main__':
parser = ArgumentParser('Parsing Server Based Logs')
parser.add_argument('iis_log', help = "Path to IIS Log",type = FileType('r'))
parser.add_argument('csv_report', help = "Path to CSV report")
parser.add_argument('-l', help = "Path to processing log",default=__name__ + '.log')
args = parser.parse_args()
logger.setLevel(logging.DEBUG)
msg_fmt = logging.Formatter(
"%(asctime)-15s %(funcName)-10s ""%(levelname)-8s %(message)s")
strhndl = logging.StreamHandler(sys.stdout)
strhndl.setFormatter(fmt = msg_fmt)
fhndl = logging.FileHandler(args.log, mode = 'a')
fhndl.setFormatter(fmt = msg_fmt)
logger.addHandler(strhndl)
logger.addHandler(fhndl)
logger.info("Starting IIS Parsing ")
logger.debug("Supplied arguments: {}".format(", ".join(sys.argv[1:])))
logger.debug("System " + sys.platform)
logger.debug("Version " + sys.version)
main(args.iis_log, args.csv_report, logger)
iologger.info("IIS Parsing Complete")
이제 대량 로그 정보에 대한 스크립트를 처리 할 main () 메서드를 정의해야합니다.
def main(iis_log, report_file, logger):
parsed_logs = []
for raw_line in iis_log:
line = raw_line.strip()
log_entry = {}
if line.startswith("#") or len(line) == 0:
continue
if '\"' in line:
line_iter = shlex.shlex(line_iter)
else:
line_iter = line.split(" ")
for count, split_entry in enumerate(line_iter):
col_name, col_pattern = iis_log_format[count]
if col_pattern.match(split_entry):
log_entry[col_name] = split_entry
else:
logger.error("Unknown column pattern discovered. "
"Line preserved in full below")
logger.error("Unparsed Line: {}".format(line))
parsed_logs.append(log_entry)
logger.info("Parsed {} lines".format(len(parsed_logs)))
cols = [x[0] for x in iis_log_format]
logger.info("Creating report file: {}".format(report_file))
write_csv(report_file, cols, parsed_logs)
logger.info("Report created")
마지막으로 스프레드 시트에 출력을 기록하는 방법을 정의해야합니다.
def write_csv(outfile, fieldnames, data):
with open(outfile, 'w', newline="") as open_outfile:
csvfile = csv.DictWriter(open_outfile, fieldnames)
csvfile.writeheader()
csvfile.writerows(data)
위의 스크립트를 실행 한 후 웹 서버 기반 로그를 스프레드 시트로 가져옵니다.
YARA를 사용하여 중요한 파일 스캔
YARA (Yet Another Recursive Algorithm)는 맬웨어 식별 및 사고 대응을 위해 설계된 패턴 일치 유틸리티입니다. 파일 스캔에 YARA를 사용합니다. 다음 Python 스크립트에서는 YARA를 사용합니다.
다음 명령을 사용하여 YARA를 설치할 수 있습니다.
pip install YARA
YARA 규칙을 사용하여 파일을 스캔하려면 아래 단계를 따를 수 있습니다.
먼저 YARA 규칙을 설정하고 컴파일합니다.
그런 다음 단일 파일을 스캔 한 다음 디렉터리를 반복하여 개별 파일을 처리합니다.
마지막으로 결과를 CSV로 내보낼 것입니다.
파이썬 코드
이 목적으로 파이썬 코드를 사용하는 방법을 살펴 보겠습니다.
먼저 다음 Python 모듈을 가져와야합니다.
from __future__ import print_function
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
import os
import csv
import yara
다음으로 명령 줄 처리기에 대한 인수를 제공합니다. 여기서 두 개의 인수를 허용합니다. 첫 번째는 YARA 규칙의 경로이고 두 번째는 스캔 할 파일입니다.
if __name__ == '__main__':
parser = ArgumentParser('Scanning files by YARA')
parser.add_argument(
'yara_rules',help = "Path to Yara rule to scan with. May be file or folder path.")
parser.add_argument('path_to_scan',help = "Path to file or folder to scan")
parser.add_argument('--output',help = "Path to output a CSV report of scan results")
args = parser.parse_args()
main(args.yara_rules, args.path_to_scan, args.output)
이제 스캔 할 yara 규칙과 파일의 경로를 수락하는 main () 함수를 정의합니다.
def main(yara_rules, path_to_scan, output):
if os.path.isdir(yara_rules):
yrules = yara.compile(yara_rules)
else:
yrules = yara.compile(filepath=yara_rules)
if os.path.isdir(path_to_scan):
match_info = process_directory(yrules, path_to_scan)
else:
match_info = process_file(yrules, path_to_scan)
columns = ['rule_name', 'hit_value', 'hit_offset', 'file_name',
'rule_string', 'rule_tag']
if output is None:
write_stdout(columns, match_info)
else:
write_csv(output, columns, match_info)
이제 디렉토리를 반복하고 추가 처리를 위해 결과를 다른 방법으로 전달하는 방법을 정의합니다.
def process_directory(yrules, folder_path):
match_info = []
for root, _, files in os.walk(folder_path):
for entry in files:
file_entry = os.path.join(root, entry)
match_info += process_file(yrules, file_entry)
return match_info
다음으로 두 가지 기능을 정의하십시오. 먼저 우리는match() 방법 yrulesobject 및 다른 사용자가 출력 파일을 지정하지 않으면 일치하는 정보를 콘솔에보고합니다. 아래 표시된 코드를 관찰하십시오-
def process_file(yrules, file_path):
match = yrules.match(file_path)
match_info = []
for rule_set in match:
for hit in rule_set.strings:
match_info.append({
'file_name': file_path,
'rule_name': rule_set.rule,
'rule_tag': ",".join(rule_set.tags),
'hit_offset': hit[0],
'rule_string': hit[1],
'hit_value': hit[2]
})
return match_info
def write_stdout(columns, match_info):
for entry in match_info:
for col in columns:
print("{}: {}".format(col, entry[col]))
print("=" * 30)
마지막으로 아래와 같이 출력을 CSV 파일에 쓰는 방법을 정의합니다.
def write_csv(outfile, fieldnames, data):
with open(outfile, 'w', newline="") as open_outfile:
csvfile = csv.DictWriter(open_outfile, fieldnames)
csvfile.writeheader()
csvfile.writerows(data)
위의 스크립트를 성공적으로 실행하면 명령 줄에서 적절한 인수를 제공하고 CSV 보고서를 생성 할 수 있습니다.