Skip to content

Instantly share code, notes, and snippets.

@navinpai
Created December 7, 2013 05:24
Show Gist options
  • Save navinpai/7837596 to your computer and use it in GitHub Desktop.
Save navinpai/7837596 to your computer and use it in GitHub Desktop.
Cicada 3301 2013 Server Implementation (From http://pastebin.com/CrRvGrkT)
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>
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