WPICTF - jocipher

description: Decrypt PIY{zsxh-sqrvufwh-nfgl} to get the flag!

category: Crypto - 100

The code was provided as a .pyc file (available at https://ctf.wpictf.xyz/files/d9543469d876cffa70bb84667ed5d369/jocipher.pyc). It’s a compiled python file which means we can get the code back with uncompyle.

pacaur -S python-uncompyle6
uncompyle6 jocipher.pyc jocipher.py

And we get the following python code.

# uncompyle6 version 3.2.6
# Python bytecode 2.7 (62211)
# Decompiled from: Python 3.7.3 (default, Mar 26 2019, 21:43:19) 
# [GCC 8.2.1 20181127]
# Embedded file name: ./jocipher.py
# Compiled at: 2019-03-01 18:41:21
import argparse, re
num = ''
first = ''
second = ''
third = ''

def setup():
    global first
    global num
    global second
    global third
    num += '1'
    num += '2'
    num += '3'
    num += '4'
    num += '5'
    num += '6'
    num += '7'
    num += '8'
    num += '9'
    num += '0'
    first += 'q'
    first += 'w'
    first += 'e'
    first += 'r'
    first += 't'
    first += 'y'
    first += 'u'
    first += 'i'
    first += 'o'
    first += 'p'
    second += 'a'
    second += 's'
    second += 'd'
    second += 'f'
    second += 'g'
    second += 'h'
    second += 'j'
    second += 'k'
    second += 'l'
    third += 'z'
    third += 'x'
    third += 'c'
    third += 'v'
    third += 'b'
    third += 'n'
    third += 'm'


def encode(string, shift):
    result = ''
    for i in range(len(string)):
        char = string.lower()[i]
        if char in num:
            new_char = num[(num.index(char) + shift) % len(num)]
            result += new_char
        elif char in first:
            new_char = first[(first.index(char) + shift) % len(first)]
            if string[i].isupper():
                result += new_char.upper()
            else:
                result += new_char
        elif char in second:
            new_char = second[(second.index(char) + shift) % len(second)]
            if string[i].isupper():
                result += new_char.upper()
            else:
                result += new_char
        elif char in third:
            new_char = third[(third.index(char) + shift) % len(third)]
            if string[i].isupper():
                result += new_char.upper()
            else:
                result += new_char
        else:
            result += char

    print result
    return 0


def decode(string, shift):
    result = ''
    shift = -1 * shift
    for i in range(len(string)):
        char = string.lower()[i]
        if char in num:
            new_char = num[(num.index(char) + shift) % len(num)]
            result += new_char
        elif char in first:
            new_char = first[(first.index(char) + shift) % len(first)]
            if string[i].isupper():
                result += new_char.upper()
            else:
                result += new_char
        elif char in second:
            new_char = second[(second.index(char) + shift) % len(second)]
            if string[i].isupper():
                result += new_char.upper()
            else:
                result += new_char
        elif char in third:
            new_char = third[(third.index(char) + shift) % len(third)]
            if string[i].isupper():
                result += new_char.upper()
            else:
                result += new_char
        else:
            result += char

    print result
    return 0


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--string', '-s', type=str, required=True, help='the string to encode or decode')
    parser.add_argument('--shift', '-t', type=int, required=True, help='the shift value to use')
    parser.add_argument('--encode', '-e', required=False, action='store_true', help='encode the string')
    parser.add_argument('--decode', '-d', required=False, action='store_true', help='decode the string')
    args = parser.parse_args()
    setup()
    p = re.compile('[a-zA-Z0-9\\-{}]')
    if p.match(args.string) is not None:
        if args.encode:
            ret = encode(args.string, args.shift)
        else:
            if args.decode:
                ret = decode(args.string, args.shift)
        if ret is not 0:
            print 'Sorry, this cipher only uses the [a-zA-Z0-9\\-{}]'
    else:
        print 'Sorry, this cipher only uses the [a-zA-Z0-9\\-{}]'
    return


if __name__ == '__main__':
    main()
# okay decompiling jocipher.pyc

The challenge’s author left a decode function, and a command line helper.

We can use the python jocipher.py --decode STRING_TO_DECODE and try to bruteforce the shift, unfortunately there were multiple flags starting by WPI

wpi_jocipher.png

The flag was WPICTF{xkcd-keyboard-mash}