An Unsecure Chat Application
Summary
I came across an old chat application I developed in 2021 using PyQt5 (a Python graphical user interface framework) and decided to examine its security from a black-box perspective by interacting with the application without reading its source code.
The Application
The application consists of three Python files: server.py, welcome.py, and chat.py.
The server.py file is executed on the server and contains the logic for handling multiple clients, transforming specific text into emojis, and broadcasting messages to all clients, either to announce a new user joining or leaving the chat or for arbitrary server messages.
The welcome.py file takes the server IP, server port, and the client's name, establishes a connection to the server, and finally launches chat.py
Welcome

Client and Server

Github Repository
The Security
Wireshark
To test its security, I decided to approach it from a black-box perspective, meaning I wouldn’t read the source code but would simply interact with the application.
My first instinct was to check if the communication was encrypted, either end-to-end or between the client and server.
I launched Wireshark, started the server, and established a connection with a client using the username hmed. At first glance, I noticed a significant amount of traffic between the server and client IP addresses.

Upon further investigation, it seems that the server continuously sends the list of currently connected client usernames, along with the number of clients in the format #/#1#.
Next, I tried typing the message hey there in the chat from the client and returned to Wireshark to filter for that text.

As you can see, it’s clear as day, client messages are unencrypted.
Digging further into the message sent by the client, I noticed that the client's username is also present in the span tag. So, hypothetically, user impersonation should be very feasible, right? If I simply send a message to the server that includes a span tag with a self-chosen username, I should be able to impersonate them.
User Impersonation
import socket
hackerSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
hackerSocket.connect(("192.168.1.127", 1337))
hackerSocket.send(b"hacker")
myAddr = hackerSocket.recv(1024).decode()
welcomeMsg = hackerSocket.recv(1024).decode()
userJoinedMsg = hackerSocket.recv(1024).decode()
listOfClients = hackerSocket.recv(1024).decode()
hackerSocket.send(b'<span style=" color: #ff0000;">hmed</span>: I am hmed!! ')I created a Python script that does the following:
Establishes a socket connection
Connects to the chat server
Sends an initial message, which the server expects to be the client's username
Processes the messages sent back by the server (including the client's IP address, the welcome message, new user joined message, and finally, the list of currently connected clients)
Sends a fraudulent message to attempt to trick the server

Well, that didn't work as expected, but it already tells us a couple of things.
We can write arbitrary HTML in the chat, as there's no escaping or validation of user input.
The server displays the client's username first, regardless of the message sent by the client.
My next strategy is to check if the server performs any validation to see whether the username is already in use. So, I tried to execute the python script, this time using hmed again as the username (bare in mind that it's already is use by the legitimate client using chat.py)

That was super easy, now we have two clients with the same username in the chat.
My next challenge is, what about impersonating the server?
I once again used Wireshark to intercept the server's broadcast message and analyze it in order to replicate it.

Notice the ******server****** pattern at the end of the message? What if that’s what indicates to the clients that this message originates from the server? Let’s test it out.
Server Impersonation
import socket
hackerSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
hackerSocket.connect(("192.168.1.127", 1337))
hackerSocket.send(b"hacker")
myAddr = hackerSocket.recv(1024).decode()
welcomeMsg = hackerSocket.recv(1024).decode()
userJoinedMsg = hackerSocket.recv(1024).decode()
listOfClients = hackerSocket.recv(1024).decode()
hackerSocket.send(b"<h4 style='color:#800080'>server: this is a hacker!</h4>******server****** ")And sure enough

We are also able to impersonate the server and send a fraudulent server broadcast message to all connected clients. (Although, you can see how the server message is preceded by an empty user message and a new line.)
Conclusion
I didn’t go further with my penetration test, but not validating client usernames on the server side and not encrypting messages are huge security flaws. Not only does this allow for snooping on all traffic, but it also opens the door to user and server impersonation.
Additionally, I believe this architecture is vulnerable to ARP poisoning, which significantly increases the attack surface by establishing MITM, potentially allowing for the manipulation of user or server messages or even denial of service. I might explore this further later.
In summary, while the chat application provides a valuable learning opportunity for PyQt5 and networking concepts, its security is severely lacking. This penetration test underscores the importance of implementing robust security measures. As such, the application should only be used for educational purposes and not in any official or production environment.
つづく
Last updated
Was this helpful?
