Python application tutorial: Part 2
August 22, 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 Kaspersky 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 Kaspersky CyberTrace Web.
Stage 1. Define the main() function
In this stage:
- Import the
socket
module.Your application uses functions from this module to establish connections with Kaspersky CyberTrace and receive data.
- Define the
main()
function. - In the
LISTEN_ADDR
andLISTEN_PORT
variables, specify the address and port where the application listens for incoming events.You can get this information in Kaspersky 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:
- 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()
- 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 theconnection
variable) and an address that established the connection on the other end (in theclient_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:
- 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. Thefor
loop then uses this generator to iterate over the received events. You will implement theparse_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()
- Implement the
parse_response()
function.Events from Kaspersky CyberTrace terminate in a newline character (
\n
). Theparse_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. Thechunk_receive()
function iterates over the data from the socket connection. You will implement this function in the next step.When the
for
loop from themain()
function proceeds to the next iteration, theparse_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
- 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 thewhile
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:
- 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. - 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() |