Fetching contributors…
Cannot retrieve contributors at this time
164 lines (127 sloc) 5.33 KB

# Creative Cheating

## Problem

Mr. Miller suspects that some of his students are cheating in an automated computer test. He captured some traffic between crypto nerds Alice and Bob. It looks mostly like garbage but maybe you can figure something out.

He knows that Alice's RSA key is (n, e) = (0x53a121a11e36d7a84dde3f5d73cf, 0x10001) (192.168.0.13) and Bob's is (n, e) = (0x99122e61dc7bede74711185598c7, 0x10001) (192.168.0.37)

## Solution

Credit: @emedvedev

We're given a PCAP dump, so let's examine it with Wireshark:

Alice (`192.168.0.13`) sends Bob (`192.168.0.37`) TCP packets with Base64-encoded data. Let's follow the stream, export all the packets and decode them one by one:

``````import base64

packets = []
with open('stream.txt') as lines:
for line in lines:
decoded = base64.b64decode(line)
packets.append(decoded)
``````

Our decoded packets look like this (notice that `SEQ` is not unique, we'll need it later):

``````SEQ = 13; DATA = 0x3b04b26a0adada2f67326bb0c5d6L; SIG = 0x2e5ab24f9dc21df406a87de0b3b4L;
SEQ = 0; DATA = 0x7492f4ec9001202dcb569df468b4L; SIG = 0xc9107666b1cc040a4fc2e89e3e7L;
SEQ = 5; DATA = 0x94d97e04f52c2d6f42f9aacbf0b5L; SIG = 0x1e3b6d4eaf11582e85ead4bf90a9L;
SEQ = 4; DATA = 0x2c29150f1e311ef09bc9f06735acL; SIG = 0x1665fb2da761c4de89f27ac80cbL;
SEQ = 18; DATA = 0x181901c059de3b0f2d4840ab3aebL; SIG = 0x1b8bdf9468f81ce33a0da2a8bfbeL;
SEQ = 2; DATA = 0x8a03676745df01e16745145dd212L; SIG = 0x1378c25048c19853b6817eb9363aL;
SEQ = 20; DATA = 0x674880905956979ce49af33433L; SIG = 0x198901d5373ea225cc5c0db66987L;
SEQ = 0; DATA = 0x633282273f9cf7e5a44fcbe1787bL; SIG = 0x2b15275412244442d9ee60fc91aeL;
[...]
``````

We'll assume that `SEQ` is the sequence order, `DATA` is the content and `SIG` is the signature. The RSA keys we're given are very short, let's query FactorDB and get `p` and `q` for both Alice and Bob:

• Alice's `p` and `q` are `38456719616722997` and `44106885765559411`.
• Bob's `p` and `q` are `49662237675630289` and `62515288803124247`.

Now, to calculate RSA parameters you would normally use RSATool, but since we're going to need some extra internal logic, let's create a custom RSA class. PyCrypto's `RSA.construct` is going to help us here:

``````import gmpy
from Crypto.PublicKey import RSA

class RSAPerson(object):

def __init__(self, e, p, q):
self.n = p * q
self.e = e
self.p = p
self.q = q
self.d = long(gmpy.invert(e, (p-1)*(q-1)))
self.key = RSA.construct((long(self.n), long(self.e), self.d))

def sign(self, message):
return self.key.sign(message, '')

def verify(self, message, signature):
return self.key.publickey().verify(message, [signature])

def encrypt(self, message):
return self.key.publickey().encrypt(message)

def decrypt(self, message):
return self.key.decrypt(message)

alice = RSAPerson(
0x10001,
38456719616722997,
44106885765559411
)
bob = RSAPerson(
0x10001,
49662237675630289,
62515288803124247
)
``````

Let's try decrypting the messages now. Since we have Alice sending packets to Bob, we'll have to decode the data with Bob's private key. We'll modify our code to:

1. Order the data entries by `SEQ`.
2. Parse the entries into `SEQ`, `DATA` and `SIG` before storing.
3. Decrypt `DATA` with Bob's key.
``````packets = []
with open('stream.txt') as lines:
for line in lines:
decoded = base64.b64decode(line)
match = regex.match(decoded).groups()
seq = int(match[0])
signature = int(match[2], 16)
data = int(match[1], 16)
data = bob.decrypt(data)
data = chr(data)
packets.append((
seq,
data,
signature
))
``````

Let's take a look at the output now:

``````(0, '\x0b', 411405985302309658304687940458766L)
(0, '&', 873819575920644857617395222352302L)
(0, '(', 254879289019714299901060498187239L)
(0, 'H', 130444345139656587207775038064682L)
(0, 'f', 525203869657262769956125397333553L)
(0, 'f', 1150325363693092780142319416277197L)
(1, '-', 828076894813745622214013032578722L)
(1, 'Y', 1485043620748068753347713371814689L)
(1, 'j', 1298874456026095076333544275801497L)
(1, 'l', 583679654300144088157196927029953L)
(1, 'u', 1055492309091995510087523902347914L)
(2, '<', 394933299120645953370114156475962L)
(2, 'U', 355616958260007551295456729191093L)
(2, 'a', 355616958260007551295456729191093L)
[...]
``````

Clearly, we must only have one character at each position, so we'll have to discard everything else. That's where `SIG` comes into play: since Alice signs every message, we'll add verification and build the string from verified characters only.

``````packets = []
with open('stream.txt') as lines:
for line in lines:
decoded = base64.b64decode(line)
match = regex.match(decoded).groups()
seq = int(match[0])
signature = int(match[2], 16)
data = int(match[1], 16)
data = bob.decrypt(data)
if alice.verify(data, signature):
data = chr(data)
packets.append((
seq,
data,
signature
))

print ''.join([packet[1] for packet in sorted(packets)])
``````

Run the script, get the flag:

``````flag{n0th1ng_t0_533_h3r3_m0v3_0n}
``````
You can’t perform that action at this time.