cryptofreeeasy

Grecian Battleship

metactf

Task: reverse a PyInstaller-packed 5x5 Battleship game that appears to be a gameplay or RE challenge, but exposes only a fixed AI coordinate script. Solution: extract the hardcoded move list from Python bytecode, reinterpret the 5x5 coordinates as Polybius-square pairs, and decode `POMAGRUNET` into the final flag.

$ ls tags/ techniques/
pyinstaller_unpackingpython_bytecode_reversingclue_driven_cipher_identificationpolybius_square_decodingcoordinate_translation

Grecian Battleship — metactf

Description

Grecian Battleship

English summary: we are given a Linux ELF that launches a small Tkinter Battleship game. At first glance this looks like a reversing or gameplay puzzle, but the actual solution is to recover a hardcoded coordinate sequence and decode it as a classical Greek-themed cipher.

Analysis

The public artifact, ancientbattleship, is a PyInstaller-packed ELF64 binary. After unpacking it, the game logic is visible as embedded Python bytecode, and the disassembly in battleship.dis shows a very normal 5x5 Battleship implementation.

That initially pushes the challenge toward a reverse/game interpretation:

  • the binary is an executable game, not an obvious ciphertext;
  • it uses Tkinter and random ship placement, which suggests we may need to beat or exploit the game;
  • the code contains turn logic, health counters, UI updates, and ship placement routines;
  • there is no obvious flag literal hidden in the bytecode.

However, reversing also shows that the AI is not dynamically playing Battleship. It simply walks a fixed scripted list of coordinates stored in the constructor:

301: 24: 32 BUILD_LIST 0 302: 34 LOAD_CONST (((2, 4), (2, 3), (2, 1), (0, 0), (1, 1), (3, 1), (3, 4), (2, 2), (0, 4), (3, 3))) 303: 36 LIST_EXTEND 1 304: 38 LOAD_FAST (self) 305: 40 STORE_ATTR (move_script)

The ai_turn() routine confirms that script_index just advances through this list in order:

1058: 129: >> 10 LOAD_FAST (self) 1059: 12 LOAD_ATTR (script_index) 1060: 14 LOAD_GLOBAL (len) 1061: 16 LOAD_FAST (self) 1062: 18 LOAD_ATTR (move_script) ... 1067: 130: 26 LOAD_FAST (self) 1068: 28 LOAD_ATTR (move_script) 1069: 30 LOAD_FAST (self) 1070: 32 LOAD_ATTR (script_index) 1071: 34 BINARY_SUBSCR 1072: 36 UNPACK_SEQUENCE 2 1073: 38 STORE_FAST (r) 1074: 40 STORE_FAST (c)

So the extracted coordinate list is:

...

🔒

Permission denied (requires auth)

Sign in to read this free writeup

This writeup is free — just sign in with GitHub to read it.

$ssh [email protected]