Story line

Moscow - Streets

The person drives into a narrow back alley and despite the danger you try to continue on and give chase. It is impossible to see who they are, clothed all in black and a helmet covering the face. You need to intercept them somehow.

Challenge: High Speed Chase (misc)

You chase them through city streets until you reach the high way. The traffic is pretty rough for a car and you see them gaining ground - should have hotwired a motorbike as well! Too late for that. You look around your car to spot anything useful, and you notice this is actually one of the new self driving cars. You turn on the autopilot, pull out your laptop, connect it to the system, and enter the not-so-hidden developer’s mode. It’s time to re-program the autopilot to be a bit more useful in a chase! To make it easier, you replace the in-car LiDAR feed with a feed from an overhead sattelite - you also display it on the the entertainment system. Now all that’s left to do, is to write a better controlCar function!

After solving

You’re closing in on the motorcycle, but before you have time to act, the person turns to a small path, which is impossible to follow by car. You will never see them again, but wait… They dropped something, a small bag! You look inside of it, and you see what looks to be like an ancient amulet. You return to AGENT X and she tells you that the amulet can be a lead, and that you should return to the base to begin some research.

https://high-speed-chase-web.2021.ctfcompetition.com/

Recon

Upon opening the website, we’re greeted with a code editor and some text:

Car Self-Driving Interface

You need to re-implement the controlCar function.

To implement it in JavaScript use the editor on the left.

When implemented, controlCar function will be called several times per second during the chase to allow for course > corrections.

The controlCar function takes a single parameter – scanArray – which is an array containing 17 integers denoting distance > from your car to the nearest obstacle:

  • [indexes 0-7]: on the left side of the car (index 7 is the measurement at the left headlight),
  • [index 8]: at the center of the car,
  • [indexes 9-16]: on the right side of the car (index 9 is the measurement at the right headlight).

See also this image (it’s not precise, but will give you an idea what you are looking at).

Radar

All measurements are parallel to each other.

A negative measurement might appear if the obstacle is very close behind our car.

The controlCar must return an integer denoting where the car should drive:

  • -1 (or any other negative value): drive more to the left,
  • 0: continue straight / straighten up the car,
  • 1 (or any other positive value): drive more to the right.

When opening the source, we also find the following comment:

<!-- Note that this is a PROGRAMMING challenge, not a reverse-engineering
        one. It's way easier to solve it in the intended way (by writing the
        code).
        That said, I'm a sign, not a cop.

        BTW, car sprites are by looneybits on CC0 (found on opengameart.org).
-->

Solving

To solve this challenge, I came up with the following script:

function controlCar(scanArray) {
  if (!window.has_reset_game) {
    window.has_reset_game = true;
    window.car_position = 0;
    window.queue = [];
  }

  if (window.queue.length === 0) {
    let left, center, right;

    if (window.car_position === -1) {
      left = 8;
      center = 12;
      right = 15;
    } else if (window.car_position === 0) {
      left = 4;
      center = 8;
      right = 12;
    } else if (window.car_position === 1) {
      left = 1;
      center = 4;
      right = 8;
    }

    left = scanArray[left];
    center = scanArray[center];
    right = scanArray[right];

    let furthest = Math.max(left, center, right);
    let closest = Math.min(left, center, right);

    console.log(left, center, right);

    let moveInDirection = (direction) => {
      console.log("Going in direction", direction);
      if (direction === window.car_position) {
        window.queue.push(0);
      } else if (direction > window.car_position) {
        window.queue.push(1, 1, 1, 1);
        window.car_position += 1;
      } else if (direction < window.car_position) {
        window.queue.push(-1, -1, -1, -1);
        window.car_position -= 1;
      }
    };

    if (closest < 0) moveInDirection(window.car_position);
    else if (furthest === left) moveInDirection(-1);
    else if (furthest === center) moveInDirection(0);
    else if (furthest === right) moveInDirection(1);
  }

  return window.queue.pop();
}

It starts off by initializing some global variables if the game is running for the first time (or after a restart). After which, it check if there are still moves in the queue. If so, it grabs the last item out of the queue and returns it.

If there are no items left in the queue, it will calculate the best location to be in. It begins by calculating the absolute left, center and right. It does this based on the current car position, as the radar (scanArray) returns relative measurements.

After calculating the distances on every lane, it will find the furthest and closest distances. With first value, it will find the lane with the most space. With the closest distance, it will check if there are any cars besides the car.

But before it will do anything with the values, it defines the function moveInDirection. This function will generate the values that controlCar needs to return to move the car one lane up, one lane down, or for it to stay in the same lane. It is important to note that controlCar needs to return -1 or 1 four times for the car to move up or down one lane respectively.

After defining this function, the script will check weather there is any car besides our car. If so, it will keep the car in its current lane. This logic is good enough in this case, as the cars are always the same amount of distance separated for each other.

If there is no car besides the car, it will check which lane has the most free space, and move one lane in that direction.

It never moves the car more then one lane at a time. This is done because the car might pass other cars in the time it is moving, and the target might not be the best lane anymore.

At last, it returns the last item out of the queue.

Solution

After entering this code and pressing engage, we get the following screen:

Flag

Here we see the flag! It’s CTF{cbe138a2cd7bd97ab726ebd67e3b7126707f3e7f}.