Introduction

The given tasks gives us only one file:

  • snake

Chall Description:

I have developed a snake game on an Arduino board, but due to some difficulties I couldn’t give everyone this board to experience so I migrated it to a linux program (including its legacy encryption scheme) so everyone can play it widely. Hope you guys will like it :D

This is a rev challenge so the flag has to be found reverse engineering the program. As the program is not stripped its not difficult to understand which are the important functions.

Solution

The code below gives us an overview of what is happening, when the player has more than 49 points the program asks for the license (flag) to the player.

if ((49 < score) && (premium_unlocked == 0)) {
  puts(&DAT_00103168);
  puts("You\'ve reached the free limit of 5 fruits!");
  printf("Enter your premium license key: ");
  tcsetattr(0,0,(termios *)&original_termios);
  printf("\x1b[?25h");
  fflush(stdout);
  keykeykey = "there_is_no_such_free_lunch";
  keybuf[0] = 0;
  keybuf[1] = 0;
  keybuf[2] = 0;
  keybuf[3] = 0;
  input_len = strlen("there_is_no_such_free_lunch");
  memcpy(keybuf,keykeykey,input_len);
  enc_flag =
  "73A1A5796D9483C29AD1580278B022DA55208AD1B1F834CA037C266B1C17AE5268B98191A95BBD7F2F976C64E8BD2D86F7CB17BAF1BDAC3FD322FC7235A32D48"
  ;
  ok = fgets(input_buffer,80,stdin);
  if (ok != (char *)0x0) {
    input_len = strcspn(input_buffer,"\n");
    input_buffer[input_len] = '\0';
    string_to_uint64(input_buffer,input_uint64array,&uint64array_len);
    verify_license(input_uint64array,uint64array_len,keybuf);
    uint64_to_hex(input_uint64array,uint64array_len,hex_array_of_license,161);
    is_ok = compare_cipher(hex_array_of_license,enc_flag);
    if (is_ok == 0) {
      puts(s__Invalid_license_key!_Game_over._001032b0);
      gameover = 1;
      goto LAB_001023c9;
    }
    puts("Congrats! Please continue your game");
    premium_unlocked = 1;
    sleep(2);
  }
  setup_terminal();
}

Looking at the verify_license function we can see that its doing encryption using the key: there_is_no_such_free_lunch.

void verify_license(uint64_t *input_licence,int len_of_license_array,uint64_t *keybuf)
{
  uint e;
  uint p;
  int rounds;
  uint64_t z;
  ulong sum;
  ulong y;

  if (1 < len_of_license_array) {
    rounds = (int)(52 / (long)len_of_license_array) + 6;
    sum = 0;
    z = input_licence[(long)len_of_license_array + -1];
    do {
      sum = sum + 0x9e3779b9;
      e = (uint)(sum >> 2) & 3;
      for (p = 0; p < len_of_license_array - 1U; p = p + 1) {
        y = input_licence[p + 1];
        input_licence[p] =
             input_licence[p] +
             ((y * 4 ^ z >> 5) + (z << 4 ^ y >> 3) ^ (keybuf[(e ^ p) & 3] ^ z) + (y ^ sum));
        z = input_licence[p];
      }
      y = *input_licence;
      input_licence[(long)len_of_license_array + -1] =
           input_licence[(long)len_of_license_array + -1] +
           ((y * 4 ^ z >> 5) + (z << 4 ^ y >> 3) ^ (keybuf[(e ^ p) & 3] ^ z) + (y ^ sum));
      z = input_licence[(long)len_of_license_array + -1];
      rounds = rounds + -1;
    } while (rounds != 0);
  }
  return;
}

From the description we knew that this is from a legacy encryption scheme, simply prompting ChatGPT with the Ghidra code gives a result: xxtea .

And as it can be seen the code is the one used in the challenge with a slight difference, our is using uint64_t.

static uint32_t * xxtea_uint_encrypt(uint32_t * data, size_t len, uint32_t * key) {
    uint32_t n = (uint32_t)len - 1;
    uint32_t z = data[n], y, p, q = 6 + 52 / (n + 1), sum = 0, e;

    if (n < 1) return data;

    while (0 < q--) {
        sum += DELTA;
        e = sum >> 2 & 3;

        for (p = 0; p < n; p++) {
            y = data[p + 1];
            z = data[p] += MX;
        }

        y = data[0];
        z = data[n] += MX;
    }

    return data;
}

Taking into consideration this slight difference the decryption function is this:

def xxtea_uint_decrypt(v, key):
    n = len(v)
    n_minus1 = n - 1

    y = v[0]
    rounds = 6 + (52 // n)
    sum_ = rounds * DELTA

    while sum_ != 0:
        e = (sum_ >> 2) & 3

        for p in range(n_minus1, 0, -1):
            z = v[p-1]
            v[p] = (v[p] - _mx(sum_, y, z, p, e, key)) & 0xffffffffffffffff
            y = v[p]

        z = v[n_minus1]
        v[0] = (v[0] - _mx(sum_, y, z, 0, e, key)) & 0xffffffffffffffff
        y = v[0]
        sum_ = sum_ - DELTA
    return v