Writeup: Locked Temple - upCTF (Reverse)
Challenge
Step on the pressure plates with the perfect 8 plate sequence. If you prove worthy, the door will open and the ultimate golden prize will be revealed to you.
Pressure plate order follows this representation:
Flag format:upCTF{PlateOrder_SECRETDIGIT}
Solution
Initial Analysis
$ file locked_temple
locked_temple: ELF 64-bit LSB pie executable, x86-64, dynamically linked, stripped
The binary is a stripped ELF that links against libSDL2-2.0.so.0. It creates a window titled "Locked Temple" with a 10x8 grid of cells, 4 pressure plates, and a player controlled with WASD keys.
Key Data Structures
From the .data section:
| Address | Content | Purpose |
|---|---|---|
0x4020 | 71 22 90 11 63 74 81 52 | XOR-encoded validation key |
0x4040 | 03 00 00 00 09 00 00 00 | Door grid position (col=3, row=9) |
0x4060 | 4 pairs of (x, y) ints | Plate coordinates: (2,1), (7,1), (2,6), (7,6) |
State Structure
The game state (passed via rdi) is a 16-byte struct:
struct state {
int count; // offset 0x00 - number of plates pressed (0-8)
char seq[8]; // offset 0x04 - recorded plate sequence
int done; // offset 0x0C - set to 1 when correct sequence entered
};
Init Function (0x1bc0) - XOR Decoding
The init function decodes three 8-byte blocks in .data using XOR loops:
Loop 1 (0x4020): key starts at 0, increments by 5
Loop 2 (0x4018): key starts at 0, increments by 8
Loop 3 (0x4010): key starts at 0, increments by 10
Decoded data at 0x4020:
data_orig = [0x71, 0x22, 0x90, 0x11, 0x63, 0x74, 0x81, 0x52]
key = 0
for i in range(8):
data_orig[i] ^= (key & 0xFF)
key += 5
# Result: [0x71, 0x27, 0x9A, 0x1E, 0x77, 0x6D, 0x9F, 0x71]
Plate Press Handler (0x1c40)
When the player steps on a plate, this function records the plate index (0-3) into seq[] and increments count. Once count == 8, it falls through to the validation logic.
Sequence Validation (0x1c68)
The validation computes expected plate values using a chained XOR + conditional rotate scheme:
data = [0x71, 0x27, 0x9A, 0x1E, 0x77, 0x6D, 0x9F, 0x71]
eax = data[0] ^ 0x55 # = 0x24
esi = 0x0B
expected = [eax & 3] # First plate: 0x24 & 3 = 0
for i in range(1, 8):
prev = expected[-1]
eax = (esi ^ 0x55 ^ data[i]) & 0xFF
if prev & 1: # If previous plate was odd
eax = ((eax << 4) | (eax >> 4)) & 0xFF # rol 4
esi += 0x0B
expected.append(eax & 3)
Result: [0, 1, 1, 2, 2, 3, 0, 1] → plate order 01122301
Secret Digit - Golden Prize (0x1b40)
When the correct sequence is entered, function 0x1b40 draws three golden lines (SDL_SetRenderDrawColor(0xFF, 0xD7, 0x00, 0xFF)):
Line 1: (210, 142) → (270, 142) horizontal top
Line 2: (270, 142) → (270, 172) vertical right (upper)
Line 3: (270, 172) → (270, 202) vertical right (lower)
Visualized:
──────┐
│
│
│
This is the digit 7.
Flag
upCTF{01122301_7}