Rogdham

GreHack 2017 write ups

Introduction

The 2017 edition of the GreHack security conference was yesterday! Just like last year, I took part in the onsite CTF competition for the Greunion team.

Bellow are the write ups for the challenges I solved.

Crypto 100

In this challenge, we have a custom implementation of DSA, where the random value usually noted as k is not random at all: it is derived from the message to sign. Here is what the challenge description says:

I think I did a major breakthrough in cryptography: I managed to implement the DSA algorithm without any need of a random source. After all, all we need is a nonce, right ?

Don't you dare tell me it's insecure: I tested it!

Well, yes it is insecure, and in fact it is a special case of the cryptopals crypto challenge number 43.

We are given the following:

Since we can compute k=nonce(m), we can retrieve the secret key x and compute the signature.

Since the Python Crypto.PublicKey does not have a DSA.importKey method, we use the following command to retrieve the public key components:

$ sed 's/DSA //g' public.pem | openssl dsa -pubin -text

And here is the Python code to sign the new message:

from Crypto.Hash import SHA from gmpy2 import invert # insert here `nonce` code from my_DSA q = 0xfc89d1aaf8a856b283525b8e58abd036696d2a87 g = 0x544d234fc6b7e05ca78d147f1100b336b6e8c38c... p = 0x8000000000000002dd00e2e801235c9fec970079... with open('signed') as f: signed = f.read() k = nonce(map(ord, signed), q) h = long(SHA.new(signed).hexdigest(), 16) with open('signature') as f: s1, s2 = eval(f.read()) with open('sign_me') as f: h_new = long(SHA.new(f.read()).hexdigest(), 16) x_times_r = (s2 * k - h) % q s2_new = (invert(k, q) * (h_new + x_times_r)) % q print '({}, {})'.format(s1, s2_new)

All is left to do is to submit the signature to the validation server, and get the flag back.

Crypto 300

In this challenge, we have an RSA ciphertext that we need to decrypt. To do so, we have an online service which is an oracle. Indeed, we send a ciphertext, and it tells us if the corresponding plaintext is odd or even.

This is the cryptopals crypto challenge number 46 and a StackExchange post tells us how to solve this challenge.

We extract the RSA modulus with the following command:

$ openssl rsa -pubin -text -in public.pem

We just need to implement this methode into code:

from socket import socket n = 0xd985bbdf2022392cf4e1ce8d67ca1137a85e9299... cipher = 0x66b588a90a92d2fb365a647508618e30994... class Oracle(object): def __init__(self, ip, port): self.s = socket() self.s.connect((ip, port)) self.buf = '' def read_until(self, end): while True: try: pos = self.buf.index(end) except ValueError: pos = -1 if pos >= 0: data = self.buf[:pos] self.buf = self.buf[pos + len(end):] return data self.buf += self.s.recv(1024) def is_even(self, c): self.read_until('Ciphertext:\n') self.s.sendall('{}\n'.format(hex(c)[2:].rstrip('L'))) data = self.read_until('\n') if 'plaintext is even' in data: return True if 'odd plaintext' in data: return False raise ValueError(data) def solve(self, cipher, n): two_e = 2 ** 65537 low, high = 0, n while low != high: cipher = (two_e * cipher) % n if self.is_even(cipher): high = (low + high) / 2 else: low = (low + high) / 2 return low flag = Oracle('192.168.4.30', 4200).solve(cipher, n) print hex(flag)[2:].rstrip('L').decode('hex')

Hardware 100

This challenge is unusual: we have a picture of a LCD wired to some kind of microcontroller and displaying the beginning of the flag:

LCD and board

The challenge descriptions says that the LCD is not wide enough to display the full flag, but we have a waveform of the signals between the board and the LCD controller. Let's fire up GTKWave and see what these signals are looking like:

Waveforms

So we have what looks like an enable signal EN, and 7 other lines. Let's report the values of these lines on an online emulator of the LCD controller:

Emulator showing an f

This is the second character of the display (the first one being a space). We don't need to reproduce the characters mapping from the LCD controller documentation, since in our case it follows the ASCII encoding: 0b01100110 is a f.

Instead, we write a quick parser for the wave file, which extracts the values of the lines when the enable signal is set. This prints us the full flag:

vals = {} flag = '' with open('vcd') as f: for l in f: a, b = l.strip().split() vals[b[1]] = int(b[0]) if vals.get('(') and int(a[1:]) > 44000000: # `(` is EN signal flag += chr(int(''.join(str(vals[c]) for c in '\'&%$#"!'), 2)) print flag

Conclusion

As always, doing a CTF is quite fun! Thanks again to the Greunion team for having me onboard, this time we ended up taking the 4th place!

Also, two of those crypto challenges were taken from the cryptopals challenges, maybe doing the other ones as preparation for future CTFs would not be stupid…

This article and source code are released under the CC BY-SA licence.

Short URL: http://r.rogdham.net/31.