Skip to content

Commit 6686a1a

Browse files
committed
SekaiCTF 2022 Console Port Pro
1 parent 790c615 commit 6686a1a

File tree

4 files changed

+166
-0
lines changed

4 files changed

+166
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
---
2+
layout: post
3+
title: "[SekaiCTF 2022] Console Port Pro"
4+
author: Robert Xiao
5+
---
6+
7+
## Overview
8+
9+
Console Port is a two-part challenge based around solving an ASCII console port of [Keep Talking and Nobody Explodes](https://keeptalkinggame.com/).
10+
In the game, your goal is to disarm a bomb in a set amount of time. The bomb has several modules which must be solved to win, such as cutting wires, pressing buttons
11+
and entering codes, and an accompanying manual which describes exactly how to solve each module.
12+
13+
![ktane on a terminal, easy mode](/assets/images/sekaictf2022/ktane-easy.png)
14+
15+
In the first part, called Console Port (100 points, 291 solves), you get 3108 ticks running at approximately 10 ticks per second (so, around 5 minutes) to solve a bomb with five modules. With some experience,
16+
it's possible to just solve this manually.
17+
18+
![ktane on a terminal, hard mode](/assets/images/sekaictf2022/ktane-pro.png)
19+
20+
In the second part, called Console Port Pro (498 points, 7 solves), you get 120 ticks, so around 12 seconds. This is not sufficient for human play, so we'll have to automate it.
21+
22+
## Problem Description
23+
24+
### Console Port
25+
26+
- Solves: 291
27+
- Score: 100
28+
- Category: misc
29+
- Difficulty: 1/5
30+
31+
> - Hey Miku, here’s [the manual](https://www.bombmanual.com/). Can you help me port the game to consoles?
32+
> - Sure, no problem.
33+
>
34+
> [ 1 week later... ]
35+
>
36+
> - Hey Miku, how’s the porting going?
37+
> - I just finished it today, wanna take a look?
38+
> - Sure, which console did you port it to?
39+
> - Huh...? What do you mean “which console”?
40+
>
41+
> Author: pamLELcu
42+
43+
### Console Port Pro
44+
45+
- Solves: 7
46+
- Score: 498
47+
- Category: professional programming & coding
48+
- Difficulty: 5/5
49+
50+
> - Hey Miku, since you’ve already done the porting, can you write an AI solving the game for me?
51+
> - Well, it shouldn’t be hard. I’ll give it a try.
52+
> - Cool. Here’s the game you need to solve. I made some changes so that you won’t cheat manually.
53+
> - Hmm...
54+
>
55+
> Author: pamLELcu
56+
57+
## Solution
58+
59+
We don't get source code. The game is written using some kind of text console framework (probably curses) which sends partial screen updates,
60+
meaning we have to maintain a virtual console and do some janky parsing to learn what all the modules are and how to interact with them.
61+
Plus, we have to implement all the horrible logic to solve the modules - which is hardcoding a ton of convoluted rules from the manual.
62+
63+
Or, we could cheat! Playing with the game a bit, I found that just clicking outside of the bomb area (e.g. in the top-left corner) caused
64+
the game timer to pause - probably because it was coded to skip a game tick if an input was detected. So if we just *click the screen repeatedly*
65+
we can freeze time and spend as long as we want defusing the bomb 😂
66+
67+
Since that's hard to do while also trying to solve the puzzle, I wrote a little wrapper script that clicks the screen for me whenever I'm
68+
not interacting with the game:
69+
70+
```python
71+
import socket
72+
import sys
73+
import time
74+
import threading
75+
76+
def setup_tty(fd):
77+
import termios
78+
import atexit
79+
80+
old = termios.tcgetattr(fd)
81+
new = termios.tcgetattr(fd)
82+
new[3] = new[3] & ~termios.ICANON & ~termios.ECHO
83+
termios.tcsetattr(fd, termios.TCSANOW, new)
84+
atexit.register(lambda: termios.tcsetattr(fd, termios.TCSANOW, old))
85+
86+
setup_tty(1)
87+
88+
s = socket.socket()
89+
s.connect(("challs.ctf.sekai.team", 6001))
90+
91+
def recvuntil(suffix):
92+
buf = bytearray()
93+
while 1:
94+
ch = s.recv(1)
95+
sys.stdout.buffer.write(ch)
96+
sys.stdout.buffer.flush()
97+
buf += ch
98+
if buf.endswith(suffix):
99+
break
100+
101+
recvuntil(b"Press any key to start")
102+
s.send(b"x")
103+
recvuntil(b"Defuse the bomb with your mouse.")
104+
105+
has_update = False
106+
def recv_thread():
107+
global has_update
108+
while 1:
109+
sys.stdout.buffer.write(s.recv(1))
110+
has_update = True
111+
sys.stdout.flush()
112+
113+
do_idle = True
114+
def idle_clicker():
115+
while 1:
116+
if do_idle:
117+
s.send(b"\x1b[<0;11;4M\x1b[<0;11;4m")
118+
time.sleep(0.1)
119+
120+
threading.Thread(target=recv_thread, daemon=True).start()
121+
threading.Thread(target=idle_clicker, daemon=True).start()
122+
123+
def read_input():
124+
ch = sys.stdin.buffer.read(1)
125+
if ch != b"\x1b":
126+
return ch
127+
128+
buf = bytearray(ch)
129+
while 1:
130+
ch = sys.stdin.buffer.read(1)
131+
buf += ch
132+
if 0x40 <= ch[0] <= 0x7e and ch[0] != ord(b"["):
133+
break
134+
return buf
135+
136+
while 1:
137+
inp = read_input()
138+
if inp.startswith(b"\x1b[<"):
139+
if inp.endswith(b"M"):
140+
do_idle = False
141+
s.send(inp)
142+
elif inp.endswith(b"m"):
143+
has_update = False
144+
s.send(inp)
145+
while not has_update:
146+
time.sleep(0.01)
147+
do_idle = True
148+
s.send(b"\x1b[<0;11;4M\x1b[<0;11;4m")
149+
else:
150+
s.send(inp)
151+
else:
152+
s.send(inp)
153+
```
154+
155+
This clicks somewhere in the top-left corner 10 times per second (matching the game's tick rate). With this wrapper,
156+
we can take as long as we want in solving the bomb. I specifically restarted the game until there was no Button module,
157+
as the Button module usually requires holding down the button until the time reaches a certain point, which might take
158+
too long. It only takes a few tries to find a suitable game.
159+
160+
After solving the bomb by hand, we get a flag. Note that the displayed flag was slightly wrong (a space should be a `_`).
161+
162+
![ktane on a terminal, solved](/assets/images/sekaictf2022/ktane-solved.png)
163+
164+
```
165+
SEKAI{ANSI?xterm?VT100?idk`\_(''/)_/`}
166+
```
432 KB
Loading
443 KB
Loading
485 KB
Loading

0 commit comments

Comments
 (0)