Ganar 35,000 dólares con un bug bounty de GitHub Private Pages
(robertchen.cc)La historia de un estudiante de último año de preparatoria que, al tener tiempo libre por el Covid, se puso a cazar bug bounties y terminó recibiendo 35,000 dólares por un bug bounty de páginas privadas de GitHub.
Lo reportó como un bug bounty de páginas privadas de GitHub y había dos bonos tipo CTF.
-
10,000 dólares: leer la flag desde
flag.private-org.github.iosin interacción del usuario. Si una cuenta fuera de la organizaciónprivate-orgpuede leer esta flag, hay un bono adicional de 5,000 dólares. -
5,000 dólares: leer la flag desde
flag.private-org.github.iocon interacción del usuario.
Flujo de autenticación
GitHub Pages se aloja en un dominio separado, github.io, por lo que las cookies de autenticación de github.com no se envían al servidor de páginas privadas. Por eso, la autenticación de páginas privadas no puede identificar al usuario sin integración adicional con github.com. Así que GitHub creó un flujo de autenticación personalizado.
-
Al visitar una página privada, el servidor revisa si existe la cookie
__Host-gh_pages_token. -
Si la cookie no existe o no es válida, el servidor de la página privada redirige a
https://github.com/login. -
Esa redirección también establece un nonce en la cookie
__Host-gh_pages_session.- Como esta cookie usa el prefijo
__Host-, evita que JavaScript la establezca desde un dominio distinto al host correspondiente.
- Como esta cookie usa el prefijo
-
/loginredirige a/pages/auth?nonce=&page_id=&path=. -
Ahí se genera una cookie de autenticación temporal que se pasa a
https://pages-auth.github.com/redirecten el parámetrotoken. -
/redirectreenvía ahttps://repo.org.github.io/__/auth. -
Ese endpoint final establece las cookies de autenticación
__Host-gh_pages_tokeny__Host-gh_pages_iden el dominiorepo.org.github.io. -
Ahí también se valida el
noncede__Host-gh_pages_sessionque se había establecido antes.
La ruta de la solicitud original y el ID de la página se guardan en los parámetros de consulta path y page_id, respectivamente, y el nonce también se guarda en el parámetro nonce.
Explotación
Retorno CRLF
-
La primera vulnerabilidad fue una inyección CRLF en el parámetro
page_iddehttps://repo.org.github.io/__/auth. -
Descubrió que el parseo de
page_idignora caracteres en blanco y que este valor se establece directamente en el encabezadoSet-Cookie. -
Se puede romper el parseo con una inyección CRLF tradicional, pero no produce otro impacto.
-
Como el encabezado
Location:se agrega después del encabezadoSet-Cookie, aunque sea una redirección 302, el encabezado Location se ignora y se renderiza el cuerpo.
Ataque
-
Al revisar el código de GitHub Enterprise, descubrió que el servidor de páginas privadas estaba implementado con openresty nginx.
-
Logró XSS agregando un byte nulo. Como ese null byte tiene que ir al inicio del body, no se puede hacer un ataque de inyección de headers.
-
Desde aquí ya era posible ejecutar JavaScript arbitrario en el dominio de la página privada.
-
Ahora solo faltaba encontrar una forma de saltarse el nonce.
Bypass del nonce
-
Observó que páginas privadas hermanas dentro de la misma organización pueden establecerse cookies entre sí.
-
Una cookie establecida en
private-org.github.iose envía aprivate-page.private-org.github.io. -
Si se pudiera evadir la protección del prefijo
__Host-, sería fácil saltarse el nonce. -
No todos los navegadores lo soportan; IE no soporta el prefijo
__Host-. -
Pero, buscando una mejor forma, se le ocurrió una idea interesante.
-
Al revisar cómo manejan mayúsculas y minúsculas las cookies, descubrió que
__HOSTy__Hostse tratan distinto, y que GitHub Private Pages ignora las mayúsculas al parsear cookies. -
Así pudo especificar el nonce desde JavaScript.
-
Eso le dio el bono de 5,000 dólares.
Envenenamiento de caché
-
La respuesta del endpoint
/__/auth?se cachea usando el valor entero delpage_idfalsificado. -
Gracias a eso, si se logra envenenar la caché con el payload XSS, también afecta a usuarios sin interacción.
-
Si el atacante compromete
unprivileged.org.github.iopara contaminar la autenticación, el payload XSS queda cacheado. -
Como las cookies se comparten en el dominio padre
org.github.io, el atacante también puede atacarprivileged.org.github.io.
Páginas privadas públicas
-
Para obtener el bono de 15,000 dólares, necesitaba que este ataque pudiera hacerlo un usuario que no perteneciera a la organización.
-
Esto era posible mediante una configuración incorrecta que dejaba páginas privadas en un repositorio público.
- Se refiere a crear Pages en un repo privado y luego cambiar ese repositorio a público.
-
Las páginas privadas en esta configuración errónea hacen que todos los usuarios pasen por el flujo de autenticación y permiten que usuarios fuera de la organización tengan permisos de lectura.
Aún no hay comentarios.