Skip to content

Latest commit

 

History

History

snakes_and_ladders

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

Snakes and Ladders

Description

The flag is fqtbjfub4uj_0_d00151a52523e510f3e50521814141c. The attached file may be useful.

Category: Reversing

Analysis

Let's see what's in main.py:

def xor(s1,s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2))

def encrypt(a):
    some_text = a[::2]

    randnum = 14
    text_length = len(some_text)
    endtext = ""
    for i in range(1, text_length + 1):
      weirdtext = some_text[i - 1]
      if weirdtext >= "a" and weirdtext <= "z":
          weirdtext = chr(ord(weirdtext) + randnum)
          if weirdtext > "z":
              weirdtext = chr(ord(weirdtext) - 26)
      endtext += weirdtext
    randtext = a[1::2]

    xored = xor("aaaaaaaaaaaaaaa", randtext)
    hex_xored = xored.encode("utf-8").hex()

    return endtext + hex_xored

def decrypt(msg):
    raise NotImplementedError

def main():
    opt = input("Would you like to [E]ncrypt or [D]ecrypt? ")
    if opt[:1].lower() == "e":
        msg = input("Enter message to encrypt: ")
        print(f"Encrypted message: {encrypt(msg)}")
    elif opt[:1].lower() == "d":
        msg = input("Enter message to decrypt: ")
        print(f"Decrypted message: {decrypt(msg)}")

if __name__ == "__main__":
    main()

Alright, we just have to implement decrypt() to undo the transformations we see in encrypt(). Let's see what happens when we encrypt a message.

kali@kali:~/Downloads$ python3 main.py
Would you like to [E]ncrypt or [D]ecrypt? e
Enter message to encrypt: helloworld
Encrypted message: vzccz040d161305
kali@kali:~/Downloads$ python3 main.py
Would you like to [E]ncrypt or [D]ecrypt? e
Enter message to encrypt: aaaaaaaa
Encrypted message: oooo00000000
kali@kali:~/Downloads$ python3 main.py
Would you like to [E]ncrypt or [D]ecrypt? e
Enter message to encrypt: bbbb
Encrypted message: pp0303

encrypt() takes the provided input string and splits it into 2 strings:

  1. some_text with the even letters (starting from 0), which gets passed into a loop that applies ROT-14 on the lowercase letters.

  2. randtext with the odd letters (starting from 1), which gets XOR'd with a static string and converted to a string of hex values.

After those steps are done, it returns the concatenation of the 2 resulting strings.

Solution

It would have been nice if there was a delimiter of some sort to separate the 2 strings. Oh well, we just need the flag, so I'll add the space in manually. Implement the decrypt() function to reverse the transformations described above, with a space separating the 2 strings.

import binascii

def xor(s1,s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2))

def unxor(s1,s2):
    return ''.join(chr(ord(a) ^ b) for a, b in zip(s1, s2))

def encrypt(a):  
    some_text = a[::2]

    randnum = 14
    text_length = len(some_text)
    endtext = ""
    for i in range(1, text_length + 1):
      weirdtext = some_text[i - 1]
      if weirdtext >= "a" and weirdtext <= "z":
          weirdtext = chr(ord(weirdtext) + randnum)
          if weirdtext > "z":
              weirdtext = chr(ord(weirdtext) - 26)
      endtext += weirdtext
    randtext = a[1::2]

    xored = xor("aaaaaaaaaaaaaaa", randtext)
    hex_xored = xored.encode("utf-8").hex()

    return endtext + ' ' + hex_xored

def decrypt(msg):
    # let's just throw a space in there manually, that seems easier.
    endtext, hex_xored = msg.strip().rstrip().split(" ")

    randnum = 14
    some_text = ""
    for i in range(1, len(endtext) + 1):
        weirdtext = endtext[i - 1]
        if weirdtext >= "a" and weirdtext <= "z":
            weirdtext = chr(ord(weirdtext) - randnum)
            if weirdtext < "a":
                weirdtext = chr(ord(weirdtext) + 26)
        some_text += weirdtext
    
    xored = binascii.unhexlify(hex_xored)
    randtext = unxor("aaaaaaaaaaaaaaa", xored)

    cleartext = "\n"
    for a, b in zip(some_text, randtext):
        cleartext += a + b

    return cleartext

def main(): 
    opt = input("Would you like to [E]ncrypt or [D]ecrypt? ")
    if opt[:1].lower() == "e":
        msg = input("Enter message to encrypt: ")
        print(f"Encrypted message: {encrypt(msg)}")
    elif opt[:1].lower() == "d":
        msg = input("Enter message to decrypt: ")
        print(f"Decrypted message: {decrypt(msg)}")

if __name__ == "__main__":
    main()

Test some known input:

kali@kali:~/Downloads$ python3 solve.py 
Would you like to [E]ncrypt or [D]ecrypt? e
Enter message to encrypt: helloworld
Encrypted message: vzccz 040d161305
kali@kali:~/Downloads$ python3 solve.py 
Would you like to [E]ncrypt or [D]ecrypt? d
Enter message to decrypt: vzccz 040d161305
Decrypted message: 
helloworld

Now we just have to run the flag from the description through it, with the extra space:

kali@kali:~/Downloads$ perl -e 'print "d\n" . "fqtbjfub4uj_0_d 00151a52523e510f3e50521814141c\n"' | python3 solve.py 
Would you like to [E]ncrypt or [D]ecrypt? Enter message to decrypt: Decrypted message: 
ractf{n3v3r_g0nn4_g1v3_y0u_up}

The flag is:

ractf{n3v3r_g0nn4_g1v3_y0u_up}