Skip to content

Instantly share code, notes, and snippets.

@Lense
Last active August 29, 2015 14:19
Show Gist options
  • Save Lense/60714e545be55ff88688 to your computer and use it in GitHub Desktop.
Save Lense/60714e545be55ff88688 to your computer and use it in GitHub Desktop.
PlaidCTF 2015: parlor2 writeup
#/usr/bin/env python
from Crypto.PublicKey import RSA, DSA
from Crypto.Random import random, atfork
from Crypto.Cipher import PKCS1_OAEP
import SocketServer,threading,os,time
import socket
from priv import privkey, privkey_enc
# corresponding public keys for clients:
_server_pub_enc = RSA.importKey('-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDGRrsdIqf8K39Ncwzsi9k2lr5G\nJ8aEFkYGrYqOQRbU5xOReMj8wWHgnSUC0fjH0gjffGiUC2HfrrNIQvXKGiSBetOu\nIWOmFiESG8IhrPyvLwX53NbMWeCihzbYGJxGyiL0bvDHxqDxzuvteSaEfNm1miPA\nQ9rs5vFnHM0R3kFjdQIDAQAB\n-----END PUBLIC KEY-----')
server_pub_enc = PKCS1_OAEP.new(_server_pub_enc)
server_pub_sig = DSA.construct([6492988819243051335053735606322819439099395961135352303030066825351059776939776358522765113843576255727411249922052441719518573282010295240606387519552263L,5720927070571587652595864015095152811124313453716975619963331476834195150780326792550956895343289380256771573459290257563350163686508250507929578552744739L,6703916277208300332610863417051397338268350992976752494932363399494412092698152568009978917952727431041521105933065433917303157931689721949082879497208537,1022875313346435070370368907571603203095488145799L])
msg = """/------------------------------------------------------------------------------\\
| Welcome to the betting parlor version 2! |
| |
| Here's how it works: you choose your betting odds and an amount to bet, and |
| then you send us your guessed value. We then pick a secure random number. |
| If random number % odds == guessed value, you win! |
| |
| In order to address complaints of cheating, your guessed value is sent to us |
| encrypted. Therefore, our random number will not be generated adversarially! |
| |
| To encourage use of our service after the issues with version 1, we have |
| decided to give all new users of the service $100. Wow! Such gratitude! |
| |
| (Oh, and if you win a billion dollars, we'll give you a flag.) |
\______________________________________________________________________________/
"""
def recv(s, length):
buf = s.recv(1)
start = time.time()
s.setblocking(0)
while (len(buf) < length) and (time.time() - start) < 0.2:
try:
buf += s.recv(1)
except socket.error:
time.sleep(0.01)
s.setblocking(1)
return buf
def getn(s):
try:
b = recv(s,1024)
return int(b)
except:
return 0
class incoming(SocketServer.BaseRequestHandler):
def process(self, public_key_string):
if True: #try:
enc_pk = [chunk.strip().decode("hex") for chunk in public_key_string.split(",")]
dec_str = ''.join(map(privkey_enc.decrypt,enc_pk))
pkdat = map(long,dec_str.split(","))
if len(pkdat) != 4:
return None
self.client_pk = DSA.construct(pkdat)
return privkey.sign(dec_str, random.randint(1,privkey.__getstate__()['q']-1))
else: #except:
return None
def verify_guess(self, guess_string):
if True: #try:
self.enc_guess, sig_guess = guess_string.split("~")
sig_guess = map(long, sig_guess.split(","))
return self.client_pk.verify(self.enc_guess, sig_guess)
else: #except:
return None
def decrypt_guess(self, privkey):
if True: #try:
privkeystr, sig_privkey = privkey.split("~")
sig_privkey = map(long, sig_privkey.split(","))
if not self.client_pk.verify(privkeystr, sig_privkey):
return None
privkey = PKCS1_OAEP.new(RSA.importKey(privkeystr))
print self.enc_guess
print sig_privkey
guess = privkey.decrypt(self.enc_guess.decode("hex"))
if not guess.startswith("I hereby commit to a guess of "):
return None
self.guess = int(guess[len("I hereby commit to a guess of "):])
return True
else: #except
return None
def handle(self):
atfork()
self.monies = 100
self.request.send(msg)
self.request.send("\nBefore we begin, please send us an encrypted version of your public DSA key.\n")
self.request.send("This will be used to ensure no one attempts to intercept our communications\n")
pk = recv(self.request,4096)
sig = self.process(pk)
if not sig:
self.request.send("There was an error processing your key. For your safety, we are aborting this session\n")
return 0
self.request.send("We received your key as (%s), with our signature (%s)\n" %
(str(self.client_pk.__getstate__()), ','.join(map(str,sig))))
self.request.send("Please verify this information before proceeding, to ensure your safety\n")
time.sleep(0.5)
self.request.send("Shall we play a game?\n")
for _ in xrange(100):
if self.monies >= 1000000000:
self.request.send("Holy shit you have a lot of money. Here's a flag: XXXXXXXXXXXXXXX\n")
while True:
self.request.send("How much money would you like to bet? (You have $%d)\n"%self.monies)
m = getn(self.request)
if m > self.monies:
self.request.send("You don't have that much money...\n")
if m < 0:
self.request.send(" :| \n")
return 0
else:
break
self.monies -= m
while True:
self.request.send("At what odds would you like to play?\n")
o = getn(self.request)
if o > 1000000:
self.request.send("Sorry, due to the expense of generating random numbers, please keep odds below 1000000\n")
if o <= 0:
self.request.send(" :| \n")
return 0
else:
break
self.request.send("Alright, what is your encrypted guess for the prefix\n")
g = recv(self.request, 4096)
if not self.verify_guess(g):
self.request.send("We had an issue validating your encrypted guess. Please ensure your connection is secure\n")
return 0
self.request.send("Ok, generating the secure random number now....\n")
time.sleep(0.2)
self.request.send("Beep ")
time.sleep(0.2)
self.request.send("Boop ")
time.sleep(0.2)
self.request.send("Beep\n")
time.sleep(0.2)
r = int(os.urandom(5).encode("hex"),16)
self.request.send("Okay, the secure RNG is %d\n"%r)
self.request.send("Now what is your secret key?\n")
k = recv(self.request, 4096)
if not self.decrypt_guess(k):
self.request.send("We had an issue verifying your encrypted guess...\n")
self.request.send("Either your connection is insecure, or your trying to cheat us D:\n")
return 0
if self.guess == r%o:
self.request.send("Congratulations! You have won $%d!\n"%(o*m))
self.monies += o*m
else:
self.request.send("Sorry, %d %% %d != %d\n"%(r,o,self.guess))
class ReusableTCPServer(SocketServer.ForkingMixIn, SocketServer.TCPServer):
pass
SocketServer.TCPServer.allow_reuse_address = True
server = ReusableTCPServer(("0.0.0.0", 4321), incoming)
server.timeout = 60
server.serve_forever()
#/usr/bin/python2
from socket import socket
from Crypto.PublicKey import RSA, DSA
from Crypto.Cipher import PKCS1_OAEP
_server_pub_enc = RSA.importKey('-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDGRrsdIqf8K39Ncwzsi9k2lr5G\nJ8aEFkYGrYqOQRbU5xOReMj8wWHgnSUC0fjH0gjffGiUC2HfrrNIQvXKGiSBetOu\nIWOmFiESG8IhrPyvLwX53NbMWeCihzbYGJxGyiL0bvDHxqDxzuvteSaEfNm1miPA\nQ9rs5vFnHM0R3kFjdQIDAQAB\n-----END PUBLIC KEY-----')
server_pub_enc = PKCS1_OAEP.new(_server_pub_enc)
server_pub_sig = DSA.construct([6492988819243051335053735606322819439099395961135352303030066825351059776939776358522765113843576255727411249922052441719518573282010295240606387519552263L, 5720927070571587652595864015095152811124313453716975619963331476834195150780326792550956895343289380256771573459290257563350163686508250507929578552744739L, 6703916277208300332610863417051397338268350992976752494932363399494412092698152568009978917952727431041521105933065433917303157931689721949082879497208537, 1022875313346435070370368907571603203095488145799L])
conn = socket()
conn.connect(('52.6.11.111', 4321))
banner = conn.recv(4096)
promptDSA = conn.recv(4096)
dsaKey = DSA.generate(1024)
dsaStr = ",".join(map(str, (
dsaKey.publickey().y,
dsaKey.publickey().g,
dsaKey.publickey().p,
dsaKey.publickey().q)))
rsaSize = _server_pub_enc.size()
sha1Size = 160
rsaChunkSize = rsaSize/8 - sha1Size/8*2 - 2 # PKCS1_OAEP
dsaClientPubEncoded = ",".join(
[server_pub_enc.encrypt(dsaStr[i:i + rsaChunkSize]).encode('hex')
for i in range(0, len(dsaStr), rsaChunkSize)])
conn.send(dsaClientPubEncoded + "\n")
serverSig = conn.recv(4096)
verifyMsg = conn.recv(4096)
gamePrompt = conn.recv(4096)
betMsg = conn.recv(4096)
money = betMsg[betMsg.index('$') + 1:betMsg.index(')')]
if int(money) <= 0:
print "no money; try again"
exit(1)
#conn.send(money+"\n") # Intended solution
conn.send("1000000000\n") # Bug
notEnoughMsg = conn.recv(4096)
oddsMsg = conn.recv(4096)
conn.send("2\n")
guessMsg = conn.recv(4096)
guess = "I hereby commit to a guess of 0"
rsaClientKey = RSA.generate(1024)
rsaClientPKCS1 = PKCS1_OAEP.new(rsaClientKey)
encGuess = rsaClientPKCS1.encrypt(guess).encode('hex')
guessSig = dsaKey.sign(encGuess, 5L)
guessSend = encGuess + "~" + str(guessSig[0]) + "," + str(guessSig[1])
conn.send(guessSend)
okMsg = conn.recv(4096)
beeps = (conn.recv(4096), conn.recv(4096), conn.recv(4096))
secureMsg = conn.recv(4096)
secKeyMsg = conn.recv(4096)
rsaClientKeyStr = rsaClientKey.exportKey()
rsaClientKeySig = dsaKey.sign(rsaClientKeyStr, 5L)
rsaClientKeySend = rsaClientKeyStr + "~" + \
str(rsaClientKeySig[0]) + "," + \
str(rsaClientKeySig[1])
conn.send(rsaClientKeySend)
congratulations = conn.recv(4096)
flag = conn.recv(4096)
if "flag" not in flag:
print "You were unlucky. Try again"
exit(1)
print flag
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment