We are Recruiting!

Yes! We are actively recruiting individuals from the MD/DC/VA area for both online, on-site, and wireless CTFs.

To join our team solve this simple puzzle/captcha to find our New Member Orientation which contains a link to our discord server.


Friday, December 26, 2014

adctf2014 - Crypto - Day 8 rotate


Rotate.zip contains two files

  • flag.jpg.enc
  • rotate.py

flag.jpg.enc is an encrypted version of flag.jpg which we must decrypt inorder to find the flag.

rotate.py contains the following code

import sys
import math
import struct

p = lambda x: struct.pack('f', x)
u = lambda x: struct.unpack('b', x)[0]

if len(sys.argv) != 3:

filename = sys.argv[1]
key = math.radians(int(sys.argv[2]))

bs = open(filename, 'rb').read()
enc = open(filename + '.enc', 'wb')

for i in range(0, len(bs), 2):
    x, y = u(bs[i]), u(bs[i+1])
    enc.write(p(x * math.cos(key) - y * math.sin(key)) + p(x * math.sin(key) + y * math.cos(key)))

rotate.py takes two arguements, the first being the file to encrypt, the second being a numeric key. The key is converted to raidians. Finally the code goes through each byte of the file and does various trigonometric functions with it. We could look at reversing the cipher, but math is for nerds.

Instead, since we know that jpeg files begin with a specific value, we can bruteforce all the possible keys to find with the known plaintext to compare against the ciphertext. Once a match is found, we can then use the discovered key to decrypt the rest of the image.

Radians, and trigonometric functions such as sin() and cos() are all cyclical, since they're all based on the circle. As the code converts the key into integers and then radians, we know there are only 360 possible values (less than 9 bits of entropy). A better alternative would be to have the key be a 64 bit floating point number.

import sys
import math
import struct

p = lambda x: struct.pack('f', x)
u = lambda x: struct.unpack('b', x)[0]

def encrypt(first,second,rotate):
  key = math.radians(rotate)
  x, y = u(first), u(second)
  return p(x * math.cos(key) - y * math.sin(key)) + p(x * math.sin(key) + y * math.cos(key))

if len(sys.argv) != 2:

filename = sys.argv[1]
enc = open(filename, 'rb').read()
bs = open(filename + '.jpg', 'wb')
rotate = 0

for i in range(0, 360):
  APP0 = "ffd8".decode('hex')
  test = encrypt(APP0[0], APP0[1], i)

  if test == enc[0:8]:
    rotate = i
    print "found key"

for i in range(0, len(enc), 8):
  for h in range(0, 256):

    for j in range(0, 256):
      test = encrypt(chr(h), chr(j), rotate)

      if test == enc[i:i+8]:
        print '.',

    if k == 0:
      print '+',
    print '!'

And when its all said and done we see...

About Crimson Agents

Formed in 2013, Crimson Agents is a DC based recreational security team that competes in various computer security wargames and hacker jeopardy contests. Our team comprised of various professionals seeking to practice and enhance our skills in penetration testing, vulnerability development, computer network defense, forensics, and reverse engineering. In addition to exploitation based CTFs, we also compete in Wireless CTFs with several members who focus solely in this domain. Our sister team Threat Inc focuses on defensive exercises such as malware analysis, forensics, honeypots, and network captures. We reuse what we learn from our "Blue Team" research to make our "Red Team" operations more effective and vice-versa.