Web Hacking

[WebHacking] - Dreamhack Carve Party 문제 풀이

Papya_j 2024. 8. 18. 23:26

따로 링크는 없고 문제 파일을 다운로드 받으면 html 파일이 하나 나온다. 그걸 실행시키면 

 

이런 웹사이트가 등장한다.

 

10000번 클릭정도면 해볼만 하다.

 

[Write Up]

해당 웹사이트에서 f12를 누른 후 script 구문을 살펴보겠다.

var pumpkin = [ 124, 112, 59, 73, 167, 100, 105, 75, 59, 23, 16, 181, 165, 104, 43, 49, 118, 71, 112, 169, 43, 53 ];
var counter = 0;
var pie = 1;

function make() {
  if (0 < counter && counter <= 1000) {
    $('#jack-nose').css('opacity', (counter) + '%');
  }
  else if (1000 < counter && counter <= 3000) {
    $('#jack-left').css('opacity', (counter - 1000) / 2 + '%');
  }
  else if (3000 < counter && counter <= 5000) {
    $('#jack-right').css('opacity', (counter - 3000) / 2 + '%');
  }
  else if (5000 < counter && counter <= 10000) {
    $('#jack-mouth').css('opacity', (counter - 5000) / 5 + '%');
  }

  if (10000 < counter) {
    $('#jack-target').addClass('tada');
    var ctx = document.querySelector("canvas").getContext("2d"),
    dashLen = 220, dashOffset = dashLen, speed = 20,
    txt = pumpkin.map(x=>String.fromCharCode(x)).join(''), x = 30, i = 0;

    ctx.font = "50px Comic Sans MS, cursive, TSCu_Comic, sans-serif"; 
    ctx.lineWidth = 5; ctx.lineJoin = "round"; ctx.globalAlpha = 2/3;
    ctx.strokeStyle = ctx.fillStyle = "#1f2f90";

    (function loop() {
      ctx.clearRect(x, 0, 60, 150);
      ctx.setLineDash([dashLen - dashOffset, dashOffset - speed]); // create a long dash mask
      dashOffset -= speed;                                         // reduce dash length
      ctx.strokeText(txt[i], x, 90);                               // stroke letter

      if (dashOffset > 0) requestAnimationFrame(loop);             // animate
      else {
        ctx.fillText(txt[i], x, 90);                               // fill final letter
        dashOffset = dashLen;                                      // prep next char
        x += ctx.measureText(txt[i++]).width + ctx.lineWidth * Math.random();
        ctx.setTransform(1, 0, 0, 1, 0, 3 * Math.random());        // random y-delta
        ctx.rotate(Math.random() * 0.005);                         // random rotation
        if (i < txt.length) requestAnimationFrame(loop);
      }
    })();
  }
  else {
    $('#clicks').text(10000 - counter);
  }
}

$(function() {
  $('#jack-target').click(function () {
    counter += 1;
    if (counter <= 10000 && counter % 100 == 0) {
      for (var i = 0; i < pumpkin.length; i++) {
        pumpkin[i] ^= pie;
        pie = ((pie ^ 0xff) + (i * 10)) & 0xff;
      }
    }
    make();
  });
});

make에 대한 function이 있고 그 밑에는 기본 function 수행 이후 make를 통해 타고 들어간다.

 

밑 function에서 counter += 1; 부분이 보인다. 이걸 건들여서 1000으로 만들면 10번안에 될 것 같다고 생각했다.

-> 그 결과 실패다. 뭔가 다른 루틴이 존재하는 것 같다. 면밀히 살펴봐야겠다. + 클릭시 호박의 모양이 점점 변해가는 것을 인지했다. 어떤 특정 루틴이 존재하는 것 같다.


코드를 상세히 분석해보겠다.

var pumpkin = [ 124, 112, 59, 73, 167, 100, 105, 75, 59, 23, 16, 181, 165, 104, 43, 49, 118, 71, 112, 169, 43, 53 ];
var counter = 0;
var pie = 1;
  • pumpkin : 정수 배열, 배열의 값이 조작되어 텍스트 메세지를 표현하게 된다.
  • counter : 클릭 횟수 저장 변수, 초기값 0
  • pie : 암호화/복호화에 사용되는 변수, 초기값 1
$(function() {
  $('#jack-target').click(function () {
    counter += 1;
    if (counter <= 10000 && counter % 100 == 0) {
      for (var i = 0; i < pumpkin.length; i++) {
        pumpkin[i] ^= pie;
        pie = ((pie ^ 0xff) + (i * 10)) & 0xff;
      }
    }
    make();
  });
});

메인 실행 함수

  • #jack-target : 클릭 할 때
  • counter가 +1 씩 올라간다.
  • counter가 10000 이하고, 1000의 배수일 때마다 for문의 pumkin 배열이 수정된다.
  • pumpkin[i] ^= pie  -> pumpkin[i] = pumpkin[i] ^ pie (XOR 연산) 진행을 통해 pumpkin 배열이 수정된다.
  • pie 값이 재구성 된다. (pie XOR 0xff + i*10) AND 0xff /// pie와 111..11 을 XOR (pie의 Bit flip) + i*10 / AND 0xff (1은 1로 0은 0으로로 -> 변화가 없음)
  • 이후 make 함수 실행
function make() {
  if (0 < counter && counter <= 1000) {
    $('#jack-nose').css('opacity', (counter) + '%');
  }
  else if (1000 < counter && counter <= 3000) {
    $('#jack-left').css('opacity', (counter - 1000) / 2 + '%');
  }
  else if (3000 < counter && counter <= 5000) {
    $('#jack-right').css('opacity', (counter - 3000) / 2 + '%');
  }
  else if (5000 < counter && counter <= 10000) {
    $('#jack-mouth').css('opacity', (counter - 5000) / 5 + '%');
  }

  if (10000 < counter) {
    $('#jack-target').addClass('tada');
    var ctx = document.querySelector("canvas").getContext("2d"),
    dashLen = 220, dashOffset = dashLen, speed = 20,
    txt = pumpkin.map(x=>String.fromCharCode(x)).join(''), x = 30, i = 0;

    ctx.font = "50px Comic Sans MS, cursive, TSCu_Comic, sans-serif"; 
    ctx.lineWidth = 5; ctx.lineJoin = "round"; ctx.globalAlpha = 2/3;
    ctx.strokeStyle = ctx.fillStyle = "#1f2f90";

    (function loop() {
      ctx.clearRect(x, 0, 60, 150);
      ctx.setLineDash([dashLen - dashOffset, dashOffset - speed]); // create a long dash mask
      dashOffset -= speed;                                         // reduce dash length
      ctx.strokeText(txt[i], x, 90);                               // stroke letter

      if (dashOffset > 0) requestAnimationFrame(loop);             // animate
      else {
        ctx.fillText(txt[i], x, 90);                               // fill final letter
        dashOffset = dashLen;                                      // prep next char
        x += ctx.measureText(txt[i++]).width + ctx.lineWidth * Math.random();
        ctx.setTransform(1, 0, 0, 1, 0, 3 * Math.random());        // random y-delta
        ctx.rotate(Math.random() * 0.005);                         // random rotation
        if (i < txt.length) requestAnimationFrame(loop);
      }
    })();
  }
  else {
    $('#clicks').text(10000 - counter);
  }
}
  • counter의 횟수에 따라 nose, left, right, mouth의 opacity(투명도)가 100%로 서서히 드러나는 부분
  • counter가 10000을 넘어가면 #jack-target 요소에 tada라는 CSS 클래스를 추가함. 이 후 canas 2D context를 가져와서 텍스트 애니메이션을 수행한다. pumpkin 배열의 숫자들을 문자로 변환한 후 txt를 생성함. ctx에 여러 속성을 통해 캔버스에 그릴 텍스트 스타일을 설정
  • function loop() 부분 / 애니메이션 루프를 실행해서 문자열을 그림
  • counter가 10000 이하면 남은 count 숫자를 표현한다.

코드 분석 결과

-> 카운트가 1씩 늘어나는 것을 10000으로 늘려도 의미가 없다. (pie가 문자열에 해당하는 pumpkin 배열을 변경하기 때문)

-> pie가 변하는 부분이 핵심이다. counter가 100의 배수가 될 때마다 실행되는 동작을 총 100번 해야지 정상적인 텍스트가 출력 된다.

-> 카운트가 올라가는 것을 2이상으로 해봤자 문자열이 순차적으로 나오기 떄문에 결국 매크로를 설정해서 click에 대한 횟수를 자동으로 올려야 될 것 같다.

 

정답

for(i=0; i<=10000; i++){
 $('#jack-target').click()
}

console 창에 위 코드를 입력하면 정상적으로 출력이 된다.

 

DH{I_lik3_pumpk1n_pi3}