[Web] 2018 starctf – simpleweb Write-up

var net = require('net');

flag='fake_flag';

var server = net.createServer(function(socket) {
    socket.on('data', (data) => { 
        //m = data.toString().replace(/[\n\r]*$/, '');
        ok = true;
        arr = data.toString().split(' ');
        arr = arr.map(Number);
        if (arr.length != 5) 
            ok = false;
        arr1 = arr.slice(0);
        arr1.sort();
        for (var i=0; i<4; i++)
            if (arr1[i+1] == arr1[i] || arr[i] < 0 || arr1[i+1] > 127)
                ok = false;
        arr2 = []
        for (var i=0; i<4; i++)
            arr2.push(arr1[i] + arr1[i+1]);
        val = 0;
        for (var i=0; i<4; i++)
            val = val * 0x100 + arr2[i];
        if (val != 0x23332333)
            ok = false;
        if (ok)
            socket.write(flag+'\n');
        else
            socket.write('nope\n');
    });
    //socket.write('Echo server\r\n');
    //socket.pipe(socket);
});

HOST = '0.0.0.0'
PORT = 23333

server.listen(PORT, HOST);

[그림1] 문제 전체 소스코드

[그림1] 문제의 전체 소스코드를 살펴보면 var형 변수를 쓰는 것과 nodejs에서의 TCP 소켓을 구현하는 방식임을 보고 미루어 보았을 때 nodejs로 구현된 TCP 소켓 서버임을 알 수 있다. 유저가 보낸 data를 받아와 ‘ ‘(공백)을 기준으로 split 한 후 배열에 넣어 여러 검증과정과 처리과정을 거친 후 나오는 결과값이 ‘0x23332333’ 인지 확인한 후 맞다면 플래그를 띄운다.

 

        arr = data.toString().split(' ');
        arr = arr.map(Number);
        if (arr.length != 5) 
            ok = false;
        arr1 = arr.slice(0);
        arr1.sort();

[그림2] 데이터 초기 처리과정

 

[그림3] Javascript 에서의 split(), map(), slice(), sort() 동작 과정

 

[그림2]의 초기 데이터 처리과정은 [그림3]과 같이 진행된다. 여기서 주의해야 할 점은 유저의 데이터가 ‘ data.toString().split(‘ ‘) ‘ 을 통해 ‘String’ 으로 형변환이 되며 arr 배열로 들어가기 때문에 뒤의 ‘ arr1.sort() ‘ 코드에서 정렬할 때 ‘int’ 형이 아닌 ‘string’ 형으로 정렬이 된다. 이 말은 곧, 숫자의 크기와 상관 없이 맨 앞글자부터 크기를 비교하기 때문에, [그림3]과 같이 [8, 21, 4, 57, 6]이 배열에 들어갔다면 [21, 4, 57, 6, 8]로 정렬이 되는 것이다. 저는 이걸 헷갈려서 한참을 헤맸다는…

if (arr.length != 5) ok = false;

for (var i=0; i<4; i++) if (arr1[i+1] == arr1[i] || arr[i] < 0 || arr1[i+1] > 127) ok = false;

[그림4] 유저의 input data 검증 과정

 

이후 [그림4]와 같이 배열의 크기가 5가 아니면 ok 플래그 변수에 false를 넣고(유저의 인풋값이 스페이스를 기준으로 나누어 5개의 값만을 넣어야 한다), 인풋값 중 같은 값이 있는 지, 혹은 0보다 작거나 127보다 큰 수가 있는 지를 검사한다(sort() 이후 검증을 하기 때문에 arr1[i+1]==arr1[i] 만으로도 같은 값이 있는 지 검사할 수 있다).

 

        arr2 = []
        for (var i=0; i<4; i++)
            arr2.push(arr1[i] + arr1[i+1]);
        val = 0;
        for (var i=0; i<4; i++)
            val = val * 0x100 + arr2[i];
        if (val != 0x23332333)
            ok = false;
        if (ok)
            socket.write(flag+'\n');
        else
            socket.write('nope\n');

[그림5] 데이터 처리 및 계산, 인증 과정

 

검증 후 arr2 배열을 새로 만들어 arr2에 arr1의 원소를 앞에서부터 두개씩 더하여 넣는다(총 4개의 원소 발생). 이 arr2 배열에 초기값이 0인 상태로 0x100씩 곱하여 원소를 더하고 이를 다시 이용하고 반복하며 이 과정이 끝났을 때 0x23332333′ 이 맞는 지 검사하고 맞다면 플래그를 띄워준다.

 


 

 

[그림6] 문제 조건에 부합하는 배열을 찾기 위한 Python 스크립트

문제 코드에서 나타난 과정을 Python으로 똑같이 구현하여 조건에 부합하는 배열을 찾아낸다. 같은 수가 나오면 안되므로 맨 뒤부터 순차적으로 숫자를 증가시키며 값을 찾아낸다.

 

[그림7] Python 스크립트 실행 결과

[그림8] 인증 결과

 

Flag Get!!!

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다