@asanso identified an interesting attack against the download endpoints for our 2FA and SAML recovery codes. We make these recovery codes accessible for download as plaintext and set the content-type of these responses as text/plain. We also set the X-Content-Type-Options: nosniff to prevent browsers from interpreting this content as valid JavaScript or other file types. However, not all browsers currently support this header.

In the case of a browser that does not support the X-Content-Type-Options: nosniff header, the recovery code download endpoints can be sourced as JavaScript files cross-origin. The recovery code download format coincidentally forms a valid JavaScript file with lines in the format of XXXXX-XXXXX, ten hex digits separated by a hyphen. This is interpreted in JavaScript as the subtraction of two variables! Using this format, @asanso demonstrated that each 5 hex digit half of the recovery code could be individually enumerated by defining a valueOf function for the corresponding variable.

While this vulnerability is mitigated in most browsers by the nosniff header and would require a significant amount of effort to brute-force, we still made changes to fix this issue. We modified the affected recovery code endpoints to only respond to POST requests instead of GET requests. With this change, the plaintext reponses can only be trigged with a valid CSRF token, preventing them from being sourced cross-origin. This issue has been fixed in GitHub Enterprise 2.9.4, 2.8.12, and 2.7.16.