XS-Leak101#2
Visited link XS-Leak라는 기법을 알아봅니다.
목차
- Visited link XS-Leak
- 배경 지식
- :visited
- requestAnimationFrame
- pbctf 2021 - vault
- getComputedStyle
- CTFd 1-day
- HTTP status code Leak
- comment
- Reference
Visited link XS-Leak
안녕하세요! Knights of the SPACE의 멤버로 활동하고 있는 임예준(Burnnnnny)입니다.
이번 주제는 XS-Leaks 중 하나인 사용자의 링크 방문 여부를 유출하는 Visited link XS-Leak에 대해 설명하도록 하겠습니다.
참고로 Visited link XS-Leak 기법은 사용자 방문 링크를 유출 할 수 있을 뿐만 아니라
Chromium 브라우저에서 HTTP 200,404 status code 차이 또한 유출 할 수 있습니다.
CTF 문제와 CTFd 1-day를 예시로 Visited link XS-Leak의 원리를 설명하겠습니다.
XS-Leaks라는 기법을 처음 보시는 분은 해당 문서나 XS-Leaks wiki를 읽고 오시면 됩니다.
배경 지식
해당 기법을 이해하기 위한 배경 지식은 <a>
태그의 :visited
CSS 의사클래스와 requestAnimationFrame
함수 입니다.
:visited
:visited
CSS 의사클래스는 사용자가 방문한 링크를 나타냅니다.
:visited
의 반대는 :link
CSS 의사 클래스이며 아직 방문하지 않은 요소를 나타냅니다.
평소 인터넷을 사용하시면 방문했던 링크가 보라색으로 변한 것을 확인할 수 있으실 겁니다.
방문 한 링크는 기본값으로 보라색으로 보여지고
방문 안한 링크는 기본값으로 파란색으로 보여집니다.
1
2
<a href="http://example.com/">visited</a>
<a href="http://example.com/#randomtext">visited X</a>
그리고 방문한 링크에서 방문하지 않은 링크로 <a>
태그의 href
속성값을 바꾸면 보라색에서 파란색으로 변하고 당연하게 반대도 가능합니다.
:visited
가 visited XS-Leak을 이해하는데 가장 중요한 핵심입니다.
그리고 방문한 링크를 visited link, 방문하지 않은 링크를 unvisited link로 정의하도록 하겠습니다.
requestAnimationFrame
requestAnimationFrame
함수는 브라우저에게 수행하기를 원하는 애니메이션을 알리고
다음 리페인트 바로 전에 브라우저가 애니메이션을 업데이트할 지정된 함수를 호출하도록 요청합니다.
이 함수는 리페인트 이전에 호출할 인수로 콜백을 받습니다.
해당 함수는 브라우저 재렌더링 시간을 유출할 때 이용됩니다.
pbctf 2021 - vault
Visited link XS-Leak 기법을 사용해서 풀 수 있는 CTF 문제입니다.
해당 문제의 전체 코드가 궁금하신 분들은 깃허브를 참고해주세요.
해당 문제의 흐름을 간단히 요약해서 설명하자면 봇이 1~32의 선택지가 존재해 그 경로로 갈 수 있습니다.
그리고 랜덤으로 선택지를 14번 반복해서 선택하여 경로로 이동한 뒤에
마지막에 FLAG를 남긴 뒤에 공격자의 URL를 방문합니다.
즉 봇이 이동한 경로를 전부 유출할 수 있다면 FLAG를 구할 수 있습니다!
getComputedStyle
참고로 getComputedStyle
함수로 :visited
<a>
태그의 색깔 CSS값을 가져올 수 있지만
“CSS History Leak” 보안 이슈가 발생하기 때문에 해당 기능이 작동하지 않도록 바뀌어서 실제 값을 얻어 올 수 없습니다.
하지만 위에서 설명한 거처럼 <a>
태그 href
속성값을 변경할 시 <a>
태그 색이 변하기 때문에
태그색이 렌더링 되는 시간을 측정하면 방문한 링크를 유출 할 수 있습니다.
exploit
<a>
태그의 href
속성값을 변경으로 인한 색상 변화에 의해 렌더링 되는데 많은 시간이 걸리게 하여 유출합니다.
해당 문제는 다양한 방법으로 익스플로잇이 가능합니다.
브라우저가 렌더링하는데 오래걸리는 CSS와 긴 중국어로도 익스플로잇이 가능하고
<a>
태그 내에 엄청나게 많은 문자열을 삽입하는 방법도 있습니다.
예) link.innerText = "aa ".repeat(0x10000)
visited link 보라색 → 파란색
unvisited link 파란색 → 파란색
즉 visited link time > unvisited link time 입니다.
결론적으로 원리는 다음과 같습니다:
- visited link에 렌더링 시간이 오래 걸리는 무거운 CSS를 적용합니다.
<a>
태그의href
속성값을 변경합니다.requestAnimationFrame
함수를 사용하여 다음 렌더링 시간을 측정합니다:- 원래 visited link였던 링크가 unvisited link로 변경될 때의 렌더링 시간
- 원래부터 unvisited link였던 링크의 렌더링 시간
- 두 시간을 비교합니다. visited link였던 링크의 렌더링 시간이 더 길 것입니다.
이 시간 차이를 통해 사용자가 특정 링크를 방문했는지 여부를 유출할 수 있습니다.
만약 저자의 익스플로잇 코드가 궁금하신 분은 해당 깃허브를 참고하시면 됩니다.
만약 본인 브라우저에서 해당 기법을 테스트 해보고 싶으신 분들은 issues.chromium.org/issues/40091173에서 attack.html을 다운받고 테스트 해보실 수 있습니다.
결론적으로 해당 공격은 피싱 사이트를 제작하거나 피싱 CAPTCHA를 만들어 사용자 알고리즘을 유출할 수 있고
지역 사이트 방문을 유출하여 거주하고 있는 지역 또한 유출 할 수 있습니다.
CTFd 1-day
XS-Leaks 취약점이 CTF 문제가 아닌 CTFd에서 발견됐습니다.
바로 특정 조건에서 admin 페이지의 쿼리값이 FLAG접두사 일치 여부로
HTTP status code 200,404 응답 차이가 발생해서 FLAG를 유출 할 수 있는 취약점이었습니다.
해당 1-day를 자세히 설명하는 것보단 원리를 설명하겠습니다.
만약 CTFd 1-day의 자세한 내용이 궁금하신 분들은 해당 블로그를 참고해주세요.
HTTP status code Leak
HTTP status code 200,404응답을 유출할 수 있는 방법은 <script>
태그의 onerror/onload
핸들러로
요청의 성공과 실패를 유출하는 방법이 있습니다.
그러나 CTFd 1-day 취약점은 admin 페이지를 공격하는 겁니다.
그리고 Chromium 브라우저에서 2020년 이후 Same-Site Cookie의 기본값이 Lax로 변경되었기 때문에
onerror/onload
핸들러로 교차 사이트에서 HTTP status code를 유출하는 기법은 CTFd에서는 할 수 없습니다.
즉 결론적으로 교차 사이트 요청에서 특정 조건을 제외하곤 쿠키 전송이 안되기 때문에
onerror/onload
핸들러로는 CTFd의 admin 페이지를 교차 사이트에서 HTTP status code를 유출 할 수 없습니다.
그렇다면 어떻게 HTTP status code 200,404응답을 유출할 수 있을까요?
바로 Visited link XS-Leak으로 200,404 응답을 유출할 수 있습니다.
원리를 설명 드리자면 Chromium 브라우저의 경우 200인 응답은 브라우저 히스토리에 저장하는 반면,
404인 응답은 저장하지 않습니다.
예시 코드로 직접 보겠습니다.
404.php
1
2
3
4
<?php
http_response_code(404);
echo "404 Not Found";
?>
200.php
1
2
3
4
<?php
http_response_code(200);
echo "200 OK";
?>
200.php와 404.php 각각 200,404를 응답하는 코드이고 Chromium 브라우저에서 둘 다 요청을 했습니다.
1
2
<a href="200.php">200</a>
<a href="404.php">404</a>
이미지에서 보이는 것처럼 404응답의 경우 브라우저 히스토리 저장을 안하는 모습을 보여주고 있습니다.
즉 200인 응답은 마치 visited link가 되고 404응답은 unvisited link가 됩니다.
그래서 최종적으로 해당 1-day 취약점의 경우 window.open
으로 하위창을 연 뒤 .location
으로 URL을 계속해서 바꾸면서 방문을 하여 200 응답을 브라우저 히스토리에서 저장한 뒤 Visited link XS-Leak으로 HTTP status code를 유출하였습니다.
해당 1-day PoC입니다.
최종적으로 CTFd는 404에러 반환을 막고 Cross-Origin-Opener-Policy
헤더를 설정하여 XS-Leaks을 방어했습니다.
comment
Visited link XS-Leak은 제가 공부한 XS-Leaks중 가장 재밌게 공부한 기법입니다.
사용자가 방문한 링크를 유출 할 수 있다는 점이 정말 흥미로웠고
브라우저에서 자체적으로 막은 방법을 어떻게든 뚫어냈다는 것 또한 배울점이 있었습니다.
그리고 XS-Leaks는 CTF에서만 나오는 기법인 줄 알았던 저에게 리얼월드 사례는 정말 신선했습니다.
저도 언젠간 XS-Leaks 취약점을 CTF나 wargame이 아닌 리얼월드에서 발견해보고 싶습니다.
긴글 읽어 주셔서 감사합니다.
Reference
- XS-Leaks Wiki-CSS Tricks
- pbCTF-2021-challs
- :visited-CSS-MDN
- requestAnimationFrame()-Web method
- Super-Guesser-pbctf 2021 web writeup
- maple3142-pbctf 2021 WriteUps
- issues.chromium.org/issues/40091173
- XS-Leaking flags with CSS: A CTFd 0day
- Retrieving your browsing history through a CAPTCHA
- Side Channel Attack on WWW 2
- CTFd 3.7.2