Tags: python 

Rating:

# EASYCTF - Soupstitution Cipher

> description: We had a flag, but lost it in a mess of alphabet soup! Can you help us find it? Connect to the server via `nc c1.easyctf.com 12484`.

> hint: I love parsing characters!

> category: Reverse Engineering

Okay, There is the source code in `python`:

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

from binascii import unhexlify as sOup
from operator import attrgetter as souP

ME_FLAGE = '<censored>'

SoUp = input
soUP = hex
sOUp = print
sOuP = ord
SOuP = open

def SoUP(sOUP):
soup = 0
while sOUP != 0:
soup = (soup * 10) + (sOUP % 10)
sOUP //= 10
return soup

def SOup(sOUP):
soup = 0
for soUp in sOUP:
soup *= 10
soup += sOuP(soUp) - sOuP('0')
return soup

def SOUP():
Soup = SoUp()[:7]
print(Soup)
if not souP('isdigit')(Soup)():
sOUp("that's not a number lol")
return

soup = SoUP(SOup(Soup))
SouP = souP('zfill')(soUP(soup)[2:])(8)[-8:]
if sOup(SouP) == souP('encode')('s0up')():
sOUp("oh yay it's a flag!", ME_FLAGE)
else:
sOUp('oh noes rip u')

if __name__ == '__main__':
SOUP()
```

Okay let's first `deobfuscate` this SoupCode:

```
from binascii import unhexlify, hexlify
from operator import attrgetter

ME_FLAGE = '<censored>'

def third(param):
soup = 0
while param != 0:
soup = (soup * 10) + (param % 10)
param //= 10
return soup

def second(param):
soup = 0
for soUp in param:
soup *= 10
soup += ord(soUp) - ord('0')
return soup

def principal(a):
Soup = a[:7]
if not attrgetter('isdigit')(Soup)():
print("that's not a number lol")
return

soup = third(second(Soup))

SouP = attrgetter('zfill')(hex(soup)[2:])(8)[-8:]

if unhexlify(SouP) == attrgetter('encode')('s0up')():
print("oh yay it's a flag!", ME_FLAGE)
exit(0)
else:
pass
```

Alright so the principal method, will take the first `7 digits`. Then the method `second()` will convert it to an int and the method `third()` will invert all digits (123 -> 321).

The goal is to enter in the `if unhexlify(SouP) == attrgetter('encode')('s0up')()`:

We have:

`attrgetter('encode')('s0up')() = 'S0up'`

And we want:

`unhexlify(SouP) = 's0up'`

So, reversing it:

`SouP = hexlify('s0up') = 73307570`

Just before we have:

`SouP = attrgetter('zfill')(hex(soup)[2:])(8)[-8:]`

This is only a conversion to hexa, and we want it to be egal to 73307570:

`0x73307570 = 1932555632`

then soup = 1932555632

The program call `second()` and `third()` so all we have to do is to reverse this number:

`soup = third(second(Soup))`

We have Soup = 2365552391

The first line of the method limit our entry to 7 digits:

`Soup = a[:7]`

At this point you can try with the maximum 'legit' entry (`9999999`), u won't be able to reach 2365552391...

Okay first I commented the code with the values we would like to get:

```
def principal(a):
Soup = a[:7] #impossible
if not attrgetter('isdigit')(Soup)():
#print("that's not a number lol")
return
#Soup = "2365552391" # this to win
soup = third(second(Soup))
print(soup)
#soup = 1932555632 # this to win
SouP = attrgetter('zfill')(hex(soup)[2:])(8)[-8:]
#SouP = "73307570" # this to win
if unhexlify(SouP) == attrgetter('encode')('s0up')(): # we want SouP = 73307570
print(Soup)
print("oh yay it's a flag!", ME_FLAGE)
exit(0)
else:
pass
```

As u can see in the hint, there is characters that are interpreted as digit. let try to list some of them:

```
def principal(a):
Soup = a[:7]
if not attrgetter('isdigit')(Soup)():
return
print(Soup, end=" ")

for i in range(2500):
principal(chr(i))
```

The result is:

> 0 1 2 3 4 5 6 7 8 9 ² ³ ¹ ٠ ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩ ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ߀ ߁ ߂ ߃ ߄ ߅ ߆ ߇ ߈ ߉ ० १ २ ३ ४ ५ ६ ७ ८ ९

Now let's see what is the value for one os this entry:

```
def principal(a):
Soup = a[:7] #impossible
if not attrgetter('isdigit')(Soup)():
return
soup = third(second(Soup))
print(soup)
#soup = 1932555632 # this to win

principal('߉')
# -> 5491

principal('߉111')
# -> 1115491

principal('߉789')
# -> 9875491
```

Alright, as we can see, all numbers added after the special char will be before it after the second() and third() methods.
So we want something like (weird_char + 552391) in entry and that weird_char = 5632

Let's brute force those values and see if the weird_char = 5632 exists:

```
def principal(a):
Soup = a[:7]
if not attrgetter('isdigit')(Soup)():
return
soup = third(second(Soup))
if soup == 5632:
print(a)
exit(0)

for i in range(10000):
principal(chr(i))

# -> ७
```

Nice ! the `७` should make us win this challenge !
Lets try it directly on the server =)

> nc c1.easyctf.com 12484

> ७552391

> oh yay it's a flag! easyctf{S0up_soup_soUP_sOuP_s0UP_S0up_s000000OOOOOOuuuuuuuuppPPppPPPp}

The flag is `easyctf{S0up_soup_soUP_sOuP_s0UP_S0up_s000000OOOOOOuuuuuuuuppPPppPPPp}` =)

Original writeup (https://ctfshellclub.github.io/2018/02/21/easyctf-Soupstitution/).