따로 링크는 없고 문제 파일을 다운로드 받으면 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}
'Web Hacking' 카테고리의 다른 글
[WebHacking] - Dreamhack blind-command 문제 풀이 (0) | 2024.08.19 |
---|---|
[WebHacking] - Dreamhack web-ssrf 문제 풀이(SSRF) (0) | 2024.08.18 |
[WebHacking] - Dreamhack : file-download-1 문제 풀이 (File Vulnerability) (0) | 2024.08.18 |
[WebHacking] - Dreamhack image-stroage 문제 풀이 (File Vulnerability) (0) | 2024.08.18 |
[WebHacking] - Dreamhack command-injection-1 문제 풀이 (command injection) (0) | 2024.08.17 |