Python application tutorial: Part 2

April 11, 2024

ID 201594

This tutorial explains how you can implement a Python application that sends and receives data from Kaspersky CyberTrace.

Part 1 of this tutorial describes an application that sends data to Kaspersky CyberTrace.

Part 2 of this tutorial describes an application that listens for incoming events from Kaspersky CyberTrace.

Introduction

In this part of the tutorial, you implement a Python application that listens for incoming events from Kaspersky CyberTrace.

You can use any name for your application. This tutorial uses the listen_events_cybertrace.py file name for this application in the examples.

About connection settings and event formats

Your application will listen on the specified address and port, accept incoming connections from Kaspersky CyberTrace, and parse received data.

To determine the address and port where to send events, Kaspersky CyberTrace uses the OutputSettings > ConnectionString parameter. You can view and configure the address and port for outgoing events on the Service settings page in CyberTrace Web.

To determine the format of events, Kaspersky CyberTrace uses formats from the OutputSettings section of the Kaspersky CyberTrace Service configuration file. Every event terminates with a newline character (\n). You can configure the event formats on the Event format settings page in CyberTrace Web.

Stage 1. Define the main() function

In this stage:

  1. Import the socket module.

    Your application uses functions from this module to establish connections with Kaspersky CyberTrace and receive data.

  2. Define the main() function.
  3. In the LISTEN_ADDR and LISTEN_PORT variables, specify the address and port where the application listens for incoming events.

    You can get this information in CyberTrace Web, on the Service settings page, in the Service sends events to area.

    import socket

     

    LISTEN_ADDR = "192.0.2.105"

    LISTEN_PORT = 9998

     

    def main():

    pass

    if __name__ == '__main__':

    main()

Stage 2. Add the server code

In this stage:

  1. In the main() function, add the code that starts a server on a specified address and port.

    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()

  2. Add the code that handles incoming connections.

    The socket.accept() function accepts an incoming connection. It returns a new socket object for the connection (in the connection variable) and an address that established the connection on the other end (in the client_address variable).

    At this stage, the application ignores data received from connections. You will add the data parsing functionality in the next stage.

    The application starts an endless loop. You can terminate it by using a standard shortcut or command for your operating system. On most operating systems, this is ^C or Control-C.

    while True:

    connection, client_address = server_socket.accept()

    try:

    print("Connection from {}".format(client_address))

    # Add data parsing here

    finally:

    connection.close()

Stage 3. Parse received data

In this stage:

  1. In the try... finally block, call a function that parses the received data into events.

    Because each connection can send any number of events, the application must parse the received data until all events are processed. The parse_response() function returns a generator. The for loop then uses this generator to iterate over the received events. You will implement the parse_response() function in the next step.

    try:

    print("Connection from {}".format(client_address))

    for event in parse_response(connection):

    print("Received:\n{}".format(event.decode()))

    finally:

    connection.close()

  2. Implement the parse_response() function.

    Events from Kaspersky CyberTrace terminate in a newline character (\n). The parse_response() function reads data from the socket connection into a buffer until the buffer contains a newline character. Then the function splits an event from the buffer and yields it. The chunk_receive() function iterates over the data from the socket connection. You will implement this function in the next step.

    When the for loop from the main() function proceeds to the next iteration, the parse_response() function splits another event from the buffer and yields it. If it is not possible, the function reads more data from the connection into the buffer. The process repeats until there is no more data received from the connection and all the events in the buffer are yielded.

    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

  3. Implement the chunk_receive() function.

    The chunk_receive() function reads 4096 bytes of data from the connection and yields this data. The function returns data until there is no more data to receive from the connection.

    The socket.recv() function receives data from the connection. If there is no data, the function waits until more data is received. The function returns zero bytes when the connection closes, so the while loop terminates on this condition.

    def chunk_receive(connection):

    data = None

    while data != b'':

    data = connection.recv(4096)

    yield data

Stage 5. (Optional) Modify the application that sends events

If you want to send events to Kaspersky CyberTrace with the application that you implemented in Part 1 of this tutorial, modify it to send events without waiting for a response.

In this stage:

  1. Comment or delete the line in the application code that sends flags. Kaspersky CyberTrace sends events to the listener application, so the X-KF-ReplyBack flag is no longer needed.
  2. Comment or delete lines that receive and output the response.

Below is the try... finally block from the application, with commented lines.

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()

 

Stage 6. Run your application

Run your application from the console:

python3 ./listen_events_cybertrace.py

The application listens for detection and alert events from Kaspersky CyberTrace, and outputs them to the console.

If you want to send events to Kaspersky CyberTrace by using the application developed in Part 1 of this tutorial, run it from the console:

python3 ./send_events_cybertrace.py

Below is an example of the application output.

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

Full code for Part 2

Below is the full code for Part 2 of this tutorial.

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()

Did you find this article helpful?
What can we do better?
Thank you for your feedback! You're helping us improve.
Thank you for your feedback! You're helping us improve.