본문 바로가기
드림핵

[드림핵] funjs 풀이

by jwcs 2024. 2. 11.
728x90

https://dreamhack.io/wargame/challenges/116

 

funjs

Description 입력 폼에 데이터를 입력하여 맞으면 플래그, 틀리면 NOP !을 출력하는 HTML 페이지입니다. main 함수를 분석하여 올바른 입력 값을 찾아보세요 !

dreamhack.io

/

제공하는 파일에 접속하면 위와 같은 입력창이 움직인다.

 

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);
    }

메인 함수 소스 코드다. 문제에서 요구한 대로 메인 함수를 분석해보자.

 

var flag = document[_0x374fd6(0x183)](_0x374fd6(0x182))['value']

`document[_0x374fd6(0x183)]`에서 `_0x374fd6(0x183)`이 그냥 16진수인 것처럼 보이지만 앞에 `_`가 붙어있다. 따라서 변수이다. 이 변수를 따라가보면 

var _0x374fd6 = _0x376c;

`0x376c`를 우리가 찾는 변수에 대입하고있다. 그럼 `0x376c`를 찾아가보자.

var _0x376c = function(_0xed94a5, _0xba8f0f) {
            _0xed94a5 = _0xed94a5 - 0x175;
            var _0x1046bc = _0x1046[_0xed94a5];
            return _0x1046bc;
        };

`_0x376c`는 함수로 정의되어 있다. 매개변수를 2개의 인수를 받아야하는 것처럼 보이지만 2번째 매개변수는 실제로 사용하지 않는다.

 첫 번째 매개변수 값에 `0x175`를 뺀다. 우리는 `0x183`을 입력하기 때문에 이 값을 10진수로 나타내면 14다. `_0x1046[14]`를 찾는데, 그냥 찾아선 안된다.

(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));

여기서 push 및 shift를 통해 순서가 바뀌고 있다. 이걸 직접 계산하는 것보다 쉬운 방법으로 가보겠다.

개발자 도구 - 소스탭

브레이크 포인트로 쉽게 찾을 수 있다. 그 전에 debugger 부분이 방해가 되서 주석처리 해줬다.

배열 확인

`submit` 버튼을 누르면 브레이크 포인트가 작동하는 것을 볼 수 있다. 여기서 우리가 찾는 `_0x1046[]`의 값들을 확인할 수 있다. `document[_0x374fd6(0x183)](_0x374fd6(0x182))['value']` -> `document['getElementById']('flag')['value']`인 것을 알 수 있다.

<body>
        <div id='formbox'>
            <h2>Find FLAG !</h2>
            <input type='flag' id='flag' value=''>
            <input type='button' value='submit' onclick='main()'>
        </div>
    </body>

id값이 flag인 것은 입력창임을 html코드를 통해 확인할 수 있다.

if (flag[_0x374fd6(0x17c)] != 0x24) {
            text2img(_0x374fd6(0x185));
            return;
        }

flag가 사용된 조건문을 확인해보겠다. flag의 길이가 36이 아니면 NOP!을 띄우는 것같다.

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;
            }
        }

바로 밑에 있는 for문이다. 

dec_1 = [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],
            dec_2 = [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 = [(a, b) => {
                return a + b;
            }, (a, b) => {
                return a - b;
            }, (a, b) => {
                return a * b;
            }, (a, b) => {
                return a ^ b;
            }],
        if (flag[_0x374fd6(0x17c)] != 0x24) {
            text2img(_0x374fd6(0x185));
            return;
        }
        for (var i = 0x0; i < flag['length']; i++) {
            if (flag['charCodeAt'](i) == operator[i % operator['length']](dec_1[i], dec_2[i])) {} else {
                text2img('NOP\x20!');
                return;
            }
        }
        text2img(flag);

보기 좋게 좀 변형하고 operator에 대한 부분도 좀 보기 좋게 만들어봤다. for문에 들어있는 조건문을 해석해보면 우리가 입력하는 i번째 문자의 유니코드 값과 operator에서 수행되는 i번째 값이 다를 때 `NOP!`을 리턴하고, 전부 같을 경우 flag를 출력하는 것으로 보인다. operator는 dec_1 배열과 dec_2 배열의 i번째 값이 연산에 사용되며, `+`, `-`, `*`, `^` 연산이 순서대로 사용되는 것을 알 수 있다.

 

이걸 계산해주면 flag를 얻을 수 있다.

 

flag

짜잔

728x90
반응형

'드림핵' 카테고리의 다른 글

[드림핵][wargame.kr] md5 password 풀이  (0) 2024.02.23
[드림핵] web-deserialize-python 풀이  (1) 2024.02.12
[드림핵] crawling 풀이  (0) 2024.02.11
[드림핵] weblog-1 풀이  (0) 2024.02.10
[드림핵] file-csp-1 풀이  (2) 2024.02.05