티스토리 뷰

728x90
반응형

문제 개요

JSON 퀴즈를 풀으라고 합니다...

다 풀면 flag 가 나오는 형식인 것 같은데 문제 수가 꽤나 많습니다. 그리고 영어로 되어 있어서 더더욱 풀기가 싫었습니다. 얼른 후다닥 치트키 쓰고 끝내고 싶었던 첫 문제였습니다...😂

 

문제 풀이

function generate() {
    let template = (q, num) => `
    <div class="mt-4 question" style="display: none" id="q${num}">
        <h4>Question ${num + 1}:</h4>
        <h5>${q.question}</h5>
        <div>
            ${q.answers.map((a, i) => `
            <div class="form-check">
                <input class="form-check-input" type="radio" id="q${num}-a${i}" name="q${num}">
                <label class="form-check-label" for="q${num}-a${i}">${a}</label>
            </div>
            `).join("\n")}
        </div>
        ${num !== 14 ? (
            `<button class="btn btn-primary mt-2" type="button" onclick="next()">Next →</button>`
        ) : (
            `<button class="btn btn-info mt-2" type="button" onclick="finish()">Finish →</button>`
        )}
    </div>`;

    return quiz.map(template).join("\n"); 
}

보니깐 문제는 총 15개로 보이고 마지막 문제를 풀었을 때 Finish 버튼이 나오고 버튼을 누르면 finish() 함수가 호출되는 것으로 보입니다.

function finish() {
    $("#q" + num).classList = "mt-4 question animate__animated animate__fadeOutLeft";
    $("#q" + num + " button").onclick = null;

    const responses = Array.from(document.querySelectorAll(".question"))
        .map(q => [
            q.children[1].innerText,
            Array.from(q.children[2].querySelectorAll("input"))
                .filter(a => a.checked)[0]?.nextElementSibling?.innerText || "???"
        ]);
    console.log(responses);
    
    // TODO: implement scoring somehow
    // kinda lazy, ill figure this out some other time

    setTimeout(() => {
        let score = 0;
        fetch("/submit", {
            method: "POST",
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            },
            body: "score=" + score
        })
        .then(r => r.json())
        .then(j => {
            if (j.pass) {
                $("#reward").innerText = j.flag;
                $("#pass").style.display = "block";
            }
            else {
                $("#fail").style.display = "block";
            }
        });
    }, 1250);
}

그리고 /submit URL 경로로 POST 방식으로 score 파라미터를 전송하고 있는데, 단순히 0 값으로 초기화해서 보내고 있는 것을 알 수 있습니다. Client 단 코드를 보면 문제 1개당 배점이 별도로 존재하고 있지는 않는 것 같아서 그냥 문제 개수대로 score 를 15로 설정하고 전송해보았습니다.

fetch("/submit", {
    method: "POST",
    headers: {
        "Content-Type": "application/x-www-form-urlencoded"
    },
    body: "score=" + 15
}).then(r=>r.text()).then(r=>console.log(r));

위 코드는 제가 문제 풀 당시에 실제로 사용한 페이로드입니다. 실행 결과를 보면 아래와 같이 Flag 가 출력되는 것을 볼 수 있었습니다.

 

본 문제는 제일 쉬운 문제에 해당되며, score 값을 Client 단에서만 검증하기에 생긴 취약점에 대한 이해를 위해 만들어진 것 같습니다. 실제로 모의해킹을 하면서 Business Logic Bug로도 많이 발생하는 취약점입니다.

 

- 끝 -

728x90
반응형
댓글