Rating:

## Rima
### Challenge

```python
#!/usr/bin/env python3

from Crypto.Util.number import *
from flag import FLAG

def nextPrime(n):
while True:
n += (n % 2) + 1
if isPrime(n):
return n

f = [int(x) for x in bin(int(FLAG.hex(), 16))[2:]]

f.insert(0, 0)
for i in range(len(f)-1): f[i] += f[i+1]

a = nextPrime(len(f))
b = nextPrime(a)

g, h = [[_ for i in range(x) for _ in f] for x in [a, b]]

c = nextPrime(len(f) >> 2)

for _ in [g, h]:
for __ in range(c): _.insert(0, 0)
for i in range(len(_) - c): _[i] += _[i+c]

g, h = [int(''.join([str(_) for _ in __]), 5) for __ in [g, h]]

for _ in [g, h]:
if _ == g:
fname = 'g'
else:
fname = 'h'
of = open(f'{fname}.enc', 'wb')
of.write(long_to_bytes(_))
of.close()
```

The flag is encoded using a bunch of weird looking operations, and then we get the two files `g.enc` and `h.enc`

### Solution

Firstly, we can deduce the flag length as 32 bytes by simply testing some letter repeated some number of times as the flag, then checking the length of the output and comparing it to the size of `g.enc`.

We will work through the steps in reverse order.

#### Step 1

```python
g, h = [int(''.join([str(_) for _ in __]), 5) for __ in [g, h]]

for _ in [g, h]:
if _ == g:
fname = 'g'
else:
fname = 'h'
of = open(f'{fname}.enc', 'wb')
of.write(long_to_bytes(_))
of.close()
```

Firstly, each file contains bytes, which we need to convert to a base 10 integer. Then, we need to convert this base 10 integer into a base 5 integer. We can do this quite easily with `gmpy2`'s `digits` function.

#### Step 2

```python
c = nextPrime(len(f) >> 2)

for _ in [g, h]:
for __ in range(c): _.insert(0, 0)
for i in range(len(_) - c): _[i] += _[i+c]
```

These next steps add some elements of the list to other elements of the list. We can work out the value of `len(_) - c` by just running the program with a random 32 byte flag, and then to reverse it, we just need to ensure that we change the addition to subtraction, and work in reverse order, as the later elements of the list are not affected by the earlier ones (but not vice versa).

We also then need to trim $c$ amound of 0's from the start of the list at the end. $c$ can again be worked out by just running the program.

#### Step 3

```python
a = nextPrime(len(f))
b = nextPrime(a)

g, h = [[_ for i in range(x) for _ in f] for x in [a, b]]
```

This step simply takes the list $f$ and duplicates it $a$ and $b$ times, storing them in $g$ and $h$. We can either manually find the repeating sequence, work out the values of $a$ and $b$ and simply split $g$ into $a$ chunks (or $h$ into $b$ chunks), or we can simply know the length of $f$, and take the first $len(f)$ elements of $g$ to get the original $f$.

#### Step 4

```python
f = [int(x) for x in bin(int(FLAG.hex(), 16))[2:]]

f.insert(0, 0)
for i in range(len(f)-1): f[i] += f[i+1]
```

Our last step is again quite similar to step 2, we work out the length of $f$ by running the program, and then going in reverse order, changing the addition to a subtraction instead. We can then obtain the flag by converting the list into a string, which should be the binary string of the flag.

### Implementation

Putting this all together, this looks like this:

```python
from gmpy2 import digits

# Step 1
g = list(digits(bytes_to_long(open("g.enc","rb").read()) ,5))
g = [int(x) for x in g]

# Step 2
for _ in [g]:
for i in range(65791,-1,-1): _[i] -= _[i+c]

# Step 3
f = g[67:67+256]
f = [int(x) for x in f]

# Step 4
for i in range(len(f)-2, -1, -1):f[i] -= f[i+1]
print("".join([str(x) for x in f]))
```

##### Flag
`CCTF{_how_finD_7h1s_1z_s3cr3T?!}`

Original writeup (https://blog.cryptohack.org/cryptoctf2021-easy#rima).