[하루한줄] Github Private Pages XSS 취약점
URL
Breaking GitHub Private Pages for $35k
Target
- github private pages
Explain
Github Private Pages에서 XSS 취약점이 발견되었습니다. 해당 취약점은 github private pages의 custom authentication flow 과정 중 __Host-
prefix를 우회하고 page_id
파라미터를 통해 발생합니다.
github pages는 별도의 github.io
도메인에서 호스팅 되어 github.com
인증 쿠키가 private pages server로 전송되지 않습니다. 따라서 private page authentication은 github.com
과의 추가적인 통합 없이는 사용자 인증할 방법이 없습니다. 이러한 이유로 github은 custom authentication flow를 만들었습니다.
취약점 제보 당시 github private pages의 custom authentication flow는 다음과 같습니다.
- private page에 접속하면 서버는
__Host-gh_pages_token
가 존재하는지 확인합니다. 쿠키가 없거나 잘못 설정된 경우https://github.com/login
으로 리다이렉트 됩니다. initial redirect는__Host-gh_pages_session
쿠키에 nonce를 저장합니다. 해당 쿠키는 __Host- cookie prefix를 사용하여 호스트가 아닌 도메인이 JavaScript에서 설정되는 것을 방지합니다. /login
은/pages/auth?nonce=&page_id=&path=
로 리다이렉트 됩니다. 이 엔드포인트는 임시 인증 토큰을 생성하여token
파라미터에https://pages-auth.github.com/redirect
에 전달합니다.nonce
,page_id
,path
는 비슷하게 전달됩니다./redirect
는https://repo.org.github.io/__/auth
로 바로 이동합니다. 마지막 엔드포인트는repo.org.github.io
도메인,__Host-gh_pages_token
,__Host-gh_pages_id
에 인증 쿠키를 설정합니다. 또한 이전에 설정된__Host-gh_pages_session
의nonce
도 확인합니다.- authentication flow에서 original request path와 page id 같은 정보는
path
와page_id
쿼리 파라미터에 저장됩니다. nonce는nonce
파라미터에서 전달됩니다.
https://repo.org.github.io/__/auth
의 page_id
파라미터는 whitespace를 무시하여 파싱 되고 정수로 변환되어 Set-Cookie
헤더에 렌더링 됩니다. page_id=12345%0d%0a%0d%0a
를 전송하여 Set-Cookie
헤더 뒤에 위치한 Location 헤더를 무시하고 body content를 렌더링할 수 있습니다. 또한 page_id
에서 null byte를 만나면 integer parsing이 종료됩니다. 헤더에 null byte가 존재하면 response가 reject 되므로 body의 시작 부분에 null byte를 삽입하여 XSS를 트리거할 수 있습니다.
"?page_id" + encodeURIComponent("\r\n\r\n\x00<script>alert(origin)</script>")
하지만 nonce가 조작된 page_id
의 전송을 방지하여 이를 우회하여야 합니다. 브라우저에서 쿠키의 대소문자를 다르게 처리하여 __Host-
와 __HOST-
를 다르게 처리합니다. 하지만 github private page server에서는 쿠키를 파싱할 때 대소문자를 무시하므로 __Host-
prefix 우회가 가능합니다. 전체적인 POC는 다음과 같습니다.
<script>
const id = location.search.substring("?id=".length)
document.cookie = "__HOST-gh_pages_session=dea8c624-468f-4c5b-a4e6-9a32fe6b9b15; domain=.private-org.github.io";
location = "https://github.com/pages/auth?nonce=dea8c624-468f-4c5b-a4e6-9a32fe6b9b15&page_id=" + id + "%0d%0a%0d%0a%00<script>alert(origin)%3c%2fscript>&path=Lw";
</script>
본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.