[PortSwigger] DOM-based vulnerabilities (1)

Lab: DOM XSS in document.write sink using source location.search

search query tracking 기능에 취약점이 있다고 한다. 검색 받는 쿼리를 가지고 documnet.write 기능을 실행하는 것 같은데 어떤 식으로 공격할 수 있을지 보자.

랩 페이지에 들어가면 블로그가 있고 검색 기능이 있다.

개발자 도구를 통해 확인한 자바스크립트 코드이다. query를 입력받으면 <img src=~>에 쿼리를 넣어서 document.write를 실행한다.

 

documnet.write에 대해서는 아래 블로그를 참고하도록 하자.

이때 입력 받는 쿼리에 대해 아무런 필터링을 거치지 않기에 alert 기능을 실행하도록 XSS 공격이 가능하다!

실제로 jin을 검색하니 query 부분에 jin이 들어간 것을 확인할 수 있었다.

그렇기에 위처럼 img 태그를 닫아주고 svg 태그를 열어서 onload 이벤트로 로드되면 alert 함수가 실행되도록 할 수 있다!

 

검색했더니 alert 함수가 잘 실행되었다.

 

성공~

 

 

Lab: DOM XSS in document.write sink using source location.search inside a select element

이번에는 재고 확인 기능에 취약점이 존재한다고 한다. 다른 부분은 저번 문제와 비슷한 거 같으니 일단 문제를 보도록 하자.

상품 페이지에 들어가서 개발자 도구로 재고 확인 부분을 보았다. 그랬더니 다음과 같은 자바스크립트 코드를 확인할 수 있었다.

var stores = ["London","Paris","Milan"];
var store = (new URLSearchParams(window.location.search)).get('storeId');
document.write('<select name="storeId">');
if(store) {
    document.write('<option selected>'+store+'</option>');
}
for(var i=0;i<stores.length;i++) {
    if(stores[i] === store) {
        continue;
    }
    document.write('<option>'+stores[i]+'</option>');
}
document.write('</select>');

 

GET 메소드로 storeId를 받아서 이를 가지고 document.write를 실행하고 있다! 그리고 store 변수가 들어가는 것이 option 태그와 그 위에 select 태그이므로 각각의 태그를 닫아주고 img 태그와 onerror 이벤트를 가지고 alert 함수를 실행시키면 된다!

💡 product?productId=1&storeId="></option></select><img%20src=1%20onerror=alert(1)>

성공~

 

Lab: DOM XSS in innerHTML sink using source location.search

이번에는 documnet.write를 사용하지 않고 innerHTML을 사용한다고 한다. 일단 보자.

검색 결과를 보여주는 페이지에서 다음과 같은 코드를 찾아냈다.

function doSearchQuery(query) {
    document.getElementById('searchMessage').innerHTML = query;
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
    doSearchQuery(query);
}

 

검색 기능에 query를 받으면 해당 query를 innerHTML로 변환해준다. 따라서 내가 query에 입력하는 값은 HTML 취급을 받게 되는 것이다.

 

이때 사용할 수 있는 것은 img 태그와 onerror 이벤트를 섞어서 사용하는 것이다! HTML로 변환되더라도 img를 불러오는 데 실패하면 onerror 이벤트가 발생하게 되므로 alert 함수를 실행시킬 수 있다!

성공! paylaod는 다음과 같다.

💡 ?search="><img%20src=1%20onerror=alert(1)>

 

 

Lab: DOM XSS in jQuery anchor href attribute sink using location.search source

피드백 제출 페이지에 취약점이 있다고 한다! 이번에는 jQuery를 이용하고 있다. href 속성을 바꿔야 한다고 한다.

일단 jQuery에 대해 간단하게 알아보고 가자. 이는 자바스크립트를 조금 더 깔끔하게 사용할 수 있게 만든 라이브러리다.

피드백 제출 페이지에서 Back 버튼을 확인해보았다.

$(function() {
    $('#backLink').attr("href", (new URLSearchParams(window.location.search)).get('returnPath'));
});

 

jQuery를 사용하여 backLink를 정의하고 있다. url의 returnPath 파라미터로 받는 것 같다.

<div class="is-linkback">
    <a id="backLink" href="/">Back</a>
</div>

 

이렇게 받은 backLink는 html로 되어 위의 코드에 사용된다.

따라서 url을 다음과 같이 입력해주었다.

💡 /feedback?returnPath=javascript:alert(documnet.cookie)

그랬더니 href가 다음과 같이 바뀐 것을 직접 확인할 수 있었다.

 

실제로 back 버튼을 누르면 다음과 같이 alert 함수가 실행되었다.

성공!

 

Lab: DOM XSS in jQuery selector sink using a hashchange event

이번엔 홈페이지에 취약점이 있다고 한다. jQuery의 selector 기능을 이용해야 할 것 같다!

홈페이지에서 블로그 포스트 부분을 보니 취약해 보이는 자바스크립트 코드가 있다.

$(window).on('hashchange', function(){
    var post = $('section.blog-list h2:contains(' + decodeURIComponent(window.location.hash.slice(1)) + ')');
    if (post) post.get(0).scrollIntoView();
});

 

on에 hashchange와 function을 넣음으로써 URL의 해시 부분이 바뀔 때 실행할 함수를 선언한다. hash 부분은 URL에서 # 뒷부분에 해당한다. hash.slice(1)로 #을 떼고, 해당 요소로 스크롤이 조정되도록 만드는 함수이다.

실제로 url을 https://0aca00f303fc4e3f80c32637009a006f.web-security-academy.net/#<img src=x onerror='alert(1)'/>로 만들면 다음과 같이 alert 함수가 실행된다. 이제 이를 exploit 서버에 넘겨줘야 한다.

 

💡 <iframe src="https:///0aca00f303fc4e3f80c32637009a006f.web-security-academy.net/#" onload="this.src+='<img src=x onerror=print()>'"></iframe>

위의 코드를 넘겨주면 이를 받는 victiom은 페이지가 로드되면 onload 때문에 url의 #뒤에 img 태그가 붙고, onerror 때문에 print 함수가 실행된다!

 

Lab: DOM XSS in AngularJS expression with angle brackets and double quotes HTML-encoded

 

Lab: Reflected DOM XSS

이번에는 Reflected DOM 취약점이다. 서버에서 Request의 데이터를 처리하고 해당 데이터를 Response에 담아줄 때 발생할 수 있는 취약점이라고 한다.

jin이라고 검색했을 때 얻은 것이다. Response에서 JSON 형태로 Request의 데이터를 다시 전송해주는 것을 확인할 수 있다.

searchResults.js를 보면 eval 함수를 써서 response의 데이터를 처리하고 있음을 알 수 있다.

위의 페이지에서 볼 수 있듯이 eval 함수는 인자로 받은 코드를 caller의 권한으로 실행하는 함수이기 때문에 사용하지 말라고 하고 있다! 때문에 검색에 alert 함수를 넣으면 이를 실행할 수 있을 것이다.

alert 함수와 , -, /를 함께 넣어줬다. 이렇게 해서 검색하면 {"searchTerm":"\"-alert(1)}//", "results":[]}가 되어서 \뒤의 “로 검색하는 문자열이 닫히게 된다. 그 다음 -을 추가해서 alert와 앞을 구분하고, 중괄호로 JSON을 닫아준 뒤 //로 뒤를 주석처리한 것이다.

성공~

 

Lab: Stored DOM XSS

이번에는 Stored DOM 취약점이다.

예시로 댓글을 달아보았다.

내가 작성한 댓글은 loadCommentsWithVulnerableEscapeHtml.js에서 처리하고 있었다.

function loadComments(postCommentPath) {
    let xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            let comments = JSON.parse(this.responseText);
            displayComments(comments);
        }
    };
    xhr.open("GET", postCommentPath + window.location.search);
    xhr.send();

    function escapeHTML(html) {
        return html.replace('<', '<').replace('>', '>');
    }

    function displayComments(comments) {
        let userComments = document.getElementById("user-comments");

        for (let i = 0; i < comments.length; ++i)
        {
            comment = comments[i];
            let commentSection = document.createElement("section");
            commentSection.setAttribute("class", "comment");

            let firstPElement = document.createElement("p");

            let avatarImgElement = document.createElement("img");
            avatarImgElement.setAttribute("class", "avatar");
            avatarImgElement.setAttribute("src", comment.avatar ? escapeHTML(comment.avatar) : "/resources/images/avatarDefault.svg");

            if (comment.author) {
                if (comment.website) {
                    let websiteElement = document.createElement("a");
                    websiteElement.setAttribute("id", "author");
                    websiteElement.setAttribute("href", comment.website);
                    firstPElement.appendChild(websiteElement)
                }

                let newInnerHtml = firstPElement.innerHTML + escapeHTML(comment.author)
                firstPElement.innerHTML = newInnerHtml
            }

            if (comment.date) {
                let dateObj = new Date(comment.date)
                let month = '' + (dateObj.getMonth() + 1);
                let day = '' + dateObj.getDate();
                let year = dateObj.getFullYear();

                if (month.length < 2)
                    month = '0' + month;
                if (day.length < 2)
                    day = '0' + day;

                dateStr = [day, month, year].join('-');

                let newInnerHtml = firstPElement.innerHTML + " | " + dateStr
                firstPElement.innerHTML = newInnerHtml
            }

            firstPElement.appendChild(avatarImgElement);

            commentSection.appendChild(firstPElement);

            if (comment.body) {
                let commentBodyPElement = document.createElement("p");
                commentBodyPElement.innerHTML = escapeHTML(comment.body);

                commentSection.appendChild(commentBodyPElement);
            }
            commentSection.appendChild(document.createElement("p"));

            userComments.appendChild(commentSection);
        }
    }
};

 

코드를 보면 escapeHTML 함수가 있다. 이는 <과 >를 변환해서 태그 삽입을 막는 것인데, 문제가 있어보인다. 해당 코드에서 escapeHTML 함수를 문자열의 앞에 있는 것만 처리하도록 하였기에 앞에 <>을 넣어주고 뒤에 다시 쓰면 뒤에 넣은 <과 >은 필터링을 피할 수 있는 것이다!

따라서 위와 같이 입력해주었다.

alert 함수가 실행되었다!

확인해보니 다음과 같이 들어가 있었다. 앞의 <>만 코멘트로 들어가고 이미지 태그는 그대로 HTML 태그로 들어가서 onerror 이벤트가 잘 발생한 것이다.

성공~

 

Lab: DOM-based open-redirection

이번에는 redirection 취약점이다. 취약점을 찾아서 exploit server로 redirect 시키면 된다고 한다.

취약점으로 Back to Blog 버튼을 찾을 수 있었다.

💡 <a href="#" onclick="returnUrl = /url=(https?://.+)/.exec(location); location.href = returnUrl ? returnUrl[1] : "/"">Back to Blog</a>

위처럼 해서 버튼을 누루면 onclick 이벤트가 실행되어 처음 화면으로 돌아가는 것이다.

💡 https://0af700ec03ae635681c9891c00c900ba.web-security-academy.net/post?postId=4&url=https://exploit-0a56001c0340634e815b883201e600ea.exploit-server.net#

위처럼 url을 검색함으로써 redirect되는 페이지를 처음 화면이 아닌 exploit server로 바꿀 수 있다. /url이 변경되었기 때문이다.

Back to Blog 버튼 위에 마우스를 올려서 확인해볼 수 있다.

성공~

 

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤