moooo - author: overllama - rev

WriteUp: Gryfman

Challenge description:

Moo, mooo mooo mooo. Mooo mooo moooo moo moo moooooooooo moo moooo (When you have the flag, the program will run printing "gotem")

With this challenge we are given a file named main.cow which is a COW (COW Programming Language) file.

After looking at the language and finding a COW interpreter, we can run the code and see the output. It first print a message : Moo. Moo moo mooo! Then its waiting for input.

Solution

I created a simple python script to better understand the code:

import re
import sys

with open("main.cow", "r", errors="ignore") as f:
    code = f.read()

tokens = re.findall(r"mOo|moO|MoO|MOo|MOO|Moo|moo|OOO|MMM|OOM|oom|mOO", code)
mapping = {
    "moo": "]",
    "mOo": "<",
    "moO": ">",
    "MoO": "+",
    "MOo": "-",
    "MOO": "[",
    "Moo": "I/O",
    "OOM": "PRINT",
    "oom": "READ_INT",
    "OOO": "CLR",
    "MMM": "REG",
    "mOO": "IND",
}

groupable = {"+", "-", "<", ">"}

pseudo = []
for t in tokens:
    instr = mapping[t]
    pseudo.append(instr)


# Function to collapse runs
def collapse(seq):
    result = []
    i = 0
    while i < len(seq):
        curr = seq[i]
        if curr in groupable:
            count = 1
            j = i + 1
            while j < len(seq) and seq[j] == curr:
                count += 1
                j += 1
            result.append(f"{curr}{count}")
            i = j
        else:
            result.append(curr)
            i += 1
    return result


collapsed = collapse(pseudo)

print("Grouped pseudo-Brainfuck / operations:")
print(" ".join(collapsed))

This script reads the COW file, tokenizes it, and maps the tokens to their corresponding operations. It then collapses consecutive operations into a more readable format.

When we run this script, we get the following output:

Grouped pseudo-Brainfuck / operations:
+11 [ >1 +7 >1 +10 >1 +10 >1 +4 >1 +3 >1 +1 <6 -1 ] >1 I/O >1 +1 I/O I/O >2 +2 I/O >1 -1 I/O <4 I/O >1 I/O I/O >3 I/O <2 -1 I/O <1 I/O I/O >3 I/O <2 I/O <1 I/O I/O I/O >3 +1 I/O >1 -1 I/O >1 CLR <1 CLR <1 CLR <1 CLR <1 CLR <1 CLR <1 CLR <1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O >1 I/O <38 -96 IND -119 IND -115 IND -97 IND -114 IND -100 IND -121 IND -107 IND -109 IND -109 IND -93 IND -107 IND -109 IND -109 IND -109 IND -93 IND -107 IND -109 IND -109 IND -109 IND -109 IND -93 IND -107 IND -109 IND -109 IND -109 IND -109 IND -109 IND -109 IND -109 IND -93 IND -107 IND -109 IND -109 IND +8 I/O >1 +3 I/O >1 +5 I/O >1 -7 I/O >1 -16 I/O

We can notice that its requesting input 39 times then it execute 34 IND commands, which are used to read integers from the input, after decrementing the value of the current cell by an offset.

Then at the end it prints 5 times the value of the current (modified by an offset) cell, then move to the next one.

Since we want to print gotem we can reverse the expected value of the last 5 cells to get the input we need to provide.

Since g is 103, o is 111, t is 116, e is 101, and m is 109, we can calculate the input as follows:

103 - 8 = 95 (for g) => 95 (_) 111 - 3 = 108 (for o) => 108 (l) 116 - 5 = 111 (for t) => 111 (o) 101 + 7 = 108 (for e) => 108 (l) 109 + 16 = 125 (for m) => 125 (})

So we know that at the end of the execution of our little code the last 5 cells should contains _lol} or 95 108 111 108 125.

It kinda looks like the end of a flag. Since we need to provide 39 inputs, then execute 34 IND commands and we are interested in the last 5 cells we can deduce that the first 34 inputs are probably the moO commands which are used to move the pointer to the right. Its the command number 2 so using the same logic as before we can deduce the following inputs:

2 + 96 = 98 (b) 2 + 119 = 121 (y) 2 + 115 = 117 (u) 2 + 97 = 99 (c) 2 + 114 = 116 (t) 2 + 100 = 102 (f) 2 + 121 = 123 ({) ….

Continuing this logic, we can deduce the first 34 inputs as follows: byuctf{moo_mooo_moooo_mooooooo_moo

We can then combine this with the last 5 inputs to get the final input: byuctf{moo_mooo_moooo_mooooooo_moo_lol}