В этом руководстве описывается реализация обмена данными с Kaspersky CyberTrace в приложении на Python.
В части 1 этого руководства описывается приложение, отправляющее данные в Kaspersky CyberTrace.
В части 2 этого руководства описывается приложение, ожидающее получения событий от Kaspersky CyberTrace.
Введение
Эта часть руководства посвящена реализации приложения Python, прослушивающего порт в ожидании событий от Kaspersky CyberTrace.
Имя для приложения можно выбрать произвольно. В примерах в этом руководстве для этого приложения используется имя файла listen_events_cybertrace.py.
О настройках подключения и форматах событий
Приложение будет прослушивать указанный адрес и порт, принимать входящие соединения от Kaspersky CyberTrace и выполнять парсинг полученных данных.
Для определения адреса и порта, на которые отправляются события, в Kaspersky CyberTrace используется параметр OutputSettings > ConnectionString. Адрес и порт для исходящих событий можно просмотреть и настроить на странице Service settings в веб-интерфейсе Kaspersky CyberTrace.
Для определения формата событий Kaspersky CyberTrace использует форматы из раздела OutputSettings конфигурационного файла Kaspersky CyberTrace Service. Каждое событие заканчивается символом новой строки (\n). Форматы событий можно настроить на странице Service alerts settings в веб-интерфейсе Kaspersky CyberTrace.
1-й этап. Определение функции main()
На этом этапе выполняются следующие действия:
socket. Функции из этого модуля используются для установления соединения с Kaspersky CyberTrace и получения данных.
main().LISTEN_ADDR и LISTEN_PORT необходимо указать адрес и порт, которые приложение будет прослушивать в ожидании входящих событий.Эту информацию можно получить в веб-интерфейсе Kaspersky CyberTrace на странице General settings, в области Входящие события.
import socket
LISTEN_ADDR = "192.0.2.105" LISTEN_PORT = 9998
def main(): pass if __name__ == '__main__': main() |
2-й этап. Добавление код сервера
На этом этапе выполняются следующие действия:
main() кода, запускающего сервер, прослушивающий указанные адрес и порт.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print("Starting on {}:{}".format(LISTEN_ADDR, LISTEN_PORT)) server_socket.bind((LISTEN_ADDR, LISTEN_PORT)) server_socket.listen() |
Функция socket.accept() принимает входящее соединение. Она возвращает новый объект сокета для соединения (в переменной connection) и адрес на другом конце, инициировавший соединение (в переменной client_address).
На этом этапе приложение игнорирует данные, полученные в результате соединений. Парсинг будет реализован на следующем этапе.
Приложение запускает бесконечный цикл. Его можно завершить стандартной комбинацией клавиш или командой операционной системы. В большинстве операционных систем это ^C или Ctrl+C.
while True: connection, client_address = server_socket.accept() try: print("Connection from {}".format(client_address)) finally: connection.close() |
3-й этап. Парсинг полученных данных
На этом этапе выполняются следующие действия:
try... finally вызывается функцию, которая выполняет парсинг полученных данных на события. Поскольку в рамках каждого соединения может быть отправлено любое количество событий, приложение должно продолжать парсинг полученных данных, пока не будут обработаны все события. Функция parse_response() возвращает генератор. Затем этот генератор используется в цикле for для итерации по полученным событиям. На следующем шаге реализуется функция parse_response().
try: print("Connection from {}".format(client_address)) for event in parse_response(connection): print("Received:\n{}".format(event.decode())) finally: connection.close() |
parse_response().События Kaspersky CyberTrace оканчиваются символом новой строки (\n). Функция parse_response() считывает данные из сокетного соединения в буфер до тех пор, пока в буфере не окажется символ новой строки. Затем функция выделяет событие из буфера и возвращает его. Функция chunk_receive() выполняет итерацию по данным из сокетного соединения. Эта функция будет реализована на следующем этапе.
Когда цикл for из функции main() переходит к следующей итерации, функция parse_response() выделяет из буфера очередное событие и возвращает его. Если это невозможно, функция считывает дополнительные данные из соединения в буфер. Процесс повторяется до тех пор, пока из соединения не перестанут поступать данные и все события в буфере не будут возвращены.
def parse_response(connection): buff = b'' for data in chunk_receive(connection): buff += data while b'\n' in buff: event, buff = buff.split(b'\n',1) yield event |
chunk_receive().Функция chunk_receive() считывает 4096 байт данных из соединения и возвращает эти данные. Функция возвращает данные до тех пор, пока в соединении не закончатся данные.
Функция socket.recv() получает данные из соединения. Если данных нет, функция ожидает получения новых данных. Функция возвращает ноль байтов при закрытии соединения, поэтому цикл while завершается при выполнении этого условия.
def chunk_receive(connection): data = None while data != b'': data = connection.recv(4096) yield data |
Этап 5. (Необязательный) Изменение приложения, отправляющее события
Если требуется отправлять события в Kaspersky CyberTrace с помощью приложения, реализованного в части 1 этого руководства, измените приложение так, чтобы события отправлялись без ожидания ответа.
На этом этапе выполняются следующие действия:
X-KF-ReplyBack становится излишним.Ниже приведен блок try... finally из приложения с закомментированными строками.
try: ct_socket.connect((CYBERTRACE_ADDR, CYBERTRACE_PORT)) #ct_socket.sendall(b'X-KF-SendFinishedEventX-KF-ReplyBack')
for event in events: print("Sending:\n{}".format(event)) ct_socket.sendall(event.encode()) #response = ct_socket.recv(16384) #print("Response:\n{}".format(response.decode()))
finally: ct_socket.close()
|
6-й этап. Запуск приложения
Запуск приложения из консоли:
python3 ./listen_events_cybertrace.py
Приложение прослушивает порт в ожидании событий обнаружения киберугроз и оповещений о событиях из Kaspersky CyberTrace и выводит их в консоль.
Если требуется отправлять события в Kaspersky CyberTrace с помощью приложения, разработанного в части 1 этого руководства, запустите это приложение из консоли:
python3 ./send_events_cybertrace.py
Ниже приведен пример вывода приложения.
Starting on 192.0.2.105:9998 Connection from ('192.0.2.42', 41882) Received: - category=KL_Malicious_Hash_MD5 matchedIndicator=776735A8CA96DB15B422879DA599F474 url=- src=- ip=- md5=776735A8CA96DB15B422879DA599F474 sha1=- sha256=- usrName=- confidence=100 MD5=776735A8CA96DB15B422879DA599F474 SHA1=3B66A1D70562E291DA023E87B349DD89DFE00213 SHA256=1963CBCBB9FDAAD45F782FAAA467EE2C115C3111C003AE14D01181880B03F6ED file_size=1431 first_seen=10.07.2015 23:53 last_seen=14.07.2020 13:35 popularity=1 threat=HEUR:Trojan.Win32.Generic Received: - category=KL_IP_Reputation matchedIndicator=192.0.2.1 url=- src=- ip=192.0.2.1 md5=- sha1=- sha256=- usrName=- confidence=100 category=test first_seen=01.01.2017 00:00 ip=192.0.2.1 ip_geo=ru last_seen=17.07.2020 09:02 popularity=1 threat_score=75 Received: - category=KL_Malicious_Hash_MD5 matchedIndicator=FEAF2058298C1E174C2B79AFFC7CF4DF url=- src=- ip=- md5=FEAF2058298C1E174C2B79AFFC7CF4DF sha1=- sha256=- usrName=- confidence=100 MD5=FEAF2058298C1E174C2B79AFFC7CF4DF SHA1=D01D17F6B13C7255A234F558ED85078EA5DD3F3D SHA256=4CA914C9791CF2BF2AC69F9A2B21006F0361E247F2CE92F0A9F166DBC6B43670 file_size=1989 first_seen=10.07.2015 23:53 last_seen=14.07.2020 13:35 popularity=1 threat=HEUR:Trojan.Win32.Generic Received: - category=KL_IP_Reputation matchedIndicator=192.0.2.3 url=- src=- ip=192.0.2.3 md5=- sha1=- sha256=- usrName=- confidence=100 category=test first_seen=15.01.2017 00:00 ip=192.0.2.3 ip_geo=ru last_seen=17.07.2020 08:51 popularity=1 threat_score=75 |
Полный код для части 2
Ниже приведен полный код для части 2 этого руководства.
import socket
LISTEN_ADDR = "192.0.2.105" LISTEN_PORT = 9998
def chunk_receive(connection): data = None while data != b'': data = connection.recv(4096) yield data
def parse_response(connection): buff = b'' for data in chunk_receive(connection): buff += data while b'\n' in buff: event, buff = buff.split(b'\n',1) yield event
def main():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print("Starting on {}:{}".format(LISTEN_ADDR, LISTEN_PORT)) server_socket.bind((LISTEN_ADDR, LISTEN_PORT)) server_socket.listen()
while True: connection, client_address = server_socket.accept() try: print("Connection from {}".format(client_address)) for event in parse_response(connection): print("Received:\n{}".format(event.decode())) finally: connection.close()
if __name__ == '__main__':
main() |