[Shakti CTF] Grocery List

In this 200 points challenge from ShaktiCon we get a txt with a base64 string:

UmV2ZXJzZSBHcm9jZXJ5UGxhY2UKCnZpY2h5c3NvaXNlICAgICAgICAgICAgIAptYW5nbyAgICAgICAgICAgICAgICAgICAKdmVybW91dGgKenVjb3R0bwpzYW5kd2ljaApsYW1iCnZlYWwKeW9ndXJ0CnZlcm1pY2VsbGkKenVjY2hpbmkKc2FsbW9uCmZlbm5lbCBzZWVkcwppY2UgY3JlYW0KY2Fycm90cwp1bmFnaQppbmNhIGJlcnJpZXMKY2FiYmFnZQp1cG1hCmdyYXBlcwpuYWFuCmFwcGxlcwpiYW5hbmFzCmFsbW9uZHMKYmFzaWwKZmVudWdyZWVrCnBvdGF0b2VzCnBpZQpzb3kgYmVhbnMKZWdncwp0dW5hZmlzaAoKRmluZCB0aGUgaW5wdXQgdG8gdGhlIGZvbGxvd2luZyBvdXRwdXQuCk9VVFBVVDogNGN1bTc3aXRRZEt5NHI3Y35ybTV1MDVwbE4=

with a base64 decoder we get this β€œGrocery List”:

Reverse GroceryPlace

vichyssoise             
mango                   
vermouth
zucotto
sandwich
lamb
veal
yogurt
vermicelli
zucchini
salmon
fennel seeds
ice cream
carrots
unagi
inca berries
cabbage
upma
grapes
naan
apples
bananas
almonds
basil
fenugreek
potatoes
pie
soy beans
eggs
tunafish

Find the input to the following output.
OUTPUT: 4cum77itQdKy4r7c~rm5u05plN

The last two lines tell us what to do so.. after some osint, I found an esoteric programming language and his stack instructions are described by the first letter of each β€œproduct” on the list but I was not able to find any compiler or interpreter for it so i had to decode it by hand πŸ™ƒ. Online there are 2 sources that explains what every instrunction does: esolang.org and progopedia.com. I tried to translate my list with the esolang table but when i saw the A instruction

a	pops the top two values on the stack, adds them together and pops the result.

It made no sense to β€œpop the result” so i tried to translate the code with progopedia table.

a (add) β€” push S0+S1.
b (bring) β€” remove bottom element and push it on the top.
c (copy) β€” duplicate S0.
d (divide) β€” push S0/S1.
e (end loop) β€” end of the loop.
f (flip) β€” flip elements S0 and S1.
g (greater than) β€” push 1, if S0>S1, and 0 otherwise.
h β€” execute command which corresponds to character a+S0%26.
i (input) β€” read a character from stdin and push its ASCII value.
j (jump) β€” jump S0 lines forward.
k (kill) β€” remove all elements from the stack.
l (loop) β€” start loop: the loop repeats as long as S0is non-zero and there are elements in the stack.
m (multiply) β€” push S0*S1.
n (number) β€” push the number of characters in the current list item (including whitespace).
o (output) β€” print S0 as a number.
p (print) β€” print S0 as a character.
q β€” no operation.
r (remainder) β€” push S0 mod S1.
s (subtract) β€” push S0-S1.
t (terminate) β€” terminate program execution.
u (unbring) β€” pop S0 and put it to the bottom of the stack.
v (value) β€” push ASCII-code of the next list item (and skip execution of the next line).
w β€” push 100.
x β€” pop S0.
y β€” remove Sn, where n is the number of characters in the current list item.
z (zero) β€” push 1 if S0=0, and 0 otherwise.

PS1: The "v" instructions push only the first letter from the word

This is my β€œTranslation” of the program:

vichyssoise             
mango                                                                stack:  m     
vermouth
zucotto		                                                        stack:  zm
sandwich	                                                        stack:  (z-m)
lamb		    while (z-m)!=0:                                     stack:  (z-m)
veal		      
yogurt			                                                    stack:  y(z-m)
vermicelli
zucchini		                                                    stack:  zy(z-m)
salmon			                                                    stack:  (z-y)(z-m)		
fennel seeds		                                                stack:  (z-m)(z-y)
ice cream		$ will be mi first input                            stack:  $(z-m)(z-y)
carrots			                                                    stack:  $$(z-m)(z-y)
unagi			                                                    stack:  $(z-m)(z-y)$
inca berries	# will be my second input                           stack:  #$(z-m)(z-y)$
cabbage			                                                    stack:  ##$(z-m)(z-y)$
upma			                                                    stack:  #$(z-m)(z-y)$#
grapes			1#$(z-m)(z-y)$# if #>$ else 0#$(z-m)(z-y)$#         
                ? will be 1 or 0, from the condition below          stack:  ?#$(z-m)(z-y)$#
naan			                                                    stack:  4?#$(z-m)(z-y)$#
apples			                                                    stack:  (4+?)#$(z-m)(z-y)$#
bananas			                                                    stack:  #(4+?)#$(z-m)(z-y)$
almonds			                                                    stack:  (#+4+?)#$(z-m)(z-y)$
basil			                                                    stack:  $(#+4+?)#$(z-m)(z-y)
fenugreek		                                                    stack:  (#+4+?)$#$(z-m)(z-y)
potatoes		print((#+4+?))                                      stack:  $#$(z-m)(z-y)
pie		    	print($)                                            stack:  #$(m-z)(z-y)
soy beans	    ends the cicle if (#-$)==0	                        stack:  (#-$)(m-z)(z-y)
eggs			
tunafish        terminates the program

after that, i translated it line by line into a python script:

st=[]
st.insert(0,ord('m'))
st.insert(0,ord('z'))
st[0]-=st.pop(1)
def inverti(primo,secondo):
    temp1=st[primo]
    temp2=st[secondo]
    st[primo]=temp2
    st[secondo]=temp1

while(st[0]!=0):
    st.insert(0,ord('y'))
    st.insert(0,ord('z'))
    st[0]-=st.pop(1)
    inverti(0,1)
    pri=input('first input: ')
    st.insert(0,ord(pri))
    st.append(ord(pri))
    seco=input('second input: ')
    st.insert(0,ord(seco))
    st.append(ord(seco))
    st.insert(0,1) if(st[0]>st[1]) else st.insert(0,0)
    st.insert(0,4)
    st[0]+=st.pop(1) #apples
    st.insert(0,st.pop(len(st)-1))
    st[0]+=st.pop(1)
    st.insert(0,st.pop(len(st)-1))
    inverti(0,1)
    print(chr(st.pop(0)))
    print(chr(st.pop(0)))
    st[0]-=st.pop(1)

As we see, the output of the program depense only by our inputs so we can simplify our script into this function

def getRes(a,b):
    firstPrint=1 if ord(b)>ord(a) else 0
    firstPrint+=4
    firstPrint+=ord(b)
    return chr(firstPrint)+a

And this function will encrypt 2 letters per time, now that we have a simple and working function, we can bruteforce the inputs and try to get the solution.

dic="1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
target="4cum77itQdKy4r7c~rm5u05plN"
result=""
while len(target)!= len(result):
    coupleAim=target[0+len(result):2+len(result)]
    for a in dic:
        for b in dic:
            if(getRes(a,b)==coupleAim):
                result+=a
                result+=b
print(result)
>   c0mp73tedMyGr0c3ry5h0pp1Ng

That’s our FLAG! We now can add shaktictf{} and we get: shaktictf{c0mp73tedMyGr0c3ry5h0pp1Ng}