CTF-writeups

Some CTF writeups


Project maintained by Qyn-CTF

RunRonRun - Medium

Description:

Run Ron, Run!

First looks

We’re given a python file with the challenge source code:

#!/usr/bin/env python3

try:
    from Crypto.Cipher import ARC4
except:
    print("PyCryptoDome is not installed!")
    exit(1)

from secret import FLAG
import os



def roncrypt_flag(offset):
    key = os.urandom(16)
    cipher = ARC4.new(key)
    return cipher.encrypt(FLAG[offset:])


def main():

    while True:
        offset = int(input("Enter Offset>"))
        
        if not (0 <= offset < len(FLAG)):
            print("Offset is not in allowed range!")
            exit(2)

        encryted_flag = roncrypt_flag(offset)
        print(encryted_flag.hex())


if __name__ == '__main__':
    main()

From here, we can see that it just simply encrypts the flag starting from a given offset with random keys.
From wikipedia, we can read about the second output output byte: The best such attack is due to Itsik Mantin and Adi Shamir who showed that the second output byte of the cipher was biased toward zero with probability 1/128 (instead of 1/256)., which actually just means that we should see the second byte twice as much as the rest. This we can easily exploit.
For example, we can write a function that just checks every offset.

Solving

from pwn import *

def findOffset(offset, r):
    indexes = {}
    amount = 256*64
    for i in range(amount):
        r.sendline(str(offset))
    for i in range(amount):
        r.recvuntil("Enter Offset>")

        res = r.recvline()
        data = bytes.fromhex(res.decode())
        byte = chr(data[1])
        if byte not in indexes:
            indexes[byte] = 0
        indexes[byte]+=1
    sortedIndexes = sorted(indexes, key=indexes.get, reverse=True)
    return sortedIndexes[0]

Note that we’re using a simple trick to speed everything up, by sending all the offsets first instead of waiting for a response and then sending the offsets.
And we can bring it all together like this:

def main():
    flag = "CSCG{"
    r = remote("xxxxx-runronrun.challenge.broker.cscg.live", 31337, ssl=True)
    while True:
        offset = len(flag)-1
        char = findOffset(offset, r)
        flag += char
        offset+=1
        print(flag)

We already guessed that the flag starts with CSCG{, because all flags do.
Running this will give us the flag:
CSCG{schnieke}

Home