Dreamhack/Web Hacking

[Dream hack] funjs

amoogotomollayo 2022. 2. 8. 17:45

1. 문제 정보

 

2. 문제 파일

더보기

 

Find FLAG ! 창이 계속 움직인다.

 

임의의 값을 넣으니 NOP !을 출력한다. 

 

<html>
    <head>
    <style>*{margin: 0;}</style>
    <script>
    var box;
    window.onload = init;
    function init() {
      box = document.getElementById("formbox");
      setInterval(moveBox,1000);
    }
    function moveBox() {
        box.posX = Math.random() * (window.innerWidth - 64); 
        box.posY = Math.random() * (document.documentElement.scrollHeight - 64); 
        box.style.marginLeft = box.posX + "px";
        box.style.marginTop  = box.posY + "px";
        debugger;
    }

    function text2img(text){
        var imglist = box.getElementsByTagName('img');
        while(imglist.length > 0) {imglist[0].remove();}
        var canvas = document.createElement("canvas");
        canvas.width = 620;
        canvas.height = 80;
        var ctx = canvas.getContext('2d');
        ctx.font = "30px Arial";
        var text = text;
        ctx.fillText(text,10,50);
        var img = document.createElement("img");
        img.src = canvas.toDataURL();
        box.append(img);
    };

    function main(){
        var _0x1046=['2XStRDS','1388249ruyIdZ','length','23461saqTxt','9966Ahatiq','1824773xMtSgK','1918853csBQfH','175TzWLTY','flag','getElementById','94hQzdTH','NOP\x20!','11sVVyAj','37594TRDRWW','charCodeAt','296569AQCpHt','fromCharCode','1aqTvAU'];
        var _0x376c = function(_0xed94a5, _0xba8f0f) {
            _0xed94a5 = _0xed94a5 - 0x175;
            var _0x1046bc = _0x1046[_0xed94a5];
            return _0x1046bc;
        };
        var _0x374fd6 = _0x376c;
        (function(_0x24638d, _0x413a92) {
            var _0x138062 = _0x376c;
            while (!![]) {
                try {
                    var _0x41a76b = -parseInt(_0x138062(0x17f)) + parseInt(_0x138062(0x180)) * -parseInt(_0x138062(0x179)) + -parseInt(_0x138062(0x181)) * -parseInt(_0x138062(0x17e)) + -parseInt(_0x138062(0x17b)) + -parseInt(_0x138062(0x177)) * -parseInt(_0x138062(0x17a)) + -parseInt(_0x138062(0x17d)) * -parseInt(_0x138062(0x186)) + -parseInt(_0x138062(0x175)) * -parseInt(_0x138062(0x184));
                    if (_0x41a76b === _0x413a92) break;
                    else _0x24638d['push'](_0x24638d['shift']());
                } catch (_0x114389) {
                    _0x24638d['push'](_0x24638d['shift']());
                }
            }
        }(_0x1046, 0xf3764));
        var flag = document[_0x374fd6(0x183)](_0x374fd6(0x182))['value'],
            _0x4949 = [0x20, 0x5e, 0x7b, 0xd2, 0x59, 0xb1, 0x34, 0x72, 0x1b, 0x69, 0x61, 0x3c, 0x11, 0x35, 0x65, 0x80, 0x9, 0x9d, 0x9, 0x3d, 0x22, 0x7b, 0x1, 0x9d, 0x59, 0xaa, 0x2, 0x6a, 0x53, 0xa7, 0xb, 0xcd, 0x25, 0xdf, 0x1, 0x9c],
            _0x42931 = [0x24, 0x16, 0x1, 0xb1, 0xd, 0x4d, 0x1, 0x13, 0x1c, 0x32, 0x1, 0xc, 0x20, 0x2, 0x1, 0xe1, 0x2d, 0x6c, 0x6, 0x59, 0x11, 0x17, 0x35, 0xfe, 0xa, 0x7a, 0x32, 0xe, 0x13, 0x6f, 0x5, 0xae, 0xc, 0x7a, 0x61, 0xe1],
            operator = [(_0x3a6862, _0x4b2b8f) => {
                return _0x3a6862 + _0x4b2b8f;
            }, (_0xa50264, _0x1fa25c) => {
                return _0xa50264 - _0x1fa25c;
            }, (_0x3d7732, _0x48e1e0) => {
                return _0x3d7732 * _0x48e1e0;
            }, (_0x32aa3b, _0x53e3ec) => {
                return _0x32aa3b ^ _0x53e3ec;
            }],
            getchar = String[_0x374fd6(0x178)];
        if (flag[_0x374fd6(0x17c)] != 0x24) {
            text2img(_0x374fd6(0x185));
            return;
        }
        for (var i = 0x0; i < flag[_0x374fd6(0x17c)]; i++) {
            if (flag[_0x374fd6(0x176)](i) == operator[i % operator[_0x374fd6(0x17c)]](_0x4949[i], _0x42931[i])) {} else {
                text2img(_0x374fd6(0x185));
                return;
            }
        }
        text2img(flag);
    }
    </script>
    </head>
    <body>
        <div id='formbox'>
            <h2>Find FLAG !</h2>
            <input type='flag' id='flag' value=''>
            <input type='button' value='submit' onclick='main()'>
        </div>
    </body>
</html>

 

 

index.html의 소스코드이다.

3. 문제 힌트

개발자 도구 중 디버깅 기능을 활용하여 각 함수들이 어떻게 동작하는 지 확인

 

4. 문제 해석

더보기

아래 소스 코드는 main() 함수의 일부로 해석을 시작하겠다.

 

main 함수 경우 메인 화면의 submit 버튼을 누르면 동작한다.

var _0x1046=['2XStRDS','1388249ruyIdZ','length','23461saqTxt','9966Ahatiq','1824773xMtSgK','1918853csBQfH','175TzWLTY','flag','getElementById','94hQzdTH','NOP\x20!','11sVVyAj','37594TRDRWW','charCodeAt','296569AQCpHt','fromCharCode','1aqTvAU'];
var _0x376c = function(_0xed94a5, _0xba8f0f) {
    _0xed94a5 = _0xed94a5 - 0x175;
    var _0x1046bc = _0x1046[_0xed94a5];
    return _0x1046bc;
};
var _0x374fd6 = _0x376c;
(function(_0x24638d, _0x413a92) {
    var _0x138062 = _0x376c;
    while (!![]) {
        try {
            var _0x41a76b = -parseInt(_0x138062(0x17f)) + parseInt(_0x138062(0x180)) * -parseInt(_0x138062(0x179)) + -parseInt(_0x138062(0x181)) * -parseInt(_0x138062(0x17e)) + -parseInt(_0x138062(0x17b)) + -parseInt(_0x138062(0x177)) * -parseInt(_0x138062(0x17a)) + -parseInt(_0x138062(0x17d)) * -parseInt(_0x138062(0x186)) + -parseInt(_0x138062(0x175)) * -parseInt(_0x138062(0x184));
            if (_0x41a76b === _0x413a92) break;
            else _0x24638d['push'](_0x24638d['shift']());
        } catch (_0x114389) {
            _0x24638d['push'](_0x24638d['shift']());
        }
    }
}(_0x1046, 0xf3764));

 

_0x1046 배열이 선언되어 있는데 안에 값들을 보면 NOP\x20!, fromCharCode, charCodeAt, flag가 존재하는데 해당 부분이 중요한 부분인 것을 암시한다.

 

_0x376 함수는 두 개의 매개 변수가 존재한다.

_0xed94a5에서 0x175(373)을 뺀 값에 해당하는 숫자를 인덱스로 하여 _0x1046에서 문자열을 가져와 리턴한다.

 

_0x376fd6 변수에 _0x376c 함수를 대입한다.

 

다음 함수는 (function(){})(인자); 이런 형식으로 되어있는데 즉시 실행 함수로 정의되자마자 즉각적으로 실행되는 자바스크립트 함수이다.

 

맨 처음 개발자 도구 소스 탭으로 들어가면 moveBox 함수의 안의 debugger 때문에 계속 멈춘다.

그래서 해당 부분은 Remove from ignore list 기능을 사용해 무시해야 한다.

 

개발자 도구의 디버깅 기능을 활용하여 내부 함수 동작을 확인해보겠다.

 

while 문이 돌아가면서 배열의 값이 push, shift 연산을 하면서 바뀐다.

 

var flag = document[_0x374fd6(0x183)](_0x374fd6(0x182))['value'],
            _0x4949 = [0x20, 0x5e, 0x7b, 0xd2, 0x59, 0xb1, 0x34, 0x72, 0x1b, 0x69, 0x61, 0x3c, 0x11, 0x35, 0x65, 0x80, 0x9, 0x9d, 0x9, 0x3d, 0x22, 0x7b, 0x1, 0x9d, 0x59, 0xaa, 0x2, 0x6a, 0x53, 0xa7, 0xb, 0xcd, 0x25, 0xdf, 0x1, 0x9c],
            _0x42931 = [0x24, 0x16, 0x1, 0xb1, 0xd, 0x4d, 0x1, 0x13, 0x1c, 0x32, 0x1, 0xc, 0x20, 0x2, 0x1, 0xe1, 0x2d, 0x6c, 0x6, 0x59, 0x11, 0x17, 0x35, 0xfe, 0xa, 0x7a, 0x32, 0xe, 0x13, 0x6f, 0x5, 0xae, 0xc, 0x7a, 0x61, 0xe1],
            operator = [(_0x3a6862, _0x4b2b8f) => {
                return _0x3a6862 + _0x4b2b8f;
            }, (_0xa50264, _0x1fa25c) => {
                return _0xa50264 - _0x1fa25c;
            }, (_0x3d7732, _0x48e1e0) => {
                return _0x3d7732 * _0x48e1e0;
            }, (_0x32aa3b, _0x53e3ec) => {
                return _0x32aa3b ^ _0x53e3ec;
            }],
            getchar = String[_0x374fd6(0x178)];
        if (flag[_0x374fd6(0x17c)] != 0x24) {
            text2img(_0x374fd6(0x185));
            return;
        }

 

flag 변수에는 맨 처음 상태에서 변수의 값을 확인해보니 charCodeAt, 37594TRDRWW이 나왔다.

아까 배열에서 getElementById를 봤는데 마지막 부분이 value인 걸 보니 document.getElementById('flag').value 

이렇게 구성돼서 입력값을 가져오는 구문이라고 생각했다.

그래서 아래와 같이 _0x374fd6(0x183) 부분이 "getElementById"이 되도록 반복문을 짰다.

while(_0x376c(0x183) != "getElementById"){
    _0x1046.push(_0x1046.shift());
}

두 개의 16진수들이 들어가 있는 배열이 존재한다.

 

operator 배열에는 두 개의 인자를 받고 인자들의 연산 결과를 반환하는 4가지의 화살표 함수가 들어가 있다.

화살표 함수는 (a, b) => a + b;로 구성되어 있으며 함수를 간결하게 표현하기 위해 만들어진 표현 식이다.

 

getchar = String.fromCharCode 이렇게 나오는 데 아직 어떻게 활용될지는 잘 모르겠다.

 

if 조건은 flag.length가 0x24인지 확인하고 아니면 NOP !을 출력한다.

 

for (var i = 0x0; i < flag[_0x374fd6(0x17c)]; i++) {
    if (flag[_0x374fd6(0x176)](i) == operator[i % operator[_0x374fd6(0x17c)]](_0x4949[i], _0x42931[i])) {} else {
        text2img(_0x374fd6(0x185));
        return;
    }
}
text2img(flag);

마지막 부분이다.

for 문이 나오는데 flag.length만큼 반복된다.

flag.charCodeAt(i)를 통해 flag 문자열에서 하나씩 가져와 아스키 값으로 변환시킨 후 

operator[i % operator[_0x374fd6(0x17c)]](_0x4949[i], _0x42931[i]) 결과 값과 비교해서 다르면 NOP !을 출력하고

맞는다면 다음 문자로 넘어간다.

 

인자로 사용되는 값들은 아까 위에서 본 숫자들이 들어있는 두 개의 배열이다.

 

해당 반복문을 보고 operator[i % operator[_0x374fd6(0x17c)]](_0x4949[i], _0x42931[i])의 리턴 값들을 합치면 FLAG가 나올 거라고 생각하여 함수를 짰다.

 

str = ""
for (var i = 0; i < 0x24; i++){
    str += String.fromCharCode(operator[i % operator[_0x374fd6(0x17c)]](_0x4949[i], _0x42931[i]))
}
str

 

결과는 아래와 같다.

 

5. 참조

!![] 가 트루인 이유 https://velog.io/@cada/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%97%90%EC%84%9C-0%EC%9D%B4-true%EC%9D%B8-%EC%9D%B4%EC%9C%A0

 

자바스크립트에서 ([] == 0)이 true인 이유

본 글은 아래에서 참고하여 작성되었습니다. 내용에 대해 더 자세하게 알고싶으신 분은 아래의 링크를 참고해주세요.Academind - Avoiding Javascript Type Conversion IssuesJavascript MDN - Truthy/Falsy자바스크립

velog.io