Brian Seel
cylussec
Published in
4 min readApr 13, 2015

--

Python Mimicry Server

I was given a little challenge with the following requirements:

1.) Develop a client and server using the Python socket library that operates as follows
* Client application is given a server IP address (in dotted decimal notation), a port, and a
password as command line parameters.
* Server is given a port to run on and a path to the password list included with this assignment (in
CSV format) as command line parameters. The password list maps a password to an integer, x.
* Client starts by creating a TCP connection to the server, sending the password, and awaiting
confirmation from the server.
* The server receives the passwords, looks up the associated integer x, and sends confirmation to
the client.
* Once the connection is established, the client application will read integers, N, interactively
from the user and send them to the server. The server will calculate N^x and return it to the client
(resulting number could be quite large).
* Client must gracefully close the connection when the application is terminated (e.g., via SIGINT).
2.) Create unit tests for the client and server applications above.3.) Extend the server to support multiple concurrent connections from different clients
(potentially with different passwords) using the multiprocessing library.
BONUS: Describe or show how you would improve the protocol above to make it resilient to an active
man-in-the-middle adversary who may attempt to alter the data sent between the client and server.

I was told that they didn’t expect that this would take much more than 2 hours. Hmmm… unit tested server and client in two hours? That’s not realistic. But I love me some python, so I dove in.

Note: The code is all at https://github.com/cylussec/pythonserver

While I use plenty of tests based on the unittest library at work, and built tests on that, I realized that I had not done a lot of exercises of just writing python code, and writing the unit tests for them. So while TDD says that we should write the tests first, I prefer to write the code, test it by hand, write the tests, get them working and iterate from there.

Design

First off, I am at the point where (unless there is a good reason) I am writing only Python 3. The only reason that you need to write Python 2.7 code is if you are using a library that requires it. That was not the case for me, so Python 3 it is.

Since I knew I was going to be writing tests for this, I at least can make that part easy on myself. Unittest requires that each test can assert something will happen. So I designed my client and server to be classes that could be created and then manipulated. Although I always tend to write my code with objects. They just make sense.

I was reading about the multiprocessing library that is part of the requirements, and noticed that it already supports a client/server. I hadn’t used the multiprocessing library before, and initially thought “Python doesn’t do threading!” And it doesn’t. Python has a Global Interpreter Lock that basically makes everything in Python thread safe by not having threads. So how does Python do it? Subprocesses. Definitely not the most efficient use of resources, as there is quite a bit more overhead in creating a process than creating a thread, but if you are really worried about performance, then write it in C++. And for this situation, subprocesses are just fine.

I am not sure if the requirements were hoping to have me take my non threaded code and thread it that way, but this just made more sense.

Tests

The unit tests require that the server is already running, and they test most of the functionality that is asked for.

  • Invalid password
  • Invalid port
  • A single valid connection with multiple values
  • Multiple connections
  • Overflow value

I actually had trouble with the last test. I allowed for 4096 bit values, which will make Python die before it overflows the protocol. So that test basically just passes. Because it can’t be tested.

The results:

brian@brian-mbp:~/Dropbox/dev/code/pythonserver$ python3 -m unittest tests
Authorized
Entering del
.Entering del
.Entering del
.Authorized
Authorized
Authorized
Entering del
Entering del
Entering del
.
----------------------------------------------------------------------
Ran 4 tests in 0.004s
OK
brian@brian-mbp:~/Dropbox/dev/code/pythonserver$

Awesome

Remarks

So this is by no means a good protocol. Here are a few of the issues:

  • It does a TCP handshake and then basically just passes values back and forth. There is very little validation or verification. I mean, its authentication is that it send back “AUTH”. Super hacky. The right way to do this is a more liberal use of struct.pack and struct.unpack. Create a protocol that sends back an ACK with a single bit set. There is no reason to send back 4*16bits when a single bit would do.
  • There is no encryption. Passwords? In the clear. Values? In the clear. Everything, in the clear. Want to MITM? Sure. Want to MOTS? Sure. Its all right there. Probably even makes dropping encryption on this easy (there is probably a built in for that), but I had already put enough time in on this.
  • As it is, the protocol and the server are not terribly extensible. Its designed to go through a single logic path (password->ACK->value->reply->value->reply… until connection closes). It bascially relies on TCP handling closing down and handles that gracefully, which is the right way to do it. HTTP does not have RST->RST ACK in its protocol because TCP handles that. There is no reason to over engineer it.

This was a fun exercise in unit testing and learning about the multiprocessing library though!

--

--

Brian Seel
cylussec

Software developer; resident of Baltimore; love trying new things