Fascination
article thumbnail
Published 2022. 1. 31. 03:16
[Dreamhack] funjs War Game & CTF/Dreamhack

funjs

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


# 접속

문제를 열어보니 입력 폼이 요기조기 돌아다닌다

 

아무 값이나 입력해보니 문제 설명대로 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>

위와 같은 소스코드를 확인할 수 있었다

text2img가 인자로 받은 값을 사용해 플래그를 출력해주는 역할을 하는 함수같았고,

_0x1046[] 리스트 안에 함수 이름들이 중간중간 보인다

 

 

# Exploit

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

플래그 생성과 직접적으로 연관이있는 부분같아 분석을 해보려고한다

우선 if문을 통해 flag[_0x376fd6(0x17c)]가 0x24 즉, 36과 같지 않으면 return; 구문때문에 그냥 종료되어 버린다

flag[_0x376fd6(0x17c)]가 무엇인지 알아봐야할 것 같다

 

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

main 윗부분을 살펴보니 _0x374fd6은 _0x376c와 같다

그리고 그 위를 보니 _0x376c는 어떠한 연산을 하여 값을 리턴하는 함수이다

_0x376fd6(0x17c) = _0x376(0x17c)

라고 할 수 있고 _0x376에 맞는 연산을 진행해보면

_0x376(0x17c) = _0x1046[0x17c-0x175]

라고 쓸 수 있다

복잡한 과정이지만 수를 자리에 맞추어 잘 대입하면 return 값을 알 수 있었다

0x17c-0x175 = 7이므로 최종적으로

flag[_0x376fd6(0x17c)] = flag[_0x1046(7)]

임을 알 수 있다

 

_0x1046[] 리스트는 function()에 의해 연산을 수행한다 (parseInt, 'push', 'shift' 등이 보이기 때문)

그래서 최종적인 index에 맞는 값을 확인하고자 54번째 flag에 데이터가 삽입되기 전 부분에

breakpoint를 걸고 배열에 저장된 값을 확인해 보았다

우리가 찾아야하는 7번째에는 "length"가 저장되어 있다

flag[_0x376fd6(0x17c)] = flag['length']

라는 것을 알 수 있다

즉, flag의 길이가 36이 되어야 한다는 조건을 파악할 수 있다

 

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

flag 출력을 위한 조건을 다시 가져와봤다

flag의 길이는 36이며 for문의 if문 조건을 통과한 후 text2img를 통해 flag를 출력하고 있기 때문에

flag 변수에 담기는 값이 무엇인지 알아내야 할 것 같다

 

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

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

이므로 이를 아까와 비슷한 방법으로 값을 계산하여 수식을 찾아내야할 것 같다

 

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

_0x374fd6(0x183) = _0x376c(0x183) = _0x1046[0x183-0x175]

_0x374fd6(0x182) = _0x376c(0x182) = _0x1046[0x182-0x175]

라고 바꿔 쓸 수 있다

 

아까 찾아둔 인덱스 결과를 바탕으로 정리해보면

_0x374fd6(0x183) = 'getElementById'

_0x374fd6(0x182) = 'flag'

임을 알 수 있고 따라서

flag = document.getElementById('flag').value

임을 알 수 있게 된다

 

즉, flag에는 사용자가 입력한 문자열이 들어간다

(input 태그의 id가 flag이기 때문에 입력 박스에 입력된 값을 가져오기 때문이다!)

 

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

다시 flag 출력 조건을 살펴보았다

if문에 flag[_0x374fd6(0x176)]이 보이는데 위에서 했던 방법과 동일하게 변경해보면

flag.charCodeAt(i)라고 쓸 수 있다

즉 입력받은 값을 한자리씩 operator 연산을 한 값과 비교해 같으면

for문을 무사히 빠져나와 flag를 출력하는 것 같다

 

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

charCodeAt이 쓰여 반대로 operator에 fromCharCode를 사용하여 flag를 구할 수 있을 것 같아

위와 같이 식을 세워보았다

 

처음으로 분석을 시작한 if문에 breakpoint를 걸고

콘솔창에서 세운 식을 실행시키니 flag를 획득할 수 있었다 !

 

 

이번 문제는 코드를 하나하나 읽어 나가는 과정이 복잡했던 것 같다..

자바스크립트를 제대로 공부하지 않아 처음에는 각 변수가 16진수로 되어있어 

이게 무엇을 의미하는건지 많이 헷갈렸는데 그냥 '_'가 붙어 있으면 변수명이라는 것을 알고

a, b 이런식으로 치환해서 생각하니 흐름을 파악할 수 있었다 :)

겁먹지 말기 💛💛💛

 

'War Game & CTF > Dreamhack' 카테고리의 다른 글

[Dreamhack] xss-2  (0) 2022.02.12
[Dreamhack] xss-1  (0) 2022.02.11
[Dreamhack] session-basic  (0) 2022.01.24
[Dreamhack] cookie  (0) 2022.01.24
[Dreamhack] Carve Party  (0) 2022.01.23
profile

Fascination

@euna-319

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!