Created
December 7, 2013 05:24
-
-
Save navinpai/7837596 to your computer and use it in GitHub Desktop.
Cicada 3301 2013 Server Implementation (From http://pastebin.com/CrRvGrkT)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In the programming language of your choice build a TCP server | |
that implements the protocol below. The server code must be | |
written by you and you alone, although you are free to use any | |
modules or libraries publicly available for the selected | |
programming language. | |
Once you have done this, make it accessible as a Tor hidden | |
service. Then provide us with the onion address and port | |
via a GPG-encrypted email to this address. | |
You have until 0:00 UTC on 3 Feb, 2013. Any emails received | |
after that time will be ignored. | |
Good luck. | |
3301 | |
==================================================================== | |
1. INTRODUCTION | |
The TCP server MUST listen on an arbitrary port, and send and | |
receive plain text with lines separated by <CRLF> (representing | |
a carriage return followed by a line feed). The TCP server MUST | |
disregard the case of input. | |
In the examples below, lines sent by the server will be preceded | |
with "S:" and lines sent by the client will be preceded by "C:" | |
Each message sent by the server MUST conform to the format: | |
[CODE] [RESPONSE NAME] [RESPONSE (optional)]<CRLF> | |
Where [CODE] and [RESPONSE NAME] is one of: | |
CODE RESPONSE NAME | |
00 Welcome | |
01 Ok | |
02 Error | |
03 Data | |
99 Goodbye | |
2. PROCEDURES | |
a. Remote Connection | |
Upon receiving a remote connection, the server MUST greet the | |
client with a 00 WELCOME message. The RESPONSE of a welcome | |
message MAY contain arbitrary text. The arbitrary text MUST | |
at the very least contain the name of the programming language | |
used to implement the server. | |
Upon receiving a 00 WELCOME message, the client may begin | |
initiating procedures. | |
Example: | |
S: 00 WELCOME [ARBITRARY RESPONSE TEXT]<CRLF> | |
b. RAND [n] | |
Upon receiving a "RAND" request by the client, the server will | |
first send a 01 OK response, and will then provide the client | |
with [n] cryptographically random numbers within the range of | |
0-255. Each number MUST be followed by <CRLF>. After the last | |
number has been sent, the server MUST send a dot (.) on a line | |
by itself. | |
Example: | |
C: RAND 3<CRLF> | |
S: 01 OK<CRLF> | |
S: [first random number]<CRLF> | |
S: [second random number]<CRLF> | |
S: [third random number]<CRLF> | |
S: .<CRLF> | |
c. QUINE | |
Upon receiving a "QUINE" request by the client, the server will | |
first send a 01 OK response, and will then provide the client | |
with a quine in the programming language used to implement the | |
server. This quine does not have to be original. After the last | |
line of code has been sent, the server MUST send a dot (.) on a | |
line by itself. | |
Example: | |
C: QUINE<CRLF> | |
S: 01 OK<CRLF> | |
S: [quine code]<CRLF> | |
S: .<CRLF> | |
d. BASE29 [n] | |
Upon receiving a "BASE29" request by the client, the server will | |
send a 01 OK response followed by the number [n] converted into | |
its base 29 representation. | |
Example: | |
C: BASE29 3301<CRLF> | |
S: 01 OK 3QO<CRLF> | |
e. CODE | |
Upon receiving a "CODE" request by the client, the server will | |
send a 01 OK response followed by its own source code. After the | |
last line of code has been sent, the server MUST send a dot(.) on | |
a line by itself. | |
Example: | |
C: CODE<CRLF> | |
S: 01 OK<CRLF> | |
S: [Server Source Code]<CRLF> | |
s: .<CRLF> | |
f. KOAN | |
Upon receiving a "KOAN" request by the client, the server will | |
send a 01 OK response followed by a koan. After the last line of | |
the koan, the server MUST send a dot (.) on a line by itself. | |
Example: | |
C: KOAN<CRLF> | |
S: 01 OK<CRLF> | |
S: A master who lived as a hermit on a mountain was asked by a<CRLF> | |
S: monk, "What is the Way?<CRLF> | |
S: "What a fine mountain this is," the master said in reply<CRLF> | |
S: "I am not asking you about the mountain, but about the Way.<CRLF> | |
S: "So long as you cannot go beyond the mountain, my son, you<CRLF> | |
S: cannot reach the Way," replied the master<CRLF> | |
S: . | |
g. DH [p] | |
Upon receiving a "DH" request by the client, the server will proceed | |
to perform a Diffie-Hellman key exchange using [p] as the prime modulus. | |
The server will then select a base [b] to use in the protocol, as well as | |
its secret integer. The server will then compute its exponent result [e] | |
as specified within the Diffie-Hellman key exchange protocol. | |
The server MUST then respond with a 01 OK response followed by the | |
selected base [b] and computed exponent [e] separated by white space. | |
The client MUST respond with its exponent result [e2], and the client and | |
server will follow the rest of the Diffie-Hellman key exchange protocol. | |
The server MUST then compute the resulting secret key, and provide it | |
using 03 DATA [k]. | |
Example: | |
C: DH 23<CRLF> | |
S: 01 OK 5 8<CRLF> | |
C: 19<CRLF> | |
S: 03 DATA 2<CRLF> | |
j. NEXT | |
Upon receiving a "NEXT" request by the client, the server will respond | |
with 01 OK and then listen for text data to be provided by the client. | |
The client will send a dot (.) on a line by itself after the last line | |
of text. The server MUST record this. This data will be the next set | |
of instructions. Once the data is received the server will respond | |
with 01 OK. | |
Example: | |
C: NEXT<CRLF> | |
S: 01 OK<CRLF> | |
C: -----BEGIN PGP SIGNED MESSAGE-----<CRLF> | |
C: [MESSAGE CONTENTS]<CRLF> | |
C: -----END PGP SIGNATURE-----<CRLF> | |
C: .<CRLF> | |
S: 01 OK<CRLF> | |
i. GOODBYE | |
Upon receiving a "DH" request by the client, the server MUST respond with | |
99 GOODBYE and then gracefully close the connection. | |
Example: | |
C: GOODBYE<CRLF> | |
S: 99 GOODBYE<CRLF> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys, socket, threading, time, traceback, random, os | |
class Logger: | |
def __init__(self, filename): | |
self.filename = filename | |
def log(self, s): | |
t = time.time() | |
stime = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(t)) + ".%03d" % (int((t * 1000)) % 1000) | |
line = stime + " " + s | |
of = open(self.filename, "ab") | |
of.write(line + "\r\n") | |
of.close() | |
print line | |
class Server(threading.Thread): | |
def __init__(self, csock, address, clientcounter): | |
threading.Thread.__init__(self) | |
self.logger = Logger("logs/clientlog-%d-%d.txt" % (int(time.time()), clientcounter)) | |
self.csock = csock | |
self.address = address | |
def logsend(self, s): | |
self.logger.log("Sending line: " + repr(s)) | |
self.csock.sendall(s + "\r\n") | |
def logrecv(self): | |
line = "" | |
while not line.endswith("\r\n"): | |
try: | |
data = self.csock.recv(1) | |
if len(data) == 0: | |
self.logger.log("Client terminated connection, incomplete line: " + repr(line)) | |
return None | |
except socket.timeout: | |
self.logger.log("Client timeout, incomplete line: " + repr(line)) | |
return None | |
line += data | |
self.logger.log("Received line: " + repr(line)) | |
return line.strip("\r\n") | |
def handle_rand(self, parts): | |
if len(parts) != 2: | |
self.logsend("02 Invalid parameters") | |
return True | |
count = 0 | |
try: | |
count = int(parts[1]) | |
except: | |
self.logsend("02 Not an integer") | |
return True | |
self.logsend("01 OK") | |
for i in xrange(count): | |
s = str(ord(os.urandom(1))) | |
self.logsend(s) | |
self.logsend(".") | |
return True | |
def send_file(self, filename): | |
self.logsend("01 OK") | |
for line in open(filename, "rb"): | |
line = line.strip("\r\n") | |
self.logsend(line) | |
self.logsend(".") | |
return True | |
def handle_base29(self, parts): | |
if len(parts) != 2: | |
self.logsend("02 Invalid parameters") | |
return True | |
n = 0 | |
try: | |
n = int(parts[1]) | |
except: | |
self.logsend("02 Not an integer") | |
return True | |
s = "" | |
while n > 0: | |
s = "0123456789ABCDEFGHIJKLMNOPQRSTUVXYZ"[n % 29] + s | |
n /= 29 | |
if s == "": | |
s = "0" | |
self.logsend("01 OK " + s) | |
return True | |
def recv_file(self): | |
self.logsend("01 OK") | |
count = 1 | |
while True: | |
filename = "received%d.txt" % count | |
if not os.path.isfile(filename): | |
break | |
count += 1 | |
of = open(filename, "wb") | |
while True: | |
line = self.logrecv() | |
if line is None: | |
break | |
if line == ".": | |
break | |
of.write(line + "\r\n") | |
of.close() | |
self.logsend("01 OK") | |
return True | |
def handle_dh(self, parts): | |
if len(parts) != 2: | |
self.logsend("02 Invalid parameters") | |
return True | |
p = 0 | |
try: | |
p = int(parts[1]) | |
except: | |
self.logsend("02 Not an integer") | |
return True | |
base = random.randint(2, p) | |
a = random.randint(2, p) | |
self.logger.log("Selected base %d and secret %d" % (base, a)) | |
A = pow(base, a, p) | |
self.logsend("01 OK %d %d" % (base, A)) | |
line = self.logrecv() | |
B = 0 | |
try: | |
B = int(line) | |
except: | |
self.logsend("02 Not an integer") | |
return True | |
s = pow(B, a, p) | |
self.logsend("03 DATA %d" % s) | |
return True | |
def handle_line(self, line): | |
parts = line.split() | |
if len(parts) == 0: | |
self.logsend("02 No command specified") | |
return True | |
cmd = parts[0].lower() | |
if cmd == "rand": # b | |
return self.handle_rand(parts) | |
elif cmd == "quine": # c | |
return self.send_file("quine.py") | |
elif cmd == "base29": # d | |
return self.handle_base29(parts) | |
elif cmd == "code": # e | |
return self.send_file("server.py") | |
elif cmd == "koan": # f | |
return self.send_file("koan.txt") | |
elif cmd == "dh": # g | |
return self.handle_dh(parts) | |
elif cmd == "next": # i | |
return self.recv_file() | |
elif cmd == "goodbye": # j | |
self.logsend("99 GOODBYE") | |
return False | |
else: | |
self.logsend("02 No such command") | |
return True | |
return False | |
def run(self): | |
try: | |
self.csock.settimeout(60 * 10) | |
self.logsend("00 WELCOME Python") | |
while True: | |
line = self.logrecv() | |
if line is None: | |
break | |
res = self.handle_line(line) | |
if res is not True: | |
break | |
except: | |
self.logger.log("Encountered exception: " + repr(sys.exc_info()[:2])) | |
print "Traceback:" | |
print traceback.format_exc() | |
self.logger.log("Closing connection and exiting") | |
self.csock.close() | |
return | |
def main(): | |
logger = Logger("logs/mainlog.txt") | |
clientcounter = 0 | |
ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
ssock.bind(("", 31415)) | |
ssock.listen(5) | |
logger.log("Listerer started") | |
while True: | |
csock, address = ssock.accept() | |
logger.log("Got connection from address " + repr(address)) | |
srv = Server(csock, address, clientcounter) | |
srv.start() | |
clientcounter += 1 | |
main() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment