# Python Reversing

Author: GabiTulbaContest: TJCTF 2018

## Problem statement:

Found this flag checking file and it is quite vulnerable

Source

## My opinion:

I still don’t understand why this problem was tagged as `Reverse Engineering`

, it was a simple cryptography problem with a classic attack, in my opinion it was way easier than Caesar’s Complication, the number of solves talk for themselves.

## Understanding the encryption

We were given the following code:

```
import numpy as np
flag = 'redacted'
np.random.seed(12345)
arr = np.array([ord(c) for c in flag])
other = np.random.randint(1,5,(len(flag)))
arr = np.multiply(arr,other)
b = [x for x in arr]
lmao = [ord(x) for x in ''.join(['ligma_sugma_sugondese_'*5])]
c = [b[i]^lmao[i] for i,j in enumerate(b)]
print(''.join(bin(x)[2:].zfill(8) for x in c))
# original_output was 1001100001011110110100001100001010000011110101001100100011101111110100011111010101010000000110000011101101110000101111101010111011100101000011011010110010100001100010001010101001100001110110100110011101
```

Ok, let’s see what’s happening step by step:

- The
`flag`

is transformed into an array called`arr`

. - Then another array
`other`

of the same length as`arr`

with random values from 1 to 4 is generated using a constant seed (clearly a bad idea since now the encryption is deterministic). - The array
`arr`

is multiplied with the array`other`

by`arr[i]*=other[i]`

. - Then the array
`b=arr`

is xored element by element with the array`lmao`

. - The encrypted flag is the concatenation of the binary representation of every value in array
`c=b`

.

One thing that makes this problem a little harder (the encryption isn’t easily reversable) is that when the array `arr`

is multiplied with the array `other`

, in some cases the result is bigger than 256. I saw that when I saw that the length of the output wasn’t divisible by 8:

`>>> <br>`

`x='1001100001011110110100001100001010000011110101001100100011101111110100011111010101010000000110000011101101110000101111101010111011100101000011011010110010100001100010001010101001100001110110100110011101'`

`>>> len(x)%8`

`2`

But still, since we know every variable (including `other`

) we can ecnrypt, so we can guess the flag character by character by encrypting every possible byte and checking if it matches the desired output, this is known as a Chosen-plaintext attack.

## Decrypting the flag:

Here’s the code:

```
import numpy as np
enc='1001100001011110110100001100001010000011110101001100100011101111110100011111010101010000000110000011101101110000101111101010111011100101000011011010110010100001100010001010101001100001110110100110011101'
flag= ''
def encrypt(flag):
np.random.seed(12345)
arr = np.array([ord(c) for c in flag])
other = np.random.randint(1,5,len(arr))
arr = np.multiply(arr,other)
b = [x for x in arr]
lmao = [ord(x) for x in ''.join(['ligma_sugma_sugondese_'*5])]
c = [b[i]^lmao[i] for i,j in enumerate(b)]
y=(''.join(bin(x)[2:].zfill(8) for x in c))
p=0
while(p<len(y) and enc[p]==y[p]):
p+=1
return p
L=0
while(L<len(enc)):
for j in range(256):
if(L<=encrypt(flag+chr(j))-8): #at least 8 new bits have to mach the output
flag+=chr(j)
L=encrypt(flag)
print L
break
print flag
```

**NOTE** : It just happened for that my solution worked, since there’s not necesarily a one to one mapping to plaintext-ciphertext.
Flag: **tjctf{pYth0n_1s_tr1v14l}**