pogn

solved by hartmannsyg

This is a pong game against a “perfect” AI.

I originally though of trying to get the ball velocity to be very high but the code more or less restricts that from happening. Instead, we had to make the ball position NaN

server.js
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// check if there has been a winner
// server wins
if (ball[0] < 0) {
ws.send(JSON.stringify([ Msg.GAME_END, 'oh no you have lost, have you considered getting better' ])); clearInterval(interval); // game still happening
} else if (ball[0] < 100) {
ws.send(JSON.stringify([ Msg.GAME_UPDATE, [ball, me] ])); // user wins } else { ws.send(JSON.stringify([ Msg.GAME_END, 'omg u won, i guess you considered getting better ' + 'here is a flag: ' + flag, [ball, me] ])); clearInterval(interval); }

We have to fail both the ball[0] < 0 and ball[0] < 100 check. But since we can’t use an integer, we use NaN:

NaN < 0 == False
NaN < 100 == False

Another thing about NaN is that any mathematical operations on NaN gives NaN:

NaN + 1 = NaN
NaN - 1 = NaN
NaN * 2 = NaN
NaN / 2 = NaN
NaN ** 2 = NaN

We see how we can modify ball[0] such that it becomes NaN

server.js
71
72
73
// update ball position
ball[0] += ballV[0] * dt;
ball[1] += ballV[1] * dt;

we see that if we want ball[0] is NaN, ballV[0] has to be NaN. We see that ballV is modified on collisions:

server.js
61
62
63
64
// collision with user's paddle
if (norm(sub(op, ball)) < collisionDist) {
  ballV = add(opV, mul(normalize(sub(ball, op)), 1 / norm(ballV)));
}

we can control opV from our input paddleV:

server.js
117
opV = mul(normalize(paddleV), 2);
server.js
41
42
const norm = ([x, y]) => Math.sqrt(x ** 2 + y ** 2);
const normalize = (v) => mul(v, 1 / norm(v));

If we get v = [0, 0], norm(v) = 0, and 1/norm(v) will be NaN, causing opV to also be NaN, which cascades down all the way to ball[0] being NaN and getting us the flag.

solve.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const { WebSocket } = require('ws');

const ws = new WebSocket('ws://pogn.chall.lac.tf/ws');

ws.on('error', console.error);
ws.on('open', function open() {});
let ballPos

ws.on('message', function message(data) {
  const o = JSON.parse(data.toString());
  const type = o[0]
  if (type == 2) {
      console.log(o)
      return console.log('game ended')
  }
  ballPos = o[1][0]
  // console.log(ballPos)
  serverPos = o[1][1]
});

let i = 0

setInterval(() => {
  if (!ballPos) return
  i++;
  let ourPos = [50, ballPos[1]]
  let v = [0, 0]
  ws.send(JSON.stringify([1, [ourPos, v]]));
}, 50);
[
  2,
  'omg u won, i guess you considered getting better here is a flag: lactf{7_supp0s3_y0u_g0t_b3773r_NaNaNaN}',
  [ [ null, null ], [ 95, 0 ] ]
]