[CTF Writeup / Web] DiceCTF 2024 - dicedicegoose

 




웹 JS 기반 게임.



W, A, S, D로 이동해서 랜덤하게 1칸씩 움직이는 검은색 블록을 잡으면 된다.

javascript를 보면 win() 함수가 있다.



  function win(history) {
    const code = encode(history) + ";" + prompt("Name?");

    const saveURL = location.origin + "?code=" + code;
    displaywrapper.classList.remove("hidden");

    const score = history.length;

    display.children[1].innerHTML = "Your score was: <b>" + score + "</b>";
    display.children[2].href =
      "https://twitter.com/intent/tweet?text=" +
      encodeURIComponent(
        "Can you beat my score of " + score + " in Dice Dice Goose?",
      ) +
      "&url=" +
      encodeURIComponent(saveURL);

    if (score === 9) log("flag: dice{pr0_duck_gam3r_" + encode(history) + "}");
  }



score는 움직인 칸의 개수다. 9칸만 움직여서 검은 블록을 잡으면 조건이 성립한다. 조건이 참일 때, code(history)의 결과만 알면 flag를 얻을 수 있다.



  function encode(history) {
    const data = new Uint8Array(history.length * 4);

    let idx = 0;
    for (const part of history) {
      data[idx++] = part[0][0];
      data[idx++] = part[0][1];
      data[idx++] = part[1][0];
      data[idx++] = part[1][1];
    }

    let prev = String.fromCharCode.apply(null, data);
    let ret = btoa(prev);
    return ret;
  }



encode() 함수는 위와 같다. history 배열 내 존재하는 값을 flatten 한 결과를 base64로 encoding 한다.


  let history = [];
  history.push([player, goose]);

// (...)

  document.onkeypress = (e) => {

// (...)

    history.push([player, goose]);

    redraw();
  };


history는 위와 같이 [player, goose] 좌표 정보를 포함한다. onkeypress event에서 history에 변화된 위치 정보를 추가한다.

앞서 score가 9라는 조건을 만족해야한다고 했다.
정상적인 플레이라면, 위 조건을 만족하는 경우의 수는 오직 한 가지다.
그 상황에서의 history를 직접 입력하여 계산하면 flag를 얻을 수 있다.
















댓글