arrow-left

All pages
gitbookPowered by GitBook
1 of 10

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Really Secret Algorithm

More of a rev really :/

Simple first step is to extract all the b85 encoded values as bytes. The ct and e are just b85 encoded, so we can store those for later use with rsactftool

P and Q on the other hand are treated differently. They are xored with a variable called 's' (state) using 'walrus operators'. The function can be expressed as:

append(s^i)
s=s^i
append(s^j)
s=s^j

The encrypted bytes of p and q are also interleaved throughout the value we receive. As XOR is commutative and s is XORed with the value we have just recovered, we can simply run the script again to recover the original values for p and q, making sure to use the recovered values of p and q bytes to update the state. We recover primes

p=8935533316664982385690426241789463156779334270200983340957286950060861311077151464930402912151709770833375547368974424564809135614170092179811531622097999
and
q=11379478034699907676633030046472807804044882783405443091999142030427354686298593670992789218031609011985520050382686352162426667346054932520656108554445759

Plugging our data into RsaCtfTool yields:

hashtag
Flag: ractf{DoY0uLik3MyW4lrus35}

B007L36 CRYP70... 4641N

Alright so this is basically obscurity crypto.

A little experimentation shows us that it for every byte of the plaintext and the matching byte of the key it adds the ascii values and then moduluses them by 256.

NOTE: because of how python .encode and decode works, for example, '\x7f'.encode() = b'\xc2\x7f'.

So, whenever we base 64 decode, we have to .decode().encode('latin-1') to get the proper byte values instead of weird UTF-8 ones.

It's possible to use modular arithmetic to get the key, in fact that probably wouldn't have been much harder, but instead I just ran a small byte by byte brute force of each char of the key, and then the flag, as this is a character by character cipher making it vulnerable to such an attack.

My script is below:

hashtag
Flag:ractf{f00l_m3_7w1c3_5h4m3_0n_m3}

plain = b"To test the encryption service, encrypt this file with your company issued secret key and ensure that it results in the ciphertext.txt file."
import base64
enc = base64.b64decode(b"w4bDkMKDw6jDi8Ouw6JQw6jDh8OZwojCmMONw4nDnsKtwqnDk8OiwqLDosKdw6XDhsOVw6rDj8Oew5NcwpTDhMOiw4vCpcOYw5bDoFTCrcOHw6LCpsKUw6PDm8ONw4jClMOdw6TDosKYwpTDmMOjw53CpX/DicObwqHCqcOAw6fCrMKUw6bDpcOUw5jDmcOKwpvDocKVw5fDkcOZw5xTw4rDi8OlVMKaw43DnVPDmcOrw6XDlsOVw5nChsOvw5bCkcOof8Odw5xTw5HDi8OfwqnCpcOTw6xTw53Dq8KSw5XDi8OZwobDnsOXwqDDnMOEw6bDnMKYw5fDmsKawqjCscOTwpnCmcOdw6nDl8KP").decode().encode('latin-1')
def encrypt(num,key):
    return (num + key) % 256
key = b""
for char in range(len(enc)):
    for possible in range(256):
        if enc[char] == encrypt(plain[char],possible):
            key += bytes([possible])
            break
print(key)
flagenc = base64.b64decode(b"w6TDgsOGw6jDjMO2w5RgwqTDi8OTw5Vmwr7CncOjZcKcwpLDmGjDnMKxw5/ClMOCwqTDlMOaw5tjw7E=").decode().encode('latin-1')
flag = b""
for char in range(len(flagenc)):
    for possible in range(256):
        if flagenc[char] == encrypt(possible,key[char]):
            flag += bytes([possible])
            break
print(flag)

Really Speedy Algorithm

Essentially a scripting challenge. You'll be given all sorts of parameters and need to calculate the missing one. Instead of going through every possible solution, which would've been really excruciating, I went through this approach -

Create a function that solves for a value.

The function's logic is as follows:

if we want a value that's already defined, just return it

if we want n, call ourself to solve p and q, then multiply

if we want ct, call ourself to solve n, then do pt^e mod n

if we want pt, call ourself to solve d and n, then do ct^d mod n

if we want d, call ourself to solve p and q,compute phi, then return inverse(e,phi)

if we want p, or q, then:

pretend p is the prime we have and q is the prime we want

if we have n, return n // p

if we have phi, return (phi // (p - 1)) + 1

if all else fails and we have e and d, use Crypto.PublicKey.RSA to compute p and q via n, e and d

import socket
from Crypto.Util.number import inverse, GCD
from Crypto.PublicKey import RSA
IP = '95.216.233.106'
PORT = 62467
def dosolve(val):
    global cur
    if val in cur.keys():
        return cur[val]
    if val == 'q' or val == 'p':
        # Prime solving
        # Pretend the prime we want is q and the prime we have is p every time for simplicity.
        if 'q' in cur.keys():
            cur['p'] = cur['q']
        if 'n' in cur.keys():
            return cur['n'] // cur['p']
        elif 'phi' in cur.keys():
            return (cur['phi'] // (cur['p'] - 1)) + 1
        elif 'e' in cur.keys() and 'd' in cur.keys():
            key = RSA.construct((cur['n'],cur['e'],cur['d']))
            primes = [key.p,key.q]
            primes.remove(cur['p'])
            return primes[0]
    elif val == 'd':
        cur['p'] = dosolve('p')
        cur['q'] = dosolve('q')
        phi = (cur['p'] - 1) * (cur['q'] - 1)
        d = inverse(cur['e'],phi)
        return d
    elif val == 'n':
        cur['p'] = dosolve('p')
        cur['q'] = dosolve('q')
        return cur['p'] * cur['q']
    elif val == 'ct':
        cur['n'] = dosolve('n')
        return pow(cur['pt'],cur['e'],cur['n'])
    elif val == 'pt':
        cur['d'] = dosolve('d')
        cur['n'] = dosolve('n')
        return pow(cur['ct'],cur['d'],cur['n'])
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP, PORT))
s.setblocking(0)

buffer = b''
cur = {}
while True:
    # Read until a prompt or line break
    try:
        chunk = s.recv(4096)
        buffer += chunk
        print(chunk.decode(), end='')
    except BlockingIOError:
        pass

    if b'\n' not in buffer and not buffer.endswith(b': '):
        continue

    # Grab the oldest line
    buffer = buffer.split(b'\n', 1)
    if len(buffer) == 1:
        line, buffer = buffer[0], b''
    else:
        line, buffer = buffer

    # Llines start with [<code>]
    if line[:1] != b'[':
        continue

    # Use slicing not indexing because indexing bytes returns ints
    mode = line[1:2]
    if mode == b'*':
        ...
    elif mode == b'c':
        cur = {}
    elif mode == b':':
        important = line[3:].decode().split(": ")
        value = int(important[1])
        cur[important[0].strip()] = value
    elif mode == b'!':
        print(line)
    elif mode == b'?':
        needed = line[3:].decode().split(": ")[0].strip()
        if needed in cur.keys():
            s.send(str(cur[needed]).encode() + b'\n')
            continue
        val = dosolve(needed)
        print(val)
        print(cur)
        s.send(str(val).encode() + b'\n')
    else:
        ...

B007l3G CRYP70

By uh... guessing?

we figure out that encoding method is:

-taking the ord value of the char

-subtracting it from 255

-generating 4 numbers that add to this number

This can be reversed easily with this script:

#script that will error but give flag
a = "41 36 37 27 35 38 55 30 40 47 35 34 43 35 29 32 38 37 33 45 39 30 36 27 32 35 36 52 72 54 39 42 30 30 58 27 37 44 72 47 28 46 45 41 48 39 27 27 53 64 32 58 43 23 37 44 32 37 28 50 37 19 51 53 30 41 18 45 79 46 40 42 32 32 46 28 37 30 43 31 26 56 37 41 61 68 44 34 26 24 48 38 50 37 27 31 30 38 34 58 54 39 30 33 38 18 33 52 34 36 31 33 28 36 34 45 55 60 37 48 57 55 35 60 22 36 38 34"

b = a.split(" ")
o = ""
for i in range(len(a)//4):
  c = i * 4
  d = int(b[c]) + int(b[c+1]) + int(b[c+2]) + int(b[c+3])
  e = 255 - d
  o += chr(e)
  print(o)

hashtag
Flag: ractf{d0n7_r0ll_y0ur_0wn_cryp70}

Access=0000

Basic CBC bit flipping - We're given ciphertext for access=9999;+ an expiry timestamp, and have to provide an IV and ciphertext that decrypt to acesss=0000.

hashtag
ractf{cbc_b17_fl1pp1n6_F7W!}

https://gchq.github.io/CyberChef/#recipe=From_Hex('Auto')XOR(%7B'option':'Hex','string':'0000000000003d393939390000000000'%7D,'Standard',true)XOR(%7B'option':'Hex','string':'0000000000003d303030300000000000'%7D,'Standard',true)To_Hex('None',0)&input=NjA3MDcwOTllNDlhYjdmOWU5NTY2NWRjZTg0ZDI4NmVhNTI0Yzc1N2JhYmNjN2QyMWI1YTlhYWU0OTY1NGY1ZGNjNGU0ZjZkZGY5ZTk1OTUxNThkYmQyMzYyMDhjMmU1arrow-up-right

Crypto

Really Simple Algorithm

We are given p, q, e and ct so this is very easy to decrypt I just used RsaCtfTool because I'm lazy :)

hashtag
Flag: ractf{JustLikeInTheSimulations}

Mysterious Masquerading Message.md

The OpenSSH private key is actually not a private key and is simply just base64.

We decode it to get a long useless message and 2 hex strings.

If we decode these hex strings we get,

ineedtoopenlocks and initialisation12

Decoding the binary gets us a long hex string.

Looking at initialisation, I immediately thought of AES. Since an IV is provided, we can assume it is AES-CBC, so I just used cyberchef to

decrypt and get the flag.

hashtag
Flag: ractf{3Asy_F1aG_0n_aEs_rAcTf}

Really Small Algorithm

See Really Simple Algorithm, except this time we aren't given P and Q, so we have to factorize n with factordb again, RsaCtfTool go brrrr

hashtag
Flag: ractf{S0m3t1mesS1zeDoesM4773r}

0x Series

  1. Monoalphabetical Substitution Cipher (trial and error)

    Flag: documents

  2. Vigenere Cipher (trial and error)

    Flag: zurich

  3. Railfence Cipher (freq anal)

    Flag: ANUALLEAVE

  4. Columnar Transposition Cipher (freq analysis, x's at end for padding)

    Flag: CONCERNEDENCRYPTED

  5. Bifid Cipher (lack of j hinted at grid cipher, cant be playfair because of doubles)

    Key: ReallyAwesome

    Flag: Campbell

  6. Periodic Gromark Cipher (number key, nothing else given)

    Key: agency

    Flag: organization